/* TuckJoy's one design layer: the two typefaces, every colour (light and dark), the scales every
   page draws from, and the plain element styles. The site's static pages load it as /design.css
   (web/site/layout.mjs) and the app imports it through tokens.css, so there is one copy.
   docs/decisions/2026-10-25-one-design-system-and-header.md says why and what the scales are.

   The rule: a page's CSS uses these variables, never a typed value. scripts/ci/css_drift.mjs counts
   the typed values each file still has (scripts/ci/css_drift_baseline.json); a count may only go
   down, and a new file starts at none.

   This is the INTERFACE type. The book's own fonts are a separate matter and come from
   config/fonts.yaml, because those get embedded in the PDF and glyph-checked. */

/* One pair (decision 8, 25 Oct 2026): Instrument Serif for headings, Plus Jakarta Sans for every
   other word. Both OFL; the licences sit beside the files in /fonts/. Instrument Serif has one
   weight, 400, so a heading never asks it for bold (font-synthesis-weight below). */
@font-face { font-family: "Instrument Serif"; font-style: normal; font-weight: 400; font-display: swap; src: url(/fonts/instrument-serif-latin.woff2) format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Instrument Serif"; font-style: normal; font-weight: 400; font-display: swap; src: url(/fonts/instrument-serif-latin-ext.woff2) format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "Instrument Serif"; font-style: italic; font-weight: 400; font-display: swap; src: url(/fonts/instrument-serif-italic-latin.woff2) format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Instrument Serif"; font-style: italic; font-weight: 400; font-display: swap; src: url(/fonts/instrument-serif-italic-latin-ext.woff2) format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "Plus Jakarta Sans"; font-style: normal; font-weight: 200 800; font-display: swap; src: url(/fonts/jakarta-latin.woff2) format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Plus Jakarta Sans"; font-style: normal; font-weight: 200 800; font-display: swap; src: url(/fonts/jakarta-latin-ext.woff2) format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }

/* .tj-light: a part of the page that stays in the light palette in both themes (the studio's stage,
   studio-heroes.css), so what sits on it reads as it does by day. The dark blocks below set
   :root only, so these values win inside it. */
:root, .tj-light {
  /* paper and ink. The paper is "Storybook Paper" (Anurag 2026-09-23): warm ivory,
     a fine grain instead of the old dot grid, and soft colour blooms on the pages
     that are selling something. */
  --paper: #FBF8F1;
  --surface: #FFFFFF;
  --sunk: #F4EFE5;
  --ink: #1C2426;
  --muted: #5F6B6D;
  --faint: #8E9A9B;
  --line: #E3DCD0;
  --line-strong: #D9D1C4;
  --rule: #EFE9DE;

  /* the accent. One variable: the artboards ship four options and render two
     different ones, so this is the single place that decision lives. */
  --accent: #2E7D73;       /* white on it 4.89:1, as link text on paper 4.61:1 */
  --accent-dark: #22625A;
  --accent-tint: #E4F0EC;  /* = the mint bloom */
  --accent-pale: #F2F8F5;
  --accent-lift: #9FCFC4;
  --on-accent: #FFFFFF;
  --ring: 0 0 0 4px rgba(46, 125, 115, .14);

  /* The blooms. Ink reads on every one of them (12.9:1 and up), so words may sit on
     a bloom — but they are washes behind a page, never the fill of a control. */
  --bloom-peach: #F8E4D8;
  --bloom-mint: #E4F0EC;
  --bloom-cream: #F4E8D7;
  /* The one warm spark: stars, a hand-drawn arrow. 1.9:1 on paper, so it is
     decoration only and never carries a word. */
  --spark: #F4A45D;
  /* The bookshelf (Anurag's approved canvas, 26 October 2026): its warm ground and blooms, the
     cream panel its tops sit on, and the dashed stand-ins an empty tab shows. */
  --ground-warm: #F7F2EA; --bloom-sea: #D9EEE5; --bloom-apricot: #FBE7D3; --bloom-sage: #E4F0E9;
  --panel: #FFFDF8; --panel-line: #ECE4D6; --hero-mint: #EEF6EC;
  --ghost: #EFE8DC; --ghost-line: #CFC6B6; --ghost-card: rgba(255, 255, 255, .55);
  --shadow-cover: 0 12px 24px -14px rgba(20, 19, 24, .55);
  --shadow-photo: 0 18px 32px -16px rgba(20, 19, 24, .55), 0 2px 6px rgba(20, 19, 24, .12);
  --warn-on-slab: #E0916B;
  /* Paper grain: dark specks at a whisper. Dark mode swaps the colour, not the idea. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .35 0 0 0 0 .28 0 0 0 0 .18 0 0 0 .55 -.2'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='.22'/%3E%3C/svg%3E");
  /* Where artwork is being judged, the ground is neutral: a tinted surround shifts
     how you see the picture's own colours, and a picture misjudged is a redraw
     it did not need. */
  --artwell: #FFFFFF;
  --artwell-line: #E6E6E6;

  /* the supporting four. Illustration tints and status colours only — never
     structural, so the page never has to decide what five colours mean at once. */
  --orange: #E86F46; --orange-dark: #C9563A; --orange-tint: #FCE8DF;
  --gold: #E3B04B;   --gold-ink: #B8860B;    --gold-tint: #F3E9D8;
  /* The glass badge (design/studio-book-view-v2, approved by Anurag 4 October 2026): a frosted
     pill whose fill and hairline are translucent, with an inset sheen and a soft drop for an
     edge, blurred behind, for a badge on the dark bar. */
  --glass: rgba(255, 255, 255, .18);
  --glass-edge: rgba(255, 255, 255, .28);
  --glass-sheen: inset 0 1px 1px rgba(255, 255, 255, .2);
  --glass-drop: 0 1px 2px rgba(0, 0, 0, .08);
  --glass-blur: 8px;
  --blue: #2F6BB0;                            --blue-tint: #E3EEF5;
  /* Chart series, fixed per gateway (config/payments.json dashboard.gateways.*.series), checked
     with the dataviz validator against both surfaces: --series-2 is under 3:1 on white, so every
     chart that uses it has a legend, tooltips and a table view (admin Payments tab). */
  --series-1: #2F6BB0; --series-2: #C98A0A; --series-3: #7A4FB5;
  --purple: #7A4FB5; --purple-dark: #5B3A8A;  --purple-tint: #EDE7F3;
  --warn: #B4552E;   --warn-soft: #FCE8DF;
  --deep: #143F39;

  /* A dark decorative panel, which is NOT the same thing as the text colour. Using
     --ink as a background made the launch strip invert and the closing band render
     near-white text on near-white — it has to stay dark in both themes. */
  --slab: #1C2426;
  --on-slab: #F6F1E8;

  /* the wordmark's two themed colours, taken from brand/logo.svg and logo-dark.svg */
  --logo-ink: #0E2951;
  --logo-star: #FFFFFF;
  /* The one navy. Storybook Paper proposed #1F2A44 and GPT later #162A44; beside
     the wordmark either would read as a second, slightly-wrong navy. */
  --navy: #0E2951;
  --hand: "Bradley Hand", "Noteworthy", "Chalkboard SE", cursive;

  --display: "Instrument Serif", "Iowan Old Style", Georgia, serif;
  --body: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* the older radius names, now on the four-step scale */
  --r-rail: var(--radius-20); --r-card: var(--radius-20); --r-control: var(--radius-12); --r-small: var(--radius-8); --r-pill: var(--radius-pill);
  --shadow-rest: 0 1px 2px rgba(28, 36, 38, .04);
  --shadow-rail: 0 16px 40px rgba(28, 36, 38, .07);
  /* what floats over the page: a sheet, a dialog (design/e2e-v2) */
  --shadow-float: 0 18px 48px rgba(28, 36, 38, .14), 0 2px 6px rgba(28, 36, 38, .06);
  --shadow-book: 0 10px 24px rgba(28, 36, 38, .16);

  /* names the older screens still ask for, so they inherit the new palette
     rather than sitting in the old one until Phase 2 rebuilds them */
  --bg: var(--paper);
  --accent-soft: var(--accent-tint);
  --radius: var(--r-control);


  /* ---- the button colour roles (decision 1) ---- */
  --on-ink: #FFFFFF;          /* words on an ink button; the ink flips to cream in dark mode */
  --cream: #F6F1E8; --on-cream: #1C2426;  /* the main action on a dark or green panel, both themes */
  --bar: #2A2326; --bar-edge: #C9A15A;     /* the one site bar, both themes, and an open button's edge on it */
  --play: #E3B04B; --on-play: #1C2426;    /* Games only */
  --on-warn: #FFFFFF;         /* the confirm step of a delete */

  /* ---- the scales (decided 25 Oct 2026). Breakpoints cannot be variables in a media query, so
     they are written as numbers, and only these: 380 (small phone), 720 (phone), 1280 (wide). ---- */
  --font-display: var(--display);
  --font-ui: var(--body);
  --text-12: 12px; --text-13: 13px; --text-14: 14px; --text-16: 16px; --text-18: 18px;
  --text-22: 22px; --text-30: 30px; --text-44: 44px;
  --text-hero: clamp(36px, 6vw, 64px);
  --weight-body: 400; --weight-medium: 600; --weight-bold: 700;
  --leading-tight: 1.1; --leading-snug: 1.35; --leading-body: 1.55;
  --radius-8: 8px; --radius-12: 12px; --radius-20: 20px; --radius-pill: 999px;
  --width-read: 720px; --width-page: 1080px; --width-wide: 1280px;
  /* layers, lowest first */
  --z-base: 1; --z-bar: 20; --z-dropdown: 40; --z-sheet: 60; --z-modal: 80; --z-toast: 100;
  /* the making page's desk photo (making.css): its own light, window and warm board, measured from
     desk.jpg, so the cover and the photos on it look lit by the same room. They are the photo's
     colours and a real book's edges, not the screens', so they keep no dark-mode copy. */
  --desk-light: 255, 250, 235; --desk-wood: 150, 112, 72; --desk-shade: 60, 40, 20; --desk-cream: 255, 244, 226;
  --desk-lit: linear-gradient(222deg, #FFF8EC 0%, #F8E6CA 50%, #EED4B1 100%);
  --desk-round: linear-gradient(90deg, #B89A78 0%, #F4E2C8 45%, #E3C7A2 100%);
  --desk-breath: radial-gradient(circle at 50% 40%, rgba(var(--desk-light), .9), rgba(var(--desk-light), 0) 60%);
  --desk-glow: linear-gradient(100deg, rgba(var(--desk-light), 0) 30%, rgba(var(--desk-light), .75) 50%, rgba(var(--desk-light), 0) 70%);
  --desk-skeleton: linear-gradient(100deg, rgba(var(--desk-wood), .22) 30%, rgba(var(--desk-wood), .08) 50%, rgba(var(--desk-wood), .22) 70%);
  --desk-press: brightness(0) invert(.32) sepia(.6) drop-shadow(0 1px 0 rgba(255, 255, 255, .65)) drop-shadow(0 -1px 0 rgba(var(--desk-shade), .18));
  --desk-gloss: linear-gradient(222deg, rgba(255, 255, 255, .22), rgba(255, 255, 255, 0) 42%);
  --desk-groove: linear-gradient(90deg, rgba(var(--desk-shade), 0), rgba(var(--desk-shade), .38) 40%, rgba(var(--desk-cream), .4) 72%, rgba(var(--desk-cream), 0));
  --desk-rim: inset -1px 0 0 rgba(255, 246, 230, .55), inset 0 -2px 3px rgba(var(--desk-shade), .18);
  --desk-tape: rgba(255, 253, 248, .78);
  --desk-cover-r: 0 3px 3px 0; --desk-rim-r: 0 4px 4px 0; --desk-spine-r: 6px 0 0 6px; --desk-photo-r: 2px;
  /* the one button (decision 2): 36px tall, a 44px touch target around it */
  --button-h: 36px; --button-pad: 0 14px; --button-font: 700 13px/1 var(--font-ui); --touch: 44px;
  /* waiting (docs/decisions/2026-10-31-waiting-animations.md): the light round an edge, the shimmer */
  --wait-light: var(--accent); --wait-edge: 2px; --wait-edge-time: 1.8s; --wait-sweep-time: 1.6s;

  color-scheme: light;
}

/* The artboards have no dark mode — a warm-paper design does not invert by flipping
   tokens. This is a real palette rather than an inversion: the paper becomes a warm
   dark, the illustration panels keep their own colours because they are artwork. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #171614; --surface: #201F1C; --sunk: #272521;
    --stage: #0F0E0D; /* the ground under a page on a dark screen (Letter), darker than the paper */
    --ink: #ECE6DB; --muted: #9FAAAB; --faint: #7A8586;
    --line: #34312C; --line-strong: #423E38; --rule: #2A2824;
    --accent: #5FBFAE; --accent-dark: #8FD3C4;
    --accent-tint: #17302C; --accent-pale: #162926; --on-accent: #10201F;
    --bloom-peach: transparent; --bloom-mint: transparent; --bloom-cream: transparent;
    --spark: #E8A060; --navy: #FFFFFF;
    --ground-warm: #171614; --bloom-sea: transparent; --bloom-apricot: transparent; --bloom-sage: transparent;
    --panel: #201F1C; --panel-line: #34312C; --hero-mint: #1B2522;
    --ghost: #2A2824; --ghost-line: #423E38; --ghost-card: rgba(255, 255, 255, .03);
    --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 .95 0 0 0 0 .85 0 0 0 .55 -.2'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='.10'/%3E%3C/svg%3E");
    --artwell: #1E1E1E; --artwell-line: #333333;
    --ring: 0 0 0 4px rgba(95, 191, 174, .18);
    --warn: #E0916B; --warn-soft: #2E2019;
    /* The four extra hues had no dark values, so anything that filled a shape with
       one of their tints stayed daylight-bright on a dark page. Used by the cast
       tints and the home page's step dots. */
    --orange: #F0906B; --orange-tint: #33211A;
    --blue: #6FA3D8;   --blue-tint: #1A2733;
    --series-1: #4F8FD6; --series-2: #B07A12; --series-3: #9473C9;
    --purple: #A98AD6; --purple-tint: #241E33;
    --gold: #E3B04B;   --gold-ink: #D9AE5A; --gold-tint: #322A1A;
    --logo-ink: #FFFFFF; --logo-star: #0B1B38;
    --slab: #242C2E;
    --shadow-rail: 0 16px 40px rgba(0, 0, 0, .40);
    --shadow-float: 0 18px 48px rgba(0, 0, 0, .45), 0 2px 6px rgba(0, 0, 0, .3);
    --shadow-book: 0 10px 24px rgba(0, 0, 0, .50);
    --shadow-rest: 0 1px 2px rgba(0, 0, 0, .30);
    --on-ink: #171614; --on-warn: #171614;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --paper: #171614; --surface: #201F1C; --sunk: #272521;
  --stage: #0F0E0D; /* the ground under a page on a dark screen (Letter), darker than the paper */
  --ink: #ECE6DB; --muted: #9FAAAB; --faint: #7A8586;
  --line: #34312C; --line-strong: #423E38; --rule: #2A2824;
  --accent: #5FBFAE; --accent-dark: #8FD3C4;
  --accent-tint: #17302C; --accent-pale: #162926; --on-accent: #10201F;
    --bloom-peach: transparent; --bloom-mint: transparent; --bloom-cream: transparent;
    --spark: #E8A060; --navy: #FFFFFF;
    --ground-warm: #171614; --bloom-sea: transparent; --bloom-apricot: transparent; --bloom-sage: transparent;
    --panel: #201F1C; --panel-line: #34312C; --hero-mint: #1B2522;
    --ghost: #2A2824; --ghost-line: #423E38; --ghost-card: rgba(255, 255, 255, .03);
    --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 .95 0 0 0 0 .85 0 0 0 .55 -.2'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='.10'/%3E%3C/svg%3E");
    --artwell: #1E1E1E; --artwell-line: #333333;
  --ring: 0 0 0 4px rgba(95, 191, 174, .18);
  --warn: #E0916B; --warn-soft: #2E2019;
  /* The four extra hues had no dark values, so anything that filled a shape with
     one of their tints stayed daylight-bright on a dark page. Used by the cast
     tints and the home page's step dots. */
  --orange: #F0906B; --orange-tint: #33211A;
  --blue: #6FA3D8;   --blue-tint: #1A2733;
  --series-1: #4F8FD6; --series-2: #B07A12; --series-3: #9473C9;
  --purple: #A98AD6; --purple-tint: #241E33;
  --gold: #E3B04B;   --gold-ink: #D9AE5A; --gold-tint: #322A1A;
  --logo-ink: #FFFFFF; --logo-star: #0B1B38;
  --slab: #242C2E;
  --shadow-rail: 0 16px 40px rgba(0, 0, 0, .40);
  --shadow-float: 0 18px 48px rgba(0, 0, 0, .45), 0 2px 6px rgba(0, 0, 0, .3);
  --shadow-book: 0 10px 24px rgba(0, 0, 0, .50);
  --shadow-rest: 0 1px 2px rgba(0, 0, 0, .30);
  --on-ink: #171614; --on-warn: #171614;
  color-scheme: dark;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  color: var(--ink);
  font: 400 16px/1.5 var(--body);
  background-color: var(--paper);
  background-image: var(--grain);
  -webkit-font-smoothing: antialiased;
  font-synthesis-weight: none;
}

/* The three blooms, one value: the page's layer below and any full-screen view that covers it
   (the reader, reader.css) wear the same wash. */
:root {
  --bloom-wash:
    radial-gradient(45% 35% at 88% 4%, var(--bloom-mint), transparent 72%),
    radial-gradient(40% 35% at 4% 30%, var(--bloom-peach), transparent 72%),
    radial-gradient(50% 40% at 70% 100%, var(--bloom-mint), transparent 70%);
}
/* The blooms sit on a fixed layer behind everything, so they stay put while the
   page scrolls — the paper moves under you, the light does not. How much of them a
   page gets is set per screen (draw() in app.js): full where we are selling, a
   trace once you are working, none where you are judging pictures. Dark mode has
   none at all: a watercolour wash on a dark page reads as a stain. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  /* the bookshelf's ground (Anurag, 26 October 2026: the same multi-coloured background on every page) */
  background: var(--bloom-wash);
  opacity: 1; transition: opacity .4s;
}
body[data-bloom="none"]::before { opacity: 0; }
body[data-bloom="full"]::before { opacity: 1; }
body[data-bloom="light"]::before { opacity: .45; }

h1, h2, h3 { margin: 0; line-height: 1.2; }
p { margin: 0; }
a { color: var(--accent); }
a:hover { color: var(--accent-dark); }
button, select, input, textarea { font-family: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------------- shell ---------------- */

/* ---------------- the one button (decisions 1 and 2) ----------------
   Every button on a customer page is this: 36px, fully round, Plus Jakarta Sans 700 at 13px.
   .tj-btn alone is the plain one (white with a hairline); one variant says its role:
     .main    black, the one main action on a screen or sheet (cream with dark words in dark mode)
     .cream   the main action on a dark or green panel
     .glass   the plain action beside .cream on a dark panel
     .danger  delete: rust outline; .danger.solid is the confirm step
     .play    Games only
   and two shapes: .icon (a 36px circle) and .wide (stretched to the width, never taller).
   aria-pressed="true" is state, so it is green, never black. .btn and .btn-cs (.second) are older
   names for .tj-btn, and .btn.primary and .btn-cs.primary for .tj-btn.main. */
:is(.tj-btn, .btn, .btn-cs) {
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--button-h); min-height: var(--button-h); padding: var(--button-pad);
  border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
  background: var(--surface); color: var(--ink); font: var(--button-font);
  white-space: nowrap; text-decoration: none; cursor: pointer;
  transition: background-color .15s, border-color .15s, color .15s;
}
:is(.tj-btn, .btn, .btn-cs) svg { width: 15px; height: 15px; flex-shrink: 0; }
:is(.tj-btn, .btn, .btn-cs):hover:not(:disabled, [aria-disabled="true"]) { border-color: var(--muted); color: var(--ink); }
:is(.tj-btn, .btn, .btn-cs):is(:disabled, [aria-disabled="true"]) { opacity: .45; cursor: default; }
:is(.tj-btn.main, .btn.primary, .btn-cs.primary) { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
:is(.tj-btn.main, .btn.primary, .btn-cs.primary):hover:not(:disabled, [aria-disabled="true"]) {
  background: color-mix(in srgb, var(--ink) 86%, var(--paper)); border-color: transparent; color: var(--on-ink);
}
.tj-btn.cream { background: var(--cream); border-color: var(--cream); color: var(--on-cream); }
/* Hover deepens the cream a touch, the same in both themes (--surface is near-black in dark mode). */
.tj-btn.cream:hover:not(:disabled, [aria-disabled="true"]) { background: color-mix(in srgb, var(--cream) 88%, var(--on-cream)); color: var(--on-cream); }
.tj-btn.glass { background: var(--glass); border-color: var(--glass-edge); color: var(--cream); }
.tj-btn.glass:hover:not(:disabled, [aria-disabled="true"]) { border-color: var(--cream); color: var(--cream); }
.tj-btn.danger { background: transparent; border-color: var(--warn); color: var(--warn); }
.tj-btn.danger:hover:not(:disabled, [aria-disabled="true"]) { background: var(--warn-soft); border-color: var(--warn); color: var(--warn); }
.tj-btn.danger.solid { background: var(--warn); color: var(--on-warn); }
.tj-btn.play { background: var(--play); border-color: var(--play); color: var(--on-play); }
.tj-btn.play:hover:not(:disabled, [aria-disabled="true"]) { background: color-mix(in srgb, var(--play) 88%, var(--ink)); color: var(--on-play); }
:is(.tj-btn, .btn, .btn-cs)[aria-pressed="true"] { background: var(--accent-tint); border-color: var(--accent); color: var(--accent-dark); }
.tj-btn.icon { width: var(--button-h); padding: 0; }
.tj-btn.wide { display: flex; width: 100%; }
/* 36px to see, 44px to tap: on a touch screen the target reaches past the pill. It is ::before,
   because ::after is the bar's icon names (toolbar.css data-tip). */
@media (pointer: coarse) {
  :is(.tj-btn, .btn, .btn-cs)::before {
    content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    width: max(100%, var(--touch)); height: var(--touch);
  }
}

/* ---------------- waiting (docs/decisions/2026-10-31-waiting-animations.md) ----------------
   Two ways to show a wait, and no spinners:
     .tj-wait     something the customer pressed is being worked on (a button, a menu row): while
                  it has aria-busy="true" a band of green and gold light runs round its edge. Its
                  icon and words stay; the words say what is happening ("Making your PDF…").
                  It draws in ::before. On a dark panel set --wait-light: var(--accent-lift).
     .tj-shimmer  something with a shape is on its way (a picture, a line of words, a page): light
                  passes over warm paper in its place until it comes.
   Under reduced motion the edge is a still line of light and the shimmer stands still. */
@property --wait-spin { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
:where(.tj-wait) { position: relative; isolation: isolate; }
.tj-wait[aria-busy="true"] { cursor: progress; }
.tj-wait[aria-busy="true"]::before {
  content: ""; position: absolute; inset: 0; z-index: -1; transform: none; width: auto; height: auto;
  border-radius: inherit; padding: var(--wait-edge); pointer-events: none;
  background: conic-gradient(from var(--wait-spin), transparent 0 62%, var(--wait-light) 80%, var(--gold) 88%, transparent 96%);
  -webkit-mask: linear-gradient(var(--ink) 0 0) content-box, linear-gradient(var(--ink) 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: tj-wait-edge var(--wait-edge-time) linear infinite;
}
@keyframes tj-wait-edge { to { --wait-spin: 360deg; } }
.tj-shimmer {
  background-color: var(--sunk); background-image: linear-gradient(100deg, transparent 30%, var(--paper) 50%, transparent 70%);
  background-size: 250% 100%; animation: tj-shimmer var(--wait-sweep-time) linear infinite;
}
@keyframes tj-shimmer { from { background-position: 125% 0; } to { background-position: -125% 0; } }
@media (prefers-reduced-motion: reduce) {
  .tj-wait[aria-busy="true"]::before { animation: none; background: var(--wait-light); }
  .tj-shimmer { animation: none; }
}
