/* Triple Tile — the same brass lamp, over a table of ivory. */

:root {
  --ink:        #12100C;
  --parchment:  #F0E4CC;
  --parchment-2:#C3B294;
  --parchment-3:#8B7C64;
  --brass:      #C79A45;
  --brass-2:    #8A6C31;
  --crimson:    #A9342B;
  --verdigris:  #3FA97A;

  --shell: 1px solid color-mix(in srgb, var(--brass) 34%, transparent);

  /* A tile is ivory, not parchment: it has to read as a different material
     from the page it sits on or the table turns into wallpaper. */
  --tile-face:  #EDE2CA;
  --tile-face-2:#D8C9A9;
  --tile-edge:  #A9977A;
  --tile-shadow:#6B5B42;

  /* Written by the renderer once it has measured the space available. These
     are only the fallbacks for the frame before that happens. */
  --tile-w: 40px;
  --tile-h: 53px;
}

*, *::before, *::after { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
  color: var(--parchment);
  background:
    radial-gradient(130% 90% at 50% -20%, #33281D 0%, #1D1710 46%, #0D0A07 100%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
}

[hidden] { display: none !important; }

.grain {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: .3;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

/* -------------------------------------------------------------- layout -- */

.page {
  position: relative;
  z-index: 2;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(8px, 1.6vh, 16px);
  padding: 12px 16px calc(16px + env(safe-area-inset-bottom));
  max-width: 980px;
  margin-inline: auto;
}

/* ----------------------------------------------------------- masthead -- */

.topbar {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px 18px;
}

.masthead {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 16px;
  min-width: 0;
}

.backlink {
  font-size: 10px;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--parchment-3);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: color .2s, border-color .2s;
}
.backlink::before { content: '\27F5\2003'; }
.backlink:hover {
  color: var(--brass);
  border-bottom-color: color-mix(in srgb, var(--brass) 50%, transparent);
}

.title {
  font-family: 'Fraunces', Georgia, serif;
  font-variation-settings: 'SOFT' 50, 'opsz' 120, 'wght' 620;
  font-weight: 620;
  font-size: clamp(1.35rem, 3.2vw, 1.85rem);
  line-height: 1;
  margin: 0;
  letter-spacing: -.015em;
  text-shadow: 0 1px 0 #000, 0 0 34px rgba(199, 154, 69, .18);
}

.stamp {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--brass);
}
.stamp__rule {
  width: 18px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--brass-2), transparent);
}

/* --------------------------------------------------------------- tiers -- */

.tiers {
  display: flex;
  gap: 6px;
  padding: 4px;
  border: var(--shell);
  border-radius: 3px;
  background: rgba(9, 7, 5, .4);
}

.chip {
  font: inherit;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .12em;
  color: var(--parchment-3);
  background: none;
  border: 1px solid transparent;
  border-radius: 2px;
  padding: 7px 13px;
  cursor: pointer;
  transition: color .18s, background .18s, border-color .18s;
}
.chip:hover { color: var(--brass); }
.chip[aria-pressed='true'] {
  color: var(--ink);
  background: var(--brass);
  border-color: var(--brass);
}
.chip:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }

.shelfnote {
  margin: 0;
  font-family: 'Fraunces', Georgia, serif;
  font-variation-settings: 'ital' 1, 'wght' 500;
  font-style: italic;
  font-size: 13px;
  color: var(--parchment-3);
  text-align: center;
}

/* --------------------------------------------------------------- table -- */

/* The table and the bench are two regions of one positioned box, and every
   tile lives in `.tiles` above both of them. That is what lets a tile fly from
   the pile to the bench on a transition instead of being re-parented. */
.table {
  position: relative;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(8px, 1.6vh, 16px);
}

/* No floor on this. The renderer now sizes a tile to fit the whole table, so
   a stack with a minimum height of its own would only be a way of pushing the
   bench off the bottom of a short window. */
.stack {
  flex: 1 1 auto;
  min-height: 0;
}

.tiles {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* --------------------------------------------------------------- tiles -- */

.tile {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--tile-w);
  height: var(--tile-h);
  padding: 0;
  margin: 0;
  display: grid;
  place-items: center;
  pointer-events: auto;
  cursor: default;
  border: 0;
  border-radius: calc(var(--tile-w) * .12);
  background:
    linear-gradient(158deg, #FBF2DF 0%, var(--tile-face) 42%, var(--tile-face-2) 100%);
  /* The right and bottom faces of a real tile, plus the shadow it casts on
     whatever it is sitting on. Both scale with the tile so the illusion
     survives a phone. */
  box-shadow:
    inset calc(var(--tile-w) * -.055) calc(var(--tile-w) * -.055) 0 rgba(122, 104, 74, .32),
    inset calc(var(--tile-w) * .05) calc(var(--tile-w) * .05) 0 rgba(255, 255, 255, .65),
    0 0 0 1px rgba(107, 91, 66, .55),
    calc(var(--tile-w) * .06) calc(var(--tile-w) * .07) calc(var(--tile-w) * .16) rgba(9, 7, 5, .55);
  transition:
    transform .34s cubic-bezier(.25, 1.1, .4, 1),
    scale .28s ease,
    opacity .3s ease,
    filter .2s ease;
  -webkit-tap-highlight-color: transparent;
}

.tile__face {
  display: grid;
  place-items: center;
  width: 72%;
  height: 72%;
  color: var(--hue, #6B5B42);
}
.tile__face svg {
  width: 100%;
  height: 100%;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Engraved rather than printed: a hairline of light under the stroke. */
  filter: drop-shadow(0 1px 0 rgba(255, 255, 255, .6));
}

/* A tile with nothing on top of it is the only thing worth pointing at. The
   rest are dimmed a little rather than greyed out, because a buried tile still
   has to be readable — planning the next four lifts is the whole game. */
.tile:not(.is-free):not(.is-benched) { filter: brightness(.74) saturate(.8); }

.tile.is-free { cursor: pointer; }
.tile.is-free:hover {
  filter: brightness(1.06);
  z-index: 80000 !important;
}
.tile:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }

.tile.is-benched { cursor: default; }

/* Gone: three that agreed. Out and away rather than a plain fade, so it reads
   as something leaving the table rather than the page losing interest.
   `scale` is its own property here on purpose — `transform` belongs to the
   renderer, which is writing a position into it on every paint. */
.tile.is-gone {
  opacity: 0;
  scale: 1.22;
  pointer-events: none;
}

.tiles.is-instant .tile { transition: none !important; }

/* A tap on something buried: the tile shakes its head, and whatever is sitting
   on it lights up, which is quicker than reading the rule. */
.tile.is-refused { animation: refuse .34s ease-out; }
@keyframes refuse {
  0%, 100% { margin-left: 0; }
  25%      { margin-left: -4px; }
  60%      { margin-left: 4px; }
}

/* The flash rides on a pseudo-element: the tile's own box-shadow is carrying
   its two carved faces and cannot be borrowed for a moment without the tile
   going flat while it flashes. */
.tile::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}
.tile.is-pinning::after { animation: pinning .5s ease-out; }
@keyframes pinning {
  0%   { box-shadow: 0 0 0 0 rgba(169, 52, 43, .85); }
  100% { box-shadow: 0 0 0 10px rgba(169, 52, 43, 0); }
}

/* A tile that has just been bought back off the bench. It flies home on its
   own transition like everything else, but it lands among tiles that look
   exactly like it, so it gets one bump to say which one it was. */
.tile.is-back::after { animation: back .6s ease-out; }
@keyframes back {
  0%   { box-shadow: 0 0 0 0 rgba(240, 228, 204, .85); }
  100% { box-shadow: 0 0 0 12px rgba(240, 228, 204, 0); }
}

/* A hint: the three tiles that make a set, ringed in brass and pulsing three
   times. Board and bench alike — a hint is often two you are already holding
   and one to go and fetch, and ringing only half of that would be worse than
   saying nothing. It rides on the same pseudo-element the refusal flash uses,
   and it outlives neither the next tap nor a few seconds. */
.tile.is-hinted { z-index: 70000 !important; }
/* Two rings, because one is not enough of either thing. The steady one on
   ::before is what you actually read — it is still there when you look back
   from the other side of the table — and the pulse on ::after is what catches
   the eye in the first place. A pulse alone spends most of each second faded
   out, which on a board of thirty-six tiles is a game of catch. */
.tile.is-hinted::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow:
    0 0 0 2px var(--brass),
    0 0 14px rgba(199, 154, 69, .6);
}
.tile.is-hinted::after { animation: hinted 1s ease-out 3; }
@keyframes hinted {
  0%   { box-shadow: 0 0 0 0 rgba(199, 154, 69, .95); }
  70%  { box-shadow: 0 0 0 9px rgba(199, 154, 69, 0); }
  100% { box-shadow: 0 0 0 9px rgba(199, 154, 69, 0); }
}

/* --------------------------------------------------------------- bench -- */

/* A tray, not a band: it hugs its six slots rather than stretching the width
   of the page, which on a desktop is most of a metre of empty rule. */
.bench-rail {
  flex: 0 0 auto;
  align-self: center;
  max-width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 12px;
  border: var(--shell);
  border-radius: 3px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .03), rgba(255, 255, 255, 0) 70%),
    rgba(9, 7, 5, .45);
}

.bench {
  display: flex;
  gap: clamp(4px, 1.2vw, 10px);
}

.slot {
  width: var(--tile-w);
  height: var(--tile-h);
  border: 1px dashed rgba(199, 154, 69, .3);
  border-radius: calc(var(--tile-w) * .12);
  background: rgba(0, 0, 0, .22);
  transition: border-color .2s, background .2s;
}
.slot.is-filled { border-color: transparent; background: transparent; }

/* The last slot filling is the only warning the game gives. */
.bench.is-full .slot { border-color: var(--crimson); animation: alarm .9s ease-out; }
@keyframes alarm {
  0%   { background: rgba(169, 52, 43, .5); }
  100% { background: rgba(169, 52, 43, 0); }
}

.bench-rail__note {
  margin: 0;
  font-size: 9px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--parchment-3);
}
.is-lost .bench-rail__note { color: var(--crimson); }

/* The bench's line, when it is answering a hint rather than counting. */
.bench-rail__note.is-saying { color: var(--brass); }

/* ------------------------------------------------------------- console -- */

.console {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 20px;
  padding-top: 10px;
  border-top: 1px solid rgba(199, 154, 69, .16);
}

.counter { margin: 0; display: flex; align-items: baseline; gap: 10px; }
.counter__label {
  font-size: 10px;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--parchment-3);
}
.counter b {
  font-family: 'Fraunces', Georgia, serif;
  font-variation-settings: 'wght' 600;
  font-size: 1.3rem;
  color: var(--parchment);
  font-variant-numeric: tabular-nums;
}
.counter__sub {
  font-size: 10px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--brass-2);
}

.actions { display: flex; gap: 6px; flex-wrap: wrap; margin-left: auto; }

.btn {
  font: inherit;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--parchment-2);
  background: rgba(255, 255, 255, .015);
  border: var(--shell);
  border-radius: 2px;
  padding: 9px 12px;
  cursor: pointer;
  transition: color .18s, background .18s, border-color .18s, transform .1s;
}
.btn:hover:not(:disabled) {
  color: var(--ink);
  background: var(--brass);
  border-color: var(--brass);
}
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .3; cursor: default; }
.btn:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }
.btn[aria-pressed='false'] { color: var(--parchment-3); border-style: dashed; }

/* --------------------------------------------------------------- howto -- */

.howto {
  width: 100%;
  font-size: 12px;
  line-height: 1.65;
  color: var(--parchment-2);
}
.howto summary {
  cursor: pointer;
  list-style: none;
  font-size: 10px;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--parchment-3);
  padding: 4px 0;
  transition: color .2s;
}
.howto summary::-webkit-details-marker { display: none; }
.howto summary::before { content: '\FF0B\2002'; color: var(--brass-2); }
.howto[open] summary::before { content: '\FF0D\2002'; }
.howto summary:hover { color: var(--brass); }
.howto ul { margin: 10px 0; padding-left: 1.1em; }
.howto li { margin-bottom: 5px; }
.howto b { color: var(--parchment); font-weight: 500; }
.howto kbd {
  font: inherit;
  font-size: 11px;
  padding: 1px 5px;
  border: 1px solid rgba(199, 154, 69, .3);
  border-radius: 2px;
  color: var(--parchment);
}
.howto__note {
  color: var(--parchment-3);
  border-left: 1px solid var(--brass-2);
  padding-left: 12px;
  max-width: 62ch;
}

/* ----------------------------------------------------------------- win -- */

.win {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(10, 8, 5, .72);
  backdrop-filter: blur(7px) saturate(.8);
  -webkit-backdrop-filter: blur(7px) saturate(.8);
  animation: fade .4s ease both;
}
.win.is-instant { animation: none; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

.win__card {
  position: relative;
  width: min(400px, 100%);
  text-align: center;
  padding: 46px 30px 30px;
  color: #2A2117;
  border-radius: 3px;
  background:
    radial-gradient(120% 90% at 50% 0%, #FBF2DF 0%, #EFE1C4 60%, #E2D2B2 100%);
  box-shadow:
    0 30px 60px -20px rgba(0, 0, 0, .8),
    inset 0 0 0 1px rgba(255, 255, 255, .5),
    inset 0 0 0 6px rgba(63, 169, 122, .08);
  animation: rise .5s cubic-bezier(.25, 1.2, .4, 1) both;
}
.win.is-instant .win__card { animation: none; }
@keyframes rise {
  from { opacity: 0; transform: translateY(22px) scale(.96); }
  to   { opacity: 1; transform: none; }
}

/* The lost card is the same card with the green taken out of it. */
.win--lost .win__card {
  box-shadow:
    0 30px 60px -20px rgba(0, 0, 0, .8),
    inset 0 0 0 1px rgba(255, 255, 255, .5),
    inset 0 0 0 6px rgba(169, 52, 43, .1);
}

.seal {
  position: absolute;
  top: -26px;
  left: 50%;
  width: 62px;
  height: 62px;
  transform: translateX(-50%) rotate(-8deg);
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: rgba(255, 226, 216, .78);
  font-size: 22px;
  background: radial-gradient(circle at 34% 28%, #C4463A, #8E2A22 62%, #6E1E18 100%);
  box-shadow:
    inset 0 -3px 8px rgba(0, 0, 0, .5),
    inset 0 3px 6px rgba(255, 255, 255, .25),
    0 8px 16px -6px rgba(0, 0, 0, .7);
  clip-path: polygon(
    50% 0%, 66% 6%, 82% 4%, 92% 18%, 100% 32%, 96% 50%, 100% 68%, 90% 82%,
    80% 96%, 62% 96%, 46% 100%, 30% 94%, 16% 90%, 8% 74%, 0% 56%, 4% 38%,
    2% 20%, 18% 10%, 34% 6%);
}
.seal--spent {
  color: rgba(240, 228, 204, .5);
  background: radial-gradient(circle at 34% 28%, #4A3E30, #2E2620 62%, #201A15 100%);
}

.win__card h2 {
  font-family: 'Fraunces', Georgia, serif;
  font-variation-settings: 'SOFT' 50, 'opsz' 140, 'wght' 700;
  font-size: 2.1rem;
  margin: 6px 0 4px;
  letter-spacing: -.01em;
}

.win__theme {
  margin: 0 0 16px;
  font-family: 'Fraunces', Georgia, serif;
  font-variation-settings: 'ital' 1, 'wght' 500;
  font-style: italic;
  font-size: 1.15rem;
  letter-spacing: .04em;
  color: #7A6244;
}

.win__line { margin: 0 0 22px; font-size: 12px; line-height: 1.8; color: #574734; }
.win__line b { color: #2A2117; font-weight: 600; }

.win__next {
  margin: 0 0 22px;
  padding: 12px 0;
  border-top: 1px solid rgba(42, 33, 23, .16);
  border-bottom: 1px solid rgba(42, 33, 23, .16);
}
.win__next-label {
  display: block;
  font-size: 9px;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: #8A7458;
  margin-bottom: 6px;
}
.win__clock {
  font-size: 1.5rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: .06em;
  color: #2A2117;
}

.win__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px;
}

.btn--ink {
  color: #574734;
  border-color: rgba(42, 33, 23, .3);
  background: transparent;
}
.btn--ink:hover:not(:disabled) {
  background: #2A2117;
  border-color: #2A2117;
  color: #EFE1C4;
}

/* The way onward. Filled rather than outlined so it reads as the one thing to
   do next, with an arrow that leans the way it goes and takes a step on hover.
   Which table it leads to is on the chips behind the card, so the face of the
   button stays a single word and the tier name rides on the label. */
.btn--next {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding-inline: 14px;
  color: #F2E6CE;
  background: linear-gradient(180deg, #493A29 0%, #2A2117 100%);
  border-color: #2A2117;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .13),
    0 3px 7px -3px rgba(42, 33, 23, .7);
}
.btn--next:hover:not(:disabled) {
  color: #FBF2DF;
  background: linear-gradient(180deg, #5B4934 0%, #372B1D 100%);
  border-color: #2A2117;
}
.btn--next:focus-visible { outline-color: #8E2A22; }

.btn__arrow {
  margin-block: -1px; /* optical: the rule sits on the cap height, not below it */
  transition: transform .2s cubic-bezier(.25, 1.2, .4, 1);
}
.btn--next:hover:not(:disabled) .btn__arrow { transform: translateX(3px); }
.btn--next:active:not(:disabled) .btn__arrow { transform: translateX(1px); }

/* -------------------------------------------------------------- motion -- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* --------------------------------------------------------------- small -- */

@media (max-width: 780px) {
  .page { padding-inline: 10px; }
  .topbar { flex-direction: column; align-items: stretch; gap: 8px; }
  .masthead { justify-content: center; gap: 4px 12px; }
  .stamp { justify-content: center; letter-spacing: .18em; }
  .stamp__rule { display: none; }
  .tiers { width: 100%; }
  .chip { flex: 1 1 0; min-width: 0; padding: 8px 4px; letter-spacing: .04em; }

  .console { justify-content: center; }
  .actions { margin-left: 0; width: 100%; justify-content: center; }
}

@media (max-width: 420px) {
  .shelfnote { display: none; } /* the chips already say which table this is */
  .bench-rail { padding: 8px 6px; }
}

/* ---------------------------------------------------------------- ledger -- */

/* The written half of the page: what the game is, and the way out to the
   others. Below the fold by design — the game gets the first screen. */
.ledger {
  position: relative;
  z-index: 2;
  max-width: 720px;
  margin: 0 auto;
  padding: 40px 20px calc(56px + env(safe-area-inset-bottom));
  display: grid;
  gap: 34px;
  border-top: 1px solid rgba(199, 154, 69, .16);
  font-size: 13px;
  line-height: 1.85;
  color: var(--parchment-2);
}

.ledger h2 {
  font-family: 'Fraunces', Georgia, serif;
  font-variation-settings: 'SOFT' 50, 'opsz' 90, 'wght' 600;
  font-weight: 600;
  font-size: 1.25rem;
  line-height: 1.2;
  margin: 0 0 14px;
  color: var(--parchment);
  letter-spacing: -.01em;
}

.ledger p { margin: 0 0 14px; }
.ledger p:last-child { margin-bottom: 0; }
.ledger strong { color: var(--parchment); font-weight: 500; }
.ledger em { color: var(--brass); font-style: italic; }

.ledger__more ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}
.ledger__more li {
  padding-left: 14px;
  border-left: 1px solid rgba(199, 154, 69, .22);
}
.ledger a {
  color: var(--brass);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color .2s;
}
.ledger a:hover { border-bottom-color: var(--brass); }
.ledger__home { margin-top: 18px; font-size: 11px; letter-spacing: .2em; text-transform: uppercase; }

.ledger__legal {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  padding-top: 18px;
  border-top: 1px solid rgba(199, 154, 69, .12);
  font-size: 10px;
  letter-spacing: .18em;
  text-transform: uppercase;
}
.ledger__legal a { color: var(--parchment-3); }
.ledger__legal a:hover { color: var(--brass); }

@media (min-width: 760px) {
  .ledger { max-width: 940px; grid-template-columns: 1.7fr 1fr; gap: 48px; }
}
