/* Numerals — "Inscription"
   Palette derived from the materials of Roman epigraphy rather than from a
   parchment cliché: quarried slate ground, chalk-filled incisions, bronze that
   has oxidised to verdigris, and iron that has rusted. Burn is oxide because a
   burnt tile is a corroded one, not an error message.

   Type is Optima where available: a humanist face Zapf drew directly from Roman
   inscriptional capitals, present on every Mac and iOS device, so the tiles
   carry genuine epigraphic character with no webfont request. */

:root {
  --slate:      #1B1E24;   /* quarried ground */
  --slate-2:    #232830;   /* raised surface */
  --slate-3:    #2C323C;   /* tile face */
  --chalk:      #E8E4DA;   /* incised fill */
  --dust:       #8A8578;   /* weathered label */
  --verdigris:  #4EA894;   /* oxidised bronze — solve */
  --oxide:      #B4553C;   /* rusted iron — burn */
  --incise:     rgba(0,0,0,.55);

  --display: Optima, "Optima Nova", "Palatino Linotype", Palatino,
             "Book Antiqua", "URW Palladio L", Georgia, serif;
  --ui: ui-sans-serif, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --gap: clamp(.75rem, 2.5vw, 1.25rem);
}

* { box-sizing: border-box; }

/* Mobile web and iOS are the primary target. The tap highlight is a grey box that
   fights the tile styling, and touch-action stops a quick double tap on a tile from
   being read as a zoom gesture, which on a board you tap repeatedly is constant. */
button { -webkit-tap-highlight-color: transparent; touch-action: manipulation; }

html, body {
  margin: 0;
  min-height: 100%;
  background: var(--slate);
  color: var(--chalk);
  font-family: var(--ui);
  -webkit-font-smoothing: antialiased;
}

/* Column layout so the tray can be sticky rather than fixed. A fixed tray needs
   the page to reserve its height as padding, and no constant is right: a Saturday
   bank runs to seven symbol rows and buries whatever sits at the bottom of the
   board. Sticky occupies its own space, so nothing is ever trapped under it. */
body { display: flex; flex-direction: column; min-height: 100dvh; }

body {
  /* a faint quarry grain, so the ground reads as stone rather than as #1B1E24 */
  background-image:
    radial-gradient(ellipse at 50% -10%, #2A303A 0%, transparent 60%),
    repeating-linear-gradient(94deg, rgba(255,255,255,.012) 0 2px, transparent 2px 5px);
}

/* The board, tray and result all set display on their own class, which beats the
   user agent's [hidden] rule. Without this the hidden attribute does nothing and
   the finished board stays on screen underneath the result. */
[hidden] { display: none !important; }

.wrap { width: 100%; max-width: 44rem; margin: 0 auto; padding: var(--gap); box-sizing: border-box; }

/* ---------------------------------------------------------------- header -- */
/* A single row, because this is played on a phone. The wordmark, puzzle number,
   hairline rule and help button used to stack into 156px of chrome, and with a
   five-row tray that pushed the fourth question off the screen entirely. The rule
   is now the header's own bottom border rather than an element with margins. */
header {
  position: relative;
  display: flex; align-items: center; gap: .5rem;
  padding: .35rem 0 .5rem;
  margin-bottom: .5rem;
  border-bottom: 1px solid rgba(138,133,120,.22);
}

.wordmark {
  font-family: var(--display);
  font-size: clamp(1.05rem, 4.4vw, 1.9rem);
  letter-spacing: .26em;
  text-indent: .26em;          /* balance the trailing tracking */
  font-weight: 400;
  margin: 0;
  color: var(--chalk);
  white-space: nowrap;
}

.puzzle-no {
  font-family: var(--display);
  color: var(--dust);
  letter-spacing: .18em;
  font-size: .75rem;
  /* pushes the help button to the far edge without a spacer element */
  margin-right: auto;
}

/* .rule removed: the hairline is the header border now. */

/* -------------------------------------------------------------- questions -- */
.board { display: grid; gap: .45rem; margin-top: .6rem; }

.q {
  background: var(--slate-2);
  border: 1px solid rgba(255,255,255,.055);
  border-radius: 3px;
  padding: .7rem .85rem;
  display: grid;
  gap: .4rem;
  transition: border-color .18s, background .18s;
}
.q:hover:not(.is-solved) { border-color: rgba(255,255,255,.16); }
.q.is-active { border-color: rgba(255,255,255,.2); background: var(--slate-3); }

/* The tappable part of a card. A button in its own right so the slot's tile
   buttons are its siblings rather than its children; nesting them was invalid and
   let a tap on the card fire when it was aimed at something inside. */
.q-open {
  background: none; border: 0; padding: 0; margin: 0; min-height: 44px;
  font: inherit; color: inherit; text-align: left; width: 100%;
  display: grid; gap: .3rem; cursor: pointer; align-content: center;
}
.q-open:focus-visible { outline: 2px solid var(--verdigris); outline-offset: 3px; }

.q-eyebrow {
  font-size: .625rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--dust); display: flex; justify-content: space-between; gap: 1rem;
}
.q-prompt { font-size: .96rem; line-height: 1.45; }

.q.is-solved { border-color: color-mix(in srgb, var(--verdigris) 45%, transparent); }
.q.is-solved .q-prompt { color: var(--dust); }

.q-answer {
  font-family: var(--display);
  font-size: 1.5rem;
  letter-spacing: .18em;
  color: var(--verdigris);
}

/* -------------------------------------------------------------- the slot -- */
.slot { display: grid; gap: .7rem; padding-top: .35rem; }

.slot-line {
  min-height: 3.4rem;
  border-bottom: 2px solid rgba(255,255,255,.1);
  display: flex; align-items: center; gap: .18rem; flex-wrap: wrap;
  padding-bottom: .4rem;
}

.slot-tile {
  font-family: var(--display);
  font-size: 1.85rem; line-height: 1;
  padding: .25rem .45rem;
  color: var(--chalk);
  cursor: pointer;
  background: none; border: 0;
  text-shadow: 0 1px 0 var(--incise);
}
.slot-tile.is-retained { color: var(--verdigris); cursor: default; }

.readout {
  font-family: var(--mono);
  font-size: .8rem; color: var(--dust);
  display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
}
.readout strong { color: var(--chalk); font-weight: 500; font-size: 1.05rem; }
.readout .hint { color: var(--oxide); text-align: right; }

.commit {
  font-family: var(--display);
  letter-spacing: .2em; text-transform: uppercase; font-size: .78rem;
  padding: .8rem 1rem;
  background: transparent;
  color: var(--chalk);
  border: 1px solid var(--dust);
  border-radius: 2px;
  cursor: pointer;
  transition: background .18s, color .18s, border-color .18s;
}
.commit:hover:not(:disabled) { background: var(--chalk); color: var(--slate); border-color: var(--chalk); }
.commit:disabled { opacity: .3; cursor: not-allowed; }
.commit:focus-visible { outline: 2px solid var(--verdigris); outline-offset: 2px; }
.commit.is-committing { opacity: .55; }

/* -------------------------------------------------------------- the tray -- */
/* The signature element. Burned tiles are not removed: they stay in the tray as
   oxidised scars, so the bank becomes a visible record of every commitment you
   got wrong. */
.tray {
  position: sticky; bottom: 0; margin-top: auto;
  background: linear-gradient(180deg, rgba(27,30,36,.9), var(--slate) 28%);
  border-top: 1px solid rgba(255,255,255,.07);
  backdrop-filter: blur(12px);
  padding: .55rem var(--gap) calc(.5rem + env(safe-area-inset-bottom));
}
.tray-inner { max-width: 44rem; margin: 0 auto; }

.tray-label {
  font-size: .625rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--dust); display: flex; justify-content: space-between;
  margin-bottom: .35rem;
}

.tray-rows { display: grid; gap: .25rem; }
.tray-row { display: flex; gap: .28rem; align-items: center; flex-wrap: wrap; }

.tile {
  font-family: var(--display);
  font-size: 1.25rem; line-height: 1;
  /* 44x44 at a 16px root: Apple's minimum touch target. Was 38 wide, which is
     the sort of miss that reads as "this game is fiddly" on a phone. */
  width: 2.75rem; height: 2.75rem;
  display: grid; place-items: center;
  background: var(--slate-3);
  color: var(--chalk);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: 2px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06), 0 1px 2px rgba(0,0,0,.4);
  cursor: pointer;
  transition: transform .12s, background .12s;
  text-shadow: 0 1px 0 var(--incise);
}
.tile:hover:not(:disabled) { transform: translateY(-2px); background: #333A46; }
.tile:active:not(:disabled) { transform: translateY(0); }
.tile:focus-visible { outline: 2px solid var(--verdigris); outline-offset: 2px; }

.tile.is-spent { opacity: .18; cursor: default; pointer-events: none; }

.tile.is-burned {
  background: color-mix(in srgb, var(--oxide) 22%, var(--slate-3));
  border-color: color-mix(in srgb, var(--oxide) 45%, transparent);
  color: color-mix(in srgb, var(--oxide) 85%, var(--chalk));
  cursor: default; pointer-events: none;
  animation: corrode .45s ease-out;
}
@keyframes corrode {
  0%   { transform: translateX(0); }
  20%  { transform: translateX(-4px); }
  45%  { transform: translateX(4px); }
  70%  { transform: translateX(-2px); }
  100% { transform: translateX(0); }
}

/* ------------------------------------------------------------- the result -- */
.result { text-align: center; padding: 2rem 0; display: grid; gap: 1.25rem; }
.surplus-word {
  font-size: .625rem; letter-spacing: .22em; text-transform: uppercase; color: var(--dust);
}
.surplus {
  font-family: var(--display);
  font-size: clamp(3rem, 16vw, 5rem); letter-spacing: .1em; line-height: 1;
  color: var(--verdigris);
}
.grid {
  font-family: var(--mono); font-size: 1.5rem; line-height: 1.3;
  white-space: pre; letter-spacing: .06em;
}
.actions { display: flex; gap: .5rem; justify-content: center; flex-wrap: wrap; }
.reveal { display: grid; gap: .4rem; text-align: left; margin-top: .5rem; }
.reveal-row {
  display: flex; justify-content: space-between; gap: 1rem; align-items: baseline;
  padding: .55rem 0; border-bottom: 1px solid rgba(255,255,255,.06);
  font-size: .85rem; color: var(--dust);
}
.reveal-row b { font-family: var(--display); font-size: 1.15rem; letter-spacing: .14em; color: var(--chalk); font-weight: 400; }

.toast {
  position: fixed; left: 50%; bottom: 8.5rem; transform: translateX(-50%);
  background: var(--chalk); color: var(--slate);
  padding: .55rem 1rem; border-radius: 2px;
  font-size: .8rem; letter-spacing: .04em;
  opacity: 0; pointer-events: none; transition: opacity .25s;
}
.toast.is-visible { opacity: 1; }

.empty { text-align: center; color: var(--dust); padding: 3rem 1rem; line-height: 1.6; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* high contrast swaps the pairing that deuteranopes cannot separate. Position
   already carries the information; this makes colour carry it too. */
body.high-contrast { --verdigris: #4A90D9; --oxide: #D98A2B; }

/* ---------------------------------------------------------- how to play -- */
/* <dialog> so the backdrop, focus trap, Escape key and inert background are the
   platform's problem rather than ours. */
.howto {
  border: 1px solid rgba(255,255,255,.09);
  border-radius: 4px;
  background: var(--slate-2);
  color: var(--chalk);
  padding: 1.5rem var(--gap) 1rem;
  width: min(30rem, calc(100vw - 2 * var(--gap)));
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  box-shadow: 0 1.5rem 3rem rgba(0,0,0,.5);
}
.howto::backdrop { background: rgba(12,14,17,.78); backdrop-filter: blur(3px); }

.ht-close-form { margin: 0; float: right; }
.ht-close {
  background: none; border: 0; color: var(--dust); cursor: pointer;
  font-size: 1.5rem; line-height: 1; padding: 0 .25rem;
}
.ht-close:hover { color: var(--chalk); }

.ht-eyebrow {
  font-size: .625rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--dust);
}

.howto h2 {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.35rem, 5vw, 1.7rem); letter-spacing: .06em;
  margin: .9rem 0 .5rem;
}
.howto p { margin: 0 0 1rem; line-height: 1.55; font-size: .9rem; color: #C9C5BA; }
.ht-note { font-size: .8rem !important; color: var(--dust) !important; }

.ht-row { display: flex; flex-wrap: wrap; gap: .3rem; align-items: center; }
.ht-tile, .ht-pick { transition: background .25s, color .25s, opacity .25s; }
/* not a control on screens where it only illustrates */
.ht-tile { cursor: default; }

.ht-pick { cursor: pointer; }
.ht-pick.is-picked {
  background: var(--verdigris); color: var(--slate);
  border-color: var(--verdigris);
}

.ht-readout {
  font-family: var(--display); font-size: 1.05rem; letter-spacing: .04em;
  margin: 1rem 0; min-height: 2.6em; line-height: 1.5;
}
.ht-readout strong { font-weight: 400; color: var(--verdigris); letter-spacing: .12em; }
.ht-readout b { font-weight: 400; color: var(--chalk); letter-spacing: .12em; }
.ht-muted { color: var(--dust); font-family: var(--ui); font-size: .82rem; letter-spacing: 0; }

.ht-swap {
  font-family: var(--ui); font-size: .75rem; letter-spacing: .12em;
  text-transform: uppercase; cursor: pointer;
  background: none; color: var(--chalk);
  border: 1px solid rgba(255,255,255,.18); border-radius: 3px;
  padding: .55rem .9rem; margin-left: .4rem;
}
.ht-swap:hover { border-color: var(--verdigris); color: var(--verdigris); }

/* On screen two this button sits inline beside the tiles, where the left margin
   separates it from them. On screen three it stands alone under the tray, where
   that same margin only indents it out of alignment with the tile row and it has
   nothing holding it off the tiles above. */
.ht-tray + .ht-swap { margin: 1.1rem 0 0; }

.ht-pairs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(4rem, 1fr));
  gap: .4rem; margin-top: .6rem;
}
.ht-pairs span { font-size: .8rem; color: var(--dust); }
.ht-pairs b {
  font-family: var(--display); font-weight: 400; color: var(--chalk);
  letter-spacing: .1em; margin-right: .3rem;
}

.ht-slot {
  border-bottom: 1px solid rgba(255,255,255,.12);
  padding-bottom: .7rem; min-height: 3.4rem;
}
.ht-tile.is-kept { background: var(--verdigris); color: var(--slate); border-color: var(--verdigris); }
.ht-tile.is-gone { opacity: .12; }

.ht-tray { margin-top: 1rem; }
.ht-tray-label {
  font-size: .625rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--dust); display: flex; justify-content: space-between;
  margin-bottom: .45rem;
}

.ht-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; margin-top: 1.5rem; padding-top: 1rem;
  border-top: 1px solid rgba(255,255,255,.07);
}
.ht-nav {
  font-family: var(--ui); font-size: .7rem; letter-spacing: .14em;
  text-transform: uppercase; cursor: pointer;
  background: none; color: var(--chalk);
  border: 1px solid rgba(255,255,255,.18); border-radius: 3px;
  padding: .6rem 1.1rem;
}
.ht-nav:disabled { opacity: .3; cursor: default; }
.ht-nav.is-primary { border-color: var(--verdigris); color: var(--verdigris); }

.ht-dots { display: flex; gap: .4rem; }
.ht-dots span {
  width: .4rem; height: .4rem; border-radius: 50%;
  background: rgba(255,255,255,.18);
}
.ht-dots span.is-on { background: var(--verdigris); }

/* The header affordance for reopening it later. In flow and right aligned rather
   than absolutely placed, because absolute overlapped the wordmark once the
   viewport got narrow enough. */
.help {
  flex: none;
  font-family: var(--ui); font-size: .58rem; letter-spacing: .12em;
  text-transform: uppercase; cursor: pointer;
  background: none; color: var(--dust);
  border: 1px solid rgba(255,255,255,.14); border-radius: 3px;
  /* still a 44px touch target, just not 44px of layout */
  padding: .8rem .6rem;
  min-height: 44px;
}
.help:hover { color: var(--chalk); border-color: rgba(255,255,255,.3); }

@media (prefers-reduced-motion: reduce) {
  .ht-tile, .ht-pick { transition: none; }
}

/* -------------------------------------------------------- challenge link -- */
/* Shown to a recipient before play. Purely a readout: the board it sits above is
   identical to everyone else's, which is the whole point of a shared board. */
.challenge {
  margin-top: 1.25rem; padding: .7rem .9rem;
  border: 1px solid rgba(78,168,148,.28);
  border-left: 2px solid var(--verdigris);
  border-radius: 3px;
  background: rgba(78,168,148,.06);
  font-size: .85rem; color: #C9C5BA; line-height: 1.5;
}
.challenge b {
  font-family: var(--display); font-weight: 400;
  letter-spacing: .12em; color: var(--verdigris);
}

/* The target marker in the tray label. */
.target {
  margin-left: .6rem; padding-left: .6rem;
  border-left: 1px solid rgba(255,255,255,.14);
  color: var(--verdigris);
}
/* Once the bank drops to the target the challenge is out of reach: surplus is
   whatever survives to the end, and it only ever falls. */
.target.is-lost { color: var(--oxide); text-decoration: line-through; }

.verdict {
  font-family: var(--display); font-size: 1.05rem; letter-spacing: .05em;
  color: var(--oxide);
}
.verdict.is-won { color: var(--verdigris); }

/* One commit control for the board, docked in the tray above the tiles so its
   position never moves and a tap that misses hits inert label text. */
.tray-commit { margin-bottom: .45rem; }
.tray-commit .commit { width: 100%; }

/* Why the board ended. A session fails exactly one way, the bank no longer being
   able to spell the unsolved answers, and without saying so the result screen reads
   as a crash rather than a loss. */
.ran-out {
  font-size: .85rem; line-height: 1.55; color: #C9C5BA; text-align: left;
  border-left: 2px solid var(--oxide);
  background: rgba(180,85,60,.07);
  padding: .7rem .9rem; border-radius: 3px;
}

/* Wrong answers remaining, shown only under the mistakes rule where the bank cannot
   run out on a wrong answer and the real budget is patience rather than tiles. */
.misses {
  margin-left: .6rem; padding-left: .6rem;
  border-left: 1px solid rgba(255,255,255,.14);
  color: var(--dust);
}
.misses.is-lost { color: var(--oxide); }
