@charset "utf-8";
/*
 * squeezy — Organic Minimalism. The one home for every colour, radius, shadow and face.
 *
 * Nothing else in the repo may hard-code a hex value. `design/build.mjs` injects this exact file into
 * every deck, so a deck cannot drift from the app: change a token here and both move together.
 * The rules behind these values, and the places the source specs disagreed, are in docs/DESIGN.md.
 *
 * TWO PALETTES, ONE HOME. `:root` is the bright kitchen and the design's default; the block at the
 * foot is the same kitchen at night. It used to say "light only, on purpose … there is no dark
 * variant to keep in sync", and the toggle was pinned so it could not half-flip the page — the owner
 * reversed that on 25 August 2026, so the comment goes with the decision rather than sitting beside
 * code that contradicts it.
 *
 * **A colour has a value in both, and nothing else has a value in either.** Every token below whose
 * value is a colour is redefined at the foot, and no radius, size, face or duration is — which is
 * what lets `design/check.mjs` prove a screen's geometry once and claim it in both. Both halves of
 * that rule are asserted in `design-tokens.test.ts`, because a dark palette that quietly stopped
 * covering a token would show up as one wrong colour on one screen and nothing else.
 *
 * **The dark half is derived against its own ground, never inverted, and has a ceiling as well as a
 * floor** (docs/DESIGN.md, "The stance"). `palette-contrast.test.ts` runs the same contrast
 * arithmetic over both palettes and proves the floor; nothing proves the top, so a later change that
 * raises the inks will pass everything and still be wrong.
 */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/inter.woff2') format('woff2');
}

@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/playfair.woff2') format('woff2');
}

@font-face {
  font-family: 'Playfair Display';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/playfair-italic.woff2') format('woff2');
}

/* The hand: what the cook NAMED, as against what squeezy printed. It wears one string in the whole
 * product — the name of the list you are shopping from. Why the role shrank to that is
 * docs/DESIGN.md's, and the short version is that a script face has to be set large to be legible
 * and a shopping list is measured in rows per screen. */
@font-face {
  font-family: 'Caveat';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/caveat.woff2') format('woff2');
}

:root {
  color-scheme: light;

  /* --- Ink. Warm browns, never neutral grey: this is where the warmth comes from.
   * Two tiers, not five. The ring reference drew a five-step ink ladder; three of its five values
   * measured under 4.5:1 on their own card surface, so the distinctions they carried (a done
   * status word, a future step's title, a group label) are carried by surface, strikethrough and
   * the status word itself instead of by a shade nobody could read. --------------------------- */
  --primary: #4a3200;
  --on-surface: #211f1b;
  --on-surface-variant: #6a665c;
  --outline: #837566;
  --outline-variant: #cfc8b6;

  /* --- Surfaces ------------------------------------------------------------------------------- */
  --surface: #f6f4ee;
  --surface-variant: #e3e2e0;
  --surface-container: #efeeeb;
  --surface-container-low: #f4f3f1;
  --surface-lowest: #ffffff;

  /* --- Yellow. A whole ramp, not one accent, and it is ordered by ROLE rather than by lightness:
   * --y-50 to --y-300 are washes and soft edges, --y-400/--y-500 are the fill that means *live*,
   * --y-700/--y-800 are the accent a word may be set in, and --y-900 is the ink that rides the fill.
   * Sun, not mustard: the ramp sits on a clean yellow hue rather than the old golden one.
   * --y-400 is *live*: a step is yellow when it is cooking without you or your hands are on it.
   * It carries --y-900 at 9.7:1. Yellow itself is never text on cream, where it is 1.3:1, and
   * `design/check.mjs` reads the fill tier back out — of whichever palette is on — to enforce that.
   * Here the two tiers are separated by lightness; on the dark ground they are not, which is why the
   * ramp is documented by role and the note at the foot re-derives them. ---------------------- */
  --y-50: #fffbe6;
  --y-100: #fff5b8;
  --y-200: #ffec70;
  --y-300: #ffe345;
  --y-400: #ffd93d;
  --y-500: #eac21f;
  /* Darkened when the ground moved to #f6f4ee: at #a16207 it measured 4.47:1 there, and axe was
   * right to fail it. The ground is the reason a text colour moves, not taste. */
  --y-700: #9a5d07;
  --y-800: #854d0e;
  --y-900: #3a2e00;

  /* --- Workstreams. One ink per parallel strand, and five of them because that is what the ring
   * holds, not because a kitchen runs five things — `station` has the Wellington on a water
   * bath, an oven, a fridge and a counter with not a burner among them. Above five the inks cycle
   * and two strands share one, which is why every strand is also named in words. Each is its
   * lane's identity everywhere it appears: the arc, the bead, the card's left edge, the status word
   * on a running step. Held apart in hue *and* saturation, so no lane reads as the greyed-out
   * one. -------------------------------------------------------------------------------------- */
  /* Darkened from #bf5a1b, which measured 4.20:1 as the running step's status word and 4.07:1 as a
   * map label. Saturation is untouched — only the value moved, and the ground is the reason a text
   * colour moves, not taste. */
  --lane-1: #ad4f16;
  --lane-2: #21456b;
  --lane-3: #147a4c;
  --lane-4: #a02b72;
  /* The fifth, added when the ring learned to hold five strands: until then a five-lane recipe wore
   * lane 1 twice, which is the one thing an ink is here to stop. Violet is the gap the other four
   * leave — the only hue between the navy and the magenta that is not a second green or a yellow,
   * and yellow can never be a lane because yellow is the one loud action. */
  --lane-5: #673fa0;

  /* Where the strands land. Green means arrival, not "good". */
  --plate: #4fa277;

  /*
   * The line the strands become once they have met: it is the dish now, on its way to the plate it
   * is drawn in the colour of. It was the ink used for body text, which read as the drawing going
   * dead at the moment the cooking comes together -- "can the join be a more positive colour than
   * black?" A deeper green than the plate's, so text on it holds up.
   *
   * Not the only other green any more. --done-soft/--done-ink below are a third, and they mean
   * something this pair does not: this one, like --plate, is arrival -- the strands landed. That
   * one is RECORDED -- an ingredient row's act, answered -- and it never rides the map at all.
   */
  --joined: #2f7d55;

  /* A lane that waits on another is drawn hopping outward from the angle it was freed at. */
  --dependency-line: #a9a08c;

  /* --- Step cards. One surface per state, each with its own edge. A card occupies identical
   * space in every state; only its colour moves.
   *
   * The live card's yellow was deepened here for a day and put straight back: the owner had said the
   * current step was not clear enough, and this was the wrong thing to reach for — _"the card itself
   * was great. The issue was the sticky bar when you scroll out of view."_ It is recorded because the
   * next person to read that sentence will reach for the same token.
   * ------------------------------------------------------------------------------------------- */
  --card: #fffdf8;
  --card-edge: #e4dfd0;
  --card-done: #f1eee5;
  --card-done-edge: #e0daca;
  --card-live: #fff8de;
  --card-live-edge: #efe0ac;
  --card-later: #fbfaf5;
  --card-later-edge: #e7e2d5;

  /* --- The mark of "this one": the wash a chosen thing is laid on and the edge inside it. The answer
   * card's standing tile (`answer-tiles.css`), the evening's chosen way (`notebook.css`) and today in
   * the plan's week strip (`home.css`) all wear this pair, and nothing else does. On paper they are the ramp's lightest wash and its yellow, as each has always worn them.
   * They are tokens of their own because the dark palette cannot wear the ramp's: --y-50 is a ground
   * there, darker than the card, so a chosen thing read as sunk, and --y-300 is 2.4:1 on the panel
   * where an edge that carries state wants 3. See the dark block's pair. ------------------------- */
  --chosen-wash: var(--y-50);
  --chosen-edge: var(--y-300);

  /* --- The brand mark's leaf (`lemon-mark.tsx`) is the only reader of these two. -------------- */
  --pasta-fill: #8daa91;
  --pasta-ink: #3f6650;

  /* --- States. The settled sage, and no card wears it: an ingredient row's act takes it once the
   * cook has answered, in the control they touched (SCREENS §13, `.cook__do--said`). A done card
   * is `--card-done`, and a done line on the map is a tick and a strikethrough — strand colour
   * there is membership, never state. #256e3d clears #dcefdc at 5.2:1. ---------------------- */
  --done-soft: #dcefdc;
  --done-ink: #256e3d;

  /* --- Alert -------------------------------------------------------------------------------- */
  --error-soft: #ffdad6;
  --error-ink: #93000a;

  /* --- Texture. The zest check, warm and half-strength: felt, not seen. ---------------------- */
  --zest-ink: rgb(109 91 70 / 2%);
  --zest-size: 20px;

  /* --- Shape. Soft: 24px cards, fully round buttons, 12px inner. A step card is --r-inner, the
   * bracket drawn around simultaneous steps is one step rounder so it reads as a container, and
   * the action is squarer than either so it reads as a control. ------------------------------- */
  --r-card: 24px;
  --r-group: 16px;
  --r-inner: 12px;
  --r-action: 10px;
  --r-chip: 4px;
  --r-pill: 9999px;

  /* --- Elevation. Low diffusion, so cards rest on the surface rather than hover above it. ---- */
  --sh-sm: 0 1px 2px 0 rgb(0 0 0 / 5%);
  --sh-md: 0 4px 6px -1px rgb(0 0 0 / 5%);
  /* The picker's two (docs/SCREENS.md section 1c), picked from a board of six: paper is a
     contact line plus a soft ambient fall, the way a card on a table shadows, for the dish
     cards; cut is the card's own shape offset with no blur at all, cut card stacked on cut
     card, for the square tiles — and only ever for a square corner. */
  --sh-paper: 0 1px 2px rgb(0 0 0 / 4%), 0 10px 24px -8px rgb(0 0 0 / 10%);
  --sh-cut: 4px 4px 0 rgb(0 0 0 / 7%);

  /* --- A control at rest is warm paper, never white: the card's own off-white under the picker's
   * paper fall (--sh-paper), with no edge. --surface-lowest is the panel a card is drawn on and is
   * not a control's ground. A control that would vanish against what it stands on keeps the faint
   * --outline-variant in its own rule, which says why. The on state stays the yellow. ---------- */
  --control: var(--card);
  --control-edge: transparent;

  /* --- Type. Serif is brand and the names of things; Inter is every instruction and quantity.
   * Playfair sits at 400 at display sizes — that lightness is most of the editorial feel. ----- */
  --serif: 'Playfair Display', Georgia, ui-serif, serif;
  --sans:
    Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* The third face, and the narrowest role there is: the name of the list you are shopping from, and
   * nothing else in the product. The fallbacks are the two cursives every platform ships, because a
   * hand that falls back to Inter would leave a title looking like a label it is not. */
  --hand: Caveat, 'Segoe Script', 'Bradley Hand', cursive;

  /* Its one size, which is the title's. It stays large because a script face reads about a third
   * smaller than Inter at the same size — and that is exactly why nothing on a ROW wears it any
   * more: a face that has to be set large costs a shopping list the thing it is measured in. */
  --fs-hand-title: 2rem;

  /* The shopping list's own two sizes, named for `--fs-ingredient-*`'s reason: a resize is one token
   * edit rather than seven numbers kept in step by hand. The name is what a cook is standing in an
   * aisle to find, so it leads; everything the row says beside it — the amount, and the note under
   * the name — is one step down and in the receding ink. Both are Inter at list sizes: rows per
   * screen is this screen's one measure (docs/SCREENS.md §1d), and type is what a row is made of. */
  --fs-list-name: 0.95rem;
  --fs-list-said: 0.8rem;

  /* The ingredient row's own two sizes, named rather than left as literals in ring.css: this is the
   * most-read type on the cooking screen (the amount is what a cook checks mid-act), so a resize is
   * a token edit in one place instead of four numbers — .cook__amount, .cook__amount--worded,
   * .cook__name and the shortened name the swipe paints in — kept in step by hand. Each is one step
   * down from what shipped (19px / 17px / 16px), landing on sizes the rest of this design already
   * uses elsewhere (16px on .cook__title, .cook__button and .cook__noteField) rather than inventing
   * new ones — see sq-aml.
   * --fs-ingredient-name also sets the shortened name in ring.css: same text, so one size. */
  --fs-ingredient-amount: 17.5px;
  --fs-ingredient-amount-worded: 16px;
  --fs-ingredient-name: 15px;

  /* THE AMOUNT, one form on every card and list. A fraction is digits and a slash at the figure's
   * own size, never a vulgar glyph, whose numerals stand at two fifths of an em, and the unit sits
   * a step lighter than the figure by weight alone, in the figure's own ink. The figure keeps its
   * surface's weight; these are the two the surfaces share, and the list's size, which is its
   * second tier of type and no smaller (it was 0.8rem, a 12.8px figure with 5px numerals). */
  --fw-amount-unit: 500;
  --fs-list-amount: 0.875rem;

  /* --- Composited values. A CSS variable can't be interpolated into an alpha channel, so every
   * translucent shade the components need is named here rather than left as a literal in a rule.
   * Without these, "one home for colour" quietly becomes "one home, plus eleven exceptions". --- */
  --hairline: rgb(207 200 182 / 30%);
  --hairline-soft: rgb(207 200 182 / 20%);
  --hairline-strong: rgb(207 200 182 / 50%);
  /* The app bar and nav sit on the ground, so their edge is a hint of the ink rather than a rule. */
  --edge: rgb(74 50 0 / 5%);
  /* Sticky chrome, translucent so content scrolling under it still reads as one surface. */
  --veil: rgb(246 244 238 / 90%);
  /* Steps that are genuinely simultaneous are bound together by a wash, not a border alone. */
  --wash-group: rgb(120 110 90 / 4.5%);
  --wash-lemon: rgb(254 252 232 / 70%);
  --wash-lemon-strong: rgb(254 252 232 / 80%);
  --wash-chip: rgb(254 249 195 / 50%);
  --alert-edge: rgb(147 0 10 / 15%);
  --shadow-yellow: rgb(234 179 8 / 20%);
  --texture-ink: rgb(0 0 0 / 2%);
  /* How much of the lemon shows behind the wordmark: a watermark the words read over, in both
   * palettes, so one figure and not a pair (the dark block holds colours alone). */
  --lemon-watermark: 0.2;

  /* --- Google's sign-in button, in Google's colours rather than ours: its brand rules fix the face,
   * the edge, the word and the four inks of the G, and the one place another brand's colours are
   * drawn still takes them from here. ------------------------------------------------------------ */
  --google-face: #ffffff;
  --google-edge: #747775;
  --google-ink: #1f1f1f;
  --google-blue: #4285f4;
  --google-red: #ea4335;
  --google-yellow: #fbbc05;
  --google-green: #34a853;

  /* --- Motion. Playfulness lives in shape and timing rather than colour noise. --------------- */
  --ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
  /* The easing of the app's one leaving motion (`.leaves`, globals.css). Its length is not here: a
   * timer holds the thing on screen for that long, so the figure is `LEAVES_AFTER_MS`
   * (`lib/leaving-motion.ts`) and reaches the stylesheet as `--leaves-after` on the document. */
  --ease-leaves: linear;

  /* --- The focus ring: one line, one gap, and the room a scroller leaves for both. ------------------
   * `:focus-visible` and `.field:focus-within` in globals.css draw it from these two and nothing else
   * does. A scroller clips whatever is drawn outside its padding box, and a classic scroll bar is
   * drawn on that same edge, so a control at a scroller's edge needs the ring's reach (5px) and a
   * line's width of air beyond it before the bar: 8px. 0.35rem left the ring 0.6px from the bar.
   * A scroller pads by `--ring-room` and takes it back with an equal negative margin, so no row moves. */
  --ring-width: 3px;
  --ring-offset: 2px;
  --ring-reach: calc(var(--ring-width) + var(--ring-offset));
  --ring-room: calc(var(--ring-reach) + var(--ring-width));

  /* --- Kitchen constraints. A phone held in one hand, with wet fingers. ---------------------- */
  --tap: 44px;

  /* The floor for anything typed into or picked from. 16px is not a taste: iOS zooms the whole page
   * when a focused field is set smaller, and on a shell that never scrolls that is the screen
   * lurching under the keyboard. Every input, textarea and select takes this token, and
   * `stylesheets.test.ts` holds them to it; the first real Safari evening found nine fields under
   * it, and the selects were found a day later. */
  --fs-field: 16px;

  /* The second size, and docs/DESIGN.md holds why there are two: this is what a control is drawn at
   * where pressing it is the reason the screen exists, rather than the floor for reaching something
   * on the way to what you came for. The type is named beside the box because they move together —
   * a large target around small words reads as padding rather than as a large control. */
  --tap-major: 56px;
  --fs-tap-major: 1rem;
}

/* The bright kitchen, said explicitly. `data-theme` is always one of these two words by the time the
 * first paint happens — `layout.tsx`'s head script resolves the OS preference into the attribute
 * before the body exists — so the palette is chosen by an attribute rather than by a media query,
 * and the dark values live in exactly one block instead of being mirrored into a second. */
:root[data-theme='light'] {
  color-scheme: light;
}

/* =================================================================================================
 * The same kitchen at night.
 *
 * Every value here was measured against the ground it sits on, not flipped: `design-tokens.test.ts`
 * runs the identical contrast arithmetic over both palettes, and `design/check.mjs` re-runs every
 * visual claim on every deck under this attribute. docs/DESIGN.md ("The stance") carries the reasoning
 * for the places a flip goes wrong; each block below keeps only what its values need.
 * ============================================================================================== */
:root[data-theme='dark'] {
  color-scheme: dark;

  /* --- Ink. Still warm, never neutral grey. HELD DOWN OFF THE CEILING ON PURPOSE: near-white on
   * near-black measured sixteen to one and read as glare ("dizzying"), so body ink lands near eleven
   * to one and the display ink near nine. The 4.5:1 floor is `palette-contrast.test.ts`'s; the top is
   * no check's. Lightness only: every hue is the one it already was. ------------------------------ */
  --primary: #cdbfa6;
  --on-surface: #dad4c6;
  --on-surface-variant: #b0a896;
  --outline: #8d8271;
  --outline-variant: #5c5442;

  /* --- Surfaces. Warm near-black, and the ladder keeps the light palette's ORDER, not its direction:
   * --surface-lowest is the raised panel a card is drawn on and the lightest here as there,
   * --surface-variant the sunken one, below the ground in both. The pair the wrong way round turns
   * the settings switch's knob into a dark knob on a slightly-less-dark track. The five span roughly
   * seventeen points of L*, with no rung under three, so the surfaces separate on their own and the
   * ink is free to come down: the other half of the glare fix (docs/DESIGN.md, "The stance"). ----- */
  --surface: #201d16;
  --surface-variant: #13110b;
  --surface-container: #312d23;
  --surface-container-low: #29251c;
  --surface-lowest: #393429;

  /*
   * --- Yellow, re-derived rather than flipped (docs/DESIGN.md, "The stance"). On paper the fill and
   * accent tiers are separated by lightness for free; here lightness does NOT tell them apart, and
   * saying it does would be the comfortable version rather than the true one. Saturation (50% against
   * the fill's 80%), role and shape do, and `design/check.mjs` fails any text node computing to a
   * FILL-tier value, reading the tier out of the palette on the page.
   *
   * Each accent is set by the darkest ground it is actually read on (--y-700 by --card, --y-800 by
   * the wordmark chip's wash over the raised panel) and remeasured whenever the grounds under it
   * move: --y-800 was once asserted at 5.5:1 and measured 4.24:1, and a contrast figure nobody
   * computed reads exactly like one somebody did. The fill is a step down from the light palette's (a
   * full slab is a torch in a dark kitchen) and still carries --y-900; the washes and soft edges
   * become dim ambers, grounds on a dark screen, so this ramp is not monotonic in lightness.
   */
  --y-50: #241d08;
  --y-100: #3d3210;
  --y-200: #5c4a12;
  --y-300: #7d6516;
  --y-400: #e8c22b;
  --y-500: #c9a41f;
  --y-700: #d2a047;
  --y-800: #e2bb78;
  --y-900: #2a2000;

  /*
   * --- Workstreams. The same hues to within 4 degrees (23/211/153/324 on paper, 24/211/149/324
   * here), lifted onto a dark ground: a lane that changed hue between palettes would be a different
   * lane at night, and the navy that reads at 9:1 on cream is invisible on near-black. Each clears
   * 4.5:1 on the ground and on the live card (`palette-contrast.test.ts`).
   */
  --lane-1: #f0975a;
  --lane-2: #84b8ee;
  --lane-3: #4fbf85;
  --lane-4: #eb92c8;
  /* The violet, lifted onto the dark ground at the same hue it carries in the light. */
  --lane-5: #bf96f1;

  /* The three greens keep their ladder — lane, joined, plate, lightest at the plate — because a
   * strand's green, the joined run's and the arrival's are three meanings and only the ladder tells
   * them apart. */
  --plate: #8ad4ac;
  --joined: #74c79a;
  --dependency-line: #7a7160;

  /*
   * --- Step cards: four grounds that encode state, read at a glance mid-step. The light palette
   * settles them by elevation against the page and that order carries over, separating better here
   * (1.11:1 at the closest pair against 1.02:1, `palette-contrast.test.ts`); all four rode the
   * surface ladder up. The live card could not be lifted: a yellow slab bright enough to mean *live*
   * cannot carry light ink, so live is a deep amber with a lit edge and the loud yellow moves to the
   * Done button that sits on it (docs/DESIGN.md, "The stance").
   */
  --card: #312b1f;
  --card-edge: #473f2d;
  --card-done: #1a180e;
  --card-done-edge: #2d271a;
  --card-live: #42340f;
  --card-live-edge: #9c7e1c;
  --card-later: #27231b;
  --card-later-edge: #3b3423;

  /* A chosen thing, lit rather than sunk: the live card's own amber wash and lit edge, written out
   * because this block holds colours and no var(). The wash is lighter than --card (L* 22.5 against
   * 17.8; --y-50 is 11.1) so the thing reads as the one lit, and the edge is 3.1:1 on that wash and
   * 3.5:1 on the panel it stands on, which the ramp's --y-300 (2.4:1) was not. */
  --chosen-wash: #42340f;
  --chosen-edge: #9c7e1c;

  /* --- The brand mark's leaf, as in the light block above. ----------------------------------- */
  --pasta-fill: #7fa086;
  --pasta-ink: #2c4a39;

  /* --- States. Recorded, not arrival — a deep sage ground with a light sage ink, 7.9:1. -------- */
  --done-soft: #153122;
  --done-ink: #7fd3a0;

  /* --- Alert -------------------------------------------------------------------------------- */
  --error-soft: #431815;
  --error-ink: #ffb4a9;

  /* --- Texture. Light on dark rather than dark on light, and weaker: a texture you feel rather than
   * see is a smaller step at night than it is by day. ----------------------------------------- */
  --zest-ink: rgb(255 236 200 / 3%);

  /* --- Elevation. A black shadow says nothing on a black ground, so the alpha climbs: what reads as
   * "resting on the surface" here is a darker well under the card, not a softer one. ----------- */
  --sh-sm: 0 1px 2px 0 rgb(0 0 0 / 40%);
  --sh-md: 0 4px 6px -1px rgb(0 0 0 / 45%);
  --sh-paper: 0 1px 2px rgb(0 0 0 / 35%), 0 10px 24px -8px rgb(0 0 0 / 50%);
  --sh-cut: 4px 4px 0 rgb(0 0 0 / 40%);

  /* A black fall under a control says little on a dark ground, and the card its paper is drawn
   * on is nearly its tone, so every control keeps the faint edge here rather than vanishing. It is
   * --outline-variant's own value, written out because this block holds colours and no var():
   * design-tokens.test.ts keeps the dark palette to literals, and field-edges.test.ts holds the
   * two equal. */
  --control-edge: #5c5442;

  /* --- Composited values. Every one is the same idea as its light twin with the direction reversed:
   * a hairline is light-on-dark at a low alpha rather than dark-on-light, and the lemon washes are
   * the fill yellow at single figures rather than a pale wash at 70%. Alphas are kept small enough
   * that what composites underneath them still clears 4.5:1 — the chip's gold on the chip's own
   * wash measures 5.4:1 over the worst ground it lands on, and axe re-checks every one of them on
   * every deck. --veil is the ground at 90% and has to move whenever the ground does — left behind,
   * it is a sticky bar in the old palette floating over a page in the new one, and nothing but a
   * screenshot would ever say so. --------------------------------------------------------------- */
  --hairline: rgb(232 222 200 / 12%);
  --hairline-soft: rgb(232 222 200 / 8%);
  --hairline-strong: rgb(232 222 200 / 20%);
  --edge: rgb(240 224 194 / 10%);
  --veil: rgb(32 29 22 / 90%);
  --wash-group: rgb(255 240 214 / 5%);
  --wash-lemon: rgb(232 194 43 / 6%);
  --wash-lemon-strong: rgb(232 194 43 / 8%);
  --wash-chip: rgb(232 194 43 / 11%);
  --alert-edge: rgb(255 180 169 / 22%);
  --shadow-yellow: rgb(232 194 43 / 25%);
  --texture-ink: rgb(0 0 0 / 35%);

  /* --- Google's dark button, by the same rules: its own near-black face, not this palette's. ---- */
  --google-face: #131314;
  --google-edge: #8e918f;
  --google-ink: #e3e3e3;
  --google-blue: #4285f4;
  --google-red: #ea4335;
  --google-yellow: #fbbc05;
  --google-green: #34a853;
}

/*
 * The docs site's own rules, after the app's tokens (`tools/docs-site.mjs` writes both into one
 * stylesheet). Every colour, radius, shadow and face is a token: none is written here.
 *
 * One column on a phone, the photograph beside its text from 56rem. Type in rem, body 1rem at 1.6,
 * nothing smaller than the app's own label size, and a tap row of 44px in the contents.
 */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html {
  background: var(--surface);
  -webkit-text-size-adjust: 100%;
}
body {
  margin: 0;
  color: var(--on-surface);
  background: var(--surface);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.6;
  overflow-wrap: break-word;
}
h1,
h2 {
  margin: 0;
  color: var(--primary);
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.2;
}
h1 {
  font-size: 1.75rem;
}
h2 {
  font-size: 1.45rem;
}
p {
  margin: 0;
}
a {
  color: var(--y-800);
  text-underline-offset: 0.18em;
}
a:focus-visible,
summary:focus-visible {
  outline: var(--ring-width) solid var(--y-800);
  outline-offset: var(--ring-offset);
  border-radius: var(--r-chip);
}
.skip {
  position: absolute;
  left: 0.75rem;
  top: -4rem;
  z-index: 2;
  padding: 0.6rem 1rem;
  background: var(--card);
  border: 1px solid var(--outline);
  border-radius: var(--r-action);
}
.skip:focus {
  top: 0.75rem;
}

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--card-edge);
}
.bar__brand {
  font-family: var(--serif);
  font-size: 1.05rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
}
.bar__where {
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--on-surface-variant);
}

.page {
  max-width: 70rem;
  margin: 0 auto;
  padding: 1.5rem 1rem 3rem;
}
.head {
  margin-bottom: 1.5rem;
}
.head__sub {
  margin-top: 0.6rem;
  max-width: 34rem;
  font-size: 1.05rem;
  color: var(--on-surface-variant);
}

.toc {
  margin: 0 0 2rem;
  padding: 1rem 1.1rem;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: var(--r-group);
  box-shadow: var(--sh-paper);
}
.toc__label {
  margin: 0 0 0.4rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}
.toc ol,
.pages {
  margin: 0;
  padding: 0;
  list-style: none;
}
.toc li + li,
.pages li + li {
  border-top: 1px solid var(--hairline);
}
.toc a {
  display: block;
  min-height: 44px;
  padding: 0.7rem 0;
  font-weight: 600;
}

.pages a {
  display: block;
  padding: 1rem 0;
  font-family: var(--serif);
  font-size: 1.3rem;
}
.pages p {
  padding-bottom: 1rem;
  color: var(--on-surface-variant);
}

.part {
  margin-top: 2rem;
  padding-top: 2rem;
  border-top: 1px solid var(--hairline-strong);
  scroll-margin-top: 1rem;
}
.part:first-of-type {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}
.part__text,
.part__sources {
  max-width: 36rem;
}
.part__text > * + *,
.part__sources > * + * {
  margin-top: 1rem;
}
.part > h2 + .part__text {
  margin-top: 1rem;
}
.part__figure,
.part__sources {
  margin: 1.5rem 0 0;
}

.quote {
  margin: 0;
  padding: 0.9rem 1.1rem;
  background: var(--card);
  border: 1px solid var(--card-edge);
  border-radius: var(--r-inner);
  box-shadow: var(--sh-paper);
}
.quote + .quote {
  margin-top: 1rem;
}
.quote cite {
  display: block;
  margin-top: 0.6rem;
  font-size: 0.88rem;
  font-style: normal;
  color: var(--on-surface-variant);
}

.shot img {
  display: block;
  width: 100%;
  max-width: 390px;
  height: auto;
  border: 1px solid var(--card-edge);
  border-radius: var(--r-card);
  box-shadow: var(--sh-paper);
}
.shot figcaption {
  max-width: 390px;
  margin-top: 0.6rem;
  font-size: 0.88rem;
  color: var(--on-surface-variant);
}
.shot__flux {
  display: block;
  margin-top: 0.3rem;
  font-weight: 600;
}

/* A doc section, whole: closed until asked, so the story reads first and the source is one tap away. */
.source {
  border: 1px solid var(--card-edge);
  border-radius: var(--r-inner);
}
.source summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.6rem;
  min-height: 44px;
  padding: 0.7rem 1rem;
  cursor: pointer;
}
.source summary h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
}
.source__from {
  font-size: 0.88rem;
  color: var(--on-surface-variant);
}
.doc {
  padding: 0 1rem 1rem;
  border-top: 1px solid var(--hairline);
}
.doc > * {
  margin: 1rem 0 0;
}
.doc h4,
.doc h5,
.doc h6 {
  font-size: 1rem;
}
.doc ul,
.doc ol {
  padding-left: 1.25rem;
}
.doc li + li {
  margin-top: 0.4rem;
}
.doc code {
  font-family: ui-monospace, monospace;
  font-size: 0.88em;
}
.doc pre {
  overflow-x: auto;
  padding: 0.75rem;
  background: var(--card);
  border-radius: var(--r-chip);
}
.doc blockquote {
  padding-left: 1rem;
  border-left: 2px solid var(--card-edge);
  color: var(--on-surface-variant);
}
.doc dt {
  font-weight: 600;
}
.doc dd {
  margin: 0 0 0.5rem;
}

.foot {
  max-width: 36rem;
  margin-top: 3rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--hairline-strong);
  font-size: 0.88rem;
  color: var(--on-surface-variant);
}

@media (min-width: 56rem) {
  h1 {
    font-size: 2.4rem;
  }
  h2 {
    font-size: 1.7rem;
  }
  .bar {
    padding: 0.9rem 2rem;
  }
  .page {
    display: grid;
    grid-template-columns: 14rem minmax(0, 1fr);
    align-items: start;
    column-gap: 3rem;
    padding: 2.5rem 2rem 4rem;
  }
  .head {
    grid-column: 1 / -1;
  }
  .toc {
    position: sticky;
    top: 1.5rem;
    margin: 0;
  }
  .toc a {
    min-height: 0;
    padding: 0.55rem 0;
  }
  .body {
    min-width: 0;
  }
  .part--shot {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 390px;
    grid-template-rows: auto auto 1fr;
    align-items: start;
    column-gap: 2.5rem;
  }
  .part--shot > :not(.part__figure) {
    grid-column: 1;
  }
  .part--shot > .part__figure {
    grid-column: 2;
    grid-row: 1 / span 3;
    margin: 0;
  }
  .foot {
    grid-column: 2;
  }
}
