/* ============================================================================
   Puzzletype design system
   No build step. Tokens first, components second, page sections last.
   ========================================================================= */

/* ---------------------------------------------------------------- tokens */
:root{
  /* brand */
  --brand-1:#4f46e5;
  --brand-2:#7c3aed;
  --brand-3:#0d9488;
  --brand-grad:linear-gradient(135deg,var(--brand-1),var(--brand-2));

  /* surfaces */
  --bg:#fcfcfd;
  --bg-alt:#f5f6fa;
  --surface:#ffffff;
  --surface-2:#f4f5f9;
  --surface-3:#eceef5;

  /* ink */
  --ink:#0f1222;
  --ink-2:#3c4258;
  /* Was #6a7185, which measured 4.75:1 on --bg — a pass against WCAG AA and a
     fail against the people who actually use this site. The audience study in
     §12.24 puts 69% of US online-puzzle players at 55 or over, and --muted is
     not a decorative token here: it carries captions, ledes, table captions,
     the footer and every piece of secondary copy. At 7.3:1 it is still clearly
     secondary to --ink-2 (9.7:1) without being the first thing that fails on a
     laptop screen in daylight. */
  --muted:#4f5568;
  --line:#e4e7f0;
  /* PUZZLE GRID SEPARATOR — not a decorative divider. --line measures 1.24:1
     against --surface in both themes, which fails WCAG 1.4.11 (3:1 for a UI
     component boundary) and is unusable on a grid whose play is counting rows
     and columns. Measured: this is 3.03:1 light, 3.23:1 dark. Use --line for
     card and panel dividers; use this ONLY between puzzle cells. */
  --grid-line:#8d94a8;
  --line-2:#ccd2e0;

  /* semantic */
  --accent:var(--brand-1);
  --accent-ink:#4338ca;
  --accent-soft:#eef0fe;
  --good:#0e8a5f;
  --good-soft:#e6f6ef;
  --bad:#c5343d;
  --bad-soft:#fdecec;
  --warn:#b2731a;
  --warn-soft:#fdf4e5;

  /* figures */
  --fig-ink:#12162a;
  --fig-bg:#ffffff;

  /* shape */
  --r-sm:9px;
  --r-md:13px;
  --r-lg:18px;
  --r-xl:24px;

  /* TYPE SCALE. Before this existed the stylesheet carried 42 distinct rem
     font sizes, thirteen of them between .83 and .98 — differences of a sixth
     of a pixel that no reader can see and no designer chose. Every size is now
     snapped to one of these thirteen steps. Reach for a token rather than
     inventing a forty-third size. */
  --fs-2xs:.66rem;
  --fs-xs:.72rem;
  --fs-sm:.78rem;
  --fs-md:.84rem;
  --fs-base:.9rem;
  --fs-lg:.96rem;
  --fs-xl:1.02rem;
  --fs-2xl:1.16rem;
  --fs-3xl:1.28rem;
  --fs-4xl:1.5rem;
  --fs-5xl:1.75rem;
  --fs-6xl:2.3rem;
  --fs-7xl:2.7rem;
  --shadow-1:0 1px 2px rgba(15,18,34,.06), 0 1px 3px rgba(15,18,34,.04);
  --shadow-2:0 2px 4px rgba(15,18,34,.05), 0 12px 28px -14px rgba(15,18,34,.22);
  --shadow-3:0 4px 8px rgba(15,18,34,.06), 0 28px 56px -28px rgba(15,18,34,.34);

  --maxw:1160px;
  --font:'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  --display:'Plus Jakarta Sans', 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark){
  :root{
    --bg:#0b0d14;
    --bg-alt:#10131c;
    --surface:#141824;
    --surface-2:#1a1f2d;
    --surface-3:#222838;
    --ink:#eef0f7;
    --ink-2:#c2c8d8;
    --muted:#8b93a7;
    --line:#242a3a;
    --grid-line:#5d6987;
    --line-2:#333b50;
    --accent:#8b83ff;
    --accent-ink:#a79fff;
    --accent-soft:#1d1d3f;
    --good:#4ad08d;
    --good-soft:#0c2b1f;
    --bad:#ff8a86;
    --bad-soft:#331a1b;
    --warn:#e0a95a;
    --warn-soft:#2d2413;
    --fig-ink:#eef0f7;
    --fig-bg:#141824;
    --shadow-1:0 1px 2px rgba(0,0,0,.5);
    --shadow-2:0 2px 4px rgba(0,0,0,.45), 0 12px 28px -14px rgba(0,0,0,.7);
    --shadow-3:0 4px 8px rgba(0,0,0,.5), 0 28px 56px -28px rgba(0,0,0,.85);
  }
}

/* ---------------------------------------------------------------- base */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; scroll-behavior:smooth; scroll-padding-top:80px}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font-family:var(--font); font-size:16.5px; line-height:1.62;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
h1,h2,h3,h4,h5{font-family:var(--display); line-height:1.15; margin:0 0 .5em; font-weight:800; letter-spacing:-.025em}
h1{font-size:clamp(2.1rem,4.4vw,3.4rem); letter-spacing:-.038em}
h2{font-size:clamp(1.55rem,2.9vw,2.15rem); letter-spacing:-.03em}
h3{font-size:1.16rem; letter-spacing:-.02em}
p{margin:0 0 1rem}
a{color:var(--accent-ink); text-decoration:none}
a:hover{text-decoration:underline}
img,svg{max-width:100%}
strong{font-weight:650}
code{font-family:var(--mono); font-size:.88em; background:var(--surface-2); padding:.12em .4em; border-radius:6px}
kbd{
  font-family:var(--mono); font-size:.76em; background:var(--surface); color:var(--ink-2);
  border:1px solid var(--line-2); border-bottom-width:2px; border-radius:6px; padding:.12em .4em;
}
hr{border:0;border-top:1px solid var(--line);margin:2.5rem 0}
::selection{background:var(--accent-soft); color:var(--accent-ink)}
:where(button,a,[tabindex],summary):focus-visible{outline:3px solid var(--accent); outline-offset:2px; border-radius:8px}

.wrap{max-width:var(--maxw); margin:0 auto; padding:0 22px}
.narrow{max-width:790px}
.muted{color:var(--muted)}
.center{text-align:center}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
section{padding:64px 0}
@media (max-width:700px){section{padding:44px 0}}
.section-head{max-width:680px;margin-bottom:32px}
.section-head p{color:var(--muted);margin:0;font-size:1.02rem}

.eyebrow{
  display:inline-flex;align-items:center;gap:8px;
  font-family:var(--display); font-size:.78rem; font-weight:700; letter-spacing:.13em;
  text-transform:uppercase; color:var(--brand-3); margin-bottom:14px;
}
.eyebrow::before{content:'';width:22px;height:2px;background:currentColor;border-radius:2px}

/* ---------------------------------------------------------------- header */
.site-header{
  position:sticky; top:0; z-index:60;
  background:color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter:saturate(180%) blur(12px);
  -webkit-backdrop-filter:saturate(180%) blur(12px);
  border-bottom:1px solid var(--line);
}
.site-header .bar{display:flex;align-items:center;gap:18px;min-height:66px}
.brand{display:inline-flex;align-items:center;gap:10px;color:var(--ink);font-family:var(--display);font-weight:800;font-size:1.16rem;letter-spacing:-.035em}
.brand:hover{text-decoration:none}
.brand .mark{width:30px;height:30px;flex:none;border-radius:9px;display:block}
/* one flex item, so the 10px gap sits between mark and word — never inside the word */
.brand .wordmark{white-space:nowrap}
.brand em{font-style:normal;background:var(--brand-grad);-webkit-background-clip:text;background-clip:text;color:transparent}
/* ---------------------------------------------------------------- menu */
/* One nav, generated into every page by tools/build.js. Two top-level items,
   and a flyout beneath the second. Below 860px the whole thing collapses behind
   a Menu button and the flyout becomes an in-flow accordion. */

.nav{display:flex;align-items:center;gap:10px;margin-left:auto}
.menu{list-style:none;display:flex;align-items:center;gap:2px;margin:0;padding:0}
.menu > li{position:relative}

.menu > li > a, .menu-top{
  display:inline-flex;align-items:center;gap:7px;
  font:inherit;font-size:.96rem;font-weight:550;color:var(--ink-2);
  background:none;border:0;cursor:pointer;
  padding:8px 12px;border-radius:var(--r-sm);white-space:nowrap;
}
.menu > li > a:hover, .menu-top:hover{background:var(--surface-2);color:var(--ink);text-decoration:none}
.menu > li > a[aria-current="page"], .menu-top.is-active{background:var(--accent-soft);color:var(--accent-ink)}
.chev{width:11px;height:11px;flex:none;transition:transform .16s ease;opacity:.7}
.has-sub[data-open="true"] .chev{transform:rotate(180deg)}

.submenu{
  list-style:none;margin:0;padding:6px;
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-md);box-shadow:var(--shadow-3);
  min-width:236px;z-index:70;
}
.submenu{position:absolute;top:calc(100% + 7px);left:0;display:none}
.has-sub[data-open="true"] > .submenu{display:block}

.submenu li{position:relative}
.submenu a, .menu-note{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:9px 11px;border-radius:9px;
  font-size:.9rem;font-weight:500;color:var(--ink-2);
}
.submenu a:hover{background:var(--surface-2);color:var(--ink);text-decoration:none}
.submenu a[aria-current="page"], .submenu a.is-here{background:var(--accent-soft);color:var(--accent-ink)}
.menu-note{color:var(--muted);cursor:default}
.menu-sep{height:1px;background:var(--line);margin:5px 7px}
.soon{
  font-family:var(--display);font-size:.66rem;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--warn);background:var(--warn-soft);
  padding:3px 7px;border-radius:999px;flex:none;
}

.nav-toggle{
  display:none;align-items:center;gap:8px;
  font:inherit;font-family:var(--display);font-size:.9rem;font-weight:650;
  color:var(--ink);background:var(--surface);border:1px solid var(--line-2);
  padding:8px 13px;border-radius:10px;cursor:pointer;
}
.nav-toggle svg{width:15px;height:15px}

@media (max-width:860px){
  .site-header .bar{min-height:0;padding:11px 0;flex-wrap:wrap;gap:10px}
  /* the menu needs its own line, so the nav's children must be able to wrap */
  .nav{margin-left:auto;gap:8px;flex-wrap:wrap;justify-content:flex-end;max-width:100%}
  .nav-toggle{display:inline-flex}
  .menu{
    display:none;order:3;flex-direction:column;align-items:stretch;
    flex-basis:100%;width:100%;min-width:0;
    gap:2px;padding-top:10px;margin-top:4px;border-top:1px solid var(--line);
  }
  .nav[data-open="true"] .menu{display:flex}
  .menu > li > a, .menu-top{width:100%;justify-content:space-between;padding:11px 12px}
  .submenu{
    position:static;display:none;box-shadow:none;border:0;
    background:transparent;min-width:0;padding:2px 0 2px 12px;
    border-left:2px solid var(--line);margin:2px 0 6px 12px;
  }
  .has-sub[data-open="true"] > .submenu{display:block}
}

/* ---------------------------------------------------------------- buttons */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:46px; padding:0 20px; border-radius:11px;
  font:inherit; font-family:var(--display); font-weight:650; font-size:.96rem; cursor:pointer;
  border:1px solid var(--line-2); background:var(--surface); color:var(--ink);
  transition:transform .07s ease, background .16s ease, border-color .16s ease, box-shadow .16s ease;
}
.btn:hover{text-decoration:none;border-color:var(--muted);box-shadow:var(--shadow-1)}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--brand-grad); border-color:transparent; color:#fff; box-shadow:var(--shadow-2)}
.btn-primary:hover{color:#fff; filter:brightness(1.06); border-color:transparent}
.btn-sm{min-height:38px;padding:0 14px;font-size:.9rem;border-radius:9px}
.btn-lg{min-height:52px;padding:0 26px;font-size:1.02rem}

/* ---------------------------------------------------------------- chips */
.chip{
  display:inline-flex;align-items:center;gap:7px;
  font-family:var(--display); font-size:.84rem; font-weight:600;
  padding:7px 13px; border-radius:999px; cursor:pointer;
  background:var(--surface); color:var(--ink-2);
  border:1px solid var(--line); transition:all .14s ease;
}
.chip:hover{border-color:var(--line-2);color:var(--ink)}
.chip-on{background:var(--accent-soft); color:var(--accent-ink); border-color:transparent}
.chip-n{font-size:.72rem; opacity:.65; font-variant-numeric:tabular-nums}
.chip-mode{cursor:default}
.badge{
  display:inline-flex;align-items:center;gap:6px;
  font-family:var(--display); font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  padding:4px 10px;border-radius:999px;background:var(--surface-2);color:var(--muted);border:1px solid var(--line);
}
.badge-live{background:var(--good-soft);color:var(--good);border-color:transparent}
.badge-soon{background:var(--warn-soft);color:var(--warn);border-color:transparent}
.badge-accent{background:var(--accent-soft);color:var(--accent-ink);border-color:transparent}

/* segmented control */
.seg{display:inline-flex;background:var(--surface-3);border-radius:10px;padding:3px;gap:2px}
.seg button{
  font:inherit;font-family:var(--display);font-size:.84rem;font-weight:600;
  border:0;background:transparent;color:var(--muted);
  padding:5px 11px;border-radius:8px;cursor:pointer;text-transform:capitalize;
}
.seg button:hover{color:var(--ink)}
@media (max-width:620px){
  /* comfortable thumb targets once the head wraps onto its own line */
  .seg{width:100%}
  .seg button{flex:1;min-height:38px;padding:0 10px}
}
.seg button[aria-pressed="true"]{background:var(--surface);color:var(--accent-ink);box-shadow:var(--shadow-1)}

/* ---------------------------------------------------------------- cards */
.card{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-lg); padding:24px; box-shadow:var(--shadow-1);
}
.card h3{margin-bottom:8px}

/* ---- reserved space for every board mount ------------------------------
   CUMULATIVE LAYOUT SHIFT, and it was the largest real performance defect on
   the site. Every board is an EMPTY div in the HTML that JavaScript fills, so
   on each load between 660px and 1296px of content appeared at once and shoved
   the whole page down. Measured before this block:

     board (mcq)   685 desktop / 1005 mobile
     grid-board    749 desktop
     nonogram      805 desktop / 1296 mobile   <- on an 812px viewport

   min-height reserves that space up front. It is deliberately set a little
   UNDER the measured height: under-reserving leaves a small shift, whereas
   over-reserving leaves a visible empty gap under a short board, and a gap is
   worse than a few pixels of movement. Once the board renders taller than the
   reservation the min-height stops mattering, so nothing is constrained. */
[data-role="board"],[data-role="grid-board"],[data-role="slide"],
[data-role="nonogram"],[data-role="binary-puzzle"],[data-role="hitori"],
[data-role="nurikabe"],[data-role="star-battle"],[data-role="heyawake"],
[data-role="slitherlink"],[data-role="masyu"],[data-role="numberlink"],
[data-role="hashi"],[data-role="hidoku"],[data-role="mathcross"],
[data-role="zebra"],[data-role="word-search"],[data-role="tangram"]{
  min-height:620px;
}
[data-role="board"]{min-height:650px}
[data-role="grid-board"],[data-role="hidoku"],[data-role="mathcross"]{min-height:710px}
[data-role="nonogram"],[data-role="binary-puzzle"],[data-role="hitori"],
[data-role="nurikabe"],[data-role="star-battle"],[data-role="heyawake"]{min-height:850px}
[data-role="slitherlink"],[data-role="masyu"],[data-role="numberlink"],
[data-role="hashi"]{min-height:700px}

@media (max-width:760px){
  [data-role="board"]{min-height:940px}
  [data-role="grid-board"],[data-role="hidoku"],[data-role="mathcross"]{min-height:980px}
  [data-role="nonogram"],[data-role="binary-puzzle"],[data-role="hitori"],
  [data-role="nurikabe"],[data-role="star-battle"],[data-role="heyawake"]{min-height:1150px}
  [data-role="slitherlink"],[data-role="masyu"],[data-role="numberlink"],
  [data-role="hashi"]{min-height:980px}
  [data-role="slide"],[data-role="zebra"],[data-role="word-search"],
  [data-role="tangram"]{min-height:880px}
}

/* ---- the three proof cards on the homepage -----------------------------
   They were bare .card boxes: a heading, a grey paragraph, nothing to look at
   and nothing to tell one from another. Each now carries the mechanic it is
   claiming as an icon, a step numeral, and a gradient rule along the top edge
   in the brand hue — so the three read as a numbered argument rather than as
   three paragraphs that happen to sit side by side.

   The numeral is decorative and marked aria-hidden: a screen reader gets the
   heading, which already says what the card is, and "01" would only be noise. */
.proof-grid{align-items:stretch}
/* A HUE PER CARD, taken from the family palette rather than invented: those
   thirteen hues are already contrast-checked in both themes and already carry
   dark-mode variants (see --fam-* below). Three identical indigo cards read as
   one repeated thing; three hues read as three points in an argument. */
.proof-card{--pc:var(--fam-patterns-and-series)}
.proof-card:nth-child(2){--pc:var(--fam-constraint-grids)}
.proof-card:nth-child(3){--pc:var(--fam-letter-grids)}
.proof-card{
  position:relative;
  display:flex;
  flex-direction:column;
  padding:26px 24px 24px;
  overflow:hidden;
  transition:transform .18s ease,box-shadow .18s ease,border-color .18s ease;
}
.proof-card::before{
  content:"";
  position:absolute;
  inset:0 0 auto 0;
  height:3px;
  background:linear-gradient(90deg,var(--pc),color-mix(in srgb, var(--pc) 40%, transparent));
  opacity:.85;
}
.proof-ico{
  width:46px;height:46px;
  border-radius:14px;
  display:grid;place-items:center;
  margin-bottom:14px;
  color:var(--pc);
  background:linear-gradient(145deg,
    color-mix(in srgb, var(--pc) 18%, transparent),
    color-mix(in srgb, var(--pc) 7%, transparent));
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--pc) 20%, transparent);
  transition:transform .18s ease,box-shadow .18s ease;
}
.proof-ico svg{width:24px;height:24px;display:block}
/* the step numeral, set large and faint in the corner so it reads as a
   watermark rather than competing with the heading */
.proof-n{
  position:absolute;
  top:14px;right:18px;
  font-family:var(--display);
  font-weight:800;
  font-size:1.6rem;
  line-height:1;
  color:var(--pc);
  opacity:.16;
  letter-spacing:.02em;
}
.proof-card h3{font-size:1.06rem;line-height:1.35;margin-bottom:10px}
/* the paragraph takes the slack, so three cards of different text length still
   line their bottoms up instead of leaving one with a hole in it */
.proof-card p{margin-bottom:0;flex:1}
.proof-card:hover{
  transform:translateY(-3px);
  box-shadow:var(--sh-md,0 8px 22px rgba(0,0,0,.09));
  border-color:color-mix(in srgb, var(--pc) 42%, var(--line));
}
.proof-card:hover .proof-ico{
  transform:translateY(-1px) scale(1.05);
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--pc) 38%, transparent);
}
@media (prefers-reduced-motion:reduce){
  .proof-card,.proof-card:hover,.proof-ico,.proof-card:hover .proof-ico{transition:none;transform:none}
}
.card p:last-child{margin-bottom:0}
.grid{display:grid;gap:20px}
.grid-3{grid-template-columns:repeat(3,1fr)}
.grid-2{grid-template-columns:repeat(2,1fr)}
@media (max-width:900px){.grid-3,.grid-2{grid-template-columns:1fr}}

/* ---------------------------------------------------------------- hero */
.hero{position:relative; padding:72px 0 44px; overflow:hidden}
.hero::before{
  content:'';position:absolute;inset:-30% -10% auto -10%;height:560px;z-index:-1;
  background:
    radial-gradient(52% 46% at 22% 32%, color-mix(in srgb,var(--brand-1) 16%, transparent), transparent 70%),
    radial-gradient(44% 44% at 82% 18%, color-mix(in srgb,var(--brand-3) 14%, transparent), transparent 70%);
}
.hero-grid{display:grid;grid-template-columns:1.02fr .98fr;gap:56px;align-items:center}
@media (max-width:980px){.hero{padding:40px 0 28px}.hero-grid{grid-template-columns:1fr;gap:36px}}
.hero .lede{font-size:1.16rem;color:var(--ink-2);max-width:35em}
.hero .cta{display:flex;gap:12px;flex-wrap:wrap;margin-top:28px}
.trust{display:flex;flex-wrap:wrap;gap:8px 20px;margin-top:24px;font-size:.9rem;color:var(--muted)}
.trust span{display:inline-flex;align-items:center;gap:7px}
.trust svg{width:15px;height:15px;color:var(--brand-3);flex:none}

/* stat strip */
/* auto-fit, not repeat(4). Hardcoding four columns left the puzzle-set brief —
   which supplies three — rendering an empty grey box in the fourth slot. The
   strip should fit however many figures it is given. */
.stats-strip{
  display:grid;grid-template-columns:repeat(var(--cells,4),1fr);gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;
}
.stats-strip div{background:var(--surface);padding:20px 18px}
.stats-strip b{display:block;font-family:var(--display);font-size:1.75rem;font-weight:800;letter-spacing:-.03em;
  background:var(--brand-grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.stats-strip span{font-size:.84rem;color:var(--muted)}
@media (max-width:760px){.stats-strip{grid-template-columns:1fr 1fr}}

/* ---------------------------------------------------------------- silos */
.silo-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
@media (max-width:980px){.silo-grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:640px){.silo-grid{grid-template-columns:1fr}}
.silo{
  display:block;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:22px;color:inherit;
  transition:border-color .16s ease, transform .1s ease, box-shadow .16s ease;
}
a.silo:hover{text-decoration:none;border-color:var(--accent);transform:translateY(-3px);box-shadow:var(--shadow-3)}
.silo .top{display:flex;align-items:center;justify-content:space-between;gap:10px}
.silo .ico{
  width:42px;height:42px;border-radius:12px;display:grid;place-items:center;
  background:var(--accent-soft);color:var(--accent-ink);
}
.silo .ico svg{width:22px;height:22px}
.silo-parent{
  display:block;margin-top:14px;
  font-family:var(--display);font-size:.66rem;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;color:var(--muted);
}
.silo-parent + h3{margin-top:3px}
.silo h3{margin:14px 0 5px}
.silo p{margin:0;font-size:.9rem;color:var(--muted);line-height:1.55}
.silo ul{margin:14px 0 0;padding:0;list-style:none;display:flex;flex-wrap:wrap;gap:6px}
.silo ul li{font-size:.72rem;color:var(--ink-2);background:var(--surface-2);border-radius:7px;padding:4px 8px}

/* ---------------------------------------------------------------- figures */
.fig{display:block;width:100%;height:auto;color:var(--fig-ink)}
.fig .poly{stroke:var(--fig-ink);stroke-width:2.6;stroke-linejoin:round}
.fig .shade-0{fill:none}
.fig .shade-1{fill:url(#pt-hatch)}
.fig .shade-2{fill:var(--fig-ink)}
.fig .spokes line{stroke:var(--fig-ink);stroke-width:2.1;stroke-linecap:round}
.fig .pin{fill:var(--fig-bg);stroke:var(--fig-ink);stroke-width:2.2}
.fig .sat{fill:var(--fig-ink)}
.fig .dot{fill:var(--fig-ink)}

/* embedded figures: line-only drawings, so the ink is all there is */
.fig-embed .embed-lines line{stroke:var(--fig-ink);stroke-width:2.6;stroke-linecap:round}
.fig-embed .embed-hi line{stroke:var(--accent);stroke-width:5.2;stroke-linecap:round;opacity:.85}

/* counting figures: the options are numbers, still drawn as SVG so they scale
   and theme like every other option */
.fig-count .count-num{fill:var(--fig-ink);font-family:var(--display);font-weight:800;font-size:46px;letter-spacing:-.03em}

/* rebus: the words ARE the figure, so they are SVG text and theme like ink */
.fig-rebus .rb, .fig-phrase .rb{fill:var(--fig-ink);font-family:var(--display);font-weight:800;text-anchor:middle;letter-spacing:.02em}
.fig-rebus .rb-rule{stroke:var(--fig-ink);stroke-width:2.4}
.fig-rebus .rb-box{fill:none;stroke:var(--fig-ink);stroke-width:2.4}
.fig-rebus .rb-label-bg{fill:var(--fig-bg)}
.fig-rebus .rb-label{font-weight:700;letter-spacing:.08em}
.fig-rebus .rb-crack{fill:none;stroke:var(--fig-ink);stroke-width:2;stroke-dasharray:4 3;opacity:.55}
.fig-phrase .rb-answer{font-weight:700;letter-spacing:-.01em}

/* stimulus renderers: text, arrows, clock faces */
.fig .glyph{
  fill:var(--fig-ink); font-family:var(--mono); font-weight:600;
  text-anchor:middle; dominant-baseline:central;
}
.fig .arrow path{stroke:var(--fig-ink);stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill:none}
.fig .clock-face{fill:none;stroke:var(--fig-ink);stroke-width:3}
.fig .tick{stroke:var(--fig-ink);stroke-width:1.6;opacity:.7}
.fig .tick-major{stroke-width:3;opacity:1}
.fig .hand{stroke:var(--fig-ink);stroke-linecap:round}
.fig .hand-h{stroke-width:4.6}
.fig .hand-m{stroke-width:3}
.fig .hand-pin{fill:var(--fig-ink)}

/* paper folding: the sheet, its creases and its punched holes */
.fig .sheet{fill:var(--surface-2);stroke:var(--fig-ink);stroke-width:2.4;stroke-linejoin:round}
.fig .sheet-ghost{fill:none;stroke:var(--line-2);stroke-width:1.6;stroke-dasharray:4 4}
.fig .sheet-moving{fill:var(--accent-soft);stroke:var(--accent);stroke-width:1.6;stroke-dasharray:3 3;opacity:.85}
.fig .crease{stroke:var(--accent);stroke-width:2.2;stroke-dasharray:6 4}
.fig .fold-arrow line{stroke:var(--accent);stroke-width:2.2;stroke-linecap:round}
.fig .fold-arrow polygon{fill:var(--accent)}
.fig .punch{fill:var(--bg);stroke:var(--fig-ink);stroke-width:2.2}

/* cube nets: the flat net, and the folded cube in three dimensions */
.fig .net-cell{fill:var(--surface-2);stroke:var(--fig-ink);stroke-width:2}
.fig .net-hl{fill:var(--accent-soft);stroke:var(--accent);stroke-width:2.6}
.fig .net-sym{
  fill:var(--fig-ink);font-family:var(--display);font-weight:700;font-size:15px;
  text-anchor:middle;dominant-baseline:central;
}
.fig .cube-face{stroke:var(--fig-ink);stroke-width:2;stroke-linejoin:round}
.fig .cube-top{fill:var(--surface-2)}
.fig .cube-left{fill:var(--surface-3)}
.fig .cube-right{fill:var(--surface)}
.fig .cube-sym{
  fill:var(--fig-ink);font-family:var(--display);font-weight:700;font-size:16px;
  text-anchor:middle;dominant-baseline:central;
}
/* dice: pips on the isometric faces, and a single face as an answer option */
.fig .pip{fill:var(--fig-ink)}
.fig .face-plain{fill:var(--surface-2);stroke:var(--fig-ink);stroke-width:2}
.fig .word-text{
  fill:var(--fig-ink);font-family:var(--display);font-weight:700;font-size:19px;
  text-anchor:middle;dominant-baseline:central;
}
.fig.die-face{max-height:96px}
.opt .fig.die{max-height:118px}
.opt .fig.word{max-height:60px}

.fig.net{max-height:210px}
.opt .fig.net{max-height:120px}
.opt .fig.cube{max-height:120px}
.seq.seq-none{display:none}

/* ---------------------------------------------------------------- board */
.board{
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-xl);box-shadow:var(--shadow-2);overflow:hidden;
}
.board-head{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:13px 18px;border-bottom:1px solid var(--line);background:var(--surface-2);
}
.board-head .spacer{margin-left:auto}
.board-body{padding:22px 20px 24px}
@media (max-width:560px){.board-body{padding:16px 12px 18px}.board-head{padding:11px 12px}}
.board-prompt{margin:0 0 16px;font-size:.96rem;color:var(--muted)}
.opt-heading{font-family:var(--font);font-size:.96rem;font-weight:650;letter-spacing:0;margin:26px 0 0;color:var(--ink-2)}
.seedline{font-family:var(--mono);font-size:.78rem;color:var(--muted)}
.statline{font-size:.84rem;color:var(--ink-2);font-variant-numeric:tabular-nums}
.statline .flame{color:var(--brand-3);font-weight:600}
.hint{font-size:.78rem;color:var(--muted)}

.seq{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;align-items:center}
@media (max-width:620px){.seq{grid-template-columns:repeat(3,1fr)}}
.cell{
  position:relative;background:var(--surface);border:1px solid var(--line-2);
  border-radius:var(--r-md);padding:9px;aspect-ratio:1/1;
  display:flex;align-items:center;justify-content:center;
}
.cell .idx{position:absolute;top:6px;left:9px;font-size:.66rem;color:var(--muted);font-weight:650;font-variant-numeric:tabular-nums}
/* A is to B as C is to ? — three figures, a separator, and the gap */
.seq-analogy{display:flex;flex-wrap:wrap;align-items:stretch;justify-content:center;gap:10px}
.seq-analogy .cell{flex:1 1 120px;max-width:170px}
.analogy-sep{
  display:flex;align-items:center;justify-content:center;
  font-size:1.5rem;color:var(--muted);font-weight:700;padding:0 2px;
}
@media (max-width:620px){.analogy-sep{display:none}}

/* a 3×3 matrix — kept square and centred so the rows and columns read as lines */
.seq-grid{
  grid-template-columns:repeat(3,1fr);
  width:min(100%, 430px);margin-inline:auto;gap:10px;
}

/* a run of stages to read left to right, each captioned */
.seq-steps{grid-template-columns:repeat(var(--steps,3),1fr);gap:12px}
@media (max-width:620px){.seq-steps{grid-template-columns:repeat(2,1fr)}}
.cell-step{aspect-ratio:auto;flex-direction:column;gap:8px;padding:10px 8px 8px;background:var(--surface-2)}
.step-cap{
  font-family:var(--display);font-size:.72rem;font-weight:650;
  color:var(--muted);text-align:center;line-height:1.3;
}

/* one stimulus to transform, rather than a series to continue */
.seq-single{display:flex;justify-content:center}
.cell-single{
  aspect-ratio:auto; width:min(100%, 430px); padding:18px 20px;
  background:var(--surface-2); border-style:solid;
}
.cell-single .fig{max-height:190px}
.opt .stim{max-height:120px}

.cell.missing{
  border-style:dashed;border-color:var(--accent);
  background:var(--accent-soft);color:var(--accent-ink);
  font-family:var(--display);font-size:2.3rem;font-weight:800;
}

.options{display:grid;grid-template-columns:repeat(var(--opts,4),1fr);gap:12px;margin-top:14px}
@media (max-width:800px){.options{grid-template-columns:repeat(2,1fr)}}
@media (min-width:801px){.options{grid-template-columns:repeat(var(--opts,4),1fr)}}
.opt{
  position:relative;background:var(--surface);border:2px solid var(--line-2);
  border-radius:var(--r-md);padding:11px;cursor:pointer;font:inherit;color:inherit;
  display:flex;flex-direction:column;gap:6px;
  transition:border-color .13s ease, transform .08s ease, background .13s ease, box-shadow .13s ease;
}
.opt:hover:not(:disabled){border-color:var(--accent);box-shadow:var(--shadow-2)}
.opt:active:not(:disabled){transform:translateY(1px)}
.opt .label{
  align-self:flex-start;font-family:var(--display);font-weight:700;font-size:.78rem;color:var(--muted);
  background:var(--surface-2);border-radius:7px;padding:2px 9px;
}
.opt[data-state="correct"]{border-color:var(--good);background:var(--good-soft)}
.opt[data-state="wrong"]{border-color:var(--bad);background:var(--bad-soft)}
.opt[data-state="dim"]{opacity:.45}
.opt .verdict{
  position:absolute;top:9px;right:9px;width:25px;height:25px;border-radius:50%;
  display:none;align-items:center;justify-content:center;color:#fff;font-size:.84rem;font-weight:800;
}
.opt[data-state="correct"] .verdict{display:flex;background:var(--good)}
.opt[data-state="wrong"] .verdict{display:flex;background:var(--bad)}
.meta-row{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-top:18px}

/* ---------------------------------------------------------------- explanation */
.explain{margin-top:22px;border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;background:var(--surface)}
.explain[hidden]{display:none}
.explain-head{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:13px 18px;font-family:var(--display);font-weight:700;border-bottom:1px solid var(--line);
}
.explain-head .spacer{margin-left:auto}
.explain-head.ok{background:var(--good-soft);color:var(--good)}
.explain-head.no{background:var(--bad-soft);color:var(--bad)}
.pattern-tag{
  font-family:var(--font);font-size:.84rem;font-weight:500;
  background:var(--surface);color:var(--ink-2);border:1px solid var(--line);
  padding:5px 11px;border-radius:999px;
}
.pattern-tag:hover{text-decoration:none;border-color:var(--accent);color:var(--accent-ink)}
.explain-body{padding:20px 18px}
.explain-body h3{
  margin:0 0 12px;font-family:var(--display);font-size:.72rem;letter-spacing:.11em;
  text-transform:uppercase;color:var(--muted);font-weight:700;
}
.summary-line{font-size:1.02rem;color:var(--ink);margin:0 0 20px;font-weight:550;line-height:1.5}
.steps{list-style:none;margin:0 0 24px;padding:0;counter-reset:s}
.steps li{position:relative;padding:0 0 0 42px;margin-bottom:16px;counter-increment:s}
.steps li::before{
  content:counter(s);position:absolute;left:0;top:0;
  width:28px;height:28px;border-radius:9px;background:var(--accent-soft);color:var(--accent-ink);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--display);font-size:.78rem;font-weight:700;
}
.steps .tech{display:block;font-size:.72rem;color:var(--muted);margin-top:4px;font-family:var(--mono)}
.chain{
  font-family:var(--mono);font-size:.84rem;background:var(--surface-2);color:var(--ink-2);
  border-radius:8px;padding:6px 10px;display:inline-block;margin-top:7px;
  max-width:100%;overflow-x:auto;white-space:nowrap;
}
.whywrong{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.whywrong li{
  display:flex;gap:12px;align-items:flex-start;font-size:.96rem;color:var(--ink-2);
  background:var(--surface-2);border-radius:var(--r-md);padding:11px 13px;
}
.whywrong .tag{
  flex:none;width:25px;height:25px;border-radius:8px;background:var(--bad-soft);color:var(--bad);
  display:flex;align-items:center;justify-content:center;font-family:var(--display);font-weight:800;font-size:.78rem;
}
.whywrong .tag.ok{background:var(--good-soft);color:var(--good)}
.explain-foot{display:flex;gap:10px;flex-wrap:wrap;align-items:center;padding:14px 18px;border-top:1px solid var(--line);background:var(--surface-2)}

/* ---------------------------------------------------------------- pattern index */
.filters{display:grid;gap:10px;margin-bottom:18px}
.filter-row{display:flex;flex-wrap:wrap;gap:8px}
.filter-count{font-size:.84rem;color:var(--muted);margin:0 0 16px;font-variant-numeric:tabular-nums}
.pattern-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
@media (max-width:1000px){.pattern-grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:660px){.pattern-grid{grid-template-columns:1fr}}
.pattern-card{
  display:flex;gap:13px;align-items:flex-start;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  padding:15px;color:inherit;transition:border-color .14s ease, transform .09s ease, box-shadow .14s ease;
}
.pattern-card:hover{text-decoration:none;border-color:var(--accent);transform:translateY(-2px);box-shadow:var(--shadow-2)}
.pattern-no{
  flex:none;font-family:var(--mono);font-size:.72rem;color:var(--accent-ink);
  background:var(--accent-soft);border-radius:7px;padding:4px 7px;font-weight:600;
}
.pattern-body{display:block;min-width:0}
.pattern-name{display:block;font-family:var(--display);font-weight:700;font-size:.96rem;letter-spacing:-.015em;margin-bottom:3px}
.pattern-blurb{display:block;font-size:.84rem;color:var(--muted);line-height:1.5}
.pattern-meta{margin-left:auto;display:flex;flex-direction:column;align-items:flex-end;gap:5px;flex:none}
.lvl{
  font-family:var(--display);font-size:.66rem;font-weight:700;letter-spacing:.04em;
  padding:3px 8px;border-radius:999px;white-space:nowrap;
}
.lvl-easy{background:var(--good-soft);color:var(--good)}
.lvl-medium{background:var(--accent-soft);color:var(--accent-ink)}
.lvl-hard{background:var(--bad-soft);color:var(--bad)}
.pattern-fam{font-size:.72rem;color:var(--muted);white-space:nowrap}
@media (max-width:660px){
  /* the meta column drops onto its own row rather than squeezing the card wider
     than the viewport — long family names used to push it off screen */
  .pattern-card{flex-wrap:wrap}
  .pattern-body{flex:1 1 auto}
  .pattern-meta{
    flex-direction:row;align-items:center;
    margin-left:0;width:100%;padding-left:44px;gap:8px;
  }
  .pattern-fam{white-space:normal}
}

/* ---------------------------------------------------------------- crumbs & prose */
.crumbs{font-size:.84rem;color:var(--muted);padding:18px 0 0}
.crumbs a{color:var(--muted)}
.crumbs span{margin:0 7px;opacity:.55}
.prose h2{margin-top:2.4rem}
.prose h3{margin-top:1.8rem}
.prose ul,.prose ol{padding-left:1.25rem}
.prose li{margin-bottom:.45rem}
.prose ol li::marker{font-weight:700;color:var(--accent-ink)}
.faq details{
  border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface);margin-bottom:12px;
  transition:border-color .16s ease,box-shadow .16s ease;
}
.faq details:hover{border-color:var(--line-strong,var(--line));box-shadow:var(--shadow-sm,0 1px 2px rgba(15,23,42,.06))}
.faq details[open]{border-color:var(--brand-3)}
.faq summary{
  cursor:pointer;font-weight:600;font-family:var(--display);
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:17px 19px;list-style:none;line-height:1.45;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::marker{content:""}
.faq summary:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px;border-radius:var(--r-md)}
/* the +/− plate */
.faq summary::after{
  content:"";flex:0 0 26px;width:26px;height:26px;border-radius:7px;
  background:var(--bg-alt);
  background-image:linear-gradient(var(--muted),var(--muted)),linear-gradient(var(--muted),var(--muted));
  background-repeat:no-repeat;background-position:center center;
  background-size:11px 1.6px,1.6px 11px;
  transition:transform .18s ease,background-color .18s ease;
}
.faq details[open] summary::after{
  /* hide the vertical bar so the plate reads as a minus */
  background-size:11px 1.6px,0 0;
  transform:rotate(180deg);
}
.faq details[open] summary{padding-bottom:4px}
.faq details>*:not(summary){padding:0 19px;margin-bottom:0}
.faq details>*:not(summary):last-child{padding-bottom:18px}
.faq details p{color:var(--muted);margin-top:8px}
.faq details .table-wrap{margin-top:12px}
.callout{
  border:1px solid var(--line);border-left:3px solid var(--brand-3);
  background:var(--surface);border-radius:var(--r-md);padding:16px 18px;margin:22px 0;
}
.callout p:last-child{margin-bottom:0}

/* ---------------------------------------------------------------- footer */
.site-footer{border-top:1px solid var(--line);margin-top:48px;padding:44px 0 48px;background:var(--surface)}
/* Brand block on the left, then three link columns: Popular puzzles,
   Categories, Site. The brand keeps a wider track because it carries the
   description; the columns sit to its right rather than under it. */
.foot-grid{display:grid;grid-template-columns:1.6fr repeat(3,1fr);gap:40px}
@media (max-width:900px){.foot-grid{grid-template-columns:repeat(3,1fr);gap:28px}
  .foot-grid>div:first-child{grid-column:1/-1}}
@media (max-width:640px){.foot-grid{grid-template-columns:1fr 1fr;gap:26px}}
.site-footer h3{font-family:var(--display);font-size:.72rem;letter-spacing:.11em;text-transform:uppercase;color:var(--muted);margin-bottom:12px}
.site-footer ul{list-style:none;margin:0;padding:0}
.site-footer li{margin-bottom:8px}
/* Small text was the real readability problem, not the body copy. Measured on
   /nonogram/: footer links 14.56px and the legal line 13.12px, both well under
   the 16px that a majority-over-55 readership needs without zooming. Raised to
   15.5px and 14.2px — the footer is still visibly secondary to the prose. */
.site-footer a{color:var(--ink-2);font-size:.96rem}
/* the closing row: legal text on the left, XML sitemap on the right. The rule
   sits on the ROW, not on each <p>, or a two-child row draws two borders. */
.foot-legal-row{display:flex;flex-wrap:wrap;gap:12px 24px;align-items:baseline;
  justify-content:space-between;margin-top:32px;padding-top:20px;border-top:1px solid var(--line)}
.foot-legal-row .legal{margin-top:0;padding-top:0;border-top:0;max-width:74ch;line-height:1.65}
/* the claim is the part worth reading, so it takes the body ink and a little
   size while the copyright and the trademark pointer stay at --muted. Without
   this the whole row reads as one block of legal grey and the numbers — the
   only interesting thing in it — disappear. */
.foot-claim{color:var(--ink);font-size:.98rem;font-weight:700}
.legal{margin-top:32px;padding-top:20px;border-top:1px solid var(--line);font-size:.9rem;color:var(--muted)}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{transition:none!important;animation:none!important}
}

/* ----------------------------------------------------- puzzle sets */
@media (prefers-color-scheme:dark){}
.test-progress{height:4px;background:var(--line);overflow:hidden}
.test-progress span{display:block;height:100%;background:var(--brand-grad);transition:width .25s ease}
.test-score{display:flex;align-items:center;gap:20px;flex-wrap:wrap;padding-bottom:8px;border-bottom:1px solid var(--line);margin-bottom:20px}
.test-score-n{display:flex;align-items:baseline;gap:6px}
.test-score-n b{font-family:var(--display);font-weight:800;font-size:2.7rem;line-height:1;letter-spacing:-.04em}
.test-score-n span{color:var(--ink-2);font-weight:600}
.test-h3{font-size:1.02rem;margin:22px 0 10px}
.test-bytype{list-style:none;padding:0;margin:0 0 6px;display:grid;gap:7px}
.test-bytype li{display:grid;grid-template-columns:minmax(9ch,15ch) 1fr auto;align-items:center;gap:12px;font-size:.96rem}
.test-bar{height:7px;border-radius:5px;background:var(--line);overflow:hidden}
.test-bar i{display:block;height:100%;background:var(--brand-grad)}
.test-review{display:grid;gap:7px}
.test-item{border:1px solid var(--line);border-radius:10px;background:var(--surface);overflow:hidden}
.test-item>summary{display:grid;grid-template-columns:auto auto 1fr auto;align-items:center;gap:10px;padding:10px 13px;cursor:pointer;font-size:.96rem}
.test-item>summary::-webkit-details-marker{display:none}
.test-n{font-variant-numeric:tabular-nums;color:var(--ink-2);font-weight:700;min-width:2ch}
.test-verdict{font-weight:800;color:#d1483a}
.test-item.is-right .test-verdict{color:#1a8f6a}
.test-q{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.test-tag{font-size:.78rem;color:var(--ink-2);background:var(--bg-alt);border:1px solid var(--line);border-radius:99px;padding:2px 9px;white-space:nowrap}
.test-body{padding:2px 13px 15px;border-top:1px solid var(--line)}
.whywrong .tag-yours{background:var(--accent-soft);color:var(--accent-ink);border-color:transparent}
@media (max-width:620px){.test-tag{display:none}.test-bytype li{grid-template-columns:minmax(8ch,11ch) 1fr auto}}

/* ---------------------------------------------------- sliding puzzle */
.slide-wrap{display:grid;grid-template-columns:minmax(0,1fr) minmax(200px,270px);gap:26px;align-items:start;margin:8px 0 18px}
.slide-grid{display:grid;grid-template-columns:repeat(var(--n),1fr);gap:6px;aspect-ratio:1;max-width:420px;background:var(--bg-alt);border:1px solid var(--line);border-radius:14px;padding:6px}
.slide-tile{display:flex;align-items:center;justify-content:center;font-family:var(--display);font-weight:800;font-size:clamp(1rem,4.2vw,1.6rem);border:1px solid var(--line);border-radius:9px;background:var(--surface);color:var(--ink);cursor:default;transition:transform .09s ease,background .12s ease;padding:0}
.slide-tile.can{cursor:pointer;border-color:var(--accent);box-shadow:var(--shadow-1)}
.slide-tile.can:hover{transform:scale(1.04);background:var(--accent-soft);color:var(--accent-ink)}
.slide-tile.home{background:var(--accent-soft);color:var(--accent-ink);border-color:transparent}
.slide-tile.home.can{border-color:var(--accent)}
.slide-gap{border-radius:9px;background:transparent}
.slide-side{display:grid;gap:16px}
.slide-label{font-size:.84rem;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-2);margin:0 0 8px}
.slide-dl{display:grid;grid-template-columns:auto auto;gap:5px 12px;margin:0;font-size:.96rem}
.slide-dl dt{color:var(--ink-2)}
.slide-dl dd{margin:0;font-weight:700;text-align:right}
.slide-dl dd small{font-weight:500;color:var(--ink-2)}
.slide-choice{display:flex;gap:8px;margin-top:14px}
.slide-mini{display:grid;grid-template-columns:repeat(var(--n),1fr);gap:3px;max-width:150px;background:var(--bg-alt);border:1px solid var(--line);border-radius:8px;padding:4px}
.slide-mini span{display:flex;align-items:center;justify-content:center;aspect-ratio:1;font-size:.66rem;font-weight:700;background:var(--surface);border-radius:4px;color:var(--ink-2)}
.slide-mini span.is-gap{background:transparent}
.slide-note{margin-top:6px;padding:13px 15px;border-radius:10px;font-size:.96rem;line-height:1.55;border:1px solid var(--line);background:var(--surface)}
.slide-note.is-ok{border-left:3px solid var(--brand-3)}
.slide-note.is-no{border-left:3px solid #d1483a}
.slide-steps{margin:12px 0 0;padding-left:20px;columns:2;column-gap:26px;font-size:.9rem}
.slide-steps li{margin-bottom:3px;break-inside:avoid}
@media (max-width:760px){.slide-wrap{grid-template-columns:1fr}.slide-grid{max-width:100%;margin:0 auto}.slide-steps{columns:1}}

/* ------------------------------------------------------------ tables */
.table-wrap{overflow-x:auto;margin:18px 0 22px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}
/* Tables carry the reference material people come back for — the eleven cube
   nets, the magic sums, the overlap table — so they were the wrong place to be
   running 15px body and 13.8px headers. */
.table-wrap table{border-collapse:collapse;width:100%;min-width:440px;font-size:1.02rem}
.table-wrap th,.table-wrap td{padding:12px 14px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
.table-wrap thead th{font-family:var(--display);font-weight:700;font-size:.9rem;letter-spacing:.01em;background:var(--bg-alt);white-space:nowrap}
/* A caption defaults to centre-aligned with no spacing, which reads as a stray
   line floating above the table rather than as its title. */
.table-wrap caption{
  text-align:left;padding:11px 14px 10px;font-size:.9rem;line-height:1.45;
  border-bottom:1px solid var(--line);background:var(--surface);
}
/* zebra striping: a wide reference table is much easier to track across when
   the rows alternate, and it costs nothing */
.table-wrap tbody tr:nth-child(even){background:var(--bg-alt)}
.table-wrap tbody tr:last-child th,.table-wrap tbody tr:last-child td{border-bottom:0}
.table-wrap tbody tr:last-child td{border-bottom:0}
.table-wrap td strong{font-weight:700}

/* [hidden] must win over any class that sets its own display. .btn is
   inline-flex, which silently beat the attribute and left the solver buttons
   showing on the solvability boards, where they mean nothing. */
[hidden]{display:none !important}

/* ------------------------------------------ picture sliding puzzle */
.slide-choosers{display:grid;gap:12px;margin:0 0 16px}
.slide-pick{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.slide-pick>span{font-size:.84rem;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-2);min-width:62px}
.slide-pick .seg{flex-wrap:wrap}
.slide-tile.is-pic{background-repeat:no-repeat;padding:0;overflow:hidden;position:relative;border-color:var(--line)}
.slide-tile.is-pic>i{position:absolute;top:3px;left:3px;font-style:normal;font-size:.66rem;font-weight:700;line-height:1;padding:3px 5px;border-radius:5px;background:rgba(255,255,255,.82);color:#12162a}
.slide-tile.is-pic.home{border-color:var(--brand-3)}
.slide-tile.is-pic.can{border-color:var(--accent);box-shadow:var(--shadow-2)}
.slide-tile.is-pic.can:hover{transform:scale(1.03)}
.slide-preview{display:block;width:100%;max-width:170px;height:auto;border-radius:9px;border:1px solid var(--line)}
@media (max-width:620px){.slide-pick{gap:8px}.slide-pick>span{min-width:100%}}

.slide-cards{display:grid;gap:26px;margin-top:24px}
.slide-card{border:1px solid var(--line);border-radius:16px;background:var(--surface);padding:20px 20px 8px;box-shadow:var(--shadow-1)}
.slide-card-head{margin-bottom:14px}
.slide-card-head h2{font-size:1.28rem;margin:10px 0 6px}
.slide-card-head p{color:var(--ink-2);font-size:.96rem;margin:0;max-width:70ch}
.slide-card .board{border:0;box-shadow:none;background:transparent}
@media (max-width:620px){.slide-card{padding:16px 14px 6px;border-radius:13px}}

/* ------------------------------------------- homepage category browser */
/* A deliberately dark feature panel. It keeps its own colours in both themes
   rather than inverting, because the point of the block is that it stands out
   from the page around it. */
.hub-browser{--hb-bg:#141a2e;--hb-bg2:#0f1424;--hb-line:#2a3350;--hb-ink:#eef1f8;--hb-mut:#9aa6c4;--hb-acc:#8b9dff;
  display:grid;grid-template-columns:minmax(220px,290px) 1fr;background:var(--hb-bg2);border-radius:18px;overflow:hidden;box-shadow:var(--shadow-3)}
.hub-rail{background:var(--hb-bg);border-right:1px solid var(--hb-line);padding:20px 14px;display:flex;flex-direction:column;gap:2px}
.hub-rail-label{font-size:.66rem;letter-spacing:.13em;text-transform:uppercase;color:var(--hb-mut);padding:0 10px 12px}
.hub-tab{display:flex;align-items:flex-start;gap:11px;width:100%;text-align:left;background:none;border:0;border-radius:10px;padding:11px 10px;cursor:pointer;color:var(--hb-ink);border-left:3px solid transparent}
.hub-tab:hover{background:rgba(255,255,255,.05)}
.hub-tab[aria-selected="true"]{background:rgba(255,255,255,.07);border-left-color:var(--hb-acc)}
.hub-tab-ico{flex:none;width:32px;height:32px;border-radius:9px;background:rgba(255,255,255,.07);display:grid;place-items:center;color:var(--hb-acc)}
.hub-tab-ico svg{width:17px;height:17px}
.hub-tab-txt{display:grid;gap:2px;min-width:0}
.hub-tab-txt b{font-family:var(--display);font-size:.96rem;font-weight:700;line-height:1.25}
.hub-tab-txt i{font-style:normal;font-size:.78rem;color:var(--hb-mut);line-height:1.35}
.hub-panels{padding:26px 26px 28px;min-width:0}
.hub-panel-head{display:flex;align-items:center;gap:11px;margin-bottom:12px}
.hub-panel-ico{flex:none;width:36px;height:36px;border-radius:10px;background:rgba(139,157,255,.16);display:grid;place-items:center;color:var(--hb-acc)}
.hub-panel-ico svg{width:19px;height:19px}
.hub-panel-eyebrow{font-size:.84rem;color:var(--hb-mut)}
.hub-panel h3{font-family:var(--display);font-size:1.5rem;color:var(--hb-acc);margin:0 0 10px;letter-spacing:-.02em}
.hub-panel>p{color:var(--hb-mut);margin:0 0 20px;max-width:62ch;line-height:1.6}
.hub-cards{display:grid;grid-template-columns:1fr 1fr;gap:14px}

/* The --hb-* tokens are declared on .hub-browser, which is the homepage panel
   ONLY. The same card component is reused on the nine category hubs, where
   those variables do not exist — so `background:var(--hb-bg)` resolved to
   nothing and the cards rendered as bare text with no border, no background and
   no hover. Same class of fault as the twenty tables that shipped unstyled
   (§12.23) and the slide renderer with no stylesheet (§12.0): a component used
   outside the scope that defines its tokens.

   Every --hb-* now carries the light-page equivalent as its fallback, so the
   card is correct wherever it is used and the dark panel still overrides it. */
.hub-card{
  position:relative;
  display:grid;
  /* icon column, then the text — the icon spans both rows so title and
     description align to one another rather than to the glyph */
  grid-template-columns:auto 1fr;
  align-items:start;
  column-gap:14px;
  row-gap:7px;
  /* the right pad leaves room for the chevron so a long title never runs under it */
  padding:18px 40px 18px 20px;
  border-radius:var(--r-lg,14px);
  background:var(--hb-bg,var(--surface));
  border:1px solid var(--hb-line,var(--line));
  color:var(--hb-ink,var(--ink));
  overflow:hidden;
  isolation:isolate;
}
/* the family stripe: one hue per topical cluster, the same system the related
   cards use, so a colour means the same thing everywhere on the site.
   It fades down the card rather than running flat, so the colour reads as
   light falling on the edge instead of as a printed rule. */
.hub-card::before{
  content:"";
  position:absolute;
  inset:0 auto 0 0;
  width:4px;
  background:linear-gradient(to bottom,
    var(--fam,var(--hb-acc,var(--brand-3))),
    color-mix(in srgb, var(--fam,var(--brand-3)) 35%, transparent));
  opacity:.75;
  transition:opacity .18s ease,width .18s ease;
}
/* a wash of the family hue that only appears on hover. It sits UNDER the
   content (z-index:-1 inside the card's own stacking context, which is what
   `isolation:isolate` above is for) so it can never grey out the text. */
a.hub-card::after{
  content:"";
  position:absolute;
  inset:0;
  z-index:-1;
  background:radial-gradient(120% 100% at 0% 0%,
    color-mix(in srgb, var(--fam,var(--brand-3)) 13%, transparent), transparent 62%);
  opacity:0;
  transition:opacity .18s ease;
}
a.hub-card:hover::after{opacity:1}
/* the chevron: a card that is a link should look like one before it is hovered,
   so this is visible at rest and merely moves and brightens on hover. Drawn
   from two borders rather than a glyph, because a text arrow changes shape with
   whatever font actually loads. */
a.hub-card .hub-card-go{
  position:absolute;
  right:17px;
  top:24px;
  width:7px;height:7px;
  border-right:2px solid var(--fam,var(--hb-acc,var(--brand-3)));
  border-top:2px solid var(--fam,var(--hb-acc,var(--brand-3)));
  transform:rotate(45deg);
  opacity:.45;
  transition:opacity .18s ease,transform .18s ease;
}
a.hub-card:hover .hub-card-go{opacity:1;transform:rotate(45deg) translate(2px,-2px)}
a.hub-card{
  text-decoration:none;
  transition:border-color .15s ease,transform .15s ease,box-shadow .15s ease,background .15s ease;
}
a.hub-card:hover{
  border-color:color-mix(in srgb, var(--fam,var(--brand-3)) 55%, var(--hb-line,var(--line)));
  text-decoration:none;
  transform:translateY(-3px);
  box-shadow:var(--sh-md,0 8px 22px rgba(0,0,0,.09));
}
a.hub-card:hover::before{opacity:1;width:6px}
a.hub-card:hover .hub-card-t{color:var(--fam,var(--hb-acc,var(--brand-3)))}
a.hub-card:focus-visible{outline:2px solid var(--fam,var(--brand-3));outline-offset:2px}
.hub-card-t{transition:color .15s ease}
@media (prefers-reduced-motion:reduce){
  a.hub-card,a.hub-card:hover,.hub-card::before,a.hub-card::after{transition:none;transform:none}
  /* the chevron keeps its 45deg rotation — that is its SHAPE, not an animation.
     Blanket `transform:none` here would flatten it into a corner bracket. */
  a.hub-card .hub-card-go,a.hub-card:hover .hub-card-go{transition:none;transform:rotate(45deg)}
}
/* a family with a single page gets one full-width card rather than a gap */
.hub-cards>.hub-card:only-child{grid-column:1 / -1}
.hub-feats{
  margin:16px 0 0;font-size:.9rem;line-height:1.6;color:var(--hb-dim,var(--muted));
}
.hub-feats b{color:var(--hb-ink,inherit);font-weight:600}
.hub-card.is-soon{opacity:.62}
/* the mechanic icon: a second way to recognise a puzzle, in the family hue */
.hub-card-ico{
  grid-row:1 / span 2;
  flex:none;
  width:44px;height:44px;
  border-radius:13px;
  display:grid;place-items:center;
  color:var(--fam,var(--hb-acc,var(--brand-3)));
  /* a tinted medallion with a hairline ring, rather than a flat wash: the ring
     is what stops the icon dissolving into the card on a light background */
  background:linear-gradient(145deg,
    color-mix(in srgb, var(--fam,var(--brand-3)) 20%, transparent),
    color-mix(in srgb, var(--fam,var(--brand-3)) 8%, transparent));
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--fam,var(--brand-3)) 22%, transparent);
  transition:background .18s ease,transform .18s ease,box-shadow .18s ease;
}
.hub-card-ico svg{width:23px;height:23px;display:block}
a.hub-card:hover .hub-card-ico{
  background:linear-gradient(145deg,
    color-mix(in srgb, var(--fam,var(--brand-3)) 32%, transparent),
    color-mix(in srgb, var(--fam,var(--brand-3)) 14%, transparent));
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--fam,var(--brand-3)) 40%, transparent);
  transform:translateY(-1px) scale(1.05);
}
@media (prefers-reduced-motion:reduce){a.hub-card:hover .hub-card-ico{transform:none}}
/* a card with no icon (nothing generated one) must not leave an empty column */
.hub-card:not(:has(.hub-card-ico)){grid-template-columns:1fr}

/* "inside this family" — the puzzles a hub contains, named but NOT linked.
   The homepage links only to hubs (the silo rule), so these are deliberately
   plain <li>s: they tell a reader what is behind the card without becoming
   another route past the hub. */
.hub-inside{margin-top:22px;padding-top:18px;border-top:1px solid var(--hb-line,var(--line))}
.hub-inside-h{
  margin:0 0 11px;
  font-size:var(--fs-2xs,.7rem);
  letter-spacing:.12em;
  text-transform:uppercase;
  color:var(--hb-mut,var(--muted));
}
.hub-inside-list{
  list-style:none;
  margin:0;padding:0;
  display:flex;flex-wrap:wrap;gap:7px;
}
.hub-inside-list li{
  /* --fs-xs computes to 11.5px here, under the floor §12.24 set for an audience
     that measures 69% aged 55+; --fs-sm is the smallest step that stays legible */
  font-size:var(--fs-sm,.94rem);
  line-height:1.2;
  padding:6px 11px;
  border-radius:999px;
  color:var(--hb-ink,var(--ink));
  background:var(--hb-bg,var(--bg-alt));
  border:1px solid var(--hb-line,var(--line));
}

.hub-card-t{font-family:var(--display);font-weight:700;font-size:1.02rem}
/* --hb-mut is homepage-only too; without the fallback the description inherited
   the card's full-strength ink on every hub page, so title and body read at the
   same weight and the card had no visual hierarchy at all */
.hub-card-d{font-size:.84rem;color:var(--hb-mut,var(--muted));line-height:1.5}
.hub-browser .btn-primary{background:var(--hb-acc);color:#131a2e;border-color:transparent}
.hub-browser .btn-primary:hover{filter:brightness(1.08)}
/* with no JavaScript every panel simply stacks, headed by its own title */
.hub-browser:not([data-enhanced]) .hub-panel + .hub-panel{margin-top:30px;padding-top:26px;border-top:1px solid var(--hb-line)}
@media (max-width:860px){
  .hub-browser{grid-template-columns:1fr}
  .hub-rail{flex-direction:row;overflow-x:auto;border-right:0;border-bottom:1px solid var(--hb-line);padding:14px;gap:8px;scrollbar-width:thin}
  .hub-rail-label{display:none}
  .hub-tab{width:auto;flex:none;border-left:0;border-bottom:3px solid transparent;border-radius:10px 10px 0 0;padding:9px 12px}
  .hub-tab[aria-selected="true"]{border-left-color:transparent;border-bottom-color:var(--hb-acc)}
  .hub-tab-txt i{display:none}
  .hub-panels{padding:20px 16px 22px}
  .hub-cards{grid-template-columns:1fr}
  .hub-panel h3{font-size:1.28rem}
}

/* ------------------------------------------------------------- tangram */
/* The seven tans get seven distinct fills. They are derived from the brand
   ramp rather than picked ad hoc, so dark mode is a token swap and the pieces
   stay distinguishable from each other at small sizes in both themes. */
.fig-tangram{width:100%;height:auto;max-width:520px;display:block;margin:0 auto}
.fig-tan{width:auto;height:26px;display:block}

/* the silhouette is the union of its atoms; the border is stroked separately
   as unordered segments, so no ring-walk can break it into fragments */
.tg-fill{fill:var(--fig-muted,#d7dbe8);stroke:none}
.tg-edge{fill:none;stroke:var(--fig-ink);stroke-width:2.5;stroke-linecap:round}
.tg-piece{stroke:var(--surface);stroke-width:1.5;stroke-linejoin:round}

.tg-lg1{fill:#4f46e5}
.tg-lg2{fill:#6366f1}
.tg-med{fill:#0ea5e9}
.tg-sq{fill:#14b8a6}
.tg-par{fill:#f59e0b}
.tg-sm1{fill:#ec4899}
.tg-sm2{fill:#a855f7}

.tg-stage{margin:18px 0 22px;padding:14px;background:var(--bg-alt);border-radius:var(--r-md)}

.tg-tray{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0 16px}
.tg-tray-item{
  display:flex;align-items:center;gap:8px;cursor:pointer;
  border:1px solid var(--line);border-radius:var(--r-sm,8px);
  background:var(--surface);padding:7px 11px;font:inherit;font-size:.84rem;
  color:var(--ink);transition:border-color .15s ease,box-shadow .15s ease;
}
.tg-tray-item:hover{border-color:var(--brand-3)}
.tg-tray-item.is-on{border-color:var(--brand-3);box-shadow:0 0 0 2px var(--brand-3);font-weight:600}
.tg-tray-item span{white-space:nowrap}

.tg-controls{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:14px}
.tg-orient{font-size:.84rem;color:var(--muted)}
.tg-note{
  font-size:.96rem;line-height:1.6;color:var(--ink-2);
  background:var(--bg-alt);border-radius:var(--r-md);padding:12px 14px;margin-bottom:14px;
}
.tg-actions{display:flex;flex-wrap:wrap;gap:8px}

@media (max-width:520px){
  .tg-tray-item span{display:none}
  .tg-tray-item{padding:8px}
  .tg-stage{padding:8px}
}

/* ------------------------------------------- any board + its 60-shape picker */
/* The catalogue sits beside the board rather than below it, because on this
   page it is a picker, not a directory. It collapses under the board on
   narrow screens, where a side rail would squeeze the puzzle. */
.play-layout{
  display:grid;grid-template-columns:minmax(0,1fr) 250px;
  gap:22px;align-items:start;margin-top:24px;
}
.pick{
  border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface);padding:15px 16px 17px;position:sticky;top:16px;
}
.pick .slide-label{margin:0 0 3px}
.pick-help{font-size:.84rem;color:var(--muted);margin:0 0 12px;line-height:1.5}

.pick-nums{display:grid;grid-template-columns:repeat(6,1fr);gap:6px}
.pick-nums .pat-num{
  display:flex;align-items:center;justify-content:center;
  aspect-ratio:1/1;border:1px solid var(--line);border-radius:7px;
  font-size:.78rem;font-weight:600;font-variant-numeric:tabular-nums;
  color:var(--ink-2);text-decoration:none;background:var(--bg-alt);
  transition:background .12s ease,color .12s ease,border-color .12s ease;
}
.pick-nums .pat-num:hover{background:var(--brand-3);border-color:var(--brand-3);color:#fff}
.pick-nums .pat-num:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px}
.pick-nums .pat-num[aria-current="true"]{
  background:var(--brand-3);border-color:var(--brand-3);color:#fff;
  box-shadow:0 0 0 2px color-mix(in srgb, var(--brand-3) 35%, transparent);
}
/* levels stay legible without a legend: a quiet left edge per difficulty */
.pick-nums .pat-num[data-level="easy"]{box-shadow:inset 3px 0 0 #14b8a6}
.pick-nums .pat-num[data-level="medium"]{box-shadow:inset 3px 0 0 #f59e0b}
.pick-nums .pat-num[data-level="hard"]{box-shadow:inset 3px 0 0 #ec4899}
.pick-nums .pat-num[aria-current="true"][data-level]{box-shadow:0 0 0 2px var(--brand-3)}

@media (max-width:900px){
  .play-layout{grid-template-columns:1fr}
  .pick{position:static}
  /* MORE columns on a phone, not fewer — but only because the cells are now a
     fixed 44px floor rather than a share of the width. auto-fill fits as many
     44px targets as the column allows (six or seven on a 375px screen instead
     of five), turning twelve rows of numbers into nine without shrinking a
     single tap target below the 44px minimum. */
  .pick-nums{grid-template-columns:repeat(auto-fill,minmax(44px,1fr));gap:7px}
}
@media (max-width:520px){
  .pick{padding:13px 11px 15px}
  .pick-nums{gap:6px}
  .pick-nums .pat-num{font-size:.84rem}
}

/* The collapsible picker — see collapseOnNarrow() in pattern-index.js.
   Open and summary-less on a desktop; shut and openable on a phone. */
.pick-collapse>summary{display:none}
.pick.pick-mobile .pick-collapse>summary{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  list-style:none;cursor:pointer;
  padding:11px 13px;margin:2px 0 0;
  min-height:44px;
  border:1px solid var(--line);border-radius:8px;
  background:var(--bg-alt);color:var(--ink);
  font-weight:600;font-size:var(--fs-sm);
}
.pick.pick-mobile .pick-collapse>summary::-webkit-details-marker{display:none}
.pick.pick-mobile .pick-collapse>summary::marker{content:""}
.pick.pick-mobile .pick-collapse>summary:hover{border-color:var(--brand-3)}
.pick.pick-mobile .pick-collapse>summary:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px}
.pick-summary-n{
  flex:0 0 auto;min-width:26px;height:22px;
  display:inline-flex;align-items:center;justify-content:center;
  border-radius:11px;background:var(--surface);color:var(--muted);
  font-size:var(--fs-2xs);font-weight:700;
}
.pick.pick-mobile .pick-collapse[open]>summary{margin-bottom:12px}
.pick.pick-mobile .pick-collapse>summary>span:first-child::after{content:" \25BE"}
.pick.pick-mobile .pick-collapse[open]>summary>span:first-child::after{content:" \25B4"}

/* picker filters + the play column */
.play-board{min-width:0}
.pick-filters{display:flex;flex-direction:column;gap:8px;margin:0 0 12px}
.pick-row{display:flex;flex-wrap:wrap;gap:5px}
.chip-sm{font-size:.72rem;padding:4px 9px}
.pick-fam select{
  width:100%;font:inherit;font-size:.78rem;padding:6px 8px;
  border:1px solid var(--line);border-radius:7px;
  background:var(--bg-alt);color:var(--ink);
}
.pick-nums .pat-num.is-dim{opacity:.26;pointer-events:none}
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* --------------------------------------------- number and letter series */
/* The stimulus for these types is the row of terms itself, so it is text —
   selectable, searchable, and read out without needing a description. */
.seq-terms{
  display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:10px;padding:22px 14px;background:var(--bg-alt);border-radius:var(--r-md);
}
.seq-terms .term{
  font-family:var(--display);font-size:1.5rem;font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--ink);
  background:var(--surface);border:1px solid var(--line);
  border-radius:9px;padding:9px 14px;min-width:52px;text-align:center;
}
.seq-terms .term-gap{
  color:var(--brand-3);border-style:dashed;border-color:var(--brand-3);
  background:transparent;
}
.opt-text{
  display:flex;align-items:center;justify-content:center;
  font-family:var(--display);font-size:1.5rem;font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--ink);
  min-height:74px;padding:6px 4px;letter-spacing:.01em;
}
@media (max-width:520px){
  .seq-terms{gap:7px;padding:16px 10px}
  .seq-terms .term{font-size:1.16rem;padding:7px 10px;min-width:42px}
  .opt-text{font-size:1.28rem;min-height:58px}
}

/* ------------------------------------------------- number grids: futoshiki + calcudoku */
/* Must scroll. Without overflow-x the grid had nowhere to go on a phone, so
   .gb-grid was capped at 300px instead — which on a 9x9 killer sudoku means
   33px cells, well under the 44px tap floor. Measured at 375px before the fix:
   33.4 x 36.4. */
.gb-wrap{display:flex;justify-content:center;margin:18px 0;overflow-x:auto;padding-bottom:4px}
.gb-grid{
  position:relative;display:grid;
  grid-template-columns:repeat(var(--n),1fr);
  gap:0;background:var(--surface);
  border:2px solid var(--fig-ink);border-radius:8px;
  width:min(100%,var(--gb-max,430px));aspect-ratio:1/1;
}
.gb-cell{
  position:relative;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--grid-line);background:transparent;cursor:pointer;
  font:inherit;font-family:var(--display);font-weight:700;
  font-size:clamp(1rem,4.2vw,1.5rem);color:var(--brand-3);
  font-variant-numeric:tabular-nums;padding:0;
}
.gb-cell:hover{background:var(--bg-alt)}
.gb-cell.is-given{color:var(--ink);cursor:default;background:var(--bg-alt)}
.gb-cell.is-sel{box-shadow:inset 0 0 0 3px var(--brand-3);background:var(--bg-alt)}
.gb-cell.is-wrong{color:#e11d48}
.gb-cell.is-revealed{color:var(--muted)}
.gb-cell:focus-visible{outline:2px solid var(--brand-3);outline-offset:-2px}

/* calcudoku cage outlines: a thick edge only where the cage ends */
.gb-calcudoku .gb-cell.cage-t{border-top:2.5px solid var(--fig-ink)}
.gb-calcudoku .gb-cell.cage-b{border-bottom:2.5px solid var(--fig-ink)}
.gb-calcudoku .gb-cell.cage-l{border-left:2.5px solid var(--fig-ink)}
.gb-calcudoku .gb-cell.cage-r{border-right:2.5px solid var(--fig-ink)}
.gb-target{
  position:absolute;top:2px;left:4px;font-size:.62em;font-weight:700;
  color:var(--ink-2);letter-spacing:.01em;line-height:1;
}

/* futoshiki signs sit in the gaps, centred on the shared edge */
.gb-sign{
  position:absolute;transform:translate(-50%,-50%);
  font-family:var(--display);font-weight:800;font-size:.96rem;
  color:var(--brand-3);background:var(--surface);
  padding:0 2px;line-height:1;pointer-events:none;
}
.gb-sign-v{font-size:.78rem}

.gb-pad{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin-bottom:14px}
.gb-key{
  min-width:46px;min-height:46px;font:inherit;font-family:var(--display);
  font-weight:700;font-size:1.02rem;cursor:pointer;
  border:1px solid var(--line);border-radius:9px;
  background:var(--surface);color:var(--ink);
}
.gb-key:hover{border-color:var(--brand-3);background:var(--bg-alt)}
.gb-key-clear{min-width:auto;padding:0 14px;font-size:.84rem;font-weight:600}

.gb-note{
  font-size:.96rem;line-height:1.6;color:var(--ink-2);
  background:var(--bg-alt);border-radius:var(--r-md);padding:12px 14px;margin-bottom:14px;
}
.gb-actions{display:flex;flex-wrap:wrap;gap:8px}

@media (max-width:520px){
  .gb-grid{--gb-max:300px}
  .gb-key{min-width:40px;min-height:44px;font-size:.96rem}
  .gb-sign{font-size:.78rem}
}

/* killer sudoku: 9x9, so smaller type and the 3x3 boxes must read as boxes */
.gb-killer-sudoku{--gb-max:520px}
.gb-killer-sudoku .gb-cell{font-size:clamp(.8rem,2.6vw,1.1rem)}
.gb-killer-sudoku .gb-target{font-size:.55em;top:1px;left:2px}
.gb-killer-sudoku .gb-cell.box-t{border-top:2px solid var(--fig-ink)}
.gb-killer-sudoku .gb-cell.box-l{border-left:2px solid var(--fig-ink)}
/* the cage outline is dashed so it never competes with the box grid */
.gb-killer-sudoku .gb-cell.cage-t{border-top:1.5px dashed var(--brand-3)}
.gb-killer-sudoku .gb-cell.cage-b{border-bottom:1.5px dashed var(--brand-3)}
.gb-killer-sudoku .gb-cell.cage-l{border-left:1.5px dashed var(--brand-3)}
.gb-killer-sudoku .gb-cell.cage-r{border-right:1.5px dashed var(--brand-3)}
.gb-killer-sudoku .gb-cell.box-t.cage-t{border-top:2px solid var(--fig-ink)}
.gb-killer-sudoku .gb-cell.box-l.cage-l{border-left:2px solid var(--fig-ink)}
@media (max-width:520px){ .gb-killer-sudoku{--gb-max:320px} }

/* ------------------------------------------------------------ clock face */
.fig-clock{width:100%;height:auto;max-width:230px;display:block;margin:0 auto}
.ck-face{fill:var(--fig-bg);stroke:var(--fig-ink);stroke-width:2.5}
.ck-tick{stroke:var(--fig-ink);stroke-width:1.8;stroke-linecap:round}
.ck-hour{stroke:var(--fig-ink);stroke-width:4;stroke-linecap:round}
.ck-min{stroke:var(--brand-3);stroke-width:2.6;stroke-linecap:round}
.ck-pin{fill:var(--fig-ink)}

/* ------------------------------------------------------------- nonogram */
/* The grid is one CSS grid: a clue gutter on the left, a clue band on top,
   and the playable cells filling the rest. Keeping it as one grid is what
   makes the clues line up with their lines at every size. */
.nn-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.nn-grid{
  display:grid;
  grid-template-columns:auto repeat(var(--w),1fr);
  gap:1px;background:var(--grid-line);border:2px solid var(--fig-ink);
  /* `min-width:100%` used to sit here, and it is why a nonogram rendered at
     802px wide with 74px cells on a 1440px screen: min-width beats max-width,
     so the grid stretched to the wrapper no matter what cap was set. Width is
     now capped by CELL COUNT (see the desktop block near the end of this file),
     which is the only thing that should decide how big a puzzle is. */
  width:100%;margin:0 auto;
}
.nn-corner{background:var(--bg-alt)}
.nn-cclue,.nn-rclue{
  display:flex;background:var(--bg-alt);color:var(--ink-2);
  font-family:var(--display);font-weight:700;font-size:.66rem;
  font-variant-numeric:tabular-nums;line-height:1.15;
}
.nn-cclue{flex-direction:column;align-items:center;justify-content:flex-end;padding:3px 0;min-width:26px}
.nn-rclue{flex-direction:row;align-items:center;justify-content:flex-end;gap:4px;padding:0 6px;min-height:26px}
.nn-cclue span,.nn-rclue span{display:block;min-height:.95em;min-width:.6em;text-align:center}

.nn-cell{
  aspect-ratio:1/1;min-width:26px;border:0;padding:0;cursor:pointer;
  background:var(--surface);position:relative;
}
.nn-cell:hover{background:var(--bg-alt)}
.nn-cell.is-on{background:var(--fig-ink)}
.nn-cell.is-off::before,.nn-cell.is-off::after{
  content:"";position:absolute;left:28%;right:28%;top:50%;height:1.6px;
  background:var(--muted);
}
.nn-cell.is-off::before{transform:rotate(45deg)}
.nn-cell.is-off::after{transform:rotate(-45deg)}
.nn-cell.gap-l{box-shadow:inset 2px 0 0 var(--fig-ink)}
.nn-cell.gap-t{box-shadow:inset 0 2px 0 var(--fig-ink)}
.nn-cell.gap-l.gap-t{box-shadow:inset 2px 0 0 var(--fig-ink),inset 0 2px 0 var(--fig-ink)}
.nn-cell:focus-visible{outline:2px solid var(--brand-3);outline-offset:-2px;z-index:1}

.nn-note{
  font-size:.96rem;line-height:1.6;color:var(--ink-2);
  background:var(--bg-alt);border-radius:var(--r-md);padding:12px 14px;margin-bottom:14px;
}
.nn-actions{display:flex;flex-wrap:wrap;gap:8px}

@media (max-width:520px){
  .nn-cell{min-width:20px}
  .nn-cclue,.nn-rclue{font-size:.66rem}
  .nn-cclue{min-width:20px}
}

/* the footer marks the current page rather than linking to it, and the tagline
   needs a width that does not belong inline on 33 pages */
/* must match .site-footer a exactly — the current page sits in the same column
   as its siblings, so a different size reads as a rendering bug */
.site-footer .here{color:var(--muted);font-size:.96rem}
.foot-tag{margin-top:10px;max-width:34ch}
.foot-trust{list-style:none;margin:14px 0 0;padding:0;display:grid;gap:6px}
.foot-trust li{
  position:relative;padding-left:20px;font-size:.9rem;color:var(--ink-2);line-height:1.45;
}
/* a tick drawn in CSS rather than an SVG per item — three more nodes in every
   page's footer is not worth the bytes */
.foot-trust li::before{
  content:"";position:absolute;left:2px;top:.42em;width:9px;height:5px;
  border-left:2px solid var(--brand-3);border-bottom:2px solid var(--brand-3);
  transform:rotate(-45deg);
}

/* ------------------------------------------------------- daily puzzle */
.daily-head{margin-bottom:16px}
.daily-when{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.daily-when strong{font-family:var(--display);font-size:1.16rem;letter-spacing:-.02em}
.daily-note{margin:8px 0 0;color:var(--ink-2)}
.daily-streak{margin:6px 0 0;color:var(--ink-2);font-size:.96rem}
/* the homepage band, so a returning visitor finds today's puzzle without hunting */
.daily-band{padding:34px 0 6px}
.daily-band-in{
  display:grid;grid-template-columns:1.6fr 1fr;gap:28px;align-items:center;
  padding:26px 28px;border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--surface);box-shadow:var(--shadow-1);
}
.daily-band-in h2{margin:6px 0 8px;font-size:1.5rem}
.daily-band-in p{margin:0;color:var(--ink-2);max-width:56ch}
.daily-band-cta{display:flex;flex-direction:column;align-items:flex-start;gap:9px}
@media (max-width:820px){
  .daily-band-in{grid-template-columns:1fr;gap:18px;padding:22px}
  .daily-band-cta{align-items:stretch}
  .daily-band-cta .btn{justify-content:center}
}

.daily-share{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:16px}
.daily-arch-h{margin-top:38px}
.daily-arch-p{margin:6px 0 14px;max-width:60ch}

/* A fixed grid, so the shape of the fortnight is learnable — the same reason
   the pattern picker is a grid of numbers rather than a reflowing list. */
.daily-archive{list-style:none;margin:0;padding:0;display:grid;gap:8px;
  grid-template-columns:repeat(7,minmax(0,1fr))}
@media (max-width:720px){.daily-archive{grid-template-columns:repeat(4,minmax(0,1fr))}}
@media (max-width:420px){.daily-archive{grid-template-columns:repeat(3,minmax(0,1fr))}}

.daily-day{
  display:flex;flex-direction:column;gap:2px;min-height:64px;
  padding:9px 10px;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface);color:var(--ink-2);text-decoration:none;
}
.daily-day:hover{border-color:var(--line-2);background:var(--surface-2);text-decoration:none}
.daily-day:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px}
.daily-d{font-family:var(--display);font-weight:700;font-size:.9rem;color:var(--ink)}
.daily-t{font-size:.78rem;line-height:1.25;color:var(--muted)}
.daily-m{margin-top:auto;font-size:.9rem;font-weight:700}
.daily-day.is-here{border-color:var(--brand-3);box-shadow:inset 0 0 0 1px var(--brand-3)}
.daily-day.is-right .daily-m{color:#1a7f4b}
.daily-day.is-wrong .daily-m{color:#b3261e}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .daily-day.is-right .daily-m{color:#5fd39a}
  :root:not([data-theme="light"]) .daily-day.is-wrong .daily-m{color:#ff8a80}
}

/* ------------------------------------------------------------ skip link */
/* First focusable element on the page, hidden until it is focused. A keyboard
   or screen-reader user should not have to walk the whole menu on all 39 pages
   to reach the puzzle. */
.skip-link{
  position:absolute;left:-9999px;top:0;z-index:200;
  background:var(--brand-3);color:#fff;padding:12px 18px;border-radius:0 0 var(--r-md) 0;
  font-weight:650;text-decoration:none;
}
.skip-link:focus{left:0}

/* ------------------------------------------------------------ printing */
/* The audience research behind gate 14 puts most of this site's readers at 55
   or over on a desktop, and every competitor measured offers a printable
   puzzle. Printing was producing the navigation, the sixty-cell pattern picker
   and the whole footer across several wasted pages.
 *
 * What should survive a print is the puzzle and the reasoning: the board, the
 * prose, the tables. Everything that only works by being clicked should not. */
@media print{
  :root{
    --bg:#fff; --bg-alt:#fff; --surface:#fff; --surface-2:#fff; --surface-3:#fff;
    --ink:#000; --ink-2:#1a1a1a; --muted:#333; --line:#bbb; --line-2:#999; --grid-line:#666;
  }
  body{background:#fff;color:#000;font-size:11.5pt;line-height:1.5}

  /* interface, not content */
  .site-header,.site-footer,.nav,.skip-link,
  .pick,.pick-nums,.pattern-index,[data-role="pattern-index"],
  .btn,.board-controls,.diff,.nn-actions,.slide-actions,.gb-actions,
  .hub-browser,.cta,.eyebrow{display:none !important}

  /* let the content use the paper */
  .wrap,.wrap.narrow,.prose{max-width:none;width:auto;padding-left:0;padding-right:0}
  main{padding:0}

  /* a puzzle should not be split down the middle by a page break */
  svg,figure,table,.faq details,.opt,.frame{break-inside:avoid;page-break-inside:avoid}
  h1,h2,h3{break-after:avoid;page-break-after:avoid}

  /* the explanation is the reason to print this at all, so open every one */
  details{display:block !important}
  details > summary{font-weight:700;list-style:none}
  details > *{display:revert !important}

  .table-wrap{overflow:visible;border:1px solid #999}
  .table-wrap table{min-width:0}
  .table-wrap tbody tr:nth-child(even){background:#f4f4f4}

  a{color:#000;text-decoration:underline}
  /* an internal link is meaningless on paper; an external one is worth naming */
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:.85em;color:#444;word-break:break-all}
}


/* ---- kakuro -------------------------------------------------------------
   The only rectangular grid on the site, and the only one whose clue lives
   INSIDE a cell rather than beside it. A block carries up to two totals split
   by a diagonal: across above the line, down below it. Lesson §12.0 applies —
   a renderer shipped without its stylesheet looks fine to every test here,
   because the suites read markup and not pixels. */
/* The grid sizes ITSELF to the rectangle and the children just fill their track.
   Putting aspect-ratio on the children instead fights the 1fr columns: the cells
   won and the blocks collapsed to 36px against the cells' 71px. */
.gb-kakuro{
  grid-template-columns:repeat(var(--n),1fr);
  grid-template-rows:repeat(var(--rows),1fr);
  aspect-ratio:var(--n)/var(--rows);
}
.gb-kakuro .gb-cell{aspect-ratio:auto;height:100%;padding:0}
.gb-kakuro .gb-cell{font-size:clamp(.85rem,3vw,1.15rem)}
.gb-block{
  position:relative;
  background:var(--fig-ink);
  border:1px solid var(--line);
  height:100%;
  overflow:hidden;
}
.gb-block .gb-slash{
  position:absolute;inset:0;
  background:linear-gradient(to bottom right,transparent calc(50% - .5px),var(--line) calc(50% - .5px),var(--line) calc(50% + .5px),transparent calc(50% + .5px));
}
.gb-clue{
  position:absolute;
  /* floor raised from .5rem: at 320px that computed to 8px, and the measured
     audience for this site skews 55+ (docs/icp.md). Two clues still fit a 41px
     block because they sit in opposite corners. */
  font-size:clamp(.64rem,2.7vw,.78rem);
  font-weight:700;
  line-height:1;
  color:var(--bg);
}
.gb-clue-a{top:9%;right:9%}
.gb-clue-d{bottom:9%;left:9%}
/* NO dark-mode override here, deliberately. The block is painted --fig-ink,
   which INVERTS between themes (dark in light mode, light in dark mode), and
   --bg inverts with it — so one rule is correct in both. Overriding to --ink in
   dark mode painted the clue the same colour as the block it sits on: contrast
   1.0, invisible. Measured, not guessed. */


/* ---- word search --------------------------------------------------------
   The grid sizes itself and the letters fill their track, the same way the
   kakuro grid does (§12.28) — putting aspect-ratio on the children fights the
   1fr columns and collapses them unevenly. */
.ws-layout{display:flex;gap:22px;align-items:flex-start;flex-wrap:wrap}
/* A 14-wide grid squeezed into 320px gives 17px cells and 10px letters, and
   READING is the whole activity here — so the grid keeps a legible cell size and
   scrolls inside its own box instead, exactly as the reference tables do. */
.ws-gridwrap{flex:1 1 320px;max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}
.ws-grid{
  display:grid;
  grid-template-columns:repeat(var(--n),1fr);
  aspect-ratio:1;
  min-width:calc(var(--n) * 28px);
  max-width:min(560px,100%);
  gap:1px;
  background:var(--line);
  border:1px solid var(--line);
  border-radius:12px;
  overflow:hidden;
}
.ws-cell{
  display:flex;align-items:center;justify-content:center;
  background:var(--bg);
  border:0;
  font-family:var(--display,inherit);
  font-weight:600;
  /* floor sized to the 28px minimum cell above, not to the viewport: the grid
     scrolls now, so there is no reason to shrink letters to fit a phone. */
  font-size:clamp(.84rem,2.1vw,1rem);
  color:var(--ink);
  cursor:pointer;
  padding:0;
  line-height:1;
  transition:background .12s,color .12s;
}
.ws-cell:hover{background:var(--bg-alt)}
.ws-cell:focus-visible{outline:2px solid var(--brand-3);outline-offset:-2px}
.ws-cell.is-sel{background:var(--brand-3);color:#fff}
/* 20% read as almost nothing against the dark ground; found cells also carry a
   left border so the state survives for anyone who cannot separate the hues. */
.ws-cell.is-found{background:color-mix(in srgb,var(--brand-3) 32%,var(--bg));color:var(--ink);font-weight:700;box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--brand-3) 55%,transparent)}
.ws-cell.is-sel.is-found{background:var(--brand-3);color:#fff}

/* the letters currently highlighted, so you can see what you are building
   rather than having to read them off the grid */
.ws-preview{
  display:inline-block;margin-left:10px;padding:2px 9px;
  border-radius:999px;background:var(--brand-3);color:#fff;
  font-family:var(--display,inherit);font-weight:700;letter-spacing:.08em;
  font-size:var(--fs-sm,.94rem);vertical-align:baseline;
}
.ws-preview[hidden]{display:none}
.ws-side{flex:0 1 200px;min-width:170px}
.ws-words{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:4px 12px;
}
.ws-word{font-size:var(--fs-sm,.94rem);letter-spacing:.02em;color:var(--ink)}
.ws-word.is-found{color:var(--muted)}

@media (max-width:560px){
  .ws-layout{gap:16px}
  .ws-side{flex:1 1 100%}
  .ws-words{grid-template-columns:repeat(auto-fill,minmax(76px,1fr))}
}


/* ---- slitherlink --------------------------------------------------------
   Drawn at double resolution: dot, edge, dot, edge ... so the tracks alternate
   between a small dot and a full cell. The edge buttons are the interactive
   part and they are thin by nature, so each one carries an invisible hit area
   larger than the line it draws — otherwise the tap target would be 6px.
   A renderer without its stylesheet passes every suite here (§12.0). */
.sl-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.sl-grid{
  display:grid;
  grid-template-columns:repeat(var(--w), var(--sl-dot) var(--sl-cell)) var(--sl-dot);
  grid-auto-rows:var(--sl-dot) var(--sl-cell);
  --sl-dot:16px;
  --sl-cell:42px;
  justify-content:start;
  align-content:start;
  width:max-content;
  margin-inline:auto;
  padding:10px;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:14px;
}
.sl-grid > *{width:100%;height:100%;display:block}

.sl-dot{
  position:relative;
}
.sl-dot::after{
  content:"";position:absolute;inset:0;margin:auto;
  width:4px;height:4px;border-radius:50%;
  background:var(--muted);
}

.sl-cell{
  display:flex;align-items:center;justify-content:center;
  font-family:var(--display,inherit);
  font-weight:700;
  font-size:clamp(.9rem,2.4vw,1.05rem);
  color:var(--ink);
  line-height:1;
}

.sl-edge{
  position:relative;
  border:0;padding:0;margin:0;
  background:transparent;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
/* the line itself */
.sl-edge::before{
  content:"";position:absolute;inset:0;margin:auto;
  border-radius:2px;
  background:transparent;
  transition:background .1s;
}
.sl-h::before{height:4px;left:1px;right:1px}
.sl-v::before{width:4px;top:1px;bottom:1px}
/* a hit area wider than the line, so a 4px stroke is not a 4px target */
.sl-edge::after{
  content:"";position:absolute;
  left:50%;top:50%;transform:translate(-50%,-50%);
  min-width:26px;min-height:26px;
  width:100%;height:100%;
}
.sl-edge:hover::before{background:var(--line)}
.sl-edge.is-on::before{background:var(--brand-3)}
.sl-edge.is-on:hover::before{background:var(--brand-3)}
.sl-edge:focus-visible{outline:2px solid var(--brand-3);outline-offset:-1px;border-radius:3px}

/* ruled out: a small cross, not a line */
.sl-edge.is-off::before{
  background:transparent;
  width:9px;height:9px;left:0;right:0;top:0;bottom:0;
  border-radius:0;
  background-image:linear-gradient(45deg,transparent 44%,var(--muted) 44%,var(--muted) 56%,transparent 56%),
                   linear-gradient(-45deg,transparent 44%,var(--muted) 44%,var(--muted) 56%,transparent 56%);
}

@media (max-width:560px){
  .sl-grid{--sl-cell:38px;--sl-dot:15px}
}


/* ---- related practice, as a row of cards under the board ----------------
   First attempt put these in the right-hand column under the pattern picker.
   That column is 250px and sticky, so four cards became a tall stack pinned
   beside the board — worse than the paragraph it replaced.

   They now span BOTH grid columns and sit below the whole play area, four
   across. The reader meets them where a puzzle actually ends rather than at the
   very bottom of the page, and a horizontal row reads as a set of choices
   rather than as a list of leftovers. */
.play-rel{
  grid-column:1 / -1;
  margin-top:34px;
  padding-top:26px;
  border-top:1px solid var(--line);
}
.play-rel > h2{
  font-size:var(--fs-xs,.78rem);
  letter-spacing:.11em;
  text-transform:uppercase;
  color:var(--muted);
  margin:0 0 14px;
}
.rel-cards{
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  gap:12px;
}
.rel-card{
  display:block;
  padding:14px 15px;
  border:1px solid var(--line);
  border-radius:var(--r-md,12px);
  background:var(--surface);
  text-decoration:none;
  transition:border-color .12s, background .12s, transform .12s, box-shadow .12s;
}
.rel-card:hover{
  border-color:var(--brand-3);
  background:var(--bg-alt);
  transform:translateY(-2px);
  box-shadow:var(--sh-sm,0 2px 10px rgba(0,0,0,.06));
}
.rel-card:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px}
.rel-card-t{
  display:block;
  font-family:var(--display,inherit);
  font-weight:700;
  font-size:var(--fs-sm,.94rem);
  color:var(--ink);
  margin-bottom:4px;
}
.rel-card-d{
  display:block;
  font-size:var(--fs-xs,.82rem);
  line-height:1.5;
  color:var(--muted);
}

@media (max-width:900px){
  .rel-cards{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:520px){
  .rel-cards{grid-template-columns:1fr}
  .play-rel{margin-top:26px;padding-top:20px}
}

/* ---- binary puzzle ------------------------------------------------------
   Three states per cell, like the nonogram board, but both filled states are
   DIGITS the player writes. Givens are ink; entries are the accent, so you can
   see your own working against the puzzle at a glance. */
.bp-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.bp-grid{
  display:grid;
  grid-template-columns:repeat(var(--n),1fr);
  aspect-ratio:1;
  gap:1px;
  background:var(--line);
  border:2px solid var(--fig-ink);
  border-radius:10px;
  overflow:hidden;
  width:100%;
  max-width:min(520px,100%);
  min-width:calc(var(--n) * 30px);
  margin-inline:auto;
}
.bp-cell{
  display:flex;align-items:center;justify-content:center;
  background:var(--surface);
  border:0;padding:0;
  cursor:pointer;
  font-family:var(--display,inherit);
  font-weight:700;
  font-size:clamp(.82rem,2.4vw,1.05rem);
  line-height:1;
  color:var(--brand-3);
  transition:background .1s;
}
.bp-cell:hover{background:var(--bg-alt)}
.bp-cell:focus-visible{outline:2px solid var(--brand-3);outline-offset:-2px}
.bp-cell.is-given{
  background:var(--bg-alt);
  color:var(--ink);
  cursor:default;
}
.bp-cell.is-mine{color:var(--brand-3)}

/* ---- family colour ------------------------------------------------------
   One hue per topical cluster, so two things share a colour exactly when they
   share an engine and a search intent. Applied to the related cards, where a
   reader is choosing between families and the colour is doing real work.

   Every hue is picked to hold at least 4.5:1 against the card background in
   both themes; they differ in hue, not in lightness, so none reads as
   "disabled" beside the others. */
:root{
  --fam-patterns-and-series:#4f46e5;
  --fam-spatial:#0d9488;
  --fam-visual-search:#b45309;
  --fam-constraint-grids:#7c3aed;
  --fam-shading-grids:#be185d;
  --fam-loop-and-path:#0369a1;
  --fam-letter-grids:#15803d;
  --fam-sequences:#c2410c;
  --fam-manipulation:#4338ca;
  --fam-word-picture:#a21caf;
  --fam-puzzle-sets:#475569;
  --fam-site:#475569;
  --fam-pillar:#475569;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --fam-patterns-and-series:#a5b4fc;
    --fam-spatial:#5eead4;
    --fam-visual-search:#fcd34d;
    --fam-constraint-grids:#c4b5fd;
    --fam-shading-grids:#f9a8d4;
    --fam-loop-and-path:#7dd3fc;
    --fam-letter-grids:#86efac;
    --fam-sequences:#fdba74;
    --fam-manipulation:#a5b4fc;
    --fam-word-picture:#f0abfc;
    --fam-puzzle-sets:#cbd5e1;
    --fam-site:#cbd5e1;
    --fam-pillar:#cbd5e1;
  }
}
:root[data-theme="dark"]{
  --fam-patterns-and-series:#a5b4fc;
  --fam-spatial:#5eead4;
  --fam-visual-search:#fcd34d;
  --fam-constraint-grids:#c4b5fd;
  --fam-shading-grids:#f9a8d4;
  --fam-loop-and-path:#7dd3fc;
  --fam-letter-grids:#86efac;
  --fam-sequences:#fdba74;
  --fam-manipulation:#a5b4fc;
  --fam-word-picture:#f0abfc;
  --fam-puzzle-sets:#cbd5e1;
  --fam-site:#cbd5e1;
  --fam-pillar:#cbd5e1;
}

/* the card takes the colour of the family it points AT, so the row reads as a
   set of destinations rather than four identical boxes */
.rel-card{position:relative;overflow:hidden}
.rel-card::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--fam,var(--brand-1));
  opacity:.9;
}
[data-fam="patterns-and-series"]{--fam:var(--fam-patterns-and-series)}
[data-fam="spatial"]{--fam:var(--fam-spatial)}
[data-fam="visual-search"]{--fam:var(--fam-visual-search)}
[data-fam="constraint-grids"]{--fam:var(--fam-constraint-grids)}
[data-fam="shading-grids"]{--fam:var(--fam-shading-grids)}
[data-fam="loop-and-path"]{--fam:var(--fam-loop-and-path)}
[data-fam="letter-grids"]{--fam:var(--fam-letter-grids)}
[data-fam="sequences"]{--fam:var(--fam-sequences)}
[data-fam="manipulation"]{--fam:var(--fam-manipulation)}
[data-fam="word-picture"]{--fam:var(--fam-word-picture)}
[data-fam="puzzle-sets"]{--fam:var(--fam-puzzle-sets)}
[data-fam="pillar"]{--fam:var(--fam-pillar)}
[data-fam="site"]{--fam:var(--fam-site)}
.rel-card .rel-card-t{color:var(--fam,var(--ink))}
.rel-card:hover{border-color:var(--fam,var(--brand-3))}

/* ---- hitori -------------------------------------------------------------
   Three states, and the KEPT one has to be visible without shouting: it is a
   working mark, not an answer. A shaded cell is the answer, so it carries the
   weight — solid ink with the number dimmed but still legible, because a solver
   needs to keep reading the numbers they have shaded out. */
.hi-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.hi-grid{
  display:grid;
  grid-template-columns:repeat(var(--n),1fr);
  aspect-ratio:1;
  gap:2px;
  background:var(--line);
  border:2px solid var(--fig-ink);
  border-radius:10px;
  overflow:hidden;
  width:100%;
  max-width:min(460px,100%);
  min-width:calc(var(--n) * 34px);
  margin-inline:auto;
}
.hi-cell{
  display:flex;align-items:center;justify-content:center;
  position:relative;
  background:var(--surface);
  border:0;padding:0;
  cursor:pointer;
  font-family:var(--display,inherit);
  font-weight:700;
  font-size:clamp(.85rem,2.4vw,1.05rem);
  line-height:1;
  color:var(--ink);
  transition:background .1s,color .1s;
}
.hi-cell:hover{background:var(--bg-alt)}
.hi-cell:focus-visible{outline:2px solid var(--brand-3);outline-offset:-2px}

/* shaded out — still readable, because you go on using the number */
.hi-cell.is-shaded{
  background:var(--fig-ink);
  color:color-mix(in srgb,var(--bg) 62%,var(--fig-ink));
}
/* kept — a ring, so it reads as a note you made rather than as an answer */
.hi-cell.is-kept::after{
  content:"";position:absolute;inset:14%;
  border:2px solid var(--brand-3);
  border-radius:50%;
  pointer-events:none;
}
.hi-cell.is-kept{color:var(--ink)}

/* ---- nurikabe -----------------------------------------------------------
   Sea is the answer, so it carries the weight: solid ink, like hitori's shaded
   cells. An island mark is a small dot — a note, not an answer — and clue
   numbers stay legible on their island cells throughout. */
.nu-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.nu-grid{
  display:grid;
  grid-template-columns:repeat(var(--n),1fr);
  aspect-ratio:1;
  gap:2px;
  background:var(--line);
  border:2px solid var(--fig-ink);
  border-radius:10px;
  overflow:hidden;
  width:100%;
  max-width:min(460px,100%);
  min-width:calc(var(--n) * 34px);
  margin-inline:auto;
}
.nu-cell{
  display:flex;align-items:center;justify-content:center;
  position:relative;
  background:var(--surface);
  border:0;padding:0;
  cursor:pointer;
  font-family:var(--display,inherit);
  font-weight:700;
  font-size:clamp(.85rem,2.4vw,1.05rem);
  line-height:1;
  color:var(--ink);
  transition:background .1s;
}
.nu-cell:hover{background:var(--bg-alt)}
.nu-cell:focus-visible{outline:2px solid var(--brand-3);outline-offset:-2px}
.nu-cell.is-clue{cursor:default;background:var(--bg-alt)}
.nu-cell.is-sea{background:var(--fig-ink)}
.nu-cell.is-land::after{
  content:"";position:absolute;inset:0;margin:auto;
  width:7px;height:7px;border-radius:50%;
  background:var(--brand-3);
  pointer-events:none;
}

/* ---- star battle --------------------------------------------------------
   Regions are drawn the way calcudoku draws cages — a heavier edge wherever
   the neighbour belongs to a different region. The star is drawn in CSS, the
   cross is a small x, and both survive both themes because they use inverting
   tokens only. */
.sb-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.sb-grid{
  display:grid;
  grid-template-columns:repeat(var(--n),1fr);
  aspect-ratio:1;
  gap:0;
  background:var(--surface);
  border:2.5px solid var(--fig-ink);
  border-radius:10px;
  overflow:hidden;
  width:100%;
  max-width:min(440px,100%);
  min-width:calc(var(--n) * 44px);   /* tap-target floor, as heyawake */
  margin-inline:auto;
}
.sb-cell{
  position:relative;
  background:var(--surface);
  border:1px solid var(--grid-line);
  padding:0;cursor:pointer;
  transition:background .1s;
}
.sb-cell:hover{background:var(--bg-alt)}
.sb-cell:focus-visible{outline:2px solid var(--brand-3);outline-offset:-2px}
.sb-cell.reg-t{border-top:2.5px solid var(--fig-ink)}
.sb-cell.reg-b{border-bottom:2.5px solid var(--fig-ink)}
.sb-cell.reg-l{border-left:2.5px solid var(--fig-ink)}
.sb-cell.reg-r{border-right:2.5px solid var(--fig-ink)}
.sb-cell .sb-val{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}
.sb-cell.is-star .sb-val::before{
  content:"★";
  font-size:clamp(1rem,3.4vw,1.4rem);
  line-height:1;
  color:var(--brand-1);
}
.sb-cell.is-out .sb-val::before{
  content:"×";
  font-size:clamp(.8rem,2.4vw,1rem);
  line-height:1;
  color:var(--muted);
  opacity:.75;
}

/* ---- heyawake -----------------------------------------------------------
   Room borders are heavy, cell borders use --grid-line (§12.36). The painted
   cell uses --fig-ink, which inverts between themes, so it reads as "filled"
   in both without a dark override. */
.hy-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.hy-grid{
  display:grid;
  grid-template-columns:repeat(var(--n),1fr);
  aspect-ratio:1;
  gap:0;
  background:var(--surface);
  border:2.5px solid var(--fig-ink);
  border-radius:10px;
  overflow:hidden;
  width:100%;
  max-width:min(440px,100%);
  /* 44px is the tap-target floor. A 7x7 then overflows 320px slightly and the
     wrapper scrolls, which is the right trade: shrinking to fit gave 37px. */
  min-width:calc(var(--n) * 44px);
  margin-inline:auto;
}
.hy-cell{
  position:relative;
  background:var(--surface);
  border:1px solid var(--grid-line);
  padding:0;cursor:pointer;
  transition:background .1s;
}
.hy-cell:hover{background:var(--bg-alt)}
.hy-cell:focus-visible{outline:2px solid var(--brand-3);outline-offset:-2px;z-index:2}
.hy-cell.rm-t{border-top:2.5px solid var(--fig-ink)}
.hy-cell.rm-b{border-bottom:2.5px solid var(--fig-ink)}
.hy-cell.rm-l{border-left:2.5px solid var(--fig-ink)}
.hy-cell.rm-r{border-right:2.5px solid var(--fig-ink)}
.hy-cell.is-paint{background:var(--fig-ink)}
.hy-clue{
  position:absolute;top:2px;left:4px;
  font:inherit;font-family:var(--display);font-weight:700;
  font-size:clamp(.78rem,2.8vw,.9rem);
  color:var(--brand-3);
  line-height:1;pointer-events:none;
}
.hy-cell.is-paint .hy-clue{color:var(--surface)}
.hy-val{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}
.hy-cell.is-white .hy-val::before{
  content:"";
  width:22%;height:22%;
  min-width:5px;min-height:5px;
  border-radius:50%;
  background:var(--muted);
  opacity:.8;
}


/* ---- zebra puzzle / logic grid ------------------------------------------
   The cross-hatch, at every width, and treated as the centrepiece it is
   rather than a bare table.

   WHAT THIS REPLACED, so nobody rebuilds it: the first version wrote all five
   candidate WORDS into all twenty-five cells. It was a wall of repeated text
   on a desktop, and 530px wide on a phone, which put two of five positions
   off screen — and a logic grid is solved by comparing every position at
   once, so that was not a harder puzzle, it was an unsolvable one.

   THE LOOK. Each list is a card with a tinted header carrying its name and
   the position numbers; rows alternate a faint tint so the eye tracks across
   five squares without losing the line; a placed square is a solid brand tick
   and a ruled-out one recedes rather than shouting. Wide screens show every
   list side by side, phones one at a time behind tabs. */
.zb-split{
  display:grid;gap:24px;
  grid-template-columns:minmax(0,1.75fr) minmax(0,1fr);
  align-items:start;margin:18px 0 4px;
}
.zb-gridwrap,.zb-clues{min-width:0}
@media (max-width:980px){.zb-split{grid-template-columns:1fr}}

.zb-modes{display:flex;gap:8px;margin:14px 0 0;flex-wrap:wrap}

/* 292px is the width at which an 88px label column still leaves five ~36px
   squares. Below it the list names truncate to "Resid…" and stop being useful. */
.zb-matrices{display:grid;gap:20px;grid-template-columns:repeat(auto-fit,minmax(292px,1fr))}
.zb-mx{
  min-width:0;
  border:1px solid var(--line);
  border-radius:12px;
  background:var(--surface);
  overflow:hidden;
  box-shadow:0 1px 2px rgba(15,23,42,.04);
}
.zb-mx:focus-within{border-color:var(--brand-3)}

.zb-matrix{width:100%;border-collapse:collapse;table-layout:fixed}
.zb-matrix th{font-weight:600;text-align:left}
.zb-matrix th:first-child{width:96px}

/* the tinted header: list name on the left, positions across */
.zb-matrix thead th{
  background:var(--bg-alt);
  border-bottom:1px solid var(--line);
  padding:9px 0;
  text-align:center;
  font-size:var(--fs-sm);color:var(--muted);
  font-variant-numeric:tabular-nums;
}
.zb-matrix .zb-corner{
  text-align:left;padding-left:12px;
  font-size:var(--fs-sm);font-weight:700;
  color:var(--brand-3);letter-spacing:.01em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.zb-matrix tbody tr:nth-child(even){background:color-mix(in srgb, var(--bg-alt) 55%, transparent)}
.zb-matrix th[scope="row"]{
  font-size:var(--fs-sm);color:var(--ink-2);
  padding:0 8px 0 11px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.zb-matrix td{padding:3px 2px}
.zb-matrix td:last-child{padding-right:9px}

.zb-sq{
  /* aspect-ratio makes a square as TALL as its column is wide, so a card that
     stretches to fill a wide play area gave 80px squares. The cap keeps them a
     button rather than a tile however much room the grid hands the card. */
  width:100%;max-width:52px;margin-inline:auto;
  aspect-ratio:1/1;min-height:34px;
  display:flex;align-items:center;justify-content:center;
  font:inherit;font-size:var(--fs-lg);font-weight:700;line-height:1;
  border:1px solid var(--grid-line);border-radius:8px;
  background:var(--surface);color:var(--ink-2);cursor:pointer;
  transition:background .12s,border-color .12s,color .12s,transform .08s;
}
.zb-sq:hover{border-color:var(--brand-3);transform:translateY(-1px)}
.zb-sq:active{transform:none}
.zb-sq:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px;z-index:1}
.zb-sq.is-yes{
  background:var(--brand-1);border-color:var(--brand-1);color:#fff;
  box-shadow:0 1px 3px color-mix(in srgb, var(--brand-1) 40%, transparent);
}
.zb-sq.is-yes:hover{transform:none}
.zb-sq.is-no{
  background:transparent;border-color:transparent;
  color:var(--muted);opacity:.55;font-weight:600;
}
.zb-sq.is-no:hover{opacity:.85;border-color:var(--grid-line);transform:none}

/* the list tabs, phone only */
.zb-cats{display:none;flex-wrap:wrap;gap:7px;margin:0 0 14px}
.zb-cat{
  font:inherit;font-size:var(--fs-sm);font-weight:600;
  display:inline-flex;align-items:center;gap:7px;
  min-height:42px;padding:6px 14px;
  border:1px solid var(--line);border-radius:999px;
  background:var(--surface);color:var(--ink-2);cursor:pointer;
  transition:background .12s,border-color .12s,color .12s;
}
.zb-cat:hover{border-color:var(--brand-3)}
.zb-cat:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px}
.zb-cat.is-on{background:var(--brand-1);border-color:var(--brand-1);color:#fff}
.zb-cat-n{
  font-size:var(--fs-2xs);font-weight:700;
  padding:2px 7px;border-radius:999px;
  background:var(--bg-alt);color:var(--muted);
  font-variant-numeric:tabular-nums;
}
.zb-cat.is-on .zb-cat-n{background:rgba(255,255,255,.24);color:#fff}
.zb-one .zb-mx{border:1px solid var(--line)}

@media (max-width:760px){
  .zb-cats{display:flex}
  /* a thumb, not a mouse */
  .zb-sq{min-height:44px;max-width:58px;font-size:1.05rem}
  .zb-matrix th:first-child{width:82px}
  .zb-matrix th[scope="row"],.zb-matrix thead th{font-size:var(--fs-base)}
  .zb-modes .btn{min-height:44px;padding-inline:18px}
}

/* the clues read as prose, so they sit near body size */
.zb-clues{
  border:1px solid var(--line);border-radius:12px;
  background:var(--surface);padding:15px 17px 8px;
}
.zb-clues .slide-label{margin:0 0 10px}
.zb-cluelist{list-style:none;margin:0;padding:0}
.zb-cluelist li{
  display:flex;gap:10px;align-items:baseline;
  font-size:var(--fs-lg);line-height:1.55;
  padding:9px 0;border-bottom:1px solid var(--line);
}
.zb-cluelist li:last-child{border-bottom:0}
.zb-num{
  flex:0 0 auto;min-width:23px;height:23px;
  display:inline-flex;align-items:center;justify-content:center;
  border-radius:7px;background:var(--bg-alt);color:var(--brand-3);
  font-size:var(--fs-2xs);font-weight:700;font-variant-numeric:tabular-nums;
}

/* ---- masyu --------------------------------------------------------------
   The loop is drawn BETWEEN cells, so the grid runs at double resolution:
   cell, gap, cell, gap … Columns alternate a fixed cell size and a narrower
   gap, which keeps the circles round and the tap targets on the gaps big
   enough to hit. */
.my-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.my-grid{
  display:grid;
  grid-template-columns:repeat(calc(var(--w) - 1), auto 18px) auto;
  justify-content:center;align-items:center;
  gap:0;
  padding:12px;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:12px;
  width:max-content;margin-inline:auto;
}
.my-cell{
  width:30px;height:30px;
  display:flex;align-items:center;justify-content:center;
  border-radius:50%;
}
.my-cell.is-white{
  border:2.5px solid var(--ink-2);
  background:var(--surface);
}
.my-cell.is-black{
  border:2.5px solid var(--ink);
  background:var(--ink);
}
/* the gaps: horizontal ones are wide and short, vertical ones tall and thin */
.my-gap{
  border:0;background:transparent;padding:0;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  position:relative;
}
.my-gap.my-h{width:18px;height:30px}
.my-gap.my-v{width:30px;height:18px}
.my-void{width:18px;height:18px}
.my-gap span{display:block;border-radius:2px;background:transparent}
.my-gap.my-h span{width:100%;height:4px}
.my-gap.my-v span{width:4px;height:100%}
.my-gap:hover span{background:var(--grid-line)}
.my-gap:focus-visible{outline:2px solid var(--brand-3);outline-offset:1px;border-radius:4px}
.my-gap.is-line span{background:var(--brand-1)}
.my-gap.is-line:hover span{background:var(--brand-1)}
/* a crossed-off gap draws a small x rather than a line */
.my-gap.is-out span{background:transparent}
.my-gap.is-out::before,.my-gap.is-out::after{
  content:"";position:absolute;left:50%;top:50%;
  width:11px;height:2px;background:var(--muted);opacity:.75;
}
.my-gap.is-out::before{transform:translate(-50%,-50%) rotate(45deg)}
.my-gap.is-out::after{transform:translate(-50%,-50%) rotate(-45deg)}

@media (max-width:560px){
  /* a thumb, not a mouse: the gaps grow, the cells shrink a little */
  .my-grid{grid-template-columns:repeat(calc(var(--w) - 1), auto 22px) auto;padding:9px}
  .my-cell{width:26px;height:26px}
  .my-gap.my-h{width:22px;height:26px}
  .my-gap.my-v{width:26px;height:22px}
  .my-void{width:22px;height:22px}
}

/* ---- hidoku -------------------------------------------------------------
   A square grid of numbers plus a pad. Printed numbers read as fixed; the
   ones you place read as yours, which is what makes a wrong guess findable. */
.hk-wrap{overflow-x:auto;margin:18px 0 14px;padding-bottom:4px}
.hk-grid{
  display:grid;grid-template-columns:repeat(var(--w),1fr);
  gap:0;aspect-ratio:1;
  width:100%;max-width:min(440px,100%);
  min-width:calc(var(--w) * 40px);
  margin-inline:auto;
  border:2.5px solid var(--fig-ink);border-radius:10px;overflow:hidden;
  background:var(--surface);
}
.hk-cell{
  display:flex;align-items:center;justify-content:center;
  border:1px solid var(--grid-line);
  background:var(--surface);cursor:pointer;padding:0;
  font:inherit;font-family:var(--display);font-weight:700;
  font-size:clamp(.78rem,2.6vw,1rem);
  font-variant-numeric:tabular-nums;
  color:var(--brand-3);
  transition:background .1s;
}
.hk-cell:hover{background:var(--bg-alt)}
.hk-cell:focus-visible{outline:2px solid var(--brand-3);outline-offset:-2px;z-index:1}
.hk-cell.is-given{background:var(--bg-alt);color:var(--ink);cursor:default}
.hk-cell.is-mine{color:var(--brand-1)}
.hk-cell.is-sel{background:color-mix(in srgb, var(--brand-1) 16%, var(--surface));box-shadow:inset 0 0 0 2px var(--brand-1)}

/* An 8x8 needs 64 keys, which at the 44px tap floor is 544px of pad on a
   phone — taller than the grid it serves, and it pushed the puzzle off screen.
   Capping the height and letting the pad scroll keeps every number reachable
   without letting the keypad become the page. The numbers run in order, so
   scrolling to one is predictable rather than a hunt. */
.hk-pad{
  display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin:0 0 4px;
  max-height:190px;overflow-y:auto;
  padding:8px;border:1px solid var(--line);border-radius:10px;
  background:var(--bg-alt);
}
.hk-key{
  min-width:38px;min-height:38px;padding:4px 8px;
  font:inherit;font-family:var(--display);font-weight:700;
  font-size:var(--fs-sm);font-variant-numeric:tabular-nums;
  border:1px solid var(--line);border-radius:8px;
  background:var(--surface);color:var(--ink-2);cursor:pointer;
}
.hk-key:hover{border-color:var(--brand-3);color:var(--ink)}
.hk-key:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px}
.hk-key.is-used{opacity:.3;pointer-events:none}

@media (max-width:560px){
  /* a thumb, not a mouse */
  .hk-grid{min-width:calc(var(--w) * 42px)}
  .hk-key{min-width:44px;min-height:44px;font-size:var(--fs-base)}
  .hk-pad{max-height:168px}
}

/* ---- hashi --------------------------------------------------------------
   Islands are scattered points, not grid cells, so the board is one SVG. The
   tap targets are wide invisible bands under the visible bridges — a 3px line
   is not something a thumb can hit. */
.hx-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.hx-svg{
  display:block;margin-inline:auto;max-width:100%;height:auto;
  background:var(--surface);
  border:1px solid var(--line);border-radius:12px;
}
.hx-line{stroke:var(--brand-1);stroke-width:3;stroke-linecap:round}
.hx-hit{
  stroke:transparent;stroke-width:26;cursor:pointer;
}
.hx-hit:hover{stroke:color-mix(in srgb, var(--brand-3) 18%, transparent)}
.hx-hit:focus-visible{stroke:color-mix(in srgb, var(--brand-3) 30%, transparent);outline:none}
.hx-isl circle{
  fill:var(--surface);stroke:var(--ink-2);stroke-width:2;
}
.hx-isl text{
  font-family:var(--display);font-weight:700;font-size:15px;
  fill:var(--ink);pointer-events:none;
}
/* an island with exactly its number of ends reads as finished */
.hx-isl.is-done circle{fill:var(--brand-1);stroke:var(--brand-1)}
.hx-isl.is-done text{fill:#fff}
.hx-isl.is-over circle{fill:var(--surface);stroke:#ec4899;stroke-width:3}
.hx-isl.is-over text{fill:#ec4899}

/* ---- math crossword -----------------------------------------------------
   Five columns across: number, operator, number, equals, number — and the
   same downward, so the operator and equals cells are narrow and the number
   cells are square. Entry is by digits, so the pad is 0-9 plus a backspace
   rather than a key per possible answer. */
.mx-wrap{overflow-x:auto;margin:18px 0 14px;padding-bottom:4px}
.mx-grid{
  display:grid;
  grid-template-columns:repeat(2, 58px 30px) 58px;
  grid-auto-rows:auto;
  justify-content:center;align-items:center;
  gap:4px;
  width:max-content;margin-inline:auto;
}
.mx-num{
  width:58px;height:58px;
  display:flex;align-items:center;justify-content:center;
  border:1.5px solid var(--grid-line);border-radius:10px;
  background:var(--surface);cursor:pointer;padding:0;
  font:inherit;font-family:var(--display);font-weight:700;
  font-size:var(--fs-xl,1.05rem);font-variant-numeric:tabular-nums;
  color:var(--brand-1);
  transition:background .1s,border-color .1s;
}
.mx-num:hover{border-color:var(--brand-3)}
.mx-num:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px}
.mx-num.is-given{background:var(--bg-alt);border-color:var(--line);color:var(--ink);cursor:default}
.mx-num.is-sel{border-color:var(--brand-1);box-shadow:0 0 0 3px color-mix(in srgb, var(--brand-1) 24%, transparent)}
.mx-op{
  display:flex;align-items:center;justify-content:center;
  font-family:var(--display);font-weight:700;font-size:var(--fs-lg);
  color:var(--muted);
}
.mx-eq{color:var(--ink-2)}
.mx-void{display:block}

.mx-pad{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin:0 0 6px}
.mx-key{
  min-width:46px;min-height:46px;
  font:inherit;font-family:var(--display);font-weight:700;
  font-size:var(--fs-base);font-variant-numeric:tabular-nums;
  border:1px solid var(--line);border-radius:9px;
  background:var(--surface);color:var(--ink);cursor:pointer;
}
.mx-key:hover{border-color:var(--brand-3)}
.mx-key:focus-visible{outline:2px solid var(--brand-3);outline-offset:2px}
.mx-back{color:var(--muted)}

@media (max-width:420px){
  .mx-grid{grid-template-columns:repeat(2, 52px 26px) 52px}
  .mx-num{width:52px;height:52px}
}

/* ---- numberlink ---------------------------------------------------------
   Links live between squares, so the grid runs at double resolution like
   masyu. Numbered squares are tinted by pair so the two ends of one path are
   findable at a glance — with up to twenty pairs, plain numbers alone are hard
   to match across a crowded grid. */
.nl-wrap{overflow-x:auto;margin:18px 0;padding-bottom:4px}
.nl-grid{
  display:grid;
  grid-template-columns:repeat(calc(var(--w) - 1), auto 16px) auto;
  justify-content:center;align-items:center;
  padding:12px;
  background:var(--surface);
  border:1px solid var(--line);border-radius:12px;
  width:max-content;margin-inline:auto;
}
.nl-cell{
  width:32px;height:32px;
  display:flex;align-items:center;justify-content:center;
  border-radius:8px;
  font-family:var(--display);font-weight:700;
  font-size:var(--fs-sm);font-variant-numeric:tabular-nums;
  color:var(--muted);
  background:var(--bg-alt);
}
.nl-cell.is-num{color:#fff}
.nl-cell.is-over{outline:2px solid #ec4899;outline-offset:1px}
/* ten pair tints — the eleventh pair reuses the first, which is fine because
   two pairs sharing a colour are still told apart by their number */
.nl-cell.pair-0{background:#4f46e5}
.nl-cell.pair-1{background:#0d9488}
.nl-cell.pair-2{background:#d97706}
.nl-cell.pair-3{background:#db2777}
.nl-cell.pair-4{background:#7c3aed}
.nl-cell.pair-5{background:#0369a1}
.nl-cell.pair-6{background:#65a30d}
.nl-cell.pair-7{background:#c026d3}
.nl-cell.pair-8{background:#b45309}
.nl-cell.pair-9{background:#0f766e}

.nl-gap{
  border:0;background:transparent;padding:0;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
}
.nl-gap.nl-h{width:16px;height:32px}
.nl-gap.nl-v{width:32px;height:16px}
.nl-void{width:16px;height:16px}
.nl-gap span{display:block;border-radius:2px;background:transparent}
.nl-gap.nl-h span{width:100%;height:5px}
.nl-gap.nl-v span{width:5px;height:100%}
.nl-gap:hover span{background:var(--grid-line)}
.nl-gap:focus-visible{outline:2px solid var(--brand-3);outline-offset:1px;border-radius:4px}
.nl-gap.is-on span{background:var(--brand-1)}

@media (max-width:560px){
  /* a thumb, not a mouse */
  .nl-grid{grid-template-columns:repeat(calc(var(--w) - 1), auto 20px) auto;padding:9px}
  .nl-gap.nl-h{width:20px}
  .nl-void{width:20px;height:20px}
  .nl-gap.nl-v{height:20px}
}


/* ------------------------------------------- tap targets on a phone
   Star battle and heyawake already floored their cells at 44px and let the
   wrapper scroll (§12.36, §12.37). Every other grid shrank to fit instead, and
   shrinking to fit is the wrong trade when TAPPING is the activity — measured
   at 375px: killer sudoku 33x36, numberlink 32x32, and the rest similar.

   Every wrapper below already had overflow-x:auto; only the floor was missing.
   Scrolling a grid sideways is a small cost. Missing the cell you aimed at,
   repeatedly, is not. */
@media (max-width:640px){
  .gb-grid{--gb-max:none;min-width:calc(var(--n) * 44px)}
  /* the nonogram grid carries a clue gutter as its first column, so the
     floor has to leave room for it as well as for the cells */
  .nn-grid{min-width:calc(var(--w) * 44px + 62px)}
  /* width alone is not a tap target: hidoku rows were 26px tall at 375px */
  .gb-cell,.nn-cell,.bp-cell,.hi-cell,.nu-cell,.hk-cell{min-height:44px}
  .bp-grid,.hi-grid,.nu-grid{min-width:calc(var(--n) * 44px)}
  .hk-grid{max-width:none;min-width:calc(var(--w) * 44px)}
  .ws-grid{min-width:calc(var(--n) * 34px)}   /* reading, not tapping — §12.29 */
}


/* ---------------------------------------- the opening tip and the walkthrough
   Two things every playable grid now offers: a hint BEFORE you start, which
   names the technique the puzzle opens with but not the square, and the full
   deduction list once the grid is finished or revealed. The solver already
   recorded every reason; it was simply being thrown away after one line. */
.gb-tip,.gb-walk{
  margin:14px 0 0;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface);
}
.gb-tip > summary,.gb-walk > summary{
  cursor:pointer;list-style:none;padding:12px 14px;min-height:44px;
  display:flex;align-items:center;gap:8px;
  font-weight:650;font-size:var(--fs-base);color:var(--ink);
}
.gb-tip > summary::-webkit-details-marker,
.gb-walk > summary::-webkit-details-marker{display:none}
.gb-tip > summary::before,.gb-walk > summary::before{
  content:"";width:8px;height:8px;flex:none;
  border-right:2px solid var(--brand-3);border-bottom:2px solid var(--brand-3);
  transform:rotate(-45deg);transition:transform .15s ease;
}
.gb-tip[open] > summary::before,.gb-walk[open] > summary::before{transform:rotate(45deg)}
.gb-tip > summary:focus-visible,.gb-walk > summary:focus-visible{
  outline:2px solid var(--brand-3);outline-offset:2px;border-radius:var(--r-md);
}
.gb-tip p,.gb-walk > p{margin:0 14px 12px;font-size:var(--fs-md);line-height:1.6}

.gb-wt-sum{
  margin:0 14px 12px;padding:9px 11px;border-radius:var(--r-sm);
  background:var(--bg-alt);font-size:var(--fs-sm);color:var(--ink-2);
}
.gb-wt{list-style:none;margin:0;padding:0 14px 14px;display:grid;gap:9px;
  max-height:420px;overflow-y:auto}
.gb-wt li{display:flex;gap:10px;align-items:flex-start;font-size:var(--fs-md);line-height:1.55}
.gb-wt-n{
  flex:none;width:26px;height:26px;border-radius:50%;
  display:grid;place-items:center;
  background:var(--surface-2);color:var(--ink-2);
  font-size:var(--fs-xs);font-weight:700;font-variant-numeric:tabular-nums;
}
.gb-wt-t{
  display:inline-block;margin-left:6px;padding:1px 7px;border-radius:99px;
  background:var(--surface-2);color:var(--muted);font-size:var(--fs-2xs);
  white-space:nowrap;
}


/* ------------------------------------------ desktop: cap the cell size
   Every grid sized itself by filling its container, with no upper bound, so a
   wide screen produced absurd puzzles: measured at 1440px, a nonogram cell was
   74x74 and the grid 802x807 — an eight-hundred-pixel square for a picture
   puzzle — killer sudoku 57px, hitori 63px. A 5x5 nonogram was worse still,
   because the same container is shared by five columns instead of ten.

   A puzzle is read as a whole, so the eye should not have to travel. Real
   boards run 30-50px a cell; these caps sit in that range and vary by how much
   each cell has to carry (a killer-sudoku cell holds a digit AND a cage total,
   so it gets the most room; a nonogram cell holds nothing at all).

   These are max-widths, so the 44px MOBILE FLOOR above still wins where the two
   would conflict — min-width beats max-width in CSS, which is exactly the
   behaviour wanted here. */
.gb-grid{max-width:calc(var(--n) * 52px)}
.nn-grid{max-width:calc(var(--w) * 40px + 74px)}   /* + the row-clue gutter */
.bp-grid,.hi-grid,.nu-grid,.sb-grid,.hy-grid{max-width:calc(var(--n) * 46px)}
.hk-grid{max-width:calc(var(--w) * 48px)}
.ws-grid{max-width:calc(var(--n) * 40px)}

/* The button that advances the run is the one control a player presses twenty
   times in a row, so it has to clear the tap floor. Measured at 375px: 38px. */
@media (max-width:640px){
  .explain-foot .btn{min-height:44px;padding-inline:20px}
}

/* ---- About: the maker block and the values cards -----------------------
   Modelled on how the niche actually does it. PuzzleMadness names its creator,
   shows a picture and gives a real background; stone-calculator.org runs the
   same shape — origin story, founder with photo, then three value cards. Both
   read as one person standing behind the work, which is a stronger trust
   signal for a site like this than a corporate "we".

   The photo is a fixed 128px square with object-fit:cover, so any crop the
   owner supplies lands correctly and the block never shifts while it loads. */
.maker{
  display:flex; gap:22px; align-items:flex-start;
  margin:30px 0 34px; padding:24px;
  border:1px solid var(--line); border-radius:var(--r-lg);
  background:var(--surface); box-shadow:var(--shadow-1);
}
.maker-photo{
  flex:none; width:128px; height:128px;
  border-radius:16px; object-fit:cover; display:block;
  background:var(--accent-soft);
}
.maker-ico{
  flex:none; width:128px; height:128px;
  border-radius:16px; display:grid; place-items:center;
  background:linear-gradient(145deg,
    color-mix(in srgb, var(--accent-ink) 16%, transparent),
    color-mix(in srgb, var(--accent-ink) 6%, transparent));
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--accent-ink) 18%, transparent);
}
.maker-ico svg{width:56px;height:56px}
.maker-body{flex:1;min-width:0}
.maker-body h2{margin-top:0;margin-bottom:10px}
.maker-body p{margin-bottom:10px}
.maker-name{font-family:var(--display);font-weight:700;font-size:1.06rem;margin-bottom:6px}
.maker-role{display:block;font-family:var(--sans);font-weight:500;font-size:.94rem;color:var(--muted)}
.maker-note{font-size:.92rem;margin-bottom:0}

/* A HUE PER CARD, from the family palette rather than invented: those hues are
   already contrast-checked in both themes and already carry dark-mode variants.
   Three identical indigo cards read as one repeated thing; three hues read as
   three separate rules, which is what they are. Same system as the homepage
   proof cards, so a reader meets one visual language rather than two. */
.values{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin:20px 0 32px;align-items:stretch}
.value-card{
  --vc:var(--fam-patterns-and-series);
  position:relative;
  display:flex; flex-direction:column;
  padding:24px 22px 22px;
  border:1px solid var(--line); border-radius:var(--r-lg);
  background:var(--surface);
  overflow:hidden; isolation:isolate;
  transition:transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.value-card:nth-child(2){--vc:var(--fam-constraint-grids)}
.value-card:nth-child(3){--vc:var(--fam-letter-grids)}

/* the hue along the top edge, fading out — light on the edge rather than a rule */
.value-card::before{
  content:"";position:absolute;inset:0 0 auto 0;height:3px;
  background:linear-gradient(90deg,var(--vc),color-mix(in srgb,var(--vc) 30%,transparent));
  opacity:.9;
}
/* a wash that only appears on hover, under the content so it can never grey the text */
.value-card::after{
  content:"";position:absolute;inset:0;z-index:-1;
  background:radial-gradient(120% 100% at 0% 0%,
    color-mix(in srgb,var(--vc) 12%,transparent), transparent 62%);
  opacity:0;transition:opacity .18s ease;
}
.value-card:hover::after{opacity:1}
.value-card:hover{
  transform:translateY(-3px);
  box-shadow:var(--sh-md,0 8px 22px rgba(0,0,0,.09));
  border-color:color-mix(in srgb,var(--vc) 48%,var(--line));
}
.value-card h3{font-size:1.04rem;line-height:1.35;margin:14px 0 9px}
.value-card p{font-size:.95rem;color:var(--muted);margin-bottom:0;line-height:1.62;flex:1}
.value-ico{
  width:44px;height:44px;border-radius:13px;display:grid;place-items:center;
  color:var(--vc);
  background:linear-gradient(145deg,
    color-mix(in srgb,var(--vc) 20%,transparent),
    color-mix(in srgb,var(--vc) 8%,transparent));
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--vc) 22%,transparent);
  transition:transform .18s ease, box-shadow .18s ease, background .18s ease;
}
.value-ico svg{width:23px;height:23px}
.value-card:hover .value-ico{
  transform:translateY(-1px) scale(1.05);
  background:linear-gradient(145deg,
    color-mix(in srgb,var(--vc) 32%,transparent),
    color-mix(in srgb,var(--vc) 14%,transparent));
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--vc) 40%,transparent);
}
@media (prefers-reduced-motion:reduce){
  .value-card,.value-card:hover,.value-ico,.value-card:hover .value-ico,
  .value-card::after{transition:none;transform:none}
}

@media (max-width:760px){
  .maker{flex-direction:column;gap:16px}
  .maker-photo,.maker-ico{width:96px;height:96px}
  .values{grid-template-columns:1fr}
}
