/* ============================================================================
   drills.css — the board/play surface shared by every drill (body.den-play).
   hangs, endgame, flashcards, lineup, gallows, crown, siege, weekly, tactics. Also the Hourglass clock and the shared drill deck, which only drills mount.
   Order in this file: DESKTOP, then MOBILE, then MOBILE-LANDSCAPE.
   ========================================================================== */
/* ============================================================================
   Drill board — shared play surface (hangs / tactics / flashcards / weekly).
   Was duplicated inline in all four; centralized here with baked hexes. --sq is
   the sole size lever; the phone block grows it to fill the width (was ~72%).
   Pages opt in with <body class="den-play">; tactics adds data-ev to reserve
   its eval bar. Tactics keeps its flash/last/eval-bar rules inline.
   ========================================================================== */
.den-play{--sq:min(58px,(100vw - 110px)/8)}
.den-play[data-ev]{--sq:min(54px,(100vw - 106px)/8)}
.den-play .main{display:flex;gap:20px;flex-wrap:wrap;align-items:flex-start}
.den-play .left{flex:0 0 auto}
.den-play .right{flex:1;min-width:260px}
.den-play .board{display:grid;grid-template-columns:repeat(8,var(--sq));grid-template-rows:repeat(8,var(--sq));
  padding:12px;background:linear-gradient(180deg,#2b2721,#171310);border:2px solid #a97d22;
  outline:1px solid #5a4a33;outline-offset:3px;border-radius:9px;box-shadow:0 20px 50px rgba(0,0,0,.5)}
.den-play .sq{position:relative;display:flex;align-items:center;justify-content:center;cursor:default}
/* square colors come from the ONE palette (base.css --lt/--dk, same values as the old baked hexes) — Phase 5b */
.den-play .sq.ltt{background:var(--lt,#eeeed2)}.den-play .sq.dkk{background:var(--dk,#769656)}
.den-play .sq.pick{cursor:pointer}
.den-play .sq.sel{box-shadow:inset 0 0 0 4px #e0b34c}
.den-play .sq.dest{cursor:pointer}
.den-play .sq.dest::after{content:"";position:absolute;width:calc(var(--sq)*.28);height:calc(var(--sq)*.28);border-radius:50%;background:rgba(0,0,0,.25)}
.den-play .sq.dest.cap::after{width:calc(var(--sq)*.79);height:calc(var(--sq)*.79);background:transparent;border:4px solid rgba(0,0,0,.25);border-radius:50%}
.den-play .sq.good{box-shadow:inset 0 0 0 4px #3fb950}.den-play .sq.good.ltt{background:#62d17a}.den-play .sq.good.dkk{background:#41a85a}
.den-play .sq.bad{box-shadow:inset 0 0 0 4px #f85149}.den-play .sq.bad.ltt{background:#f3a39f}.den-play .sq.bad.dkk{background:#d7706b}
.den-play .sq.mv{box-shadow:inset 0 0 0 4px #7cc4ff}
/* colorblind-safe glyph on the graded square, so right/wrong reads without hue */
.den-play .sq.good::after,.den-play .sq.bad::after,.den-play .sq.mv::after{content:"";position:absolute;top:3px;right:3px;width:18px;height:18px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;line-height:1;z-index:2}
.den-play .sq.good::after{content:"\2713";background:#3fb950;color:#0e2214}
.den-play .sq.bad::after{content:"\2715";background:#f85149;color:#2a1414}
.den-play .sq.mv::after{content:"~";background:#7cc4ff;color:#0c2233}
.den-play .pc{width:calc(var(--sq)*.92);height:calc(var(--sq)*.92);user-select:none;-webkit-user-drag:none;pointer-events:none;z-index:1;filter:drop-shadow(0 2px 3px rgba(0,0,0,.5))}

/* icon action bar (back / retry / show-best / show-mine / next) — shared across the drill pages */
.den-play .btns{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:8px}
.den-play .btns .iconbtn{flex:0 0 auto;width:48px;height:48px;padding:0;display:flex;align-items:center;justify-content:center;font-size:28px;line-height:1}
.den-play .btns .iconbtn img{width:44px;height:44px;object-fit:contain;display:block;pointer-events:none}
.den-play .btns .iconbtn.on{outline:2px solid var(--den-gold,#e0b34c);outline-offset:1px;background:var(--den-btnh,#2e2547)}
.den-play .btns .iconbtn:disabled{opacity:.3;cursor:default}

/* shared drill header bar: title left, grounds emblem upper-right (matches weekly) */
.den-play .hdr{display:flex;align-items:center;justify-content:space-between;gap:12px}
.den-play .hdr h1{margin:0;flex:0 0 auto}
.den-play .hdr .grounds{flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;color:var(--accent,#e0b34c);text-decoration:none;font-size:20px;line-height:1}
.den-play .hdr .grounds img{height:30px;width:auto;display:block}

/* knight-reveal caption (smug = best, delinquent = the move you played) — shared across drills */
.den-play .revealcap{border-radius:10px;padding:8px 12px;margin:0 0 10px;font-size:14px;text-align:center}
.den-play .revealcap.mine{background:#2a1414;border:1px solid #5a2c2c;color:#f3a39f}
.den-play .revealcap.best{background:#0e2214;border:1px solid #2c5a38;color:#84e29a}
.den-play .revealcap b{color:var(--den-ink,#f4ecdc)}

/* weekly layout principles, shared: two-line mission that unfurls, verdict unfurl, practice chip,
   current-dot indicator, spread score row */
.den-play .mission{transform-origin:top center}
.den-play .mission .ml1{display:block}
.den-play .mission .ml2{display:block;color:#84e29a;font-weight:700;margin-top:2px}
.den-play .mission.furled{visibility:hidden}
.den-play .mission.unfurl{animation:den-unfurl .55s cubic-bezier(.2,.85,.3,1) both}
.den-play .verdict{transform-origin:top center}
.den-play .verdict.show{animation:den-unfurl .5s cubic-bezier(.2,.85,.3,1) both}
@keyframes den-unfurl{0%{transform:scaleY(0);opacity:0}70%{transform:scaleY(1.05)}100%{transform:scaleY(1);opacity:1}}
.den-play .practice-chip{position:relative;display:inline-block;transform:rotate(-7deg);background:#241a10;color:#f5b23e;border:1px solid #4a3618;border-radius:6px;padding:1px 9px;font-size:12px;font-weight:700;margin-left:-2px;vertical-align:middle;white-space:nowrap}
.den-play .scorebar{justify-content:space-between}
.den-play .progress{padding-top:14px}
.den-play .progress .dot{position:relative}
.den-play .progress .dot.cur::after{content:'';position:absolute;left:50%;bottom:calc(100% + 4px);transform:translateX(-50%);width:7px;height:7px;border-radius:50%;background:#fff}
/* end-of-run overlay (weekly principle): the finale as a semi-transparent modal panel */
.den-play .finale{display:none}
.den-play .finale.show{display:flex;position:fixed;inset:0;z-index:50;overflow-y:auto;overscroll-behavior:contain;background:rgba(10,8,16,.72);padding:18px}
.den-play .finale .panel{background:var(--den-card,#201a2b);border:1px solid var(--den-line,#3a3147);border-radius:16px;padding:22px 20px;max-width:440px;width:100%;margin:auto;text-align:center;box-shadow:0 24px 60px rgba(0,0,0,.55);max-height:90vh;overflow:auto}

/* phone: pin a compact drill-name bar (Goal 2 — always know which drill you're in),
   slim everything else, and size the board to the viewport — width AND height — so the
   whole position + controls fit without scrolling. */

/* ---- The Hourglass: the shared decision clock (Den.hourglass / Den.hourglassBar) ----
   Practice-space only — it never times a Weekly first attempt, which is the permanent record. */
.den-glassbar{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:0 0 12px;font-size:13px}
.den-glassbar .lbl{color:var(--mut);margin-right:4px}
.den-glassbar button{background:var(--btn,#241d33);color:var(--mut);border:1px solid var(--line);
  border-radius:8px;padding:5px 11px;font-size:12.5px;cursor:pointer;font-weight:600;line-height:1.2}
.den-glassbar button span{display:block;font-size:10.5px;font-weight:400;opacity:.75}
.den-glassbar button.on{background:var(--accent,#e0b34c);color:var(--ptxt,#241302);border-color:var(--accent,#e0b34c)}
.den-glassbar button.locked{opacity:.65;font-style:italic}
.den-glassbar button:hover{filter:brightness(1.12)}

.den-glass{position:relative;width:44px;height:44px;flex:0 0 auto}
.den-glass svg{width:44px;height:44px;transform:rotate(-90deg)}
.den-glass circle{fill:none;stroke-width:3.5}
.den-glass .trk{stroke:var(--line)}
.den-glass .run{stroke:var(--accent,#e0b34c);transition:stroke .2s ease}
.den-glass.low .run{stroke:var(--crim,#c8283c)}
.den-glass b{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:13px;font-variant-numeric:tabular-nums;color:var(--ink)}
.den-glass.out{animation:den-glass-out .5s ease}
.den-glass.out .run{stroke:var(--crim,#c8283c)}
@keyframes den-glass-out{0%{transform:scale(1)}40%{transform:scale(1.12)}100%{transform:scale(1)}}

/* ---- The shared drill deck (Den.drillDeck) — the rep loop the drill tails mount ---- */
.den-deck{background:var(--card,#201a2b);border:1px solid var(--line);border-radius:14px;padding:16px;margin:12px 0}
.den-deck .dd-top{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:10px}
.den-deck .dd-count{color:var(--mut);font-size:13.5px}
.den-deck .dd-meta{color:var(--mut);font-size:12.5px}
.den-deck .dd-verdict{display:none;border-radius:10px;padding:11px 14px;margin:10px 0 0;font-size:14px}
.den-deck .dd-verdict.show{display:block}
.den-deck .dd-verdict.hit{background:#0e2214;border:1px solid #2c5a38}
.den-deck .dd-verdict.miss{background:#2a1414;border:1px solid #5a2c2c}
/* The graded move's own eval + the reply it invites — a quiet second line under the verdict. */
.den-deck .dd-eval{margin-top:5px;font-size:12.5px;color:var(--den-mut)}
.den-deck .dd-eval b{color:var(--den-ink);font-weight:600}
.den-deck .dd-note{display:block;color:var(--mut);font-size:12.5px;margin-top:5px}
.den-deck .dd-btns{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.den-deck .dd-btns button{background:var(--btn,#241d33);color:var(--ink);border:1px solid var(--line);
  border-radius:9px;padding:9px 15px;font-size:13.5px;cursor:pointer;font-weight:600}
.den-deck .dd-btns button.primary{background:var(--accent,#e0b34c);color:var(--ptxt,#241302);border-color:var(--accent,#e0b34c)}
.den-deck .dd-btns button:disabled{opacity:.35;cursor:default}
/* the deck's score row + review bar (5c) — weekly's one-line no-wrap row and gold numbers, deck-scoped */
.den-deck .dd-score{flex-wrap:nowrap;white-space:nowrap}
.den-deck .dd-score b{color:var(--gold,#e0b34c)}
.den-deck .dd-reviewbar{align-items:center;justify-content:space-between}
/* deck-rendered run dots (D9) — scoped under .den-deck so the seven pages' own .progress copies are never touched */
.den-deck .progress{display:flex;gap:5px;flex-wrap:wrap;margin:0 0 10px}
.den-deck .dot{position:relative;width:22px;height:8px;border-radius:4px;background:#20263a;border:1px solid var(--line);cursor:pointer}
.den-deck .dot.cur{outline:2px solid var(--accent2,#c8283c)}
.den-deck .dot.s3{background:var(--gold,#e0b34c);border-color:var(--gold,#e0b34c)}
.den-deck .dot.s2{background:var(--good,#3fb950);border-color:var(--good,#3fb950)}
.den-deck .dot.s1{background:#8fae5c}
.den-deck .dot.s0{background:var(--bad,#f85149);border-color:#7a2a26}

/* ---------------------------------------------------------------------------
   MOBILE (portrait). Width-keyed: a rotated phone is ~844px wide and never
   matches this block — landscape is the section below.
   --------------------------------------------------------------------------- */
@media(max-width:560px){
  /* THE PORTRAIT DRILL LAYOUT, re-imagined to the app's structural rule (2026-08-24, closing the
     2026-07-20 design flag): the FIXED rows are laid out first and the BOARD takes what remains —
     so the column can never grow past the fold and the advance control can never fall below it.
     Stack: bar → identity row → run rows → board (flex:1, the one flexible row) → the zone (a
     fixed-height window holding prompt/verdict/controls, pinned in the thumb zone above the
     register). The old stack failed the other way round: the column grew from the top and #next
     took whatever was left — measured at 707px on a 667px screen.
     Design record: _Overview/chessden/design/portrait-drill-mockup.html.
     Board rules (mobile-app-standing-orders.md §0): #10 — tracks minmax(0,1fr), never bare 1fr;
     min-width/height:0 on .sq; the board sizes off a container, not vh. #12 — nothing outside the
     fixed rows changes height mid-card: the zone's height is a fixed basis and everything that
     appears (verdict, eval line, reveal caption) appears INSIDE that window, scrolling it, never
     the board. Adding any sibling row outside the zone re-breaks this. */
  .den-play .wrap{height:100dvh;overflow-y:auto;overflow-x:hidden;position:relative;
    display:flex;flex-direction:column;padding:0 4px}
  /* the register (site.css) is fixed over the viewport, so the drill shell yields it the height */
  body.has-register.den-play .wrap{height:calc(100dvh - 51px - env(safe-area-inset-bottom,0px))}
  .den-play .bgglow{display:none}
  /* board fills the width on tall phones (Brian: the golden border always fills the width); the
     height term is the fallback sizing for engines without size containers — the @supports block
     below replaces it with true container sizing. */
  .den-play{--sq:min(64px,(100vw - 28px)/8,(100vh - 310px)/8)}
  .den-play[data-ev]{--sq:min(64px,(100vw - 28px)/8,(100vh - 310px)/8)}
  .den-play .board{padding:6px}
  .den-play .sq{min-width:0;min-height:0}
  /* the drill title: a slim identity row at the top of the shell (Goal 2 — always know which drill
     you're in). Sticky is harmless now the wrap is the viewport, and still guards the long alternate
     states (chooser, report wall) that scroll. */
  .den-play h1{position:sticky;top:0;z-index:6;margin:0 -10px 6px;padding:6px 12px;font-size:18px;
    background:#1c1830;-webkit-background-clip:border-box;background-clip:border-box;flex:0 0 auto;
    color:#e0b34c;-webkit-text-fill-color:#e0b34c;border-bottom:1px solid #3a3147}
  .den-play .hdr{position:sticky;top:0;z-index:6;background:#1c1830;border-bottom:1px solid #3a3147;
    margin:0 -4px 6px;padding:5px 10px;flex:0 0 auto}
  .den-play .hdr h1{position:static;margin:0;padding:0;border:none;background:none}
  .den-play .hdr .grounds img{height:22px}
  .den-play .aka{display:none}
  .den-play .den-epigraph{display:none}
  .den-play .sub{font-size:0;margin:0 0 8px;flex:0 0 auto}   /* keep only the "the grounds" back link */
  .den-play .sub a{font-size:13px}
  /* progress dots + score pills + motif chips → single compact scrollable rows */
  .den-play .progress{flex-wrap:nowrap;overflow-x:auto;margin:0 0 4px;flex:0 0 auto}
  .den-play .scorebar{flex-wrap:nowrap;overflow-x:auto;gap:8px;margin:0 0 4px;flex:0 0 auto}
  .den-play .mix{flex-wrap:nowrap;overflow-x:auto;margin:0 0 4px;flex:0 0 auto}
  /* the rows SCROLL, they never squeeze (v0.83.0 is the reference look, Brian 2026-08-04): these rows
     were always meant to overflow-x scroll, but flex children kept shrink:1, so pills squeezed below
     their text width and wrapped into tall two-line cards instead of scrolling. The site bar stays at
     its full height — v0.83.0 kept it. */
  .den-play .progress>*,.den-play .scorebar>*,.den-play .mix>*{flex:0 0 auto}
  .den-play .scorebar .pill{white-space:nowrap}
  .den-play .card{padding:8px 4px}
  .den-play .card .top{margin-bottom:2px}
  .den-play .census{display:none!important}    /* endgame's stats panel: desktop-only, keeps the drill no-scroll on phone */
  .den-play .mission,.den-play .prompt{margin:6px 0}
  .den-play .btns{flex-wrap:nowrap;gap:6px}
  .den-play .btns button{flex:1 1 0;min-width:0;padding:9px 6px;font-size:13px}
  .den-play .btns .iconbtn{width:44px;height:44px}
  .den-play .btns .iconbtn img{width:40px;height:40px}

  /* ---- the height chain, as landscape proved it: the pages set display INLINE from JS
     (`app.style.display='block'`), and an inline style beats a stylesheet rule — so the flex chain
     is forced through #app/#game, but NEVER while the JS is hiding the element. Both spellings are
     needed: server markup writes `display:none`, el.style.display serialises to `display: none`. */
  .den-play #app{flex:1;min-height:0}
  .den-play #game{flex:1;min-height:0;margin:0}
  .den-play #app:not([style*="display:none"]):not([style*="display: none"]){
    display:flex!important;flex-direction:column;gap:4px}
  .den-play #game:not([style*="display:none"]):not([style*="display: none"]){
    display:flex!important;flex-direction:column;gap:4px}

  /* SHAPE A — .main holds .left (the board) over .right (the zone). The board box is the flexible
     row; the zone is a FIXED-height scrolling window (rule #12), controls pinned at its bottom by
     margin-top:auto so the advance sits in the thumb zone whatever the card is saying above it. */
  .den-play .main{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;flex-wrap:nowrap;
    gap:6px;margin:0;align-items:stretch}
  .den-play .left{flex:1 1 auto;min-height:0;display:flex;align-items:center;justify-content:center;
    flex-direction:column;gap:3px}
  .den-play .left>.meta{flex:0 0 auto;margin:0;font-size:11px}   /* the Lineup's card evidence */
  .den-play .right{flex:0 0 clamp(118px,22dvh,180px);min-width:0;width:100%;min-height:0;
    display:flex;flex-direction:column;gap:5px;overflow-y:auto}
  .den-play .right .btns{margin-top:auto;flex:0 0 auto}
  /* verdicts size to their text and scroll inside themselves — they may never push the controls */
  .den-play .right .verdict,.den-play .right .verdictbox{flex:0 1 auto;min-height:0;overflow-y:auto}

  /* tactics: .boardwrap is its .left, carrying the eval bar beside the board */
  .den-play .boardwrap{flex:1 1 auto;min-height:0;display:flex;flex-direction:row;gap:5px;
    align-items:center;justify-content:center}
  .den-play .boardwrap .evbar{flex:0 0 11px;width:11px;align-self:stretch}

  /* SHAPE B — gallows/crown/siege stack everything in #game with a .playrow board. The playrow is
     the flexible row; everything after it is the fixed tail. */
  .den-play #game:has(>.playrow)>.playrow{flex:1 1 auto;min-height:0;margin:0;
    display:flex;align-items:center;justify-content:center}
  .den-play #game:has(>.playrow)>*:not(.playrow){flex:0 0 auto;min-width:0}

  /* ---- the board takes the largest square that fits BOTH axes of the row the shapes above hand
     it (the app's own sizing, rule #10). `container-type:size` stops the board's content feeding
     back into the height; `minmax(0,1fr)` is what makes a collapsed rank impossible — a bare 1fr
     keeps a content floor and steals the missing height from ONE rank. Engines without size
     containers keep the --sq fallback above: squashed, never overflowing. */
  /* Tactics is deliberately NOT here: its .main hangs under #deckbody with no height chain to the
     wrap, and a size container with no chained height collapses the board to nothing — the 14px
     sliver, the exact failure the landscape build hit first. It keeps the --sq fallback until its
     chain is proven on glass. */
  @supports (container-type:size){
    .den-play .left,.den-play #game:has(>.playrow)>.playrow{container-type:size}
    .den-play .left>.board,.den-play #game:has(>.playrow)>.playrow>.board{
      width:min(100cqw,100cqh);aspect-ratio:1;height:auto;box-sizing:border-box;
      grid-template-columns:repeat(8,minmax(0,1fr));grid-template-rows:repeat(8,minmax(0,1fr))}
    .den-play .left .pc,.den-play #game:has(>.playrow) .pc{width:92%;height:92%}
    .den-play .left .sq.dest::after,
    .den-play #game:has(>.playrow) .sq.dest::after{width:28%;height:28%}
    .den-play .left .sq.dest.cap::after,
    .den-play #game:has(>.playrow) .sq.dest.cap::after{width:79%;height:79%}
  }
}

/* ---------------------------------------------------------------------------
   MOBILE LANDSCAPE. Height is the scarce axis here, so sizes come off dvh/cqh
   rather than vw. Keyed on orientation + a height cap so laptops are excluded.
   --------------------------------------------------------------------------- */
@media (orientation:landscape) and (max-height:520px){
  /* The board sizes off its CONTAINER, not a hand-counted viewport budget: the pages carry
     different chrome (progress dots, score pills, a .top row) so any fixed subtrahend is wrong
     somewhere, and being wrong here clips the board silently.
     WIDTH comes from the container's HEIGHT (100cqh), not from aspect-ratio on an auto width:
     that way round is circular — the rail's auto width is resolved before the board's
     height-derived width is known, so the board silently overlaps the comms rail. */
  .den-play .board{box-sizing:border-box;width:100cqh;height:auto;aspect-ratio:1;padding:5px;
    grid-template-columns:repeat(8,1fr);grid-template-rows:repeat(8,1fr);flex:0 0 auto}
  .den-play .pc{width:92%;height:92%}
  .den-play .sq.dest::after{width:28%;height:28%}
  .den-play .sq.dest.cap::after{width:79%;height:79%}

  /* overflow-y stays auto, not hidden: the drill itself is sized to fit, but these pages also host
     long alternate states (the tactics trial chooser, the report wall, a finished-run card) that
     must still scroll. Clipping them would trade one unreachable control for another. */
  .den-play .wrap{height:100dvh;overflow-y:auto;overflow-x:hidden;position:relative;
    display:flex;flex-direction:column;padding:0 5px}
  .den-play .bgglow{display:none}

  /* The drill name rides IN the bar row beside the wordmark, so it costs the board no row at all.
     Every page title is short ("The Gallows", "Fork, Pin & Skewer", 9–20 chars); anything longer
     ellipsises rather than growing. Lifted out of flow, so it needs no space in the column. */
  .den-play h1{position:fixed;top:0;left:146px;z-index:5;height:26px;
    display:flex;align-items:center;max-width:152px;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
    font-size:13px;line-height:1;margin:0;padding:0;border:none;background:none;
    color:var(--gold,#e0b34c);-webkit-text-fill-color:var(--gold,#e0b34c)}
  .den-play .aka,.den-play .den-epigraph,.den-play .sub,
  .den-play .hint,.den-play .legend,.den-play .census,.den-play .foot{display:none!important}
  /* the drill header band (weekly, tactics) — its own row, so it pays for itself in board height */
  .den-play .hdr{margin:0 -5px 3px;padding:3px 8px;gap:8px;flex:0 0 auto}
  .den-play .hdr h1{font-size:15px;margin:0}
  .den-play .weeksub{font-size:13px}

  /* score pills stay — they're the run's state — but slim, on one line, and packed left so they
     read as a group instead of being flung to the edges by the desktop space-between. */
  .den-play .scorebar,.den-play .mix{flex:0 0 auto;flex-wrap:nowrap;justify-content:flex-start;
    overflow-x:auto;margin:0;gap:4px;font-size:11px;padding:0}
  .den-play .scorebar button{padding:1px 8px;font-size:11px}
  .den-play .pill{padding:0 8px;font-size:11px}
  /* the dots ride INSIDE the bar's row, centred between the wordmark and the account chip, so
     they cost no vertical space of their own. Lifted out of flow, hence .wrap's position:relative. */
  /* A BOUNDED band, not a free-floating centred box: anchored off the title on one side and the
     account chip on the other, the dots can never collide with either — a long display name eats
     into the band instead of overlapping it. They centre within whatever room is left. */
  .den-play .progress{position:fixed;top:0;left:308px;right:200px;z-index:4;
    height:26px;display:flex;align-items:center;justify-content:center;
    flex-wrap:nowrap;gap:4px;margin:0;padding:0;overflow:hidden;pointer-events:none}
  /* deck dots stay in flow — the bar band above is the drill PAGES' treatment, not a report-page deck's */
  .den-play .den-deck .progress{position:static;left:auto;right:auto;height:auto;pointer-events:auto;margin:0 0 8px}
  .den-play .progress .dot{height:4px;width:13px;flex:0 0 auto}
  .den-play #game > .top{font-size:10.5px;line-height:1.25;margin:0}

  /* The height has to reach the board through #app/#game, or every flex:1 below is inert.
     These pages set display INLINE from JS (`app.style.display='block'`), and an inline style beats
     a stylesheet rule — that breaks the chain, and because .main is a size container, a container
     with no height makes the board 100cqh = ~0. It renders as a 14px sliver: no board at all.
     So force it, but NEVER while the JS is hiding the element. Both spellings are needed: server
     markup writes `display:none`, el.style.display serialises to `display: none`.
     Both #game arms keep the :has() so neither out-specifies the other on ID weight. */
  .den-play #app{flex:1;min-height:0}
  .den-play #game{flex:1;min-height:0;margin:0}
  /* the turn + badge row packs left too: desktop's space-between throws the badge at the far edge,
     which in a narrow column parks it under the account chip */
  .den-play #game > .top{flex:0 0 auto;margin:0;font-size:10.5px;line-height:1.25;
    justify-content:flex-start;gap:8px}
  /* pages with no .main (crown/siege keep the Shape B grid on #game) stay a plain column */
  .den-play #app:not([style*="display:none"]):not([style*="display: none"]):not(:has(.main)){
    display:flex!important;flex-direction:column}

  /* the two-rail row for pages that own no #app (tactics: .wrap > #deck > #deckbody > .main) */
  .den-play .main{flex:1;min-height:0;flex-wrap:nowrap;gap:8px;align-items:stretch;
    container-type:size}

  /* SHAPE A — ONE grid on #app, so the board owns a full-height left column and EVERYTHING else
     rides to its right: score pills, the turn row, mission, verdict, controls.
     display:contents flattens #game and .main out of the box tree so their children become items
     of THIS grid — that is what lets the score pills, which sit two levels above .right, share a
     column with it without touching the markup. A display:contents box also can't be a container,
     so the board's 100cqh resolves against #app, which is where the height actually is. */
  /* The board is taken OUT of the row flow and pinned down the left edge, with the column simply
     indented past it. Row-SPANNING was the wrong tool: a spanning item's height is shared out
     across the rows it covers, so the board's height inflated the gaps between the score pills,
     the turn row and the comms block instead of leaving them stacked at the top. */
  .den-play #app:not([style*="display:none"]):not([style*="display: none"]):has(.main){
    display:flex!important;flex-direction:column;gap:4px;position:relative;
    padding:0 0 0 calc(100cqh + 8px);container-type:size}
  .den-play #app:has(.main) > #game,
  .den-play #app:has(.main) .main{display:contents;container-type:normal}
  .den-play #app:has(.main) > *,
  .den-play #app:has(.main) #game > *,
  .den-play #app:has(.main) .main > *{min-width:0;margin:0}
  .den-play #app:has(.main) .left{position:absolute;left:0;top:0;bottom:0;width:100cqh;
    display:flex;flex-direction:column;align-items:flex-start;gap:3px;overflow:hidden}
  .den-play .left > .board{width:100%}
  /* the Lineup hangs its card evidence under the board, so that board is a row shorter */
  .den-play .left:has(> .meta) > .board{width:calc(100% - 18px)}
  .den-play .left > .meta{flex:0 0 auto;margin:0;font-size:10.5px;text-align:left}
  /* .boardwrap is tactics' .left — same slot, and it carries the eval bar beside the board */
  .den-play .boardwrap{flex:0 0 auto;min-height:0;gap:5px;align-items:stretch}
  .den-play .boardwrap > .board{width:100cqh}
  .den-play .boardwrap .evbar{flex:0 0 11px;width:11px}
  .den-play .right{display:flex;flex-direction:column;gap:5px;min-width:0;min-height:0;
    align-self:stretch;flex:1}

  /* SHAPE B — gallows/crown/siege stack everything in .card with no .main. Grid gives the
     board a full-height first column and flows the rest into the second. */
  /* the size container is the GRID, not .playrow: container-type carries contain:size, and a
     .playrow that contributes no intrinsic width collapses its auto column to nothing, so the
     board escapes over the comms rail. The grid's own height comes from flex, so it is safe. */
  .den-play #game:not([style*="display:none"]):not([style*="display: none"]):has(> .playrow){
    display:grid!important;grid-template-columns:auto minmax(0,1fr);
    gap:0 8px;align-content:space-between;padding:4px 0;overflow:hidden;container-type:size}
  .den-play #game:has(> .playrow) > .playrow{grid-column:1;grid-row:1/span 99;
    align-self:start;min-height:0;margin:0;display:block}
  .den-play #game:has(> .playrow) > .playrow > .board{width:calc(100cqh - 8px)}
  .den-play #game:has(> .playrow) > *:not(.playrow){grid-column:2;min-width:0;margin:0}

  /* comms first, controls last */
  .den-play .mission,.den-play .prompt,.den-play .verdict,.den-play .verdictbox,
  .den-play .revealcap,.den-play .cliff,.den-play .meta,.den-play .turn{flex:0 0 auto;margin:0;font-size:12.5px}
  .den-play .revealcap{padding:5px 9px;font-size:11.5px}

  /* The verdict at desktop size (19px head, 14px body, three dashed sections) overran the column
     and pushed the controls off-screen. Shrink it, and let IT be the block that scrolls so the
     controls never move. The in-game recap inside it goes: the two knight buttons already reveal
     the best move and the move you played, so it was saying the same thing twice. */
  /* 0 1 auto, not 1 1 auto: it sizes to its text rather than stretching into a tall empty panel,
     but can still shrink and scroll inside itself when the text is long. */
  .den-play .verdict,.den-play .verdictbox{flex:0 1 auto;min-height:0;overflow-y:auto;
    padding:6px 9px;font-size:11.5px}
  .den-play .verdict h3{font-size:13px;line-height:1.25;margin:0 0 2px}
  .den-play .verdict p{font-size:11.5px;line-height:1.35;margin:2px 0}
  .den-play .verdict .gamefact{display:none}
  .den-play .src{font-size:10.5px;margin:0}
  /* controls left-justified, at the bottom of the comms column */
  .den-play .btns{margin-top:auto;justify-content:flex-start;flex-wrap:nowrap;gap:7px}
  .den-play .btns .iconbtn{width:42px;height:42px}
  .den-play .btns .iconbtn img{width:38px;height:38px}
  .den-play .btns button{flex:0 0 auto;padding:9px 13px;font-size:12.5px}

  /* the finale is already a fixed overlay; just keep it inside a short viewport */
  .den-play .finale .panel{max-height:94dvh;padding:14px 16px}
}
