/* Digi Puzzles — link engine. One copy serves every link page in reports/.
   Extracted 2026-10-15 from 2027-06-01-word-link-0297.html by tools/engine-shells.js.
   The page supplies its puzzle as window.DN_PUZZLE before loading this file.
   Edit here to change every link; keep element ids and localStorage keys
   stable (dn-track.js and players' saved progress depend on them). */
/* shared sheets first, so dark mode and the finish sheet are right from the first paint (18 Oct 2026) */
@import url("finish.css");
@import url("house.css");

/* ---------- Digi Puzzles house style (shared chrome, unchanged) ---------- */
:root{
  --paper:#EDEEF2; --card:#FFFFFF; --ink:#141726; --ink-soft:#5A5F73;
  --line:#141726; --brand:#2E5BFF; --brand-deep:#1B3B9E;
  --active:#FFD54A; --active-word:#FFF0B8; --good:#1F9E6B; --bad:#E4483B;
  --radius:14px; --r:11px;
  /* one shadow language: a resting chip barely leaves the paper, a hovered one
     lifts. Nothing on this page is outlined in ink except the header rule and
     the four mistake pips. */
  --sh-rest:0 1px 2px rgba(20,23,38,.07),0 0 0 1px rgba(20,23,38,.055);
  --sh-hover:0 4px 12px rgba(20,23,38,.11),0 0 0 1px rgba(20,23,38,.07);
  --mono:"Space Mono",ui-monospace,Menlo,Consolas,monospace;
  --sans:"Space Grotesk",system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
  /* the four group colours, easiest → hardest */
  --g1:#1F9E6B; --g2:#2E5BFF; --g3:#FFD54A; --g4:#141726;
}
*{box-sizing:border-box}
html,body{margin:0}
body{background:var(--paper);color:var(--ink);font-family:var(--sans);
  line-height:1.45;-webkit-font-smoothing:antialiased;padding:18px;display:flex;justify-content:center}
.app{width:100%;max-width:560px}
header{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;margin-bottom:11px;border-bottom:1.5px solid var(--ink);padding-bottom:10px}
.masthead{display:flex;align-items:center;gap:10px}
.mono-mark{flex:0 0 auto;width:32px;height:32px;border-radius:7px;background:var(--brand);color:#fff;
  font-family:var(--mono);font-weight:700;font-size:13px;letter-spacing:.04em;
  display:flex;align-items:center;justify-content:center;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.20),0 2px 6px rgba(46,91,255,.30)}
.mh-text{display:flex;flex-direction:column;line-height:1.05}
.mh-name{font-family:var(--sans);font-size:20px;font-weight:700;letter-spacing:-.01em;color:var(--ink)}
.mh-name .accent{color:var(--brand)}
.mh-eyebrow{font-family:var(--mono);font-size:9.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-soft);margin-top:2px}
/* the streak is reference, not a badge — it reads, it doesn't announce */
.streakbox{font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-soft);text-align:right;line-height:1.7;white-space:nowrap}
.streakbox b{color:var(--ink);font-size:12.5px}
.meta{font-family:var(--mono);font-size:11px;color:var(--ink-soft);letter-spacing:.08em;margin-bottom:16px;display:flex;gap:9px;align-items:center}
/* solid cobalt is reserved for the DP mark and the Submit button — the band
   label is a tint, so the eye isn't asked to rank three equal blues */
.tag{background:rgba(46,91,255,.11);color:var(--brand);border-radius:999px;padding:2.5px 9px;text-transform:uppercase;font-weight:700;font-size:9.5px;letter-spacing:.12em}
#timer{margin-left:auto;color:var(--ink);font-weight:700}
#rank{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--bad);font-weight:700}
main{position:relative;display:flex;flex-direction:column;align-items:center;gap:0}

/* ---------- the board ---------- */
/* --tf is the ONE tile font size, set once in JS from the longest tile in the
   set. Every tile shares it — ragged per-word sizing reads as a bug, not a
   design. */
.board{width:min(100%,420px);display:flex;flex-direction:column;gap:7px;--tf:14px}
.row{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
.tile{position:relative;min-width:0;aspect-ratio:1/.74;border:0;border-radius:var(--r);
  background:var(--card);color:var(--ink);font-family:var(--sans);font-weight:700;
  font-size:var(--tf);line-height:1.05;letter-spacing:.025em;
  display:flex;align-items:center;justify-content:center;text-align:center;padding:2px 5px;
  overflow:hidden;
  cursor:pointer;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent;
  box-shadow:var(--sh-rest);
  transition:background .2s ease,color .2s ease,box-shadow .16s ease,transform .16s cubic-bezier(.2,.9,.25,1.1)}
@media(hover:hover){.tile:hover{box-shadow:var(--sh-hover);transform:translateY(-1.5px)}}
/* the house signature: marigold marks the live choice, as in the Mini and the
   Doku. A chosen chip is HELD — it draws in and gives up its lift, so a
   selection reads as picked up rather than merely tinted. */
/* Solid marigold is the rank-3 GROUP colour, so a selection must not also be
   solid marigold — one hue, two meanings, sitting inches apart on the board.
   A held chip is the pale companion with a marigold ring: unmistakably the
   marigold move, never mistakable for a solved group. */
.tile.sel{background:var(--active-word);box-shadow:inset 0 0 0 2.5px var(--active);transform:scale(.955)}
@media(hover:hover){.tile.sel:hover{background:var(--active-word);box-shadow:inset 0 0 0 2.5px var(--active);transform:scale(.955)}}
.tile .tt{flex:0 0 auto;white-space:nowrap;display:inline-block}
/* A wrong guess used to be a small shake and one pip quietly hollowing out —
   easy to miss entirely on a phone. It now answers on three channels at once:
   the four chips go red and wobble, the pip you just spent flashes as it goes,
   and the device taps back. Redundant on purpose: any one of them can be
   missed, all three together cannot. */
.tile.miss{background:#FBE3E0;color:var(--bad);box-shadow:inset 0 0 0 2.5px var(--bad);
  animation:wobble .52s cubic-bezier(.36,.07,.19,.97)}
@keyframes wobble{
  0%,100%{transform:scale(.955) rotate(0)}
  15%{transform:scale(.955) translateX(-7px) rotate(-1.6deg)}
  30%{transform:scale(.955) translateX(7px) rotate(1.6deg)}
  45%{transform:scale(.955) translateX(-5px) rotate(-1.1deg)}
  60%{transform:scale(.955) translateX(5px) rotate(1.1deg)}
  78%{transform:scale(.955) translateX(-2px) rotate(-.4deg)}}
/* THE SETTLE — this type's signature beat. A correct four turns its group's
   colour in sequence, left to right, and only then collapses into the band.
   The flip is what teaches the colour ladder: it names the group before the
   label does, so the ranks stop being decoration. */
.tile.hit{color:#fff;transform:scale(1);box-shadow:var(--sh-rest)}
.tile.hit.g1{background:var(--g1)} .tile.hit.g2{background:var(--g2)}
.tile.hit.g3{background:var(--g3);color:var(--ink)} .tile.hit.g4{background:var(--g4)}

/* a solved group: one settled band. Bands stack in the order you found them —
   the board is a record of your solve, not a tidy difficulty ladder. */
/* CONSERVATION OF SPACE: a band is exactly as tall as the row of four chips it
   replaced (--bh, set from the measured chip height). So the board never
   collapses as you solve — the play area holds still, the four chips become one
   band in the same footprint, and the flight only has to squash horizontally. */
.band{min-height:var(--bh,0);display:flex;flex-direction:column;align-items:center;justify-content:center;
  border-radius:var(--r);padding:10px 12px;text-align:center;color:#fff;box-shadow:var(--sh-rest)}
.band .bname{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.17em;text-transform:uppercase;opacity:.78}
.band .bmem{font-family:var(--sans);font-weight:700;font-size:18px;letter-spacing:.015em;margin-top:6px;
  line-height:1.2;white-space:nowrap;overflow:hidden;max-width:100%}
.band .bmem span{display:inline-block;white-space:nowrap}
.band.r1{background:var(--g1)} .band.r2{background:var(--g2)}
.band.r3{background:var(--g3);color:var(--ink)} .band.r4{background:var(--g4)}
.band.r3 .bname{opacity:.58}   /* ink at 78% on marigold read olive, not quiet */
/* only the band just solved animates; a re-render must not replay the others */
.band.new{animation:bandIn .42s cubic-bezier(.2,.9,.25,1.06);transform-origin:top center}
@keyframes bandIn{from{transform:scaleY(.66);opacity:0}to{transform:none;opacity:1}}
.band.new .bname,.band.new .bmem{animation:bandText .32s ease .12s both}
@keyframes bandText{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}

/* ---------- lives + controls ---------- */
/* The four pips are the only countdown on the page, so they get the page's
   only hard ink and sit centred under the board where the eye lands after a
   wrong guess. */
.lives{display:flex;align-items:center;justify-content:center;gap:9px;font-family:var(--mono);
  font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-soft);margin-top:16px}
.pips{display:flex;gap:6px}
.pip{width:9px;height:9px;border-radius:50%;background:var(--ink);transition:background .25s ease,transform .25s cubic-bezier(.2,.9,.25,1.1)}
.pip.gone{background:transparent;box-shadow:inset 0 0 0 1.5px rgba(20,23,38,.18);transform:scale(.8)}
.pip.lost{animation:piplost .62s ease both}
@keyframes piplost{
  0%{background:var(--bad);box-shadow:none;transform:scale(1.65)}
  45%{background:var(--bad);box-shadow:none;transform:scale(.7)}
  100%{background:transparent;box-shadow:inset 0 0 0 1.5px rgba(20,23,38,.18);transform:scale(.8)}}
.msg{min-height:18px;margin:14px 0 0;font-size:14px;color:var(--ink-soft);text-align:center;
  font-family:var(--mono);letter-spacing:.04em;transition:color .15s}
.msg.bad{color:var(--bad);font-weight:700;letter-spacing:.1em;text-transform:uppercase;font-size:13px}
.controls{display:flex;gap:8px;align-items:center;justify-content:center;margin:10px 0 0;width:min(100%,420px)}
button{font-family:var(--sans);font-size:13.5px;font-weight:600;letter-spacing:.01em;
  border:1px solid rgba(20,23,38,.16);background:var(--card);color:var(--ink);
  border-radius:999px;padding:11px 18px;cursor:pointer;
  transition:background .15s,border-color .15s,color .15s,opacity .15s}
button:hover{border-color:rgba(20,23,38,.34)}
button.primary{background:var(--brand);color:#fff;border-color:var(--brand);padding:11px 26px}
button.primary:hover{background:var(--brand-deep);border-color:var(--brand-deep)}
/* Submit only turns cobalt when there is something to submit — a washed-out
   filled button reads as broken, an outline reads as waiting. */
button[disabled]{color:rgba(20,23,38,.42);cursor:not-allowed}
button.primary[disabled],button.primary[disabled]:hover{background:var(--card);border-color:rgba(20,23,38,.16);color:rgba(20,23,38,.42)}
button[disabled]:hover{border-color:rgba(20,23,38,.16);background:var(--card)}
:focus-visible{outline:2.5px solid var(--brand);outline-offset:2px}
/* How to play stays folded away: the board is the page, and the rules are
   there for the first visit, not every visit. */
.how{width:min(100%,420px);margin-top:26px;border-top:1px solid rgba(20,23,38,.10);padding-top:12px}
.how summary{font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-soft);cursor:pointer;list-style:none;display:flex;align-items:center;gap:7px}
.how summary::-webkit-details-marker{display:none}
.how summary::after{content:"+";font-size:13px;line-height:1}
.how[open] summary::after{content:"\2212"}
.how summary:hover{color:var(--ink)}
.how p{font-size:13.5px;line-height:1.55;color:var(--ink-soft);margin:10px 0 0}
.how b{color:var(--ink);font-weight:600}
/* the flight: four fixed-position stand-ins for the correct chips, which
   converge on the band's footprint and fuse into it */
.ghost{position:fixed;z-index:50;border-radius:var(--r);pointer-events:none;
  box-shadow:var(--sh-rest);transform-origin:center center;will-change:transform,opacity}
.ghost.r1{background:var(--g1)} .ghost.r2{background:var(--g2)}
.ghost.r3{background:var(--g3)} .ghost.r4{background:var(--g4)}
.band.arriving{opacity:0}
/* the fusion needs a full stop — one short swell as the four become one */
.band.landed{animation:landpulse .34s cubic-bezier(.2,.9,.25,1.2)}
@keyframes landpulse{0%{transform:scale(1)}40%{transform:scale(1.025)}100%{transform:scale(1)}}
.gate-fail{font-family:var(--mono);font-size:13px;color:var(--bad);padding:24px;line-height:1.7}

/* ---------- win celebration (shared, unchanged) ---------- */
.confetti{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:60;display:none}
.confetti.on{display:block}
.modal{position:fixed;inset:0;z-index:70;display:none;align-items:center;justify-content:center;padding:20px;
  background:rgba(20,23,38,.42);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
.modal.on{display:flex}
/* the results card was still speaking the old hard-shadow dialect — it now uses
   the same one shadow language as the chips and bands */
.modalcard{width:100%;max-width:344px;background:var(--card);border:0;border-radius:18px;
  padding:24px 22px 20px;text-align:center;
  box-shadow:0 24px 64px rgba(20,23,38,.32),0 0 0 1px rgba(20,23,38,.06);
  animation:rise .44s cubic-bezier(.2,.9,.25,1.1)}
@keyframes rise{from{transform:translateY(22px) scale(.94);opacity:0}to{transform:none;opacity:1}}
.modalcard .check{width:48px;height:48px;margin:0 auto 12px;border-radius:50%;background:var(--good);color:#fff;
  font-size:28px;font-weight:700;display:flex;align-items:center;justify-content:center}
.modalcard .check.miss{background:var(--bad)}
.modalcard h2{margin:0 0 3px;font-size:25px;letter-spacing:-.015em}
.modalcard .sub{font-family:var(--mono);font-size:9.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-soft);margin-bottom:18px}
.statrow{display:flex;gap:8px;margin-bottom:16px}
.stat{flex:1;border:0;border-radius:10px;padding:10px 4px;background:var(--paper)}
.stat b{display:block;font-family:var(--mono);font-size:21px;color:var(--ink);line-height:1.15}
.stat span{font-family:var(--mono);font-size:8.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-soft)}
.modalbtns{display:flex;gap:8px}
.modalbtns button{flex:1;margin:0}
@media(prefers-reduced-motion:reduce){
  .band.new,.modalcard{animation:none}
  .band.new .bname,.band.new .bmem{animation:none}
  .tile.miss,.band.landed{animation:none}
  .pip.lost{animation:none}
  .tile,.pip,button{transition:none}
}
footer{font-family:var(--mono);font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;color:rgba(20,23,38,.30);margin-top:18px;text-align:center}

/* ---------- APP MODE — no keyboard to fit, so the board takes the room ---------- */
html.app-mode,html.app-mode body{height:100%;overflow:hidden;overscroll-behavior:none}
html.app-mode body{padding:0;display:block}
html.app-mode .app{height:100vh;height:100dvh;height:100svh;height:var(--dn-vh,100svh);max-width:520px;margin:0 auto;
  display:flex;flex-direction:column;padding:calc(14px + var(--dn-top,env(safe-area-inset-top,0px))) 10px calc(10px + var(--dn-bot,env(safe-area-inset-bottom,0px)))}
html.app-mode header{margin-bottom:11px;padding-bottom:11px}
html.app-mode .mono-mark{width:28px;height:28px;font-size:12px}
html.app-mode .mh-name{font-size:17px}
html.app-mode .streakbox{font-size:12px;padding:3px 10px}
html.app-mode .meta{margin-bottom:18px;font-size:11px}
/* On a phone, main gets all the leftover height. Centring its contents left a
   void above the board and another below the buttons — the board floated in the
   middle of the screen instead of owning it. The board now takes the slack and
   the tiles grow into it. */
html.app-mode main{flex:1 1 auto;min-height:0;width:100%;gap:0;justify-content:flex-start}
html.app-mode .board{flex:1 1 auto;min-height:0;justify-content:flex-start}
html.app-mode .row{flex:0 0 auto}
html.app-mode .tile{aspect-ratio:auto;height:var(--th,74px)}
/* board pinned to the top of the play area, controls pinned to the bottom, and
   the slack lives in ONE deliberate gap between them rather than floating the
   board in the middle of two */
html.app-mode .lives{margin-top:auto;padding-top:16px}
html.app-mode .controls{margin:12px 0 2px}
html.app-mode .how,html.app-mode footer{display:none}
html.app-mode .msg{min-height:18px;margin:12px 0 0;font-size:12.5px}
html.app-mode .controls{margin:10px 0 0}
html.app-mode button:not(.tile){padding:12px 18px}
html.app-mode button.primary{padding:12px 28px}
@media (max-height:660px){
  html.app-mode .app{padding-top:calc(8px + var(--dn-top,env(safe-area-inset-top,0px)))}
  html.app-mode header{margin-bottom:7px;padding-bottom:7px}
  html.app-mode .mh-eyebrow{display:none}
  html.app-mode .meta{margin-bottom:10px}
}

/* 15 Oct 2026 — trimmed controls (Alec): one daily puzzle, nothing extra.
   Hidden rather than deleted: the engine binds to these ids, and the markup
   lives in each puzzle page, so the elements stay in the HTML but never show.
   The footer was a build note ("SHELL vN · … code, not eye") meant for us, not players. */
footer{display:none!important}

/* Readable words at the narrowest phone width: wrap only when a whole word
   cannot fit at the shared minimum size. Never crop it inside its tile. */
.board.wrap-words .tile .tt{white-space:normal;overflow-wrap:anywhere;word-break:break-all;text-wrap:balance;flex:0 1 auto;max-width:100%}
html.app-mode .controls{flex-wrap:wrap}
html.app-mode .controls button{flex:1 1 0;min-width:0;padding:10px 8px;white-space:normal;overflow-wrap:anywhere}

/* Short landscape phones: keep all four rows clear of the action buttons. */
@media (pointer:coarse) and (hover:none) and (orientation:landscape) and (max-height:500px){
  html.app-mode .app{max-width:900px;padding-left:max(10px,env(safe-area-inset-left,0px));padding-right:max(10px,env(safe-area-inset-right,0px))}
  html.app-mode main{display:grid;grid-template-columns:minmax(0,3fr) minmax(140px,1fr);
    grid-template-rows:auto minmax(0,1fr) auto;gap:8px 16px;
    grid-template-areas:"board lives" "board message" "board tools"}
  html.app-mode .board{grid-area:board;width:100%;height:100%}
  html.app-mode .lives{grid-area:lives;flex-direction:column;margin:0;padding:0;gap:6px}
  html.app-mode .msg{grid-area:message;margin:0}
  html.app-mode .controls{grid-area:tools;flex-direction:column;align-items:stretch;margin:0;width:100%}
  html.app-mode .controls button{flex:none}
}
