/* Cube section tokens and layout. Extends the Tree of Life token names
   (--page/--surface/--surface-2/--ink/--muted/--line/--shadow) so the two
   sections feel like one site, and adds the six sticker colours plus plastic
   and "we don't know this sticker yet". view.js reads the --cube-* tokens off
   a .cube-probe element and re-reads them when the colour scheme changes, so
   these values are the single source of truth for painted stickers. */
:root {
  color-scheme: light dark;
  --page: #eef1f5;
  --surface: #fffefb;
  --surface-2: #e9edf3;
  --ink: #17212b;
  --muted: #5d6b79;
  --line: #ccd5df;
  --accent: #1b5e9c;
  --accent-ink: #114069;
  --warn: #8c2f39;
  --shadow: 0 18px 45px rgba(19, 34, 48, .13);
  /* Sticker colours: plain, high-contrast, and distinguishable in print. */
  --cube-U: #fbfbf9;
  --cube-R: #c62828;
  --cube-F: #2e7d32;
  --cube-D: #f9c823;
  --cube-L: #ef6c00;
  --cube-B: #1565c0;
  --cube-plastic: #20242a;
  --cube-unknown: #98a2ae;
  --cube-sticker-edge: rgba(0, 0, 0, .28);
  /* Puzzle galaxy (slice 8): the map's sky, its lines, and one colour per
     family, in family order from data/puzzles.json. */
  --gx-sky: #f3f6fa;
  --gx-glow: #e1e9f5;
  --gx-link: rgba(70, 86, 104, .55);
  --gx-f1: #1b5e9c; --gx-f2: #c2410c; --gx-f3: #2e7d32; --gx-f4: #8e24aa;
  --gx-f5: #a16207; --gx-f6: #00838f; --gx-f7: #c2185b; --gx-f8: #5d4037;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --page: #121820;
    --surface: #1b2430;
    --surface-2: #232f3c;
    --ink: #eef3f8;
    --muted: #a6b4c2;
    --line: #37475a;
    --accent: #7cc0f5;
    --accent-ink: #a9d6fb;
    --warn: #f09aa4;
    --shadow: 0 18px 45px rgba(0, 0, 0, .34);
    /* Lifted a step against dark plastic; hue stays the same so red and
       orange keep the same relationship the kid sees on the real cube. */
    --cube-U: #f4f5f1;
    --cube-R: #e0453f;
    --cube-F: #3f9d48;
    --cube-D: #ffd747;
    --cube-L: #ff8a2b;
    --cube-B: #3d8ce0;
    --cube-plastic: #0c0f13;
    --cube-unknown: #6d7a89;
    --cube-sticker-edge: rgba(0, 0, 0, .55);
    --gx-sky: #0d1219;
    --gx-glow: #1c2940;
    --gx-link: rgba(166, 180, 194, .5);
    --gx-f1: #7cc0f5; --gx-f2: #ff9a5c; --gx-f3: #7bd389; --gx-f4: #d49cf0;
    --gx-f5: #f2c14e; --gx-f6: #5fd4dc; --gx-f7: #f48fb8; --gx-f8: #c9a88f;
  }
}
:root[data-theme="dark"] {
  --page: #121820;
  --surface: #1b2430;
  --surface-2: #232f3c;
  --ink: #eef3f8;
  --muted: #a6b4c2;
  --line: #37475a;
  --accent: #7cc0f5;
  --accent-ink: #a9d6fb;
  --warn: #f09aa4;
  --shadow: 0 18px 45px rgba(0, 0, 0, .34);
  --cube-U: #f4f5f1;
  --cube-R: #e0453f;
  --cube-F: #3f9d48;
  --cube-D: #ffd747;
  --cube-L: #ff8a2b;
  --cube-B: #3d8ce0;
  --cube-plastic: #0c0f13;
  --cube-unknown: #6d7a89;
  --cube-sticker-edge: rgba(0, 0, 0, .55);
  --gx-sky: #0d1219;
  --gx-glow: #1c2940;
  --gx-link: rgba(166, 180, 194, .5);
  --gx-f1: #7cc0f5; --gx-f2: #ff9a5c; --gx-f3: #7bd389; --gx-f4: #d49cf0;
  --gx-f5: #f2c14e; --gx-f6: #5fd4dc; --gx-f7: #f48fb8; --gx-f8: #c9a88f;
}

* { box-sizing: border-box; }
html { min-height: 100%; background: var(--page); }
body { margin: 0; min-height: 100vh; color: var(--ink); background: var(--page); font: 17px/1.5 ui-rounded, -apple-system, BlinkMacSystemFont, "SF Pro Rounded", system-ui, sans-serif; }
a { color: var(--accent-ink); text-underline-offset: .18em; }
button, input, select, textarea { font: inherit; }
h1, h2, h3 { line-height: 1.1; }

/* Every interactive thing a finger aims at is at least 48px tall. */
button, .button, .chip, .net-grid button, .transport button { min-height: 48px; }
a:focus-visible, button:focus-visible, select:focus-visible, input:focus-visible,
.cube-stage:focus-visible, .net-grid button:focus-visible, [tabindex]:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 12px;
}

.skip-link { position: fixed; left: 12px; top: -80px; z-index: 99; padding: 12px 16px; min-height: 48px; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; font-weight: 750; }
.skip-link:focus { top: 12px; }
.site-header { min-height: 58px; padding: 8px clamp(14px, 3vw, 34px); display: flex; align-items: center; justify-content: space-between; gap: 14px; border-bottom: 1px solid var(--line); background: var(--surface); position: relative; z-index: 20; }
.brand, .cube-home { color: var(--ink); font-weight: 800; text-decoration: none; padding: 10px 2px; }
.cube-home { color: var(--accent-ink); }

main { display: block; }
.wrap { width: min(1080px, calc(100% - 28px)); margin: clamp(16px, 3vw, 34px) auto 56px; }
.page-head h1 { margin: 0 0 6px; font-size: clamp(28px, 5vw, 46px); }
.page-head p { margin: 0 0 4px; color: var(--muted); font-size: 17px; }

/* --------------------------------------------------------------- cards */
.card { display: block; padding: clamp(16px, 2.4vw, 22px); border: 1px solid var(--line); border-radius: 20px; background: var(--surface); box-shadow: var(--shadow); }
.card h2 { margin: 0 0 6px; font-size: 24px; }
.card p { margin: 0; color: var(--muted); font-size: 16px; }
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: clamp(12px, 2vw, 18px); margin: clamp(18px, 3vw, 30px) 0 0; padding: 0; list-style: none; }
.card-grid > li { display: flex; }
.card-grid .card { flex: 1; min-height: 148px; }
a.card { text-decoration: none; color: var(--ink); }
a.card:hover { border-color: var(--accent); transform: translateY(-2px); }
a.card h2 { color: var(--accent-ink); }
.card .emoji { display: block; font-size: 38px; line-height: 1; margin-bottom: 8px; }
/* A "coming soon" card is deliberately not a link: there is no page yet. */
.card.soon { background: var(--surface-2); box-shadow: none; }
.card.soon h2 { color: var(--ink); }
.card .soon-tag { display: inline-block; margin-top: 10px; padding: 5px 11px; border-radius: 999px; background: var(--surface); border: 1px solid var(--line); color: var(--muted); font-size: 14px; font-weight: 750; }

/* ---------------------------------------------------------- cube stage */
/* The canvas host. touch-action:none because pointer.js owns every drag,
   pinch and two-finger turn on this element. */
.cube-stage { position: relative; display: block; width: 100%; min-height: 320px; height: min(62vh, 560px); margin: 0; border: 1px solid var(--line); border-radius: 22px; background: var(--surface); overflow: hidden; touch-action: none; user-select: none; -webkit-user-select: none; }
.cube-stage canvas { display: block; width: 100%; height: 100%; touch-action: none; }
.cube-stage:focus-visible { outline-offset: -4px; border-radius: 22px; }
.stage-hint { position: absolute; left: 10px; bottom: 8px; margin: 0; padding: 5px 10px; border-radius: 10px; background: var(--surface-2); color: var(--muted); font-size: 14px; pointer-events: none; }
/* Probe elements: view.js resolves --cube-* through getComputedStyle(color)
   so the painted stickers always match the stylesheet, light or dark. */
.cube-probe { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.cube-probe[data-face="U"] { color: var(--cube-U); }
.cube-probe[data-face="R"] { color: var(--cube-R); }
.cube-probe[data-face="F"] { color: var(--cube-F); }
.cube-probe[data-face="D"] { color: var(--cube-D); }
.cube-probe[data-face="L"] { color: var(--cube-L); }
.cube-probe[data-face="B"] { color: var(--cube-B); }
.cube-probe[data-face="plastic"] { color: var(--cube-plastic); }
.cube-probe[data-face="unknown"] { color: var(--cube-unknown); }

/* ------------------------------------------------------------- player */
.player { display: grid; gap: 12px; margin-top: 14px; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; padding: 0; list-style: none; }
.chips.scroller { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 6px; scrollbar-width: thin; }
.chip { display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto; padding: 10px 15px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); color: var(--ink); font-size: 16px; font-weight: 750; cursor: pointer; }
.chip:hover { border-color: var(--accent); }
.chip[aria-current="step"] { border-color: transparent; background: var(--accent); color: var(--surface); }
/* A chip that says which one you picked has to LOOK picked. aria-pressed was
   the only channel carrying it, which is right for a screen reader and no help
   at all to the kid reading step 1. Same rule serves the patterns page. */
.chip[aria-pressed="true"] { border-color: transparent; background: var(--accent); color: var(--surface); }
.chip[aria-pressed="true"] .chip-count { color: var(--surface); }
.chip[aria-current="step"] .chip-count { color: var(--surface); }
.chip[disabled] { opacity: .55; cursor: default; }
.chip-count { color: var(--muted); font-weight: 650; font-size: 14px; }
.transport { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.transport button { min-width: 56px; padding: 10px 16px; border: 1px solid var(--line); border-radius: 15px; background: var(--surface); color: var(--ink); font-size: 18px; font-weight: 800; cursor: pointer; }
.transport button:hover { border-color: var(--accent); }
.transport button[aria-pressed="true"], .transport button.primary { border-color: transparent; background: var(--accent); color: var(--surface); }
.transport button[disabled] { opacity: .5; cursor: default; }
.transport .spacer { flex: 1 1 12px; }
.transport label { display: inline-flex; align-items: center; gap: 7px; font-size: 16px; font-weight: 700; }
.transport select { min-height: 48px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 13px; background: var(--surface); color: var(--ink); }
.move-readout { margin: 0; font-size: 17px; font-weight: 750; font-variant-numeric: tabular-nums; }
.move-readout code { font: 700 17px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; }

/* --------------------------------------------------------- net editor */
/* role="grid" of buttons: one button per facelet. Sized so a 7x7 face
   still clears 48px on the iPad and stays tappable at 390px. */
.net-grid { display: grid; gap: 3px; margin: 0; padding: 0; }
.net-grid [role="row"] { display: grid; grid-auto-flow: column; gap: 3px; }
.net-grid button { min-width: 48px; min-height: 48px; padding: 0; border: 1px solid var(--cube-sticker-edge); border-radius: 7px; background: var(--cube-unknown); color: transparent; cursor: pointer; }
.net-grid button span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.net-grid button[data-color="U"] { background: var(--cube-U); }
.net-grid button[data-color="R"] { background: var(--cube-R); }
.net-grid button[data-color="F"] { background: var(--cube-F); }
.net-grid button[data-color="D"] { background: var(--cube-D); }
.net-grid button[data-color="L"] { background: var(--cube-L); }
.net-grid button[data-color="B"] { background: var(--cube-B); }
.net-grid button[data-color="?"] { background: var(--cube-unknown); }
.net-grid button[aria-selected="true"] { outline: 3px solid var(--ink); outline-offset: -3px; }
.net-grid button[data-unsure="true"] { box-shadow: inset 0 0 0 3px var(--warn); }
.net-grid.letters button { color: var(--cube-plastic); font-size: 16px; font-weight: 850; }
.net-grid.letters button span { position: static; width: auto; height: auto; clip-path: none; }

/* ------------------------------------------------------------- status */
/* One live region per page. It says what is true, including "not found". */
.status { margin: 12px 0 0; padding: 12px 15px; border: 1px solid var(--line); border-radius: 15px; background: var(--surface-2); color: var(--ink); font-size: 16px; font-weight: 700; }
.status:empty { display: none; }
.status.working { border-color: var(--accent); }
.status.problem { border-color: var(--warn); color: var(--warn); }
.status small { display: block; margin-top: 4px; color: var(--muted); font-weight: 600; }
noscript .status { display: block; }

/* ------------------------------------------------------------ layouts */
/* iPad portrait (820px): stage over controls, one column. */
@media (max-width: 860px) {
  .wrap { width: calc(100% - 20px); margin-top: 14px; }
  .page-head h1 { font-size: clamp(26px, 6vw, 36px); }
  .cube-stage { height: min(54vh, 480px); min-height: 300px; border-radius: 18px; }
  .card-grid { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
}
/* Phone (390px): full-bleed stage, chips scroll sideways, nothing shrinks
   below 16px text or a 48px target. */
@media (max-width: 430px) {
  body { font-size: 16px; }
  .wrap { width: calc(100% - 16px); }
  .page-head h1 { font-size: 27px; }
  .card-grid { grid-template-columns: 1fr; }
  .card { padding: 14px; border-radius: 16px; }
  .card h2 { font-size: 21px; }
  .cube-stage { height: min(48vh, 380px); min-height: 260px; border-radius: 14px; }
  .chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 6px; }
  .transport button { min-width: 52px; padding: 10px 12px; }
  .net-grid button { min-width: 38px; min-height: 38px; border-radius: 5px; }
  .brand { font-size: 0; }
  .brand::first-letter { font-size: 24px; }
}
/* --------------------------------------------------------- solver page */
/* Slice 2. Everything here is additive and `cf-`-prefixed except the handful
   of rules that extend classes cube.css already owns (.net-grid, .card).
   Two ownership rules keep this block from fighting src/solver.js:
   - solver.js injects its own <style> AFTER this stylesheet, so at equal
     specificity solver.js wins. These rules are therefore presentation
     (colour, border, weight, minimum size) and the page keeps the layout it
     needs. The one deliberate exception is .cf-net-palette .cf-net-swatch,
     which is two classes on purpose so it outranks app.js's 48px swatch: a
     colour is the thing a seven-year-old taps most on this page, so it gets
     56px.
   - Nothing here animates. The progress bar is a real <progress value>, never
     indeterminate, so reduced motion needs no override beyond silencing the
     UA's own indeterminate animation further down. */

/* solver.html stacks five bare <section class="card"> in <main>, and .card
   carries no margin of its own, so without this they touch. Two classes deep,
   so solver.js's own block can still say otherwise. Nothing else on the site
   puts one .card directly after another inside <main>. */
main > .card + .card { margin-top: clamp(12px, 2vw, 18px); }

/* The five numbered steps are all on screen from the start. A step that is not
   reachable yet is dimmed and its buttons are disabled — never hidden, because
   a kid should be able to see what is coming. */
.cf-step-now { border-color: var(--accent); }
/* NOT opacity: dimming the whole card composites its text with the page and
   drops the very sentence that explains how to unlock the step to about 2:1.
   The buttons inside are already `disabled` and carry their own .5 opacity, so
   the container only has to look set aside — a dashed edge and the recessed
   background do that without touching a single contrast ratio. */
.cf-step-locked { background: var(--surface-2); box-shadow: none; border-style: dashed; }
.cf-step h2 { font-size: 22px; }
.cf-step > p { margin: 0 0 4px; color: var(--muted); font-size: 16px; }

/* Anything big enough to be the main thing you press on a step. */
.cf-big { min-height: 56px; padding: 13px 20px; border: 1px solid var(--line); border-radius: 16px; background: var(--surface); color: var(--ink); font-size: 18px; font-weight: 800; cursor: pointer; }
.cf-big.primary { border-color: transparent; background: var(--accent); color: var(--surface); }
.cf-big:hover { border-color: var(--accent); }
.cf-big[disabled] { opacity: .5; cursor: default; }
.cf-actions { display: flex; flex-wrap: wrap; gap: 10px; margin: 14px 0 0; }
.cf-sub { display: block; margin-top: 4px; color: var(--muted); font-size: 14px; font-weight: 650; }
.cf-reason { margin: 8px 0 0; color: var(--muted); font-size: 15px; }

/* "How to start" and "Which pattern were you making?": left-aligned because
   each one carries a sentence under its name. */
.cf-choices { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); margin: 12px 0 0; padding: 0; list-style: none; }
.cf-choices > li { display: flex; min-width: 0; }
.cf-choice { flex: 1; min-width: 0; min-height: 64px; padding: 12px 16px; border: 1px solid var(--line); border-radius: 16px; background: var(--surface); color: var(--ink); font: inherit; font-weight: 800; text-align: left; cursor: pointer; }
.cf-choice:hover { border-color: var(--accent); }
.cf-choice[aria-pressed="true"], .cf-choice[aria-checked="true"] { border-color: var(--accent); background: var(--surface-2); }

/* Two-way choice: stop at the pattern, or go all the way to solved. */
.cf-toggle { display: inline-flex; margin: 12px 0 0; padding: 3px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface-2); }
.cf-toggle button { min-height: 48px; padding: 10px 18px; border: 0; border-radius: 999px; background: none; color: var(--ink); font-size: 16px; font-weight: 750; cursor: pointer; }
.cf-toggle button[aria-pressed="true"], .cf-toggle button[aria-checked="true"] { background: var(--accent); color: var(--surface); }

/* The colour you are painting with. 56px, and never colour alone: the swatch
   carries its letter in letters/marks mode and its name in the aria-label. */
.cf-net-palette .cf-net-swatch { width: 56px; height: 56px; min-width: 56px; min-height: 56px; border-radius: 14px; }
.cf-net-palette { align-items: center; }

/* Sticker counts. Arithmetic, not a verdict — a colour that is over or under
   gets a dashed ring as well as the warn colour, so the difference survives
   both dark mode and a colour-blind reader. */
.cf-counts { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 0; padding: 0; list-style: none; }
.cf-count { display: inline-flex; align-items: center; gap: 8px; min-height: 48px; padding: 9px 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font-size: 16px; font-weight: 750; font-variant-numeric: tabular-nums; }
.cf-count .cf-dot { flex: 0 0 auto; width: 18px; height: 18px; border: 1px solid var(--cube-sticker-edge); border-radius: 6px; }
.cf-count-off { border-style: dashed; border-width: 2px; border-color: var(--warn); color: var(--warn); }

/* A sticker the check says could be wrong, and a sticker a repair would
   change. The first reuses the existing [data-unsure] ring; the second is a
   dashed ring so the two are told apart by shape, not only by colour. */
.net-grid button[data-repair="true"] { outline: 3px dashed var(--accent); outline-offset: -3px; }

/* The verdict. `.ok` and `.problem` differ by a 6px edge and by their heading,
   and the heading's first word is always "Yes." or "No.". */
.cf-answer { margin: 14px 0 0; padding: 14px 16px; border: 1px solid var(--line); border-left: 6px solid var(--line); border-radius: 16px; background: var(--surface-2); }
.cf-answer h3 { margin: 0 0 6px; font-size: 20px; }
.cf-answer p { margin: 0 0 8px; font-size: 16px; }
.cf-answer > :last-child { margin-bottom: 0; }
.cf-answer.ok { border-left-color: var(--accent); }
.cf-answer.problem { border-left-color: var(--warn); }
.cf-answer.problem h3 { color: var(--warn); }
.cf-answer .cf-alg { display: block; margin: 8px 0; }
/* display:list-item, not flex: a browser only paints the disclosure triangle on
   a summary whose display is list-item, and without it "The details" reads as a
   bold heading rather than something you can open — which hides the page's own
   evidence. The padding keeps the 48px target the flex box was there for. */
.cf-answer summary { min-height: 48px; display: list-item; list-style-position: inside; padding: 12px 0; font-weight: 800; cursor: pointer; }
.cf-facts { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; margin: 8px 0 0; font-size: 15px; }
.cf-facts dt { color: var(--muted); font-weight: 700; }
.cf-facts dd { margin: 0; font-variant-numeric: tabular-nums; }

/* One block per problem the engine reported, with its own "Show me where". */
.cf-problems { margin: 10px 0 0; padding: 0; list-style: none; }
.cf-problem { margin: 0 0 10px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 13px; background: var(--surface); font-size: 16px; }
.cf-problem:last-child { margin-bottom: 0; }
.cf-problem p { margin: 0 0 8px; }
.cf-repair { min-height: 48px; margin: 6px 6px 0 0; padding: 10px 15px; border: 1px solid var(--line); border-radius: 13px; background: var(--surface); color: var(--ink); font-size: 16px; font-weight: 750; text-align: left; cursor: pointer; }
.cf-repair:hover { border-color: var(--accent); }

/* Search progress. Always a real value: this bar is a guess from a measured
   speed, and an indeterminate barber pole would be a different, emptier claim.
   The numbers live in #search-progress, outside the live region. */
.cf-progress { display: block; width: 100%; height: 18px; margin: 12px 0 0; appearance: none; -webkit-appearance: none; border: 1px solid var(--line); border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.cf-progress::-webkit-progress-bar { background: var(--surface-2); }
.cf-progress::-webkit-progress-value { background: var(--accent); border-radius: 999px; }
.cf-progress::-moz-progress-bar { background: var(--accent); border-radius: 999px; }
.cf-progress-note { margin: 6px 0 0; color: var(--muted); font-size: 14px; }

/* "Keep looking", and the honest version of it when the next rung is slow. */
.cf-keep-looking { display: block; text-align: left; }
.cf-slow { border-style: dashed; border-color: var(--warn); }
.cf-slow .cf-sub { color: var(--warn); }

/* Confirm rows ("Changing to a 5x5 throws away the cube you put in."). */
.cf-confirm { margin: 12px 0 0; padding: 12px 14px; border: 1px solid var(--warn); border-radius: 14px; background: var(--surface); }
.cf-confirm p { margin: 0 0 10px; font-size: 16px; }

.cf-hint { margin: 10px 0 0; color: var(--muted); font-size: 15px; }
/* The rule is for the textarea itself (`resize` is a no-op on a div); the box
   around it only has to stack the label, the field and the buttons. */
.cf-paste-box { display: grid; gap: 6px; margin: 12px 0 0; }
.cf-paste-box label { font-weight: 750; font-size: 16px; }
.cf-paste { width: 100%; min-height: 96px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 13px; background: var(--surface); color: var(--ink); resize: vertical; }

/* Slice 4: solutions the big solver on the Mac worked out.

   "Open a solution file" is a <label> wrapping a real <input type="file">, so
   the picker works with no JavaScript between the tap and the file dialog. The
   input is visually hidden but NOT display:none and NOT hidden — both take it
   out of the tab order and out of reach of a switch — so it is a 1px clipped
   box that still focuses, and :focus-within puts the focus ring on the label
   the kid can actually see. */
/* A big button that carries a sentence under its name reads as a block of
   text, so it is laid out as one: full width, left-aligned, like "Keep
   looking" beside it. Centred, the name and its sub-line look like two
   separate things. */
.cf-big.cf-wide { display: block; width: 100%; text-align: left; }
.cf-file { position: relative; display: block; width: 100%; text-align: left; cursor: pointer; }
.cf-file-input { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.cf-file:focus-within { outline: 3px solid var(--accent); outline-offset: 2px; }

/* The saved list. One row per solution, and a row is a name, what it is, and
   two 48px buttons — nothing is a link, because every one of these does
   something to this iPad rather than going somewhere. */
.cf-cache-list { display: grid; gap: 10px; margin: 12px 0 0; padding: 0; list-style: none; }
.cf-cache-row { min-height: 48px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 16px; background: var(--surface); }
.cf-cache-row .cf-actions { margin: 8px 0 0; }
.cf-cache-row .cf-confirm { margin: 10px 0 0; }
.cf-cache-name { display: block; font-size: 17px; font-weight: 800; }
.cf-cache-meta { display: block; margin-top: 2px; color: var(--muted); font-size: 15px; font-variant-numeric: tabular-nums; }
/* Which phase of the answer a block of moves belongs to. */
.cf-phase-label { margin: 12px 0 0; font-weight: 800; font-size: 16px; }
.cf-label { align-self: center; font-weight: 800; }
.cf-choices > .cf-choice { min-width: 0; }

/* ---- the camera scan (slice 3) --------------------------------------------

   The scan is not a page: it opens inside step 2's card, in the same host the
   pattern picker and the paste box use, and the flat net stays on screen
   underneath the whole time. So everything here has to survive being ~600px of
   card on an iPad and ~330px of card on a phone, with no horizontal scroll at
   either width.

   Two deliberate choices about colour:

   1. The stage is black in BOTH schemes. A camera frame is a photograph; a
      surface that went white at sunrise would change what the kid thinks the
      cube's plastic looks like, and letterboxing (object-fit: contain, never
      cover — see D12) needs a neutral that is not competing with the picture.
   2. The grid lines, the corner handles and the "taken" check are painted in
      FIXED ink, not in --ink/--accent/--line. Those tokens flip with the
      colour scheme; the thing they have to stay legible against is the kid's
      cube, which does not. Every one of them is drawn twice, a dark stroke
      under a light one, so it reads on a white sticker and on black plastic
      alike. scan.js draws the same pair into the overlay canvas.

   Nothing in here animates in CSS. Auto-capture's ring (a line walking round
   the box while the cube is held still) and its white flash when a picture
   takes itself are canvas drawing in scan.js, not CSS, so the reduced-motion
   block below cannot reach them: scan.js asks prefers-reduced-motion itself
   and, when it says reduce, draws a still thick outline instead of the ring
   and no flash at all. A viewfinder that pulsed or a thumbnail that slid in
   would be motion a kid cannot turn off in the middle of holding a cube up to
   a lens. */

.cf-scan { margin: 12px 0 0; }
.cf-scan h3 { margin: 0 0 4px; font-size: 20px; }

/* The instruction for this shot is the largest text in the card on purpose:
   it is read while looking at a cube, not at the screen. */
.cf-scan-shot { margin: 0 0 6px; font-size: 19px; font-weight: 800; line-height: 1.35; }
.cf-scan-progress { margin: 0; color: var(--muted); font-size: 15px; font-weight: 750; }

/* Auto-capture's one line under the picture ("Hold still…", or why nothing is
   happening). Readable at arm's length, but smaller than the instruction
   above it. Room for its longest sentence is always kept, so the shutter
   below does not jump up and down while the text changes under a kid
   holding a cube. It is
   not a live region: scan.js says the sentences that matter through the
   page's one #cube-status. */
/* Two classes deep, because `.card p` (muted, 16px, no margin) outranks one.
   Every sentence scan.js puts here is at most ~85 characters, which is two
   lines down to a 390 px phone (measured 2026-09-27: 344 px of text); on a
   narrower screen they take three, so three are kept. */
.cf-scan p.cf-scan-auto { margin: 10px 0 0; min-height: 2.8em; color: var(--ink); font-size: 17px; font-weight: 750; line-height: 1.4; }
@media (max-width: 360px) { .cf-scan p.cf-scan-auto { min-height: 4.2em; } }

/* The "Take pictures by themselves" switch. The shared pressed-chip look (a
   solid accent pill) made it a twin of the solid "Take the picture" shutter
   right beside it, so it gets a switch's look instead: a small track with a
   knob that sits left when off and right (on accent) when on, and only an
   accent outline on the chip itself. The track is an empty ::before, so it
   adds nothing to the button's name; aria-pressed says on/off. The knob
   jumps, it does not slide: nothing here animates. */
.cf-scan .chip[data-fk="scan-auto"]::before {
  content: ""; flex: none; width: 36px; height: 22px; border-radius: 999px;
  background: radial-gradient(circle at 11px 50%, var(--surface) 7px, transparent 7.5px), var(--muted);
}
.cf-scan .chip[data-fk="scan-auto"][aria-pressed="true"] { border-color: var(--accent); background: var(--surface-2); color: var(--ink); box-shadow: inset 0 0 0 1px var(--accent); }
.cf-scan .chip[data-fk="scan-auto"][aria-pressed="true"]::before {
  background: radial-gradient(circle at 25px 50%, var(--surface) 7px, transparent 7.5px), var(--accent);
}

/* 4:3 is the shape of the default constraint and of the fixture stills; the
   inline style scan.js sets from the real track's aspect ratio overrides it,
   which is why this is a plain declaration and not an !important. `contain`
   then letterboxes into whatever is left, against the black.

   The `46vh` cap is the whole reason this is not just `width: 100%`. On an iPad
   the card is ~700px wide, so an uncapped 4:3 stage is ~525px tall and pushes
   the shot counter, the six slots and the shutter off the bottom of the screen
   — and the shutter is the one control a kid presses while holding a cube up
   with the other hand. Capped, the viewfinder is still a large target and the
   whole panel fits on one screen at both widths. */
.cf-scan-stage { position: relative; margin: 10px auto 0; width: 100%; max-width: min(100%, 46vh); aspect-ratio: 4 / 3; overflow: hidden; border: 1px solid var(--line); border-radius: 16px; background: #05070a; }
.cf-scan-video, .cf-scan-still { display: block; width: 100%; height: 100%; object-fit: contain; background: #05070a; }
/* D12: no mirror, anywhere. A mirrored preview makes a kid's correction go the
   wrong way and silently flips the sampler's coordinate frame. */
.cf-scan-video { transform: none; }
.cf-scan-overlay { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; }

/* 48px of hit area around a 20px dot. The dot is what you aim at; the button
   is what you can actually hit with a thumb while the other hand holds a cube.
   The handles sit ON the frame, so both the ring and the fill are fixed ink.

   `pointer-events: none` is load-bearing. The four buttons are siblings of the
   overlay canvas and paint on top of it, and the drag recogniser is bound to the
   overlay — so without this a pointerdown that lands on the dot goes to the
   button (which listens for keys only) and the drag never starts. Pressing the
   very thing that looks grabbable did nothing at all; only a ring of pixels
   outside the button worked. The button stays in the tab order and keeps its
   keydown handler, so the arrow-key path is untouched. */
.cf-scan-handle { position: absolute; width: 48px; height: 48px; margin: -24px 0 0 -24px; padding: 0; border: 0; border-radius: 50%; background: none; cursor: grab; touch-action: none; pointer-events: none; }
.cf-scan-handle::before { content: ""; position: absolute; inset: 14px; border: 3px solid #05070a; border-radius: 50%; background: #fdfdfb; box-shadow: 0 0 0 1px rgba(253, 253, 251, .9); }
.cf-scan-handle:focus-visible { outline: 3px solid #ffd747; outline-offset: -2px; border-radius: 12px; }
.cf-scan-handle:focus-visible::before { inset: 11px; }

/* Six slots, always six, so "3 of 6" is visible as a shape and not only as a
   sentence. A filled slot shows the raw medians the sampler took — pixels, not
   a classification (D2) — and the check glyph, because colour alone is not a
   channel a colour-blind kid has. */
.cf-scan-strip { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 0; padding: 0; list-style: none; }
.cf-scan-thumb { position: relative; flex: 0 0 auto; min-width: 64px; padding: 6px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface-2); color: var(--muted); font-size: 13px; font-weight: 750; text-align: center; }
.cf-scan-thumb[aria-current="step"] { border-color: var(--accent); border-width: 2px; padding: 5px; }
.cf-scan-thumb.done { border-color: var(--accent); color: var(--ink); }
.cf-scan-thumb.done::after { content: "✓"; position: absolute; top: 2px; right: 6px; color: var(--accent); font-size: 15px; font-weight: 900; }
.cf-scan-thumb canvas { display: block; width: 56px; height: 56px; margin: 0 auto 4px; border-radius: 6px; background: #05070a; }
/* display:grid lives on the same selector as the size. Split across two rules
   the more specific one won, the grid collapsed to a block, and every swatch
   inside it was 0x0 under a caption saying "these are the colours the camera
   saw". The rows are set inline by scan.js, beside the columns, because n is
   not known here. */
.cf-scan-thumb .cf-scan-cells { display: grid; gap: 1px; overflow: hidden; width: 56px; height: 56px; margin: 0 auto 4px; border-radius: 6px; background: #05070a; }
.cf-scan-thumb .cf-scan-cells > span { display: block; min-width: 0; min-height: 0; }

/* Same 6px edge as .cf-answer.problem, so "something is wrong" looks the same
   whatever card it happens in. */
.cf-scan-problem { margin: 12px 0 0; padding: 12px 14px; border: 1px solid var(--line); border-left: 6px solid var(--warn); border-radius: 16px; background: var(--surface-2); }
.cf-scan-problem p { margin: 0 0 8px; font-size: 16px; }
.cf-scan-problem > :last-child { margin-bottom: 0; }

/* The rings a scan leaves on the net are the SAME mark a wrong-piece diagnosis
   leaves — one attribute, [data-unsure], line 180 above. This rule exists only
   to say so: slice 3 adds no second ring, and if it ever needs one the two
   have to be told apart by more than colour. */

@media (max-width: 430px) {
  .cf-choices { grid-template-columns: 1fr; }
  .cf-big { font-size: 17px; padding: 12px 16px; }
  .cf-toggle button { padding: 10px 13px; }
  .cf-facts { grid-template-columns: 1fr; gap: 0; }
  .cf-facts dd { margin: 0 0 6px; font-weight: 750; }
  /* The handles keep their 48px hit area at every width — they are the one
     control a kid drags rather than taps. Only the slots get smaller. */
  .cf-scan-shot { font-size: 18px; }
  .cf-scan-thumb { min-width: 52px; padding: 4px; }
  .cf-scan-thumb canvas, .cf-scan-thumb .cf-scan-cells { width: 44px; height: 44px; }
}

/* ------------------------------------------------------------ solve all the way (slice 6) */
/* The Learner / Shortest card on the solver page: two big buttons, then the
   answer as an ordered list of phases, each with its sentence and its moves.
   A 7x7 beginner answer runs to hundreds of moves, so the move text wraps
   anywhere rather than pushing the card sideways. */
.cf-answer.working { border-left-color: var(--accent); }
.cf-solve-phases { margin: 10px 0 12px; padding-left: 24px; display: grid; gap: 10px; }
.cf-solve-phases li p { margin: 4px 0 0; }
.cf-solve-phases .cf-alg { overflow-wrap: anywhere; font-size: 15px; }

/* ------------------------------------------------------------ timer (slice 5) */
/* The clock surface is touch-action:none and unselectable: two thumbs held on
   an iPad otherwise start a text selection or the long-press callout, which
   iOS answers with pointercancel. The pads are inset from the screen edges
   (the page gutter plus their own padding) because a thumb resting on the
   bezel edge is a system-gesture zone. Red is "holding", green is "go" — the
   same two sticker colours the kid knows, and the words under the clock say
   it too, so colour is never the only signal. */
.timer-scramble { margin-top: 4px; }
.timer-scramble-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.timer-scramble-head h2 { margin: 0; }
.timer-scramble-body { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 14px; align-items: center; margin-top: 10px; }
.timer-scramble-text { margin: 0; color: var(--ink); font: 700 22px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; word-spacing: .18em; overflow-wrap: anywhere; }
.timer-scramble-text[data-size="4"] { font-size: 19px; }
.timer-scramble-text[data-size="5"], .timer-scramble-text[data-size="6"], .timer-scramble-text[data-size="7"] { font-size: 16px; }
.timer-preview { width: 160px; height: 120px; display: block; }
.timer-note { margin: 10px 0 0; color: var(--muted); font-size: 15px; }
.timer-surface { position: relative; margin: 16px 0 0; padding: 18px clamp(14px, 4vw, 40px) clamp(18px, 3vw, 30px); border: 1px solid var(--line); border-radius: 26px; background: var(--surface); box-shadow: var(--shadow); touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.timer-clock { margin: 0; text-align: center; font: 850 clamp(66px, 15vw, 150px)/1.05 ui-rounded, -apple-system, system-ui, sans-serif; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.timer-hint { margin: 6px 0 16px; text-align: center; color: var(--muted); font-size: 18px; font-weight: 750; }
.timer-pads { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(28px, 12vw, 160px); }
.timer-pad { display: grid; place-items: center; min-height: clamp(130px, 20vh, 190px); border: 3px solid var(--line); border-radius: 26px; background: var(--surface-2); font-size: 44px; }
.timer-surface[data-phase="holding"] .timer-clock { color: var(--cube-R); }
.timer-surface[data-phase="holding"] .timer-pad[data-held="true"] { border-color: var(--cube-R); background: color-mix(in srgb, var(--cube-R) 30%, var(--surface-2)); }
.timer-surface[data-phase="ready"] .timer-clock { color: var(--cube-F); }
.timer-surface[data-phase="ready"] .timer-pad { border-color: var(--cube-F); background: color-mix(in srgb, var(--cube-F) 38%, var(--surface-2)); }
.timer-surface[data-phase="ready"] .timer-hint { color: var(--ink); }
/* While the clock runs the whole screen is the stop button. */
body.timer-running { overflow: hidden; }
body.timer-running .timer-surface { position: fixed; inset: 0; z-index: 60; margin: 0; border: 0; border-radius: 0; display: grid; align-content: center; background: var(--page); box-shadow: none; }
body.timer-running .timer-pads { visibility: hidden; }
.timer-editor { margin: 12px 0 0; padding: 12px 16px; border: 1px solid var(--line); border-radius: 18px; background: var(--surface); }
.timer-editor .cf-row { margin: 8px 0 0; }
.timer-editor-title { margin: 0; font-size: 18px; }
.timer-editor-title strong { font-variant-numeric: tabular-nums; }
.timer-editor-scramble { overflow-wrap: anywhere; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 14px; }
.timer-danger[data-armed="true"] { border-color: transparent; background: var(--warn); color: var(--surface); }
.timer-stats, .timer-list { margin-top: 26px; }
.timer-stats h2, .timer-list h2 { margin: 0 0 10px; font-size: 24px; }
.timer-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.timer-tile { display: grid; gap: 2px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 16px; background: var(--surface); }
.timer-tile-label { font-size: 15px; font-weight: 750; color: var(--muted); }
.timer-tile-value { font-size: 30px; font-weight: 850; font-variant-numeric: tabular-nums; }
.timer-tile-short { font-size: 14px; color: var(--muted); }
.timer-solves { display: grid; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 8px; margin: 0 0 12px; padding: 0; list-style: none; }
.timer-solve { display: flex; width: 100%; min-height: 48px; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); color: var(--ink); font: inherit; font-variant-numeric: tabular-nums; cursor: pointer; }
.timer-solve-n { color: var(--muted); font-size: 14px; }
.timer-solve-t { font-weight: 800; }
.timer-solve[aria-pressed="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); }
.timer-tools { margin-top: 14px; }
.timer-label { margin: 0; font-size: 17px; }

/* ------------------------------------------------------------ facts (slice 5) */
.facts-tools { display: grid; gap: 12px; margin: 18px 0 4px; }
.facts-surprise { justify-self: start; }
.facts-jump { margin: 0; }
.facts-jump a.chip { text-decoration: none; }
.facts-topic { scroll-margin-top: 12px; }
.facts-topic h2 { margin: 28px 0 12px; font-size: clamp(22px, 3.4vw, 28px); }
.facts-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 14px; margin: 0; padding: 0; list-style: none; }
.facts-cards > li { display: flex; min-width: 0; }
.fact { flex: 1; min-width: 0; scroll-margin: 16px; }
.fact h3 { margin: 0 0 6px; font-size: 20px; }
.card.fact p { color: var(--ink); overflow-wrap: break-word; }
.card.fact .fact-source { margin-top: 10px; color: var(--muted); font-size: 14px; }
.fact-source a { overflow-wrap: anywhere; }
.fact.picked { outline: 4px solid var(--accent); outline-offset: 2px; }
/* ------------------------------------------------------------ records (slice 7) */
.records { scroll-margin-top: 12px; margin: 18px 0 8px; }
.records h2 { margin: 28px 0 10px; font-size: clamp(22px, 3.4vw, 28px); }
.records-asof, .records-kinds, .records-problem { margin: 0 0 10px; max-width: 70ch; color: var(--ink); }
.records-kinds strong { color: var(--ink); }
.records-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 14px; margin: 14px 0 0; }
.card.record { min-width: 0; }
.record h3 { margin: 0 0 4px; font-size: 24px; }
.record-row { padding: 10px 0 0; }
.record-row + .record-row { margin-top: 10px; border-top: 1px solid var(--line); }
.card.record .record-kind { margin: 0; color: var(--muted); font-size: 14px; font-weight: 750; text-transform: uppercase; letter-spacing: .04em; }
.card.record .record-time { margin: 2px 0 4px; color: var(--muted); font-size: 16px; }
.record-value { color: var(--ink); font-size: 32px; font-weight: 800; font-variant-numeric: tabular-nums; }
.card.record .record-shared { margin: 0 0 4px; color: var(--accent-ink); font-size: 15px; font-weight: 700; }
.record-holders { margin: 0; padding: 0; list-style: none; }
.record-holder { display: grid; gap: 1px; padding: 3px 0; overflow-wrap: anywhere; }
.record-who { color: var(--ink); font-weight: 700; }
.record-where { color: var(--muted); font-size: 15px; }
.records-source { margin: 16px 0 0; max-width: 70ch; color: var(--muted); font-size: 14px; }
.records-source a { overflow-wrap: anywhere; }
.record-retry { min-height: 48px; }
@media (max-width: 430px) {
  .records-cards { grid-template-columns: 1fr; }
  .record-value { font-size: 28px; }
}
/* ------------------------------------------------------------ end records (slice 7) */

@media (max-width: 430px) {
  .timer-scramble-body { grid-template-columns: 1fr; }
  .timer-preview { justify-self: center; }
  .timer-scramble-text { font-size: 19px; }
  .timer-pads { gap: 22px; }
  .timer-tile-value { font-size: 25px; }
  .facts-cards { grid-template-columns: 1fr; }
}

/* --------------------------------------------------- puzzle galaxy (slice 8) */
.gx-tools { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 12px; margin: 18px 0 14px; }
.gx-find { position: relative; flex: 1 1 260px; max-width: 420px; }
.gx-find label { display: block; margin-bottom: 4px; font-weight: 750; }
.gx-find input { width: 100%; min-height: 48px; padding: 10px 14px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); color: var(--ink); }
.gx-options { position: absolute; z-index: 30; left: 0; right: 0; top: 100%; margin: 4px 0 0; padding: 6px; max-height: 320px; overflow: auto; list-style: none; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); box-shadow: var(--shadow); }
.gx-options[hidden] { display: none; }
.gx-options li { min-height: 44px; display: flex; align-items: center; padding: 8px 12px; border-radius: 10px; cursor: pointer; font-weight: 650; }
.gx-options li[aria-selected="true"], .gx-options li:hover { background: var(--accent); color: var(--surface); }
.gx-options li.gx-none { cursor: default; color: var(--muted); background: none; }
.gx-modes { display: flex; gap: 8px; }
.gx-layout { display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); gap: clamp(12px, 2vw, 20px); align-items: start; }
@media (max-width: 900px) { .gx-layout { grid-template-columns: minmax(0, 1fr); } }
.gx-stage-wrap[hidden], .gx-list[hidden] { display: none; }
.gx-stage { display: block; width: 100%; aspect-ratio: 1 / 1; max-height: min(72vh, 680px); border: 1px solid var(--line); border-radius: 20px; background: var(--gx-sky); touch-action: none; cursor: grab; }
.gx-stage:active { cursor: grabbing; }
.gx-hint { margin: 8px 0 0; color: var(--muted); font-size: 15px; }
.gx-legend { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-top: 8px; }
.gx-legend-list { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 0; padding: 0; list-style: none; font-size: 14px; color: var(--muted); }
.gx-legend-list li { display: inline-flex; align-items: center; gap: 6px; }
.gx-dot { display: inline-block; width: 12px; height: 12px; flex: 0 0 auto; border-radius: 50%; }
.gx-line { display: inline-block; width: 28px; height: 0; flex: 0 0 auto; border-top: 3px solid var(--muted); }
.gx-line-shape { border-top-style: dashed; }
.gx-line-same-idea { border-top-style: dotted; }
.gx-line-cut { border-top-style: dashed; border-top-width: 2px; box-shadow: 0 3px 0 -1px var(--muted); }
.gx-card { position: sticky; top: 12px; }
.gx-card:focus { outline: none; }
.gx-card:focus-visible { outline: 3px solid var(--accent); }
.gx-card h2 { margin: 0; font-size: 26px; }
.gx-card h3 { margin: 16px 0 4px; font-size: 18px; }
.gx-card p { margin: 0 0 6px; color: var(--ink); }
.gx-card .fact-source { color: var(--muted); font-size: 14px; }
.gx-card-head { display: flex; align-items: center; gap: 14px; margin-bottom: 10px; }
.gx-icon { flex: 0 0 auto; }
.gx-also { color: var(--muted) !important; font-size: 15px; margin-top: 4px !important; }
.gx-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; padding: 0; list-style: none; }
.gx-chip { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface-2); font-size: 14px; font-weight: 700; }
.gx-wca { border-color: var(--accent); }
.gx-size { font-weight: 750; }
.gx-news { margin-top: 8px !important; font-weight: 700; }
.gx-note { color: var(--muted) !important; font-size: 15px; }
.gx-number.gx-small { font-family: inherit; font-size: 20px; font-weight: 750; color: var(--ink) !important; }
.gx-number { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 14px; overflow-wrap: anywhere; color: var(--muted) !important; }
.gx-how summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; font-weight: 700; color: var(--accent-ink); }
.gx-formula { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 16px; overflow-wrap: anywhere; }
.gx-related { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.gx-related li p { margin: 4px 0 0; font-size: 15px; }
.gx-jump .gx-line { width: 22px; }
.gx-list { display: grid; gap: 18px; }
.gx-group h2 { display: flex; align-items: center; gap: 8px; margin: 0 0 4px; font-size: 22px; }
.gx-group-blurb { margin: 0 0 8px; color: var(--muted); }
.gx-list-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 8px; margin: 0; padding: 0; list-style: none; }
.gx-item { width: 100%; min-height: 56px; display: flex; align-items: center; gap: 10px; padding: 6px 12px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); color: var(--ink); font-weight: 700; text-align: left; cursor: pointer; }
.gx-item:hover { border-color: var(--accent); }
.gx-item[aria-current="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); }
@media (max-width: 430px) {
  .gx-list-items { grid-template-columns: 1fr; }
  .gx-card { position: static; }
}
/* ------------------------------------------------------ end puzzle galaxy */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; transition-duration: .001ms !important; animation-duration: .001ms !important; }
  a.card:hover { transform: none; }
  /* The `*` above cannot reach a UA shadow pseudo-element, and WebKit animates
     an indeterminate progress bar. The page never renders one; this is the
     belt on top of that brace. */
  .cf-progress::-webkit-progress-value { animation: none !important; }
  .cf-progress::-moz-progress-bar { animation: none !important; }
}
@media print {
  .site-header, .transport, .skip-link { display: none; }
  body { background: white; color: black; }
  .card { box-shadow: none; }
}
