/* The WordFinder grid, its hint list, the start/pause gate and the rules.
   Every colour comes from tokens.css — no new tokens: the grid reads as a
   printed grid of letters, exactly the binary's board does, so it takes the
   binary's own tokens (--bin-cell, --bin-line, --bin-edge) rather than minting
   a second set for what is visually the same idea. The hint rows take the
   acrostic's slot tokens (--slot-line) for the same reason.

   The header, the gate, the entry bar and the result panel are lifted from
   binary.css near verbatim — three games are one product, and a player who
   knows where Pause is on one should not have to look for it on the next. What
   is genuinely new here is the grid and the hint list, which is where the
   selection model lives (public/js/wordfinder.js). */

/* progress hairline pinned to the very top, above the masthead */
#line{
  position:fixed;top:0;left:0;height:3px;width:0;background:var(--accent);
  z-index:9;transition:width .35s var(--ease);
}

/* ---------------- header ---------------- */
.wrap header{
  padding:26px 0 14px;margin-bottom:20px;border-bottom:1px solid var(--rule);
  display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap;
}
.wrap h1{
  font-family:var(--display);font-weight:700;font-size:clamp(26px,4.4vw,36px);
  letter-spacing:-.035em;line-height:1.05;margin:0;
}
.wrap h1 em{font-style:normal;color:var(--accent-ink)}
/* The joker. Its resting look is every play-bar button's (site.css,
   .playbar button) — quiet, and disabled while it counts down to the next one
   ("Joker 3:12"). */
#joker{font-variant-numeric:tabular-nums}
#joker[hidden]{display:none}
#joker:disabled{cursor:default}
/* A joker in hand is the loudest thing in the play bar (feedback, 2026-09-22:
   "make the joker more prominent"): a filled pill with the count in its own
   bubble — the same type and height as Pause, so the bar stays one size. ink/soft is the pair check:contrast holds
   at 4.5 both ways, in both themes and all four accents. */
.playbar #joker.ready{
  color:var(--accent-soft);background:var(--accent-ink);border-color:var(--accent-ink);
  padding:0 5px 0 12px;border-radius:999px;
  display:inline-flex;align-items:center;gap:8px;
  box-shadow:0 6px 16px -8px var(--accent-glow);
}
.playbar #joker.ready:hover{color:var(--accent-soft);background:var(--accent-ink);border-color:var(--accent-ink);box-shadow:0 0 0 3px var(--accent-glow)}
.joker-n{
  display:inline-grid;place-items:center;min-width:22px;height:22px;padding:0 6px;
  border-radius:999px;background:var(--accent-soft);color:var(--accent-ink);
  font-size:11px;letter-spacing:.02em;
}
/* picking: the same pill, outlined, saying Cancel — the way back out */
.playbar #joker[aria-pressed="true"]{
  color:var(--accent-ink);background:var(--accent-soft);border-color:var(--accent-ink);
  border-radius:999px;padding:0 12px;
  box-shadow:0 0 0 3px var(--accent-glow);
}
/* a newly earned joker glows twice, then settles */
#joker.new{animation:jokerGlow 1.1s var(--ease) 2}
@keyframes jokerGlow{
  0%,100%{box-shadow:0 6px 16px -8px var(--accent-glow)}
  50%{box-shadow:0 0 0 7px var(--accent-glow)}
}
@media (prefers-reduced-motion:reduce){ #joker.new{animation:none} }
.dateline{
  flex-basis:100%;margin:6px 0 0;font-size:12px;letter-spacing:.01em;color:var(--dim);
}

/* ---------------- the gate ---------------- */
.gate{
  display:none;
  background:var(--card);border:1px solid var(--rule-firm);border-radius:var(--radius-card);
  border-top:4px solid var(--accent);
  min-height:300px;padding:44px 26px;
  text-align:center;
}
body.is-gated .gate{display:grid;place-items:center}
/* everything the gate stands in for */
body.is-gated .playfield,
body.is-gated .rules,
body.is-gated .note,
body.is-gated .entry{display:none}
body.is-gated{padding-bottom:56px}

.gate-in{max-width:440px}
.gate-kicker{
  font-size:11px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  color:var(--accent-ink);
}
.gate h2{
  font-family:var(--display);font-weight:700;font-size:clamp(24px,4vw,32px);
  letter-spacing:-.03em;line-height:1.1;margin:10px 0 0;
}
.gate p{margin:12px 0 0;font-size:15px;line-height:1.6;color:var(--slate)}
#gateBtn{margin-top:22px;padding:14px 30px;font-size:14px}
.gate-figures{
  margin-top:16px;font-size:11px;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:var(--dim);
}

/* ---------------- the rules, shared by the gate and the foot of the page ---------------- */
.gate-rules{
  margin:22px auto 0;text-align:left;
  border-top:1px solid var(--rule);padding-top:16px;
}
.gate-rules-head{
  font-size:11px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  color:var(--dim);text-align:center;margin-bottom:12px;
}
.rules-list{
  margin:0;padding:0 0 0 20px;
  display:grid;gap:8px;
  font-size:13.5px;line-height:1.55;color:var(--slate);
}
.rules-list li::marker{color:var(--accent-ink);font-weight:800}
.rules-list b{color:var(--ink);font-weight:800}

/* ---------------- the play area: grid and hints, side by side or stacked ---------------- */
/* `align-items:stretch` (the flex default) below 900px, not `flex-start`, is
   load-bearing: `.wf-board` keeps its own explicit `width:fit-content` either
   way, but `.wf-hints` has none, so `flex-start` used to size it by SHRINKING
   to its content — the widest TIP's own text width, not the panel's. A
   real batch's shorter tips shrank that box well under what twelve `.wf-slot`s
   need, and `.wf-slot`'s own width (a clamp on `.wf-slots`, its ancestor)
   floored out and wrapped — a length the row exists to give away read as two
   shorter words instead (verify round 1, defect 1: EFFERVESCENT read 10+2).
   Stretching ties the hints column's width to the LAYOUT — the panel's own
   content width — so it stops moving with whichever puzzle's tips happen to
   be longest or shortest. Overridden back to `flex-start` at 900px+, where
   `.wf-hints{flex:1}` already gives it a layout-driven width of its own and
   the un-stretched top alignment is what keeps a short grid from being
   stretched to a tall hint list's height. */
.wf-layout{display:flex;flex-direction:column;gap:26px;align-items:stretch}
@media (min-width:900px){
  .wf-layout{flex-direction:row;align-items:flex-start}
  .wf-board{flex:none}
  .wf-hints{flex:1;min-width:0}
}

/* The board is only as wide as the grid inside it — a 10x10 in an 880px box
   would be a small square marooned in a large one. */
.wf-board{width:fit-content;max-width:100%;margin:0 auto}

/* One cell per column, sized by --wf-n (set inline per puzzle on .wf-layout in
   views/wordfinder.ejs, so the hint slots below can read it too) and --wf-w.
   On a wide screen the BOARD is a constant ~440px and the cell is that divided
   by the side — 44px on a 10x10, ~36.7px on a 12x12 — rather than one cell
   size for both, which is what left the 12x12 letters small (feedback,
   2026-09-22: "letters should be bigger"). Narrow screens override it below. The 1px gap
   over a --bin-line ground is the grid itself — one hairline between any two
   cells and none doubled, the binary's own trick. Letters are the puzzle and
   are rendered plainly (DESIGN.md §2.4 only withholds an answer's letters from
   the HINT slots, never from the grid — a word search is meant to be read). */
.grid{
  --wf-w:calc(440px / var(--wf-n));
  display:grid;
  grid-template-columns:repeat(var(--wf-n), var(--wf-w));
  gap:1px;background:var(--bin-line);
  border:1px solid var(--bin-edge);border-radius:var(--radius-card);
  /* the four corner cells are square; without the clip they paint over the
     rounded border (a found or tinted corner cell showed it most) */
  overflow:hidden;
  outline:none;
  /* the whole grid is the one click/drag target: nothing on it should be
     selectable text, and a drag on a phone must never scroll the page */
  touch-action:none;user-select:none;-webkit-user-select:none;
  /* iOS's long-press callout ("Copy" / "Look Up") is the same kind of
     accidental selection §2.4 already rules out for the grid — a long press
     here is the middle of a drag, not a request to select or look up a
     letter. */
  -webkit-touch-callout:none;
  box-shadow:0 20px 44px -34px var(--shadow-board);
}
.wf-cell{
  width:var(--wf-w);height:var(--wf-w);
  background:var(--bin-cell);
  display:grid;place-items:center;
  font-family:var(--serif);font-weight:700;
  font-size:calc(var(--wf-w) * .58);line-height:1;
  font-variation-settings:"SOFT" 70,"WONK" 1;
  color:var(--ink);
  transition:background .12s var(--ease),color .12s var(--ease);
}
/* The roving keyboard cursor. Ordered BEFORE the anchor/live rules below, not
   after: it stays visible for the whole keyboard selection now (review round
   2, nit — an off-axis cursor used to vanish the moment an anchor was set,
   with only the snapped line left on screen and nothing marking where the
   cursor actually was), and cascade ties between rules of equal specificity
   go to whichever is LATER, so a cell that is both the cursor and something
   more specific (the anchor, the live line) still shows that more specific
   ring — the cursor ring is what is left showing when nothing else claims
   the cell, not a fourth thing to layer on top of them. */
.wf-cell.is-cursor{box-shadow:inset 0 0 0 2px var(--brand)}
/* the armed anchor — the ONE end already chosen, waiting for the other */
.wf-cell.is-anchor{box-shadow:inset 0 0 0 3px var(--ink)}
/* the live preview, while dragging or moving the keyboard cursor with an
   anchor already set — a distinct treatment from a found band, so a player
   never confuses "still choosing" with "already found" */
.wf-cell.is-live{box-shadow:inset 0 0 0 2px var(--accent)}
.wf-cell.is-anchor.is-live{box-shadow:inset 0 0 0 3px var(--accent-deep)}
/* A found word's band: --wf-found, a wash of the day's accent one step off the
   unfound cell's ground (tokens.css), with the letter left in the page's own
   --ink and set heavier. A highlighter over printed letters, in other words.
   Both earlier attempts changed the LETTER and paid for it: --accent-soft
   behind --accent-ink was within a shade of --bin-cell in light and read as
   barely there ("selected but barely visible", 2026-09-22), and the reverse —
   --accent-ink as the fill — measured 9-12:1 against an unfound cell and read
   as a wall of bright blocks ("too bright ... distracting", 2026-09-24).
   check:contrast holds the band in a WINDOW, 1.5 to 2.6 against an unfound
   cell, so neither failure can come back unnoticed, and the letter at the 4.5
   text floor. A cell shared by two found words is simply the same wash. */
.wf-cell.is-found{background:var(--wf-found);color:var(--ink);font-weight:900}
/* --brand and --accent both fall under 2:1 against the light bands, so on a
   found cell every ring is drawn in --ink, which clears 7.72:1 on all eight
   bands (the floor is dark accent 1) and flips with the theme. As when the band
   was dark, that leaves TWO rings on a found cell, not three: the cursor and
   the live preview are the same 2px declaration and are told apart only by the
   3px anchor. Losing that distinction on found cells only is the same trade the
   dark band made — an already-found cell is not one the player is choosing. */
.wf-cell.is-found.is-cursor{box-shadow:inset 0 0 0 2px var(--ink)}
.wf-cell.is-found.is-live{box-shadow:inset 0 0 0 2px var(--ink)}
.wf-cell.is-found.is-anchor{box-shadow:inset 0 0 0 3px var(--ink)}
/* a miss: the highlight shudders once and leaves nothing behind */
.wf-cell.is-miss{animation:wfshake .4s var(--ease)}
@keyframes wfshake{
  0%,100%{transform:translate(0,0)}
  20%{transform:translate(-2px,0)}
  40%{transform:translate(2px,0)}
  60%{transform:translate(-1px,0)}
  80%{transform:translate(1px,0)}
}
.grid:focus-visible{box-shadow:0 0 0 3px var(--accent-glow),0 20px 44px -34px var(--shadow-board)}
@media (prefers-reduced-motion:reduce){
  .wf-cell.is-miss{animation:none}
}

/* ---------------- the hints ---------------- */
.wf-hints .section-head{margin-bottom:8px}
.wf-hint-list{list-style:none;margin:0;padding:0}
.wf-hint{
  display:grid;grid-template-columns:24px 1fr;gap:3px 10px;
  padding:13px 0;border-bottom:1px dashed var(--rule-firm);
}
.wf-hint:first-of-type{border-top:1px dashed var(--rule-firm)}
.wf-label{
  grid-row:1 / 3;
  font-family:var(--display);font-weight:800;font-size:13px;
  color:var(--accent-ink);padding-top:1px;
}
.wf-tip{font-size:15.5px;font-weight:500;line-height:1.4;color:var(--ink);text-wrap:pretty}
.wf-hint.done .wf-tip{
  color:var(--muted);font-weight:400;
  text-decoration:line-through;text-decoration-thickness:1px;
}
/* `container-type:inline-size` turns THIS box into what `cqw` below measures
   — not a visual change on its own, but load-bearing for `.wf-slot`'s sizing
   right under it. See the long note there (verify round 2, defect 1): a `%`
   stored in a custom property is re-resolved per CONSUMING property, which is
   exactly what broke; `cqw` is not, so it is what replaces `%` here. */
.wf-slots{display:flex;gap:5px;flex-wrap:wrap;container-type:inline-size}
.wf-slots .wf-gap{width:9px}
/* Sized off the ROW's own width, not a fixed pixel figure, so twelve slots —
   the longest a WordFinder answer gets (varchar(12)) — always fit one line at
   any width this page runs at. Wrapping is reserved for what it is actually
   FOR: the gap between two words of a multi-word answer (Judgement call 6 —
   the length the slot row gives away has to stay a length, not read as two
   shorter words because the twelfth slot dropped to a line of its own, which
   is exactly what a fixed 17px cost at 320px, review round 2 should-fix 2).

   `cqw`, NOT `%` — verify round 2, defect 1. `100%` here used to mean "100%
   of .wf-slots' WIDTH" only at the one call site it was written for
   (`width:var(--wf-slot-w)`); reused in `height:calc(var(--wf-slot-w)*1.26)`
   the very same `%` token is re-resolved against the HEIGHT axis, and in
   `font-size:calc(var(--wf-slot-w)*.74)` against the parent's font-size —
   two bases `.wf-slots`' width was never meant to feed, both landing deeply
   negative, both clamped to zero by `min()` with no floor to catch the fall.
   That is genuinely how CSS defines a percentage inside a custom property:
   the token is substituted as-is and resolved fresh at each USE, against
   whatever basis that use's own property calls for. `cqw` has no such
   per-property basis to be reinterpreted against — 1cqw is always 1% of the
   query container's OWN inline size, full stop, wherever it is read — so the
   same custom property now means the same physical length in `width`,
   `height` and `font-size` alike. 55px is 11 gaps at 5px each.

   `min()`, deliberately with NO floor — verify round 1, defect 1. A floor is
   a promise the box might not be able to keep: real content's shorter tips
   shrink `.wf-hints` below what a 14px floor needs even after `.wf-layout`
   was made to stretch it (below 900px), and at 900px+ the fixed 34px board
   plus the 880px `.wrap` cap can leave `.wf-hints` under 215px — both
   measured, both wrapped a 12-slot row before that fix. Dividing the REAL
   available width by 12 always fits by construction, however small that
   makes a slot; 19px is a ceiling so a short, roomy row does not go
   oversized, never a floor that could ask for more room than the row has. */
/* The row is sized for THIS puzzle's longest answer, --wf-long (set on
   .wf-layout from the payload), with --wf-long - 1 gaps, rather than always for
   twelve — every row of a puzzle shares one slot size, and none can need more
   than the longest does. The ceiling went from 19px to 26px for the same
   feedback as the grid's letters (2026-09-22): the slots are where a found
   word is read back, and twelve-letter slots on a puzzle whose longest word is
   ten were giving away room for nothing. */
.wf-slot{
  --wf-slot-w:min(26px, calc((100cqw - (var(--wf-long) - 1) * 5px) / var(--wf-long)));
  position:relative;width:var(--wf-slot-w);height:calc(var(--wf-slot-w) * 1.26);
  background:var(--page);border:1px solid var(--slot-line);border-radius:var(--radius-slot);
  display:grid;place-items:center;
  font-family:var(--serif);font-weight:900;font-size:calc(var(--wf-slot-w) * .74);
  font-variation-settings:"SOFT" 70,"WONK" 1;color:transparent;
  transition:color .2s ease,border-color .2s ease;
}
.wf-slot.on{color:var(--accent-ink);background:var(--accent-soft);border-color:var(--accent-deep)}
/* A joker's reveal: the word is shown, but NOT found — plain ink on the empty
   slot's own ground behind a dashed rim, so it can never be mistaken for the
   tinted found row it turns into once the player finds it in the grid. */
.wf-slot.rev{color:var(--ink);border-style:dashed;border-color:var(--ink)}

/* picking a hint to spend a joker on: every hint that can take one is marked,
   and carries its own Reveal button; the whole row is the tap target */
.wf-hint.is-pickable{cursor:pointer}
.wf-hint.is-pickable .wf-label{color:var(--ink)}
.wf-reveal{
  grid-column:2;justify-self:start;margin-top:8px;min-height:32px;
  font-family:var(--ui);font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;
  color:var(--accent-ink);background:var(--accent-soft);cursor:pointer;
  border:1px solid var(--accent-deep);border-radius:var(--radius-card);padding:7px 14px;
  touch-action:manipulation;
}
.wf-reveal[hidden]{display:none}
.wf-reveal:hover,.wf-reveal:focus-visible{color:var(--accent-soft);background:var(--accent-ink)}

/* give up: deliberately quiet, and two-step — the whole grid is one big click
   target, so one stray drag must not end a puzzle the player is still working
   on. The same control, drawn the same way, as the acrostic's. */
.giveup{text-align:center;padding:20px 0 4px}
.giveup.gone{display:none}
#giveup{
  background:transparent;border:1px solid var(--rule-firm);border-radius:var(--radius-card);
  padding:9px 18px;cursor:pointer;
  font-family:var(--ui);font-size:11px;font-weight:800;
  letter-spacing:.1em;text-transform:uppercase;color:var(--dim);
  touch-action:manipulation;
  transition:color .15s,border-color .15s,background .15s;
}
#giveup:hover{color:var(--ink);border-color:var(--ink)}
#giveup.armed{
  color:var(--accent-ink);border-color:var(--accent-deep);background:var(--accent-soft);
  box-shadow:0 0 14px 1px var(--accent-glow);
}

/* ---------------- the status bar ---------------- */
.entry{
  position:fixed;left:0;right:0;bottom:0;z-index:6;
  margin:0;
  padding:12px 14px calc(12px + env(safe-area-inset-bottom,0px));
  background:var(--page);
  border-top:1px solid var(--rule-firm);
}
.entry .status,.entry .result{max-width:520px;margin-left:auto;margin-right:auto}
body{padding-bottom:calc(76px + env(safe-area-inset-bottom,0px))}
.entry.done{
  position:static;max-width:520px;margin:26px auto 0;padding:0;
  background:none;border-top:0;
}
body:has(.entry.done){padding-bottom:56px}
.status{
  min-height:19px;font-size:12.5px;color:var(--slate);text-align:center;
  letter-spacing:.01em;line-height:1.5;
}

.rules{width:100%;max-width:640px;margin:30px auto 0}

.note{
  max-width:640px;margin:24px auto 0;padding:16px 18px;
  background:var(--brand-soft);border-left:4px solid var(--brand);
  border-radius:var(--radius-card);
  font-size:13px;line-height:1.7;color:var(--slate);
}
.note b{color:var(--ink);font-weight:800}

/* ---------------- result (replaces the status line) ---------------- */
.result{display:none;text-align:center}
.entry.done .status{display:none}
.entry.done .result{
  display:block;padding:26px 20px;
  background:var(--card);border:1px solid var(--rule);border-radius:var(--radius-card);
  border-top:3px solid var(--accent);
  animation:rise .4s var(--ease);
}
@keyframes rise{from{transform:translateY(8px);opacity:0}to{transform:none;opacity:1}}
.result .eyebrow{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent-ink);font-weight:800}
.result .final{
  font-family:var(--serif);font-weight:900;font-size:56px;line-height:1.05;
  font-variation-settings:"SOFT" 80,"WONK" 1;
  font-variant-numeric:tabular-nums;margin:8px 0 0;letter-spacing:-.02em;
}
.result .flabel{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim);margin-top:4px}
/* the second figure, under the time: how many jokers it took (DESIGN.md §12.7) */
.result .jokers-used{margin-top:10px;font-size:14px;font-weight:700;color:var(--slate)}
.result button{margin-top:20px;width:100%}

@media (max-width:680px){
  /* The chrome between the viewport and the grid at this width: the wrap's 14px
     margins (28), the playfield's 1px border and 14px padding either side (30),
     the grid's own 1px border (2) — 60px, less the one hairline the n*1px
     double-counts, so 59px + n*1px. On the grid, not :root — a :root rule
     computing this would resolve against a fallback --wf-n rather than the
     puzzle's real 12. */
  .grid{
    --wf-w:clamp(24px, calc((100vw - 59px - var(--wf-n) * 1px) / var(--wf-n)), 40px);
  }
  .wf-cell{font-size:calc(var(--wf-w) * .58)}
  .wrap header{padding-top:20px}
  .gate{min-height:240px;padding:34px 18px}
  .gate-rules .rules-list{font-size:13px}
  .wf-hint{grid-template-columns:20px 1fr}
  .wf-tip{font-size:14.5px}
  .result .final{font-size:44px}
  .status{font-size:12px}
}

/* A 24px floor is what 375px was measured against (Judgement call 1: "~26px
   cells"), and it fits with room to spare there. Below ~359px the same floor
   stops fitting at all — clamp() cannot shrink past its own floor, so the grid
   would rather overflow the phone than go to 23px, which is exactly backwards.
   320px (an iPhone SE, still worth serving) needs both a lower floor AND some
   of the chrome given back: `.wrap` and `.playfield` are re-tightened here,
   for this page only, rather than in site.css where it would pinch every
   other game's margins too. The same subtraction as the block above, with the
   new numbers: wrap's 8px margins (16), the playfield's 1px border and 8px
   padding either side (18), the grid's own 1px border (2), less one
   double-counted hairline — 35px + n*1px. At 320px that lands the cell at
   ~22.7px: short of 24, but tappable, and — the part that actually matters —
   arrived at by the sum, not by a floor pretending the sum is bigger than it
   is. */
@media (max-width:400px){
  .wrap{padding:0 8px}
  .playfield{padding:12px 8px 10px}
  .grid{
    --wf-w:clamp(20px, calc((100vw - 35px - var(--wf-n) * 1px) / var(--wf-n)), 40px);
  }
}
