/* Signature design system — tokens, the brand mark, and the Tailwind palette
   overrides. See design_signature.md.

   LOAD ORDER IS LOAD-BEARING: this file must be linked AFTER tailwind.css.
   Most of what follows overrides Tailwind utilities at *equal* specificity
   (html:not(.dark) .bg-white, .shadow-*), so source order is the only thing
   making these win. Both link tags live next to each other in
   shared/_spa_theme.html.erb for exactly that reason — keep them adjacent, and
   see spec/system/stylesheet_order_spec, which fails on a swap. This is the
   only copy of that argument; the other stylesheets point here rather than
   restating it, because it was already drifting in four places.

   Comments are real CSS here rather than the ERB comments this used to carry
   inline: the file is fingerprinted and cached, so their bytes are paid once
   instead of on every page of every Turbo navigation. */

html { transition: background-color 200ms ease; }

/* Signature design tokens — the single source for every chrome colour. The
   ramps below read from these, so a palette change is an edit here rather
   than a sweep over the ~190 literals this file, _app_header and
   _docs_prose used to carry independently.

   Every text token was measured against every ground it can sit on before
   being written down. muted and faint are not the values the plan proposed:
   at 4.69 and 3.00 against the paper, faint failed AA outright, and the
   nearest passing faint landed on top of muted. A three-step ramp needs
   real separation, so the whole ramp was re-spaced — ink 16.2:1, muted
   8.0:1, faint 5.5:1 on paper. faint is the floor for text; anything
   lighter (--ghost) is for icons and dividers only. */
:root {
  --paper: #F5EFF0;        /* page ground — flat pigment, no gradient */
  --surface: #FFFFFF;      /* cards; elevation is this value step, not a shadow */
  --sunk: #EADFE2;         /* inset panels, inputs, signature boxes */
  /* Two deeper ground steps, named because they were not. Each was written
     twice below — once for the utility and once for its :hover twin — and a
     second copy is the failure this palette is organised to prevent. */
  --sunk-2: #E2D4D8;
  --sunk-3: #D5C3C8;
  --rule: #D9C7CC;
  --rule-strong: #C9B2B8;
  --ink: #1E1013;          /* 16.24:1 on paper */
  --ink-soft: #403135;     /* prose body */
  --muted: #524548;        /* 8.03:1 paper / 9.12 surface / 7.01 sunk */
  /* Darkened on 2026-09-25: the old value measured 4.02:1 on sunk, where the
     header search and version pill set their labels. */
  --faint: #6F5B60;        /* 5.53:1 paper / 6.29 surface / 4.83 sunk — text floor */
  --ghost: #9A868B;        /* 3.00:1 — non-text only */
  --accent: #C40F3F;       /* links: 5.30:1 on paper */
  --accent-fill: #E11D48;  /* the mark, buttons — red as punctuation */
  --accent-tint: #F6DDE3;  /* active-tab and active-row wash */
  /* A token because two mono surfaces are not elements the `code, kbd, pre,
     samp` rule below can reach — the playground's editor and its theme
     <select>. Both restated a generic stack and were still painting in Menlo
     after everything else moved to JetBrains Mono. */
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Same reason as --font-mono, for a surface no stylesheet can reach at all:
     HTML email, which must carry every value as a literal in the markup (see
     SignatureTokens). The `body` rule below reads it so there is one stack. */
  --font-sans: 'Manrope', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* Radius — the "Press" system, eased (docs/mockups/radius-ease.html; the
     original square-structure version is docs/mockups/radius-proposals.html).
     Structure takes 12px, controls take 8px, and only true counters and dots
     are round. Three values, and nothing between 12px and full: the scatter
     this replaced was 13 hand-written radii plus Tailwind's scale, which is
     how a 999px nav pill ended up beside an 8px version pill on the same
     32px line.

     The control tick is 8px, not the 4px it shipped at. 4px on a 32px
     control is 12.5% of its height — enough to stop reading as square, not
     enough to read as rounded, so the header chips looked like a wobble
     rather than a rule. The grammar only works if the states are far enough
     apart to be told apart at a glance. 8px still holds its shape at every
     control height on the site, which is the property 999px lacks: it
     silently resolves to half the element and stops being a decision.

     Structure was 0 until 2026-09-04 (square by default, rounding as the
     thing you ask for). With every card, code block and rail meeting the
     page at 90°, the site read as stern rather than exact.

     Why 12: a control sits 4px inside a padded panel edge at its closest, so
     8 + 4 keeps the inner corner concentric with the outer one. 16 leaves a
     visible gap between the two curves; 10 reads as a rounding error next
     to 8. */
  --r-panel: 12px;
  --r-control: 8px;
  --r-badge: 999px;

  /* Header height (py-2 + a 32px control line + 1px rule), so sticky bars below
     it stop guessing — the jobs toolbar sat at top-14 and left a 7px slot cards
     scrolled through. The header reads it back as a min-height below, so the
     token causes the height rather than describing it. The docs rails keep
     top-16 on purpose: they are cards with ground above them, not a flush bar. */
  --header-h: 49px;

  /* Elevation — the lift. Mixed from --ink rather than neutral black, because
     a grey shadow on pigmented paper greys the pigment it falls on; that is
     what got shadows removed wholesale in the first place. Two layers: a
     contact shadow that seats the edge and a wide, offset ambient one. */
  --lift: 0 1px 2px rgba(30, 16, 19, .05), 0 10px 24px -14px rgba(30, 16, 19, .22);
  --lift-hover: 0 2px 4px rgba(30, 16, 19, .06), 0 18px 40px -18px rgba(30, 16, 19, .30);

  /* The deeper red, and what a filled accent carries as a label. Both flip in
     dark and both were being spelled out per call site instead: --accent-strong
     as #FF8DA1 in three rules, --on-accent as `text-white` on five surfaces
     that then measured 2.99:1 once the fill became dark's lighter pink. */
  --accent-strong: var(--accent);
  --on-accent: #fff;
}

/* A lifted surface is a panel: the same shadow utilities the elevation block
   below remaps to --lift (and whose border it nulls) take the panel radius
   here, so a card cannot carry the lift and keep square corners. Declared
   BEFORE the hooks, not beside the shadow remap, because the ink-pill sheet
   button is shadow-lg + r-ctrl and the later rule has to be the explicit
   one. The lg: form must stay inside its query or it applies at every
   width; it exists for the method spine, a flush bottom sheet below lg. */
.shadow-md, .shadow-lg, .shadow-xl { border-radius: var(--r-panel); }
@media (min-width: 1024px) { .lg\:shadow-md { border-radius: var(--r-panel); } }

/* The only three radius hooks a template may use. Guarded by
   spec/radius_spec.rb, which fails on a Tailwind rounded-* utility or a raw
   border-radius outside this file. .r-panel is for structure that carries no
   lift utility: overlays, code blocks, the mail shell. The sm: form is the
   settings panel, a flush bottom sheet below sm — pinned to bottom:0, rounded
   bottom corners would show the page through them. */
.r-panel { border-radius: var(--r-panel); }
.r-ctrl { border-radius: var(--r-control); }
.r-badge { border-radius: var(--r-badge); }
@media (min-width: 640px) { .sm\:r-panel { border-radius: var(--r-panel); } }

/* The brand mark: red punctuation, never a red word. One definition shared
   by the footer wordmark and the documentation headings so the grammar
   cannot drift — the red "." in ruby.help and the red "#" in String#chomp
   are the same mark. Leading 0.85 suits display sizes; the footer overrides
   it to 0.78 because its glyphs bleed off the page edge. */
.mark { font-weight: 800; letter-spacing: -0.045em; line-height: 0.85; }
/* .mark-text is the mark at text size: an index row's name needs only the
   red punctuation, not .mark's display leading and weight. */
.mark .sep, .mark-text .sep { color: var(--accent-fill); }
/* Compound selector, not a Tailwind leading-* utility: this file loads after
   tailwind.css, so an equal-specificity utility would lose to .mark. */
.mark.bleed { line-height: 0.78; }
/* Wrapped marks need real leading — at 0.85 line one's descenders land in line
   two's ascenders. Compound selector for the same reason as .bleed. */
.mark.stack { line-height: 1.05; }

/* Selected chrome — tint ground, accent text, 600 — written once. It was
   spelled out at six call sites, two of which used Tailwind's .text-red-700;
   that maps to #FF8DA1 in dark while --accent is #FF5C79, so the jobs pills
   and the method spine were a visibly different red from the header nav.
   The component files keep only what they add on top (a border, a swatch
   outline); .st-aaopt.is-on is deliberately absent — its Aa preview must not
   be bolded, since weight is part of what it is previewing. */
.pick-on,
.hnav a.is-on,
/* --accent-strong, not --accent: on dark's tint --accent is 4.40:1, sub-AA for
   every selected pill and chosen settings row. Light is unaffected — the
   token aliases --accent there, at 4.70:1. */
.st-opt.is-on { background-color: var(--accent-tint); color: var(--accent-strong); font-weight: 600; }

/* Index tabs (/docs and the landing catalog). The active tab is a 2px red
   edge on the tab row's rule — the same mark the method spine uses for its
   current row — not a tinted pill: on an index every pill reads as a button,
   and a tab is a place. One .is-on class, flipped by the doc-categories
   controller, so no theme-remapped utility can out-specify it. */
.dx-tabs { display: flex; align-items: flex-end; gap: 4px; border-bottom: 1px solid var(--rule); }
.dx-tab { padding: 8px 12px; margin-bottom: -1px; border-bottom: 2px solid transparent;
          font-size: 14px; font-weight: 500; color: var(--muted); cursor: pointer; white-space: nowrap;
          transition: color .15s ease, border-color .15s ease; }
.dx-tab:hover { color: var(--ink); }
.dx-tab:focus-visible { outline: 2px solid var(--accent-fill); outline-offset: -2px; }
.dx-tab.is-on { color: var(--ink); font-weight: 700; border-bottom-color: var(--accent-fill); }
@media (prefers-reduced-motion: reduce) { .dx-tab { transition: none; } }

/* ===== Docs navigation: a link has to look like one before it is hovered =====
   Measured 2026-10-09, before this block: an index row's name was ink and
   700, the same as the corpus figures beside it that are not links; a rail
   row was --ink-soft, the same as body prose; and the only cue on either was
   a colour change on hover, which a phone never fires. The rail also sat on
   the bare page with no ground, so the one region made of nothing but links
   was the one that read as background.

   --wash is the hover ground for any row. Mixed from --ink, not a ramp step,
   so one value works on --paper and --surface in both modes: --sunk is DARKER
   than --surface in dark, so a --sunk hover sank the row instead of lifting
   it. Declared on :root, the element html.dark retargets --ink on, so the mix
   follows the mode. */
:root { --wash: color-mix(in srgb, var(--ink) 7%, transparent); }

/* The docs rail is a panel at lg, like the content card beside it. Below lg
   the same element is the bottom sheet, which already has its own ground.
   box-shadow, not --tw-shadow: html:not(.dark) .shadow-2xl (the sheet's
   shadow) sets --tw-shadow at (0,2,1) and would win over a utility here.
   The sheet's border goes with lg:border-0 at the call site, since the
   border colour remaps are (0,2,1) too. */
@media (min-width: 1024px) {
  .rail-card { box-shadow: var(--lift); border-radius: var(--r-panel); }
}

/* A row in the rail (sections and methods). Horizontal padding only, so the
   call site's py-* still sets the touch height. */
.rail-link { display: block; padding-inline: 10px; border-radius: var(--r-control);
             color: var(--ink-soft); transition: background-color .12s ease, color .12s ease; }
.rail-link:focus-visible { outline: 2px solid var(--accent-fill); outline-offset: -2px; }
/* Selected row: tint ground, ink, bold, and a red edge at the start. Ink, not
   accent: red is punctuation, never a word. An inset shadow, not a border, so
   selection never moves the text. Shared with method_spine_controller and
   doc_toc_controller, which only toggle this class. Compound, so its :hover
   form outranks the hover wash below. */
.rail-link.rail-on, .rail-link.rail-on:hover { background-color: var(--accent-tint); color: var(--ink); font-weight: 700;
                                               box-shadow: inset 2px 0 0 var(--accent-fill); }

/* One /docs index row: the whole row is the link. The name takes the link
   colour at rest; the hover wash bleeds 10px past the column on a pseudo-
   element, so the name stays on the group heading's left edge. */
.ix-row { position: relative; isolation: isolate; }
.ix-row::before { content: ""; position: absolute; inset: 0 -10px; z-index: -1;
                  border-radius: var(--r-control); transition: background-color .12s ease; }
.ix-row:focus-visible { outline: none; }
.ix-row:focus-visible::before { outline: 2px solid var(--accent-fill); }
.ix-name { color: var(--accent); }

/* A link inside a line of chrome (other versions, a class's parent and
   mixins): the prose link of docs_prose.css, so colour is not the only cue. */
.text-link { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.text-link:hover { text-decoration-thickness: 2px; }

/* A secondary control (Show source, type signatures): a bordered button. It
   was --faint 12px text over a dotted rule, which read as a footnote. */
.btn-quiet { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px;
             border: 1px solid var(--rule-strong); border-radius: var(--r-control);
             font-family: var(--font-mono); font-size: 12px; color: var(--ink-soft);
             transition: background-color .12s ease, color .12s ease, border-color .12s ease; }
.btn-quiet:focus-visible { outline: 2px solid var(--accent-fill); outline-offset: 2px; }
/* Hover only where a pointer can hover: a phone keeps :hover on the last
   row it tapped, so the wash stuck on a row the reader had left. */
@media (hover: hover) {
  .rail-link:hover { background-color: var(--wash); color: var(--ink); }
  .ix-row:hover::before { background-color: var(--wash); }
  .ix-row:hover .ix-name { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px; }
  .btn-quiet:hover { background-color: var(--wash); color: var(--ink); }
}
@media (prefers-reduced-motion: reduce) {
  .rail-link, .ix-row::before, .btn-quiet { transition: none; }
}

/* ===== Light: Signature paper =====
   The ground is flat pigment. What it replaced was a fixed-attachment
   diagonal gradient whose light stops were 100% saturated in HSL — a light
   source painted into a document, which read as a landing page on every
   docs route. The four layout wrappers used to carry the gradient utilities
   and this file overrode them; both sides are gone, and the wrappers now
   name the token directly. */
body {
  background-color: var(--paper); color: var(--ink);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}
code, kbd, pre, samp, .font-mono { font-family: var(--font-mono); }
/* Ligatures off: JetBrains Mono draws `=>` as one ⇒ glyph and `#{` as one
   joined glyph, so a sample stops showing the characters you would type. A
   documentation site cannot silently redraw its own source. The playground
   editor sets its own, in playground.css. */
code, kbd, pre, samp, .font-mono { font-variant-ligatures: none; }

html:not(.dark) .bg-white      { background-color: var(--surface); }
html:not(.dark) .bg-gray-50    { background-color: var(--sunk); }
html:not(.dark) .bg-gray-100   { background-color: var(--sunk-2); }
html:not(.dark) .bg-gray-200   { background-color: var(--sunk-3); }

/* Tinted brand surfaces */
html:not(.dark) .bg-red-50     { background-color: rgba(225,29,72,.06); }
/* Grouped, not a second copy: the hover twin carries the same value, and
   spelling it twice is how the neighbouring .text-red-100 drifted once already.
   Grouping keeps each selector's own specificity. */
html:not(.dark) .bg-red-100,
html:not(.dark) .hover\:bg-red-100:hover { background-color: rgba(225,29,72,.12); }
html:not(.dark) .bg-blue-50    { background-color: #e8eef9; }
html:not(.dark) .bg-blue-100   { background-color: #d4def2; }
html:not(.dark) .bg-green-50   { background-color: #e6f1e3; }
html:not(.dark) .bg-green-100  { background-color: #cce4c8; }
html:not(.dark) .bg-yellow-100 { background-color: #f3e2b3; }
html:not(.dark) .bg-purple-100 { background-color: #e6d8f0; }
html:not(.dark) .bg-orange-50  { background-color: #faebd9; }
html:not(.dark) .bg-orange-100 { background-color: #f4d8b8; }
/* Warm ink, not Tailwind's #111827 — a blue-grey near-black on warm paper. */
html:not(.dark) .bg-gray-900   { background-color: var(--ink); }

/* Text ramp, warmed to the paper and re-spaced for AA (see :root) */
html:not(.dark) .text-gray-900 { color: var(--ink); }
html:not(.dark) .text-gray-800 { color: #2E1F23; }
html:not(.dark) .text-gray-700 { color: var(--ink-soft); }
html:not(.dark) .text-gray-600 { color: var(--muted); }
html:not(.dark) .text-gray-500 { color: var(--faint); }
/* text-gray-400 is text in ~90 views (footer headings, counts), so it sits on
   the text floor. On --ghost it measured 3.00:1 (axe, 2026-09-25). */
html:not(.dark) .text-gray-400 { color: var(--faint); }

html:not(.dark) .text-red-600  { color: var(--accent-fill); }
html:not(.dark) .text-red-700  { color: var(--accent); }
/* Paired with the remapped .bg-green-100: Tailwind's green-700 on it is 3.70:1
   (the "remote only" badge); #166534 is 5.34:1. */
html:not(.dark) .text-green-700 { color: #166534; }
/* Value-identical to --accent-tint and written out here as a second copy,
   which is how it drifted out of step with the token once already. */
html:not(.dark) .text-red-100  { color: var(--accent-tint); }

html:not(.dark) .border-gray-100   { border-color: var(--rule); }
html:not(.dark) .border-gray-200   { border-color: var(--rule-strong); }
html:not(.dark) .border-gray-300   { border-color: #B89EA6; }
html:not(.dark) .border-red-100    { border-color: rgba(225,29,72,.18); }
html:not(.dark) .border-blue-200   { border-color: #c8d4ec; }
html:not(.dark) .border-orange-200 { border-color: #ecc89e; }

html:not(.dark) .hover\:bg-gray-50:hover  { background-color: var(--sunk); }
html:not(.dark) .hover\:bg-gray-100:hover { background-color: var(--sunk-2); }
html:not(.dark) .hover\:bg-gray-200:hover { background-color: var(--sunk-3); }

/* ===== Dark: Signature near-neutral =====
   Dark is deliberately near-neutral. Oxblood's pigmented ground was kept on
   the light side and dropped here: the same chroma that reads as paper
   stock in reflected light reads as a stain on a lit screen. The identity
   is carried by the mark, not the ground, so dropping it costs nothing. */
html.dark {
  color-scheme: dark;
  --paper: #15141B;
  --surface: #201F29;
  --sunk: #1A1922;
  /* Dark has no separate values for the two deeper steps — the ground range is
     narrower here, and the elevation cue is carried by --surface. Aliased
     rather than left undefined so the tokens mean something in both modes. */
  --sunk-2: var(--sunk);
  --sunk-3: var(--rule);
  --rule: #35333F;
  --rule-strong: #45424F;
  --ink: #F2EFF4;
  --ink-soft: #C6C1D0;
  --muted: #AEA8B8;        /* 7.92:1 on paper */
  --faint: #8B8696;        /* 5.18:1 paper / 4.61 surface — text floor */
  --ghost: #6E6979;
  --accent: #FF5C79;
  --accent-fill: #FF5C79;
  --accent-tint: rgba(255, 92, 121, 0.16);
  /* Both invert here. The accent is a light pink in dark, so it needs a lighter
     step above it for text on the tint (5.92:1) and ink rather than white as a
     label on the fill (5.79:1, where white is 2.99:1). */
  --accent-strong: #FF8DA1;
  --on-accent: var(--paper);
  /* Dark carries no pigment to grey, so the lift is plain black and has to be
     deeper: on a #15141B ground a .22 ambient shadow is invisible. */
  --lift: 0 1px 2px rgba(0, 0, 0, .40), 0 10px 24px -14px rgba(0, 0, 0, .65);
  --lift-hover: 0 2px 6px rgba(0, 0, 0, .45), 0 18px 40px -18px rgba(0, 0, 0, .75);
}
/* The dark ground lost a red radial glow at 0.55 alpha over the top third.
   It was the loudest element on every page, and it was also the cause of
   the dark-mode flash on Turbo navigation: its percentage stops re-anchored
   to each swapped wrapper, so moving between a tall scrolled page and a
   short one made the red pop in and out. */
html.dark body { background-color: var(--paper); color: var(--ink); }

html.dark .bg-white       { background-color: var(--surface); }
html.dark .bg-gray-50     { background-color: var(--sunk); }
html.dark .bg-gray-100    { background-color: var(--sunk-2); }
html.dark .bg-gray-200    { background-color: var(--sunk-3); }
/* Cards are `bg-white dark:bg-gray-900`, so in dark this is the elevated
   surface, not the page ground — the value step is the only elevation cue
   left now that the shadows are gone. */
html.dark .bg-gray-900    { background-color: var(--surface); }

html.dark .bg-red-50      { background-color: rgba(255, 77, 109, 0.10); }
html.dark .bg-red-100     { background-color: rgba(255, 77, 109, 0.18); }
html.dark .bg-blue-50     { background-color: rgba(96, 165, 250, 0.08); }
html.dark .bg-blue-100    { background-color: rgba(96, 165, 250, 0.16); }
html.dark .bg-green-50    { background-color: rgba(74, 222, 128, 0.08); }
html.dark .bg-green-100   { background-color: rgba(74, 222, 128, 0.16); }
html.dark .bg-yellow-100  { background-color: rgba(250, 204, 21, 0.16); }
html.dark .bg-purple-100  { background-color: rgba(192, 132, 252, 0.16); }
html.dark .bg-orange-50   { background-color: rgba(251, 146, 60, 0.08); }
html.dark .bg-orange-100  { background-color: rgba(251, 146, 60, 0.16); }

html.dark .text-gray-900  { color: var(--ink); }
html.dark .text-gray-800  { color: #DEDAE4; }
html.dark .text-gray-700  { color: var(--ink-soft); }
html.dark .text-gray-600  { color: var(--muted); }
html.dark .text-gray-500  { color: var(--faint); }
html.dark .text-gray-400  { color: var(--faint); } /* see the light rule */
html.dark .text-gray-300  { color: #565161; }

html.dark .text-red-600   { color: var(--accent-fill); }
html.dark .text-red-700   { color: var(--accent-strong); }
html.dark .text-red-100   { color: #FFD8DF; }
html.dark .text-blue-600  { color: #60a5fa; }
html.dark .text-blue-700  { color: #93c5fd; }
html.dark .text-green-600 { color: #4ade80; }
html.dark .text-green-700 { color: #86efac; }
html.dark .text-yellow-700{ color: #fde047; }
html.dark .text-purple-700{ color: #d8b4fe; }
html.dark .text-orange-600{ color: #fb923c; }
html.dark .text-orange-700{ color: #fdba74; }

html.dark .border-gray-100  { border-color: var(--rule); }
html.dark .border-gray-200  { border-color: var(--rule-strong); }
html.dark .border-gray-300  { border-color: #55525F; }
html.dark .border-gray-800  { border-color: #2A2833; }
html.dark .border-red-100   { border-color: rgba(255, 77, 109, 0.22); }
html.dark .border-blue-200  { border-color: rgba(96, 165, 250, 0.28); }
html.dark .border-orange-200{ border-color: rgba(251, 146, 60, 0.28); }

html.dark .hover\:bg-gray-50:hover  { background-color: var(--sunk); }
html.dark .hover\:bg-gray-100:hover { background-color: var(--rule); }
html.dark .hover\:bg-gray-200:hover { background-color: var(--rule-strong); }
html.dark .hover\:text-gray-600:hover { color: var(--ink-soft); }
html.dark .hover\:text-gray-900:hover { color: var(--ink); }

html.dark kbd { background-color: var(--surface) !important; border-color: var(--rule) !important; color: var(--ink) !important; }
html.dark input { color: var(--ink); caret-color: var(--accent-fill); }
html.dark input::placeholder { color: var(--ghost); }
html.dark select option { background-color: var(--surface); color: var(--ink); }

html.dark .bg-black.bg-opacity-50 { background-color: rgba(0,0,0,0.75) !important; }

/* Filled and hovered accent. .text-red-600 read --accent-fill and .bg-red-600
   did not, so every filled button painted Tailwind's #dc2626 beside marks
   painting #E11D48 — and the red-* hover variants are separate class names, so
   they escaped the remaps of their base utilities the same way. All of it goes
   through the tokens, which already flip by mode: "darker on hover" is only
   darker on paper, and a white label only works on the light fill. */
.bg-red-600 { background-color: var(--accent-fill); }
.bg-red-600.text-white { color: var(--on-accent); }
.hover\:bg-red-700:hover,
.group:hover .group-hover\:bg-red-700 { background-color: var(--accent-strong); }
.hover\:text-red-700:hover { color: var(--accent-strong); }
/* 50 call sites, and it had no remap: hover painted Tailwind's #DC2626,
   3.79:1 on the dark page. --accent is the link token, AA in both modes.
   `html` lifts both rules to (0,2,1), level with the text-gray-* and
   border-gray-* remaps above, and later in the file: at (0,2,0) a link that
   also carried text-gray-500 kept its gray on hover. */
html .hover\:text-red-600:hover { color: var(--accent); }
html .hover\:border-red-500:hover, html .hover\:border-red-600:hover { border-color: var(--accent-fill); }
.group:hover .group-hover\:text-red-600 { color: var(--accent-fill); }
html:not(.dark) .hover\:bg-red-100:hover { background-color: rgba(225,29,72,.12); }

/* Tailwind's ring-red-500 is a third red (#ef4444). --tw-ring-color is the only
   hook the utility exposes; one rule serves both modes since the token flips. */
.focus\:ring-red-500:focus,
.focus-within\:ring-red-500:focus-within { --tw-ring-color: var(--accent-fill); }

/* Elevation.
   Shadows were removed outright here, on the argument that paper -> surface is
   already a value step and a neutral shadow greys pigmented paper. The second
   half of that was right and the first half was not: with every card outlined
   in --rule and nothing lifted, the whole site rendered as 179 hairline boxes
   against 3 shadows — a wireframe, which is what "cold and sharp" turned out
   to mean when it was measured rather than argued about.

   So elevation is back, but as an ALTERNATIVE to the outline, never alongside
   it: an element that asks for a shadow gives up its border colour in the same
   rule. Two ways to say "this is a distinct surface" applied at once is the
   duplication the original note was really objecting to. The border stays in
   the box model at 1px, so nothing reflows.

   .shadow-sm is deliberately excluded and stays flat: its only callers are
   admin buttons, and a lifted button on a flat page is a costume.

   shadow-2xl keeps its own value: the mobile method sheet floats over the page
   rather than sitting on it, so its shadow is a z-order cue, not elevation.
   Declared after tailwind.css at equal specificity, which is what lets these
   win.

   Corners used to be neutralised here too — .rounded-xl/.rounded-2xl forced to
   0, plus a second rule inside a min-width query to catch lg:rounded-xl. Both
   are gone: the utilities were swept out of the templates instead, so nothing
   needs cancelling. That also closed the hole they left, since the rule only
   ever named two utilities while 38 rounded-lg and 27 rounded uses carried on
   at Tailwind's own scale.

   These set --tw-shadow, not box-shadow: Tailwind composes box-shadow from
   ring-offset + ring + shadow, so a flat box-shadow here deletes the ring from
   the chain — overriding only the shadow slot is what lets a job card carry
   both a lift and a focus-within ring. `none` is not a legal list item. */
.shadow-sm { --tw-shadow: 0 0 #0000; }
.shadow-md, .shadow-lg, .shadow-xl { --tw-shadow: var(--lift); }
/* Its own selector, so the rule above never reached it. Must stay inside the
   query it is emitted from, or it applies at every width. */
@media (min-width: 1024px) { .lg\:shadow-md { --tw-shadow: var(--lift); } }
.hover\:shadow-xl:hover { --tw-shadow: var(--lift-hover); }
/* Mode-qualified so it can out-specify the .border-gray-100/-200 remaps above,
   which carry html:not(.dark) and would otherwise repaint the outline this
   rule is here to retire. Same specificity, later in the file. */
html:not(.dark) :is(.shadow-md, .shadow-lg, .shadow-xl),
html.dark :is(.shadow-md, .shadow-lg, .shadow-xl) { border-color: transparent; }
html:not(.dark) .shadow-2xl { --tw-shadow: 0 24px 42px -10px rgba(30, 16, 19, 0.22); }

/* Ink plaques — the company monogram and the method page's mobile sheet button.
   Components, not `bg-gray-900 dark:bg-*`: html.dark .bg-gray-900 (0,2,1) beats
   Tailwind's dark variant (0,2,0), so the dark class lost and the plaque painted
   --surface (the card behind the monogram) while the sheet button's unremapped
   dark:text-gray-900 left #111827 on it — 1.08:1, invisible. Reading --ink and
   --paper inverts on its own, so .ink-pill needs no dark rule at all. The
   monogram takes one because on a card it needs separation, not maximum
   contrast: white on --ghost is 5.21:1, and --paper on it would be 3.5:1. */
.ink-pill,
.monogram { background-color: var(--ink); color: var(--paper); }
html.dark .monogram { background-color: var(--ghost); color: #fff; }

/* Job tag chip — the geometry, so a call site adds only its colour pair. It was
   spelled out at nine sites across the board, the detail page, the posting
   preview and the JS that rebuilds that preview, which is how the board ended
   up at 11px/--muted while the preview of the board stayed 10px/--faint. */
.tag { font-family: var(--font-mono); font-size: 11px; padding: 2px 8px;
       border-radius: var(--r-control); }

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 32px: the header's settings gear (this class's only consumer) sits on
     the same 32px control line as the search chip and version pill. */
  width: 2rem;
  height: 2rem;
  border-radius: var(--r-control);
  background-color: var(--sunk);
  color: var(--ink-soft);
  transition: background-color 150ms ease, color 150ms ease, transform 150ms ease;
}
.theme-toggle:hover { background-color: var(--rule); transform: translateY(-1px); }
html.dark .theme-toggle { background-color: var(--sunk); color: var(--ink); }
html.dark .theme-toggle:hover { background-color: var(--rule); }

