@font-face {
  font-family: 'Shippori Mincho B1';
  src: url(../fonts/shippori-mincho-800.woff2) format('woff2');
  font-weight: 800;
  font-display: swap;
}
@font-face {
  font-family: 'Zen Kaku Gothic New';
  src: url(../fonts/zen-kaku-400.woff2) format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Zen Kaku Gothic New';
  src: url(../fonts/zen-kaku-700.woff2) format('woff2');
  font-weight: 700;
  font-display: swap;
}

:root {
  color-scheme: light;
  --bg: #efe8db;
  --bg-2: #e7dfcf;
  --ink: #23201b;
  --muted: #655d51;
  --card: #f8f4ec;
  --line: rgba(35, 32, 27, .14);
  --accent: #b8381f;
  --accent-ink: #fff8f0;
  --chip: rgba(35, 32, 27, .07);
  --press: rgba(35, 32, 27, .15);
  --mat: #2f5a4e;
  --mat-line: #4d7a6c; /* the cutting mat's grid, in 3D */
  --tick: color-mix(in srgb, var(--ink) 50%, var(--bg)); /* a step to come on the bar of ticks, 3:1 against the panel */
  --display: 'Shippori Mincho B1', 'Hiragino Mincho ProN', 'Yu Mincho', Georgia, serif;
  --ui: 'Zen Kaku Gothic New', 'Hiragino Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --gutter: 16px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #151816;
    --bg-2: #1b1f1c;
    --ink: #ece6da;
    --muted: #a99f90;
    --card: #1f2420;
    --line: rgba(236, 230, 218, .13);
    --accent: #e06a45;
    --accent-ink: #1b0d07;
    --chip: rgba(236, 230, 218, .08);
    --press: rgba(236, 230, 218, .17);
    --mat: #264a40;
    --mat-line: #3e675b;
    --tick: color-mix(in srgb, var(--ink) 40%, var(--bg));
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #151816;
  --bg-2: #1b1f1c;
  --ink: #ece6da;
  --muted: #a99f90;
  --card: #1f2420;
  --line: rgba(236, 230, 218, .13);
  --accent: #e06a45;
  --accent-ink: #1b0d07;
  --chip: rgba(236, 230, 218, .08);
  --press: rgba(236, 230, 218, .17);
  --mat: #264a40;
  --mat-line: #3e675b;
  --tick: color-mix(in srgb, var(--ink) 40%, var(--bg));
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 16px;
  line-height: 1.55;
  -webkit-tap-highlight-color: transparent;
}
button { font: inherit; color: inherit; }
[hidden] { display: none !important; }
/* The gallery and the player fade in as they show, rather than cutting from one to the other in a frame. */
.screen:not([hidden]) { animation: screen-in .18s ease-out; }
@keyframes screen-in { from { opacity: 0; } }
.noscript { padding: 2rem; text-align: center; }
/* While the page's script loads: faded in only after a second, so a quick start never shows it. */
.boot {
  margin: 0; min-height: 100dvh; display: grid; place-items: center; text-align: center; text-wrap: balance;
  padding: 24px max(var(--gutter), calc(50% - 13em)); color: var(--muted);
  animation: boot .4s 1s both;
}
@keyframes boot { from { opacity: 0; } }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
h1, h2, h3 { text-wrap: balance; }

.icon-btn {
  appearance: none; border: 0; background: var(--chip); width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer; flex: none;
}
.icon-btn:active { background: var(--press); }
.icon-btn svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- Home ---------- */
.home {
  max-width: 1040px;
  margin: 0 auto;
  padding: max(28px, env(safe-area-inset-top)) max(var(--gutter), env(safe-area-inset-right)) max(16px, env(safe-area-inset-bottom)) max(var(--gutter), env(safe-area-inset-left));
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.mast { max-width: 36em; }
.eyebrow {
  margin: 0 0 6px; font-size: .78rem; letter-spacing: .14em; text-transform: uppercase; font-weight: 700; color: var(--accent);
}
h1 {
  font-family: var(--display); font-weight: 800; font-size: clamp(2.6rem, 9vw, 4.2rem);
  line-height: 1; margin: 0 0 12px; letter-spacing: -.01em;
}
.lede { margin: 0; font-size: 1.06rem; color: var(--muted); }
.fold-along { margin: 8px 0 0; font-size: .95rem; color: var(--muted); }
/* A quiet link down to the key. Its hit area reaches a finger's height without spacing the lines out. */
.to-key {
  appearance: none; border: 0; background: none; cursor: pointer; margin: -6px 0; padding: 12px 0; display: inline-block;
  font-size: .95rem; color: var(--accent); text-decoration: underline; text-underline-offset: 3px;
}
.symbols h2:focus { outline: none; }

.paper-pick { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.pick-label { font-weight: 700; font-size: .9rem; }
.swatches { display: flex; gap: 10px; flex-wrap: wrap; }
.swatch {
  appearance: none; width: 36px; height: 36px; border-radius: 4px; border: 0; cursor: pointer; padding: 0;
  background: linear-gradient(135deg, var(--c) 0 50%, #f3eee3 50% 100%);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .2), inset 0 0 0 1px rgba(0, 0, 0, .08);
  transform: rotate(0deg);
  transition: transform .15s;
  position: relative;
}
/* Small things to tap reach a finger's 44 px without taking more room: here, and on Slow, the slider, the bar of
   ticks and the footer links. */
.swatch::after { content: ''; position: absolute; inset: -4px; }
.swatch[aria-checked="true"] { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ink); }
/* Only where there's a pointer to hover with: on a phone a tapped swatch or card would stay tilted or lifted. */
@media (hover: hover) { .swatch:hover { transform: rotate(-4deg); } }

.models {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(136px, 1fr)); gap: 12px; /* two across at 320 px */
}
/* The cards in a row share their rows, so a name on two lines moves the step counts and words down in its
   neighbours too, and they all line up. */
.models li { display: grid; grid-row: span 4; grid-template-rows: subgrid; min-width: 0; }
.model {
  appearance: none; border: 0; width: 100%; height: 100%; min-width: 0; text-align: left; cursor: pointer;
  background: var(--card); border-radius: 14px; padding: 12px; display: grid; gap: 10px; align-content: start;
  grid-row: span 4; grid-template-rows: subgrid; row-gap: 10px;
  box-shadow: 0 1px 0 var(--line), 0 6px 18px rgba(40, 30, 15, .07);
  transition: transform .15s;
}
@media (hover: hover) { .model:hover { transform: translateY(-2px); } }
.model:active { transform: scale(.98); }
.model .thumb {
  aspect-ratio: 1; border-radius: 9px; background: var(--mat); display: grid; place-items: center; overflow: hidden;
}
.model .thumb svg { width: 78%; height: 78%; filter: drop-shadow(0 3px 4px rgba(0, 0, 0, .25)); }
.model .title { display: block; font-family: var(--display); font-weight: 800; font-size: 1.3rem; line-height: 1.2; overflow-wrap: break-word; text-wrap: balance; }
@media (max-width: 359px) { .model .title { font-size: 1.15rem; } } /* two across on a 320 px phone, Waterbomb and all */
/* The level, the paper to fold it from and the steps, with a dot between them. Where the line wraps, the dot that
   would start the new line sits outside the box and is cut off, so a line never starts with one. Beside a card whose
   line wraps, a line that doesn't stays at the top, level with its neighbour's first. */
.model .meta { font-size: .82rem; color: var(--muted); display: flex; flex-wrap: wrap; column-gap: 14px; align-items: center; align-content: flex-start; overflow: hidden; }
.model .meta > * + *::before { content: '·'; content: '·' / ''; display: inline-block; width: 14px; margin-left: -14px; text-align: center; }
.model .level { font-weight: 700; color: var(--accent); }
.model .blurb { display: block; font-size: .88rem; line-height: 1.45; }
@media (min-width: 700px) { .models { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; } }
/* Wide enough for four to six across, as many as make full rows of the models (app.js sets it): ten make two rows of
   five, rather than 4, 4 and 2. */
@media (min-width: 960px) { .models { grid-template-columns: repeat(var(--across, 5), minmax(0, 1fr)); } }
/* A phone held sideways: a smaller title and wider pictures, so the first cards show on the first screen and a
   card's picture and its name fit on the screen together. */
@media (orientation: landscape) and (max-height: 500px) {
  .home { gap: 16px; padding-top: max(16px, env(safe-area-inset-top)); }
  h1 { font-size: clamp(2.2rem, 11vh, 3rem); }
  .model .thumb { aspect-ratio: 4 / 3; }
}

.symbols { background: var(--card); border-radius: 14px; padding: 16px; box-shadow: 0 1px 0 var(--line); scroll-margin-top: 12px; }
.symbols h2 { font-family: var(--display); font-weight: 800; font-size: 1.4rem; margin: 0 0 4px; }
.symbols > p { margin: 0 0 12px; color: var(--muted); max-width: 40em; }
.symbol-list { margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr)); gap: 14px; }
.symbol-list dt { font-weight: 700; display: flex; align-items: center; gap: 10px; }
.symbol-list dd { margin: 4px 0 0; font-size: .92rem; color: var(--muted); }
.symbol-list svg { width: 60px; height: 30px; flex: none; fill: none; stroke: var(--ink); stroke-width: 2; stroke-linecap: round; }
.symbol-list .ln.valley { stroke-dasharray: 6 4; }
.symbol-list .ln.mountain { stroke-dasharray: 6 3 1 3; }
.symbol-list .head { stroke-linejoin: round; }
.symbol-list .head.solid { fill: var(--ink); }
.symbol-list .head.hollow { fill: var(--card); stroke-width: 1.6; }

.jd-foot { text-align: center; font-size: .85rem; color: var(--muted); padding: 8px 0 max(4px, env(safe-area-inset-bottom)); }
.jd-foot a { color: inherit; text-decoration: none; display: inline-block; padding: 12px 4px; margin: -12px -4px; }
.jd-foot a:hover, .jd-foot a:focus-visible { text-decoration: underline; }

/* ---------- Player ---------- */
/* A swipe down in the player is never meant to reload the page and lose the fold. */
html:has(#player:not([hidden])) { overscroll-behavior-y: none; }
.player {
  height: 100dvh;
  display: grid;
  grid-template-columns: minmax(0, 1fr); /* a long model name shortens with an ellipsis instead of widening the page */
  /* The paper keeps at least 40% of a short screen, and the words, slider and buttons have the rest. */
  grid-template-rows: auto minmax(clamp(150px, 40dvh, 360px), 1fr) minmax(0, auto);
  grid-template-areas: "bar" "stage" "panel";
}
.bar {
  grid-area: bar;
  display: flex; align-items: center; gap: 10px;
  padding: max(8px, env(safe-area-inset-top)) max(var(--gutter), env(safe-area-inset-right)) 8px max(var(--gutter), env(safe-area-inset-left));
}
.bar-title { flex: 1; min-width: 0; }
.bar-title h2 { font-family: var(--display); font-weight: 800; font-size: 1.2rem; margin: 0; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bar-title p { margin: 0; font-size: .82rem; color: var(--muted); }
/* The model's name takes the focus when a model opens, for the keyboard and screen readers; it isn't a control. */
#modelName:focus { outline: none; }
.bar-end { display: flex; gap: 8px; }

.stage { grid-area: stage; position: relative; min-height: 0; container-type: size; }
.stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; cursor: grab; }
.stage canvas:active { cursor: grabbing; }
.stage canvas:focus-visible { outline-offset: -3px; } /* inside the paper's edges, where the bar and panel can't cover it */
.hint {
  position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%); margin: 0;
  font-size: .8rem; padding: 4px 12px; border-radius: 99px; background: rgba(20, 30, 26, .55); color: #eef3ef;
  pointer-events: none; transition: opacity .6s;
  width: max-content; max-width: calc(100% - 20px); text-align: center; text-wrap: balance; /* one line, but two on a page zoomed in */
}
.hint span span { white-space: nowrap; } /* where it wraps, after a dot */
.hint.gone { opacity: 0; }
/* On a stage only a few fingers tall the hint would cover the paper. */
@container (max-height: 240px) { .hint { display: none; } }
/* With a mouse you click rather than tap, so the hint says that instead. */
.hint .mouse { display: none; }
@media (hover: hover) and (pointer: fine) {
  .hint .touch { display: none; }
  .hint .mouse { display: inline; }
}
/* What you'll make, in a corner of the mat on step 1 (app.js fades it with the marks): the finished model's picture on a
   piece of the mat, as on its card, ringed in light so it reads as a card lying there rather than paper on the table. */
.goal-card {
  position: absolute; top: 10px; left: max(10px, env(safe-area-inset-left)); width: clamp(56px, 19cqmin, 84px); aspect-ratio: 1;
  border-radius: 9px; background: var(--mat); display: grid; place-items: center; pointer-events: none;
  box-shadow: 0 0 0 1.5px rgba(238, 243, 239, .45), 0 3px 10px rgba(0, 0, 0, .3);
}
.goal-card svg, .goal-pic svg { width: 80%; height: 80%; filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .25)); }
/* While a model is folded for the first time, the mat waits with a quiet word, which only shows if that takes a moment.
   It fades in by its opacity, which the browser goes on animating while the page is busy folding; a colour would stay
   where it was until the folding ended, so the word never showed. */
.gl-wait {
  position: absolute; inset: 0; margin: 0; display: grid; place-items: center; background: var(--mat);
  color: rgba(238, 243, 239, .8); font-size: .95rem;
}
.gl-wait span { animation: wait .3s .2s both; }
@keyframes wait { from { opacity: 0; } }
.gl-wait:not([hidden]) ~ .hint { opacity: 0; } /* nothing to turn yet */
/* Where the paper can't be drawn, the mat is still there, saying why. */
.gl-error {
  position: absolute; inset: 0; display: grid; place-items: center; align-content: center; gap: 14px; text-align: center; margin: 0;
  padding: 24px max(24px, calc(50% - 13em)); background: var(--mat); color: #eef3ef; text-wrap: balance;
}
/* Reload, when the 3D has stopped: drawn like Slow, in the mat's light ink, and as big as a finger past its edges. */
.gl-error .reload {
  appearance: none; border: 1.5px solid rgba(238, 243, 239, .55); background: none; border-radius: 99px; height: 36px; padding: 0 16px;
  font-size: .85rem; font-weight: 700; cursor: pointer; color: inherit; position: relative;
}
.gl-error .reload::before { content: ''; position: absolute; inset: -5.5px -4px; }
.gl-error .reload:active { background: rgba(238, 243, 239, .14); }
.gl-error .reload:focus-visible { outline-color: #eef3ef; }

.panel {
  grid-area: panel;
  padding: 10px max(var(--gutter), env(safe-area-inset-right)) max(12px, env(safe-area-inset-bottom)) max(var(--gutter), env(safe-area-inset-left));
  display: flex; flex-direction: column; gap: 10px;
  min-height: 0; overflow: hidden;
  background: var(--bg);
  border-top: 1px solid var(--line);
  --result: 50px; /* the picture of how the step ends, in the top right corner of the words (and their hidden copy) */
}
.ticks, .scrub, .controls { flex: none; }
/* The ticks come after the step's words in the page, so a screen reader reaches the words straight after the paper,
   but they're shown above them. */
.ticks { list-style: none; margin: -10px 0; padding: 0; display: flex; gap: 4px; touch-action: pan-y; order: -1; } /* a finger slides along it */
.ticks li { flex: 1; }
.ticks li.base-end { margin-right: 6px; } /* a wider gap after the last step of a base */
.ticks button {
  appearance: none; border: 0; padding: 20px 0; width: 100%; background: none; cursor: pointer; display: block;
}
/* Each tick is a button whose bar is all there is to see of it, so every bar stands out from the panel by 3:1 at
   least (WCAG 2.2, 1.4.11): the steps to come in grey, the steps done in a lighter red than the step you're on. */
.ticks button::before { content: ''; display: block; height: 4px; border-radius: 2px; background: var(--tick); }
.ticks button.done::before { background: color-mix(in srgb, var(--accent) 75%, var(--bg)); }
.ticks button[aria-current="step"]::before { background: var(--accent); height: 6px; margin-top: -1px; }

/* Words too long for the room left scroll in their own box, fading at the bottom while there's more, so the
   slider and buttons stay where they are. Under the paper, the box keeps one height through a model, that of its
   longest words (measured by the page into --words), so the paper doesn't grow and shrink from step to step; on a
   short screen the paper's own room (above) still comes first. */
.step-now {
  display: grid; align-content: start; gap: 4px; position: relative;
  flex: 1 1 var(--words, auto); min-height: 0; overflow-y: auto; overscroll-behavior: contain;
}
.step-now.more {
  -webkit-mask-image: linear-gradient(#000 calc(100% - 1.3em), transparent);
  mask-image: linear-gradient(#000 calc(100% - 1.3em), transparent);
}
/* The step's number in large type before its symbol and name, the way origami books number their diagrams: in the
   models' own typeface, about twice the height of the name. The bar above says which step of how many. */
.step-head { margin: 0; display: flex; align-items: center; gap: 10px; min-width: 0; padding-right: calc(var(--result) + 14px); }
.step-num { font-family: var(--display); font-weight: 800; font-size: 1.55rem; line-height: 1.2; font-variant-numeric: lining-nums tabular-nums; }
/* The fold's name is a button that opens the key of marks at that fold, with a small question mark after it to say so.
   It reaches a finger's 44 px down over the words, rather than up into the bar of ticks. */
.chip {
  appearance: none; border: 0; background: none; padding: 0; margin: 0; cursor: pointer; position: relative; text-align: left;
  display: inline-flex; align-items: center; gap: 8px; font-size: .78rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted); min-width: 0;
}
.chip::before { content: ''; position: absolute; inset: -6px -8px -20px; }
.chip:not(:empty)::after {
  content: '?'; content: '?' / ''; flex: none; display: grid; place-items: center; width: 16px; height: 16px; border-radius: 50%;
  border: 1.5px solid currentColor; font-size: .64rem; letter-spacing: 0; line-height: 1; margin-left: -2px;
}
@media (hover: hover) { .chip:hover { color: var(--ink); } }
.chip svg { width: 34px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chip .hollow { stroke-width: 1.6; }
.say { margin: 0; font-size: 1.05rem; line-height: 1.45; min-height: 4.35em; display: flow-root; } /* three lines at least */
/* Beside the fold's name, the picture of how the paper should look once the fold has landed (app.js): the step's result,
   drawn as the gallery draws the finished models, on a piece of the mat. It's a button that takes the fold to its end and
   back, ringed in the accent colour while the fold is there (4 px in from the corner, so the words' box doesn't cut the
   ring off). The words' first lines keep clear of it, round a space as big as the rest of it below the fold's name (the
   number sets that line's height). */
.result {
  position: absolute; top: 4px; right: 4px; width: var(--result); aspect-ratio: 1; padding: 0; border: 0; margin: 0;
  border-radius: 8px; background: var(--mat); display: grid; place-items: center; cursor: pointer;
}
.result::before { content: ''; position: absolute; inset: -2px; } /* a finger's 44 px and more, within the words' box */
.result svg { width: 84%; height: 84%; filter: drop-shadow(0 1.5px 2px rgba(0, 0, 0, .25)); }
.result[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent); }
.say::before { content: ''; float: right; width: calc(var(--result) + 14px); height: calc(var(--result) - 1.55rem * 1.2); }
/* On step 1 of a model that shares a base with others, a quiet offer under the words to skip the base, a line for each,
   with the step to skip to in the accent colour. The words above keep no room of their own under them then, but keep
   the height of their own lines: where the box is too short for both (a small phone, a phone held sideways, Aa on),
   they scroll together, rather than the offer being drawn over the words. */
.say:has(+ .skip:not([hidden])) { min-height: auto; }
.skip { margin: 2px 0 0; display: grid; justify-items: start; }
.skip button {
  appearance: none; border: 0; background: none; cursor: pointer; text-align: left; padding: 6px 0; margin: 0;
  font-size: .9rem; line-height: 1.4; color: var(--muted);
}
.skip button span { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; font-weight: 700; }
/* The key of marks, over the player (app.js opens it from the fold's name): a sheet up from the bottom of a phone, and
   a box in the middle of a wider screen, with the step's entry ringed in the accent colour. Its entries are the
   gallery's own, copied in, so the hollow arrowheads take the colour under them. */
.sheet {
  border: 0; padding: 0; color: var(--ink); background: var(--card); overflow: hidden;
  width: 100%; max-width: 100%; max-height: 82dvh; margin: auto 0 0; border-radius: 16px 16px 0 0;
  box-shadow: 0 -6px 30px rgba(0, 0, 0, .25);
}
.sheet[open] { display: flex; flex-direction: column; animation: sheet-in .2s ease-out; }
.sheet:focus { outline: none; }
@keyframes sheet-in { from { transform: translateY(24px); opacity: 0; } }
.sheet::backdrop { background: rgba(20, 18, 14, .45); }
.sheet-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px max(12px, env(safe-area-inset-right)) 8px max(var(--gutter), env(safe-area-inset-left));
  border-bottom: 1px solid var(--line); /* the entries scroll under it */
}
.sheet-head h2 { font-family: var(--display); font-weight: 800; font-size: 1.3rem; margin: 0; }
.sheet-body {
  overflow-y: auto; overscroll-behavior: contain;
  padding: 6px max(6px, env(safe-area-inset-right)) max(14px, env(safe-area-inset-bottom)) max(6px, env(safe-area-inset-left));
}
.sheet .symbol-list { gap: 2px; }
.sheet .symbol-list > div { --under: var(--card); background: var(--under); padding: 10px; border-radius: 10px; }
.sheet .symbol-list > div.here { --under: color-mix(in srgb, var(--accent) 9%, var(--card)); box-shadow: inset 0 0 0 2px var(--accent); }
.sheet .symbol-list .head.hollow { fill: var(--under); }
@media (min-width: 600px) {
  .sheet { margin: auto; width: min(680px, calc(100% - 48px)); max-height: min(86dvh, 760px); border-radius: 16px; box-shadow: 0 10px 40px rgba(0, 0, 0, .3); }
  .sheet-body { padding: 8px 10px 14px; }
  .sheet-head { padding: 12px 12px 8px 20px; }
}

/* A hidden copy of the words' box, holding every step's words at once, to find the longest (see app.js). */
.say-gauge { position: absolute; top: 0; left: 0; height: 0; overflow: hidden; visibility: hidden; pointer-events: none; }
.say-gauge > div { display: grid; gap: 4px; }

/* The slider, in the page's own style rather than the browser's: a thin track in faint ink, filled in the accent
   colour up to the thumb (--p, which the page sets), and a round thumb ringed in the page's colour. It reaches 8 px
   above and below the room it takes, and sits 8 px lower on its line to make up for it, so the track stays where it
   always was. */
.scrub input {
  -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer;
  width: 100%; margin: -8px 0; height: 44px; vertical-align: -8px;
  --track: color-mix(in srgb, var(--ink) 18%, transparent);
  --thumb: 0 0 0 3px var(--bg), 0 1px 4px rgba(0, 0, 0, .3);
}
.scrub input::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px;
  background: linear-gradient(var(--accent), var(--accent)) 0 0 / var(--p, 0%) 100% no-repeat, var(--track);
}
.scrub input::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 22px; height: 22px; margin-top: -9px; border: 0; border-radius: 50%;
  background: var(--accent); box-shadow: var(--thumb);
}
.scrub input::-moz-range-track { height: 4px; border-radius: 2px; background: var(--track); }
.scrub input::-moz-range-progress { height: 4px; border-radius: 2px; background: var(--accent); }
.scrub input::-moz-range-thumb { width: 22px; height: 22px; border: 0; border-radius: 50%; background: var(--accent); box-shadow: var(--thumb); }
/* From the keyboard, the ring goes round the thumb, not the whole slider. */
.scrub input:focus-visible { outline: none; --thumb: 0 0 0 3px var(--bg), 0 0 0 6px var(--accent); }
.controls { display: flex; align-items: center; gap: 10px; justify-content: center; }
.ctl {
  appearance: none; border: 0; background: var(--chip); border-radius: 99px; min-width: 52px; height: 48px; padding: 0 14px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px; cursor: pointer; font-weight: 700;
}
.ctl svg { width: 22px; height: 22px; fill: currentColor; stroke: currentColor; stroke-width: 1.5; stroke-linejoin: round; }
.ctl.main { background: var(--accent); color: var(--accent-ink); min-width: 150px; font-size: 1.02rem; }
.ctl:not([aria-disabled="true"]):active { background: var(--press); }
.ctl.main:not([aria-disabled="true"]):active { background: color-mix(in srgb, var(--accent) 82%, #000); }
.ctl[aria-disabled="true"] { opacity: .35; cursor: default; }
/* Slow, and Aa beside it: switches, drawn as small pills. */
.toggle {
  appearance: none; border: 1.5px solid var(--line); background: none; border-radius: 99px; height: 36px; padding: 0 12px;
  font-size: .85rem; font-weight: 700; cursor: pointer; color: var(--muted); position: relative; flex: none;
}
.toggle::before { content: ''; position: absolute; inset: -5.5px -4px; }
.toggle:active { background: var(--chip); }
/* On, it's filled in like Play, so it shows from across the table; off, it's the plain outline. */
.toggle[aria-pressed="true"] { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); }
.toggle[aria-pressed="true"]:active { background: color-mix(in srgb, var(--accent) 82%, #000); border-color: transparent; }
#bigWords { padding: 0 10px; min-width: 38px; } /* with Slow's reach past its edges, a finger's 44 px */
/* Narrow phones, and the panel beside the paper on a phone held sideways (further down): the buttons draw in a
   little so the row fits, and Play keeps one width for every label. */
@media (max-width: 379px), (min-width: 560px) and (orientation: landscape) and (max-height: 500px) {
  .controls { gap: 6px; }
  .ctl { min-width: 46px; padding: 0 10px; }
  .ctl.main { min-width: 128px; padding: 0 8px; }
  .toggle { padding: 0 10px; }
}

/* A short phone held upright: the panel draws in so the words keep two lines or so beside the paper's 40%. */
@media (max-height: 560px) and (orientation: portrait) {
  .panel { gap: 6px; padding-top: 6px; padding-bottom: max(8px, env(safe-area-inset-bottom)); }
  .ctl { height: 44px; }
}

/* A tablet held upright: the words, slider and buttons keep to a column in the middle, so a line of words runs about
   60 characters rather than 80 to 100 across the whole width, and the words are a little bigger, to read at arm's
   length from the paper in your hands. */
@media (min-width: 600px) and (max-width: 899px) and (orientation: portrait) {
  .panel > * { width: 100%; max-width: 36rem; margin-inline: auto; }
  .say { font-size: 1.2rem; }
  .panel { --result: 64px; }
}

/* Aa, beside Slow, makes the words bigger, to read from the table with the phone at arm's length: the step's words a
   third bigger and the fold's name a little bigger. Only on phones and tablets: with a mouse, the computer's own zoom
   does it well, where a phone's shrinks the paper and the buttons too. The words' box grows with them (app.js measures
   it again), and the longest words scroll in it on a small phone.
   With Aa, the row has five buttons, and they draw in to fit the panel's width (the panel is the container): at 358 px
   (a 390 px phone) a little, narrower (a 360 px phone, or beside the paper held sideways) more, and at 288 px (a 320 px
   phone) Play's words a little smaller too. Narrower still, Slow and Aa go under the others rather than off the edge. */
@media (hover: hover) and (pointer: fine) { #bigWords { display: none; } }
.panel { container: panel / inline-size; }
@media not all and (hover: hover) and (pointer: fine) {
  .big-words .say { font-size: 1.375rem; }
  .big-words .chip { font-size: .9rem; }
  .big-words .skip button { font-size: 1rem; }
  @media (min-width: 600px) and (max-width: 899px) and (orientation: portrait) { .big-words .say { font-size: 1.575rem; } }
  @media (min-width: 300px) {
    @container panel (min-width: 356px) {
      .controls { gap: 8px; }
      .ctl { min-width: 48px; padding: 0 12px; }
      .ctl.main { min-width: 132px; padding: 0 10px; }
    }
    @container panel (max-width: 355.98px) {
      .controls { gap: 6px; flex-wrap: wrap; row-gap: 8px; }
      .ctl { min-width: 44px; padding: 0 8px; }
      .ctl.main { min-width: 116px; padding: 0 8px; }
      .toggle { padding: 0 10px; }
      #bigWords { padding: 0 8px; }
    }
    @container panel (max-width: 315.98px) {
      .controls { gap: 5px; }
      .ctl { min-width: 40px; padding: 0 6px; }
      .ctl.main { min-width: 100px; padding: 0 5px; gap: 6px; font-size: .95rem; }
      .toggle { padding: 0 8px; }
      #bigWords { padding: 0 7px; min-width: 36px; }
    }
  }
}

/* A narrow phone: a long model name takes two lines rather than losing its end, and the bar draws in. Narrower
   still, Slow goes under the other buttons rather than off the edge. */
@media (max-width: 359px) {
  .bar, .bar-end { gap: 6px; }
  .bar-title h2 { white-space: normal; line-height: 1.1; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
}
/* Narrower than a phone, as a phone's page zoomed to 150% or 200% is (a 390 px iPhone at 200% lays out 195 px
   wide), the paper, words and buttons were squeezed into the screen's height until no words showed and Play was cut
   off. The player scrolls as one page instead, with the paper at most a little over half the screen, so there's always
   some page to swipe past it; the words show whole, and the buttons keep to the bottom of the screen as they scroll
   by. In the bar, the view buttons go under the model's name rather than squeezing it to a letter. */
@media (max-width: 299px) {
  .player { height: auto; min-height: 100dvh; grid-template-rows: auto minmax(180px, 55dvh) auto; }
  .bar { flex-wrap: wrap; row-gap: 4px; }
  .bar-title { flex: 1 1 auto; }
  .bar-title h2 { font-size: 1rem; } /* so a one-word name (Waterbomb) still fits */
  .bar-title p { white-space: nowrap; }
  .bar-end { margin-left: auto; }
  .panel { overflow: visible; padding-bottom: 0; }
  .step-now, .step-now.more { flex: none; overflow: visible; -webkit-mask-image: none; mask-image: none; }
  .controls {
    flex-wrap: wrap; row-gap: 6px; position: sticky; bottom: 0; z-index: 1;
    margin: 0 calc(-1 * max(var(--gutter), env(safe-area-inset-right))) 0 calc(-1 * max(var(--gutter), env(safe-area-inset-left)));
    padding: 6px max(var(--gutter), env(safe-area-inset-right)) max(8px, env(safe-area-inset-bottom)) max(var(--gutter), env(safe-area-inset-left));
    background: var(--bg); border-top: 1px solid var(--line); /* the words scroll under it */
  }
  .ctl.main { flex: 1 1 128px; min-width: 0; }
}

.step-list { display: none; }

/* A wide screen, but for a phone held sideways (below), however wide: no more than 500 px tall, it's always sideways. */
@media (min-width: 900px) and (min-height: 500.02px) {
  .player {
    grid-template-columns: minmax(0, 1fr) calc(380px + env(safe-area-inset-right));
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "bar bar" "stage panel";
  }
  /* The words and controls keep their size at the top; the list of steps scrolls under them. The panel's
     left side is mid-screen, so only its right side keeps clear of a notch, and the panel is that much wider. */
  .panel { border-top: 0; border-left: 1px solid var(--line); min-height: 0; overflow: visible; padding-top: 16px; padding-left: var(--gutter); }
  .panel > * { flex-shrink: 0; }
  .ticks { display: none; }
  .step-list {
    display: block; margin: 6px 0 0; counter-reset: s;
    border-top: 1px solid var(--line);
    flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain;
  }
  .step-list ol { list-style: none; margin: 0; padding: 0; }
  /* What you'll make heads the list: the finished model's picture, as on its card, and what the card says of it. */
  .goal {
    display: grid; grid-template-columns: 88px 1fr; gap: 12px; align-items: center; margin: 0; padding: 12px 6px;
    font-size: .92rem; line-height: 1.45; color: var(--muted); border-bottom: 1px solid var(--line);
  }
  .goal-pic { width: 88px; aspect-ratio: 1; border-radius: 9px; background: var(--mat); display: grid; place-items: center; }
  .goal-card { display: none; }
  .step-list li button {
    appearance: none; border: 0; background: none; width: 100%; text-align: left; cursor: pointer; position: relative;
    display: grid; grid-template-columns: 2em 1fr; gap: 8px; padding: 10px 6px; border-radius: 8px; font-size: .92rem; color: var(--muted);
  }
  .step-list li button::before { counter-increment: s; content: counter(s); font-weight: 700; font-variant-numeric: tabular-nums; }
  .step-list li button[aria-current="step"] { background: var(--chip); color: var(--ink); }
  .step-list li button:focus-visible { outline-offset: -3px; } /* inside the button: the list's edges cut off the sides of a ring outside it */
  /* Under the last step of a base, a small heading naming it and its steps, with a rule to the edge. */
  .step-list .base-end {
    display: flex; align-items: center; gap: 10px; margin: 2px 0 8px; padding: 0 6px;
    font-size: .74rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--accent);
  }
  .step-list .base-end::after { content: ''; flex: 1; border-top: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); }
  /* At the end of the list, a quiet link to print the steps as a paper diagram (app.js), as the gallery's link to the key. */
  .print-steps {
    appearance: none; border: 0; background: none; cursor: pointer; margin: 4px 6px 8px; padding: 12px 0;
    font-size: .9rem; color: var(--accent); text-decoration: underline; text-underline-offset: 3px;
  }
  .step-now { flex: none; overflow: visible; }
  .panel { --result: 64px; }
  .say { min-height: 0; }
}

/* A phone held sideways is too short for the words and buttons under the paper: they left it a strip a finger or
   two tall. They go beside it instead, as on a wide screen, with the steps still along the top and only the
   panel's right side kept clear of a notch; long words scroll in their own box, above the slider and buttons.
   The words run the full height of the screen, from the top: the bar, with the back arrow, the model's name and the
   step, keeps to the paper's side, and the buttons that look straight down and put the view back sit over the paper's
   top right corner, round and dark like the hint, rather than taking a strip of the words' column. However wide the
   phone: the biggest are more than 900 px wide held sideways (956 px for an iPhone 16 Pro Max), and they had the wide
   screen's layout, the bar across the top and a sliver of the list of steps under the buttons. */
@media (min-width: 560px) and (orientation: landscape) and (max-height: 500px) {
  .player {
    grid-template-columns: minmax(0, 1fr) calc(clamp(320px, 50vw, 380px) + env(safe-area-inset-right));
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "bar panel" "stage panel";
  }
  .panel { border-top: 0; border-left: 1px solid var(--line); padding-left: var(--gutter); padding-top: max(10px, env(safe-area-inset-top)); }
  .say { min-height: 0; }
  .bar { position: relative; padding-right: var(--gutter); }
  .bar-end { position: absolute; top: calc(100% + 10px); right: 10px; z-index: 2; }
  .bar-end .icon-btn { background: rgba(20, 30, 26, .55); color: #eef3ef; }
  .bar-end .icon-btn:active { background: rgba(20, 30, 26, .75); }
}
/* Held sideways, the words' box doesn't keep the height of a model's longest words: beside the paper it doesn't
   change the paper's size, and under it (below) the page scrolls. */
@media (orientation: landscape) and (max-height: 500px) {
  .step-now { flex-basis: auto; }
}

/* Narrower and short as well, as a window zoomed in to 300% or 400% is, there's no room beside the paper: the words
   took it all and the paper was gone. They go under it, and the page scrolls to reach them, with the paper kept a
   usable size. */
@media (max-width: 559px) and (orientation: landscape) and (max-height: 500px) {
  .player { height: auto; min-height: 100dvh; grid-template-rows: auto max(200px, 75dvh) auto; }
}

/* ---------- Printing ----------
   A model printed from the player is its steps as a printed diagram (app.js makes it as printing begins), and none of the
   player: the name and the picture of what you'll make, then the steps three across, each numbered in large type as books
   number them, drawn flat with its marks, and its words under it. A step isn't split across two pages. In black ink on
   white, whatever the screen's colours; the paper is a paler shade of yours. The gallery prints as it shows. */
.print-out { display: none; }
@media print {
  @page { margin: 12mm; }
  html:has(#player:not([hidden])) body { background: #fff; color: #1d1b18; min-height: 0; }
  html:has(#player:not([hidden])) .screen { display: none !important; }
  html:has(#player:not([hidden])) .print-out { display: block; }
}
.print-out { color: #1d1b18; font-size: 9pt; line-height: 1.35; }
.po-head { display: grid; grid-template-columns: 30mm 1fr; gap: 6mm; align-items: center; margin: 0 0 6mm; padding: 0 0 5mm; border-bottom: .3mm solid #b8b0a4; }
.po-head .po-pic svg { width: 30mm; height: 30mm; display: block; }
.po-head .rim { display: none; } /* the light edge the pictures have on the mat */
.po-head h1 { font-family: var(--display); font-weight: 800; font-size: 24pt; line-height: 1.1; margin: 0 0 1.5mm; letter-spacing: 0; }
.po-meta { margin: 0; font-size: 7.5pt; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #6b6357; }
.po-blurb { margin: 1.5mm 0 0; font-size: 10pt; }
.po-from { margin: 1.5mm 0 0; font-size: 8pt; color: #6b6357; }
.po-steps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 5mm 6mm; }
.po-steps li { break-inside: avoid; position: relative; }
.po-n { position: absolute; top: 0; left: 0; font-family: var(--display); font-weight: 800; font-size: 16pt; line-height: 1; font-variant-numeric: lining-nums tabular-nums; }
.po-steps .po-pic svg { width: 100%; height: 40mm; display: block; }
.po-steps p { margin: 1.5mm 0 0; }
.po-steps b { font-size: 7pt; letter-spacing: .07em; text-transform: uppercase; margin-right: .5mm; }
.po-steps .po-base { margin-top: 1mm; font-size: 7pt; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: #6b6357; }

@media (prefers-reduced-motion: reduce) {
  .model, .swatch { transition: none; }
  .screen:not([hidden]), .sheet[open] { animation: none; }
}
