/* tool.css — shared tool-page styles.
   Pages set --accent / --accent-dim to their area colour; page-local <style>
   loaded after this sheet can still override. Prefer var(--accent) here so
   amber/green/indigo/etc. all work without per-page copies of the chrome. */

*{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; }
svg{ user-select:none; -webkit-user-select:none; -moz-user-select:none; }
body{
  margin:0;
  background:
    radial-gradient(1200px 600px at 78% -10%, var(--bg-rad-1) 0%, transparent 55%),
    radial-gradient(900px 500px at 0% 0%, var(--bg-rad-2) 0%, transparent 50%),
    var(--ink);
  color:var(--text);
  font-family:'Inter',system-ui,sans-serif;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
.wrap{ max-width:1180px; margin:0 auto; padding:26px 20px 64px; }
@media (max-width:560px){
  .wrap{ padding:18px 14px 48px; }
  header.mast{ align-items:flex-start; }
  .mast .right{ text-align:left; width:100%; }
  .refbar{ margin:18px 0 14px; font-size:11px; }
  section.block{ margin-top:48px; }
  .grid{ gap:var(--sp-4); }
  .panel{ padding:14px; border-radius:var(--r-panel); }
  .hero{ padding:12px; border-radius:var(--r-panel); }
  .playbar, .controls{ gap:var(--sp-3); margin-top:10px; }
  .playbtn{ padding:10px 14px; min-height:44px; } /* touch target */
  .field{ margin-bottom:12px; }
  .ro{ padding:11px 12px; }
  .ro .v{ font-size:clamp(14px,4.2vw,18px); }
  /* simulators: keep the viz legible, avoid horizontal page scroll */
  .hero svg, .panel svg, #anim, #springViz, #forceChart, #step,
  #dLoad, #dShear, #dMoment, #dDefl, svg[id$="Svg"], svg[id$="Chart"]{
    max-width:100%; height:auto;
  }
}
@media (max-width:400px){
  .ro-grid,
  .ro-grid.primary,
  .ro-grid.secondary{ grid-template-columns:1fr 1fr; gap:var(--sp-3); }
  .seg{ grid-template-columns:1fr 1fr; }
  .seg.seg3{ grid-template-columns:1fr 1fr 1fr; }
}

/* ---- chrome: crumb, refbar, mast ---- */
nav.crumb{
  display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
  padding-bottom:13px; margin-bottom:7px;
  font-family:'IBM Plex Mono',monospace; font-size:11.5px;
}
nav.crumb a{ color:var(--muted); text-decoration:none; }
nav.crumb a:hover{ color:var(--text); }
nav.crumb .sep{ color:var(--muted-2); }
nav.crumb .here{ color:var(--accent); font-weight:600; letter-spacing:.03em; }
.refbar{
  display:flex; align-items:center; gap:var(--sp-3); padding:9px 13px; margin:28px 0 18px;
  background:linear-gradient(90deg,var(--banner-a),var(--banner-b));
  border:1px solid var(--line-soft); border-radius:var(--r-card);
  font-size:11.5px; color:var(--muted-2);
}
.refbar b{ color:var(--muted); font-weight:600; }
.refbar svg{ width:14px; height:14px; flex:none; color:var(--amber); }
header.mast{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:var(--sp-6); flex-wrap:wrap; padding-bottom:18px;
  border-bottom:1px solid var(--line-soft); margin-bottom:20px;
}
.brand{ display:flex; align-items:center; gap:var(--sp-4); }
.glyph{ width:42px; height:42px; flex:none; }
.mast h1{
  font-family:'Space Grotesk',sans-serif; font-weight:700;
  font-size:clamp(22px,3vw,30px); letter-spacing:-.02em; margin:0; line-height:1;
}
.mast .sub{ font-size:12.5px; color:var(--muted); margin-top:5px; }
.mast .kicker{
  font-size:10px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--accent); font-weight:600; font-family:'IBM Plex Mono',monospace;
}
.mast .right{ font-family:'IBM Plex Mono',monospace; font-size:11px; color:var(--muted-2); text-align:right; }
.mast .right b{ color:var(--text); font-weight:600; }

/* ---- layout grid (sidebar + main) ---- */
.grid{ display:grid; grid-template-columns:300px 1fr; gap:var(--sp-5); align-items:start; }
@media (max-width:960px){ .grid{ grid-template-columns:1fr; } }
/* CSS Grid blowout: nested content must be allowed to shrink below content min-size */
.grid > *{ min-width:0; }

/* THE CONTROL RAIL IS A PLAIN COLUMN, AND IT STAYS THAT WAY.
   It was briefly sticky with a viewport height cap and `overflow-y:auto`, to stop a short left
   column leaving a long empty gutter beside a page of charts. That traded a cosmetic complaint
   for a real one. On 34 of the 93 two-column tool pages the rail is TALLER than the viewport, so
   the cap turned it into a scroll region nested inside a scrolling page and put its own controls
   out of reach: measured at 1440x900, rocket-equation hid 796 px of them, room-visualiser 752,
   data-transfer-time 667.

   A scrollbar inside a scrolling page is the wrong answer whatever it is hiding. It swallows the
   wheel when the pointer crosses it, it puts two scrollbars on screen that mean different
   things, and it hides content behind an affordance nobody is looking for. An empty gutter is
   what a short panel beside a long column of charts looks like on every calculator ever built,
   and it costs the reader nothing.

   `align-items:start` on .grid above already gives the rail its natural height. Do not add
   max-height, overflow or position:sticky here - build-registry.py check 43 fails the build if
   any of them come back. */

/* ---- panels ---- */
.panel{
  background:linear-gradient(180deg,var(--panel),var(--ink-2));
  border:1px solid var(--line); border-radius:var(--r-panel); padding:18px;
}
.panel + .panel{ margin-top:14px; }
.panel .head{ display:flex; align-items:center; gap:var(--sp-3); margin-bottom:14px; }
.panel .head .dot{
  width:8px; height:8px; border-radius:50%;
  background:var(--accent); box-shadow:0 0 10px var(--accent);
}
.panel .head h2{ font-family:'Space Grotesk',sans-serif; font-size:15px; font-weight:600; margin:0; }
.panel .head .tag{
  margin-left:auto; font-size:9.5px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-2); font-family:'IBM Plex Mono',monospace;
}

/* ---- fields, selects, ranges ---- */
.field{ margin-bottom:14px; }
.field:last-child{ margin-bottom:0; }
.field > label{
  display:flex; justify-content:space-between;
  font-size:10.5px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600; margin-bottom:7px;
}
.field > label .vv{
  font-family:'IBM Plex Mono',monospace; font-size:12px; color:var(--text);
  text-transform:none; letter-spacing:0; font-weight:600;
}
.field > label .term{ color:var(--accent); font-family:'IBM Plex Mono',monospace; }
/* A second unit alongside the first — "8.8 oz (250 g)". Quieter than the primary reading so the
   eye lands on one number, but present so nobody has to convert. */
.unit-alt{ color:var(--muted-2); font-weight:500; }

select{
  width:100%; background:var(--ink); border:1px solid var(--line); border-radius:var(--r-ctl);
  color:var(--text); font-family:'IBM Plex Mono',monospace; font-size:12.5px;
  padding:9px 9px; outline:none; cursor:pointer;
}
select:focus{
  border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent);
}

.slider{ display:flex; align-items:center; gap:var(--sp-4); }
.slider .val{
  font-family:'IBM Plex Mono',monospace; font-size:12.5px; color:var(--text);
  min-width:54px; text-align:right;
}

/* Range fill: --fill is 0%–100% of value (set by rot.js). Paint with background-size so
   the coloured bar is exactly as wide as the value fraction — no dual track layers that
   can drift from the thumb. Firefox uses native ::-moz-range-progress instead. */
input[type="range"]{
  -webkit-appearance:none; appearance:none; width:100%; height:4px; border-radius:var(--r-mark);
  background-color:var(--line);
  background-image:linear-gradient(var(--c,var(--accent)), var(--c,var(--accent)));
  background-size:var(--fill,0%) 100%;
  background-repeat:no-repeat;
  background-position:left center;
  outline:none; flex:1; cursor:pointer;
}
input[type="range"]::-webkit-slider-runnable-track{
  height:4px; border-radius:var(--r-mark); background:transparent; /* fill lives on the input itself */
}
input[type="range"]::-webkit-slider-thumb{
  -webkit-appearance:none; width:17px; height:17px; border-radius:50%;
  background:var(--c,var(--accent)); border:2px solid var(--ink);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--c,var(--accent)) 28%,transparent);
  cursor:pointer; margin-top:-6.5px; /* centre 17px thumb on 4px track */
  position:relative; z-index:1;
}
/* Firefox: native progress follows the thumb exactly — don't also paint input fill */
input[type="range"]::-moz-range-track{
  height:4px; border-radius:var(--r-mark); border:none;
  background:var(--line);
}
input[type="range"]::-moz-range-progress{
  height:4px; border-radius:var(--r-mark); border:none;
  background:var(--c,var(--accent));
}
input[type="range"]::-moz-range-thumb{
  width:15px; height:15px; border-radius:50%;
  background:var(--c,var(--accent)); border:2px solid var(--ink); cursor:pointer;
  box-shadow:0 0 0 3px color-mix(in srgb,var(--c,var(--accent)) 28%,transparent);
}
@supports (-moz-appearance:none){
  input[type="range"]{
    background-color:transparent;
    background-image:none;
  }
}

/* ---- presets / segment toggles ---- */
.presets{ display:flex; flex-wrap:wrap; gap:var(--sp-2); }
.presets.grid2{ display:grid; grid-template-columns:repeat(2,1fr); }
.presets button{
  font-family:'IBM Plex Mono',monospace; font-size:11px; color:var(--muted);
  background:var(--ink); border:1px solid var(--line); border-radius:var(--r-ctl);
  padding:7px 10px; cursor:pointer;
}
.presets button:hover{ border-color:var(--accent); color:var(--text); }
.presets button.on{
  color:var(--ink); background:var(--accent); border-color:var(--accent); font-weight:600;
}

.seg{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--sp-2); }
.seg.seg2{ grid-template-columns:repeat(2,1fr); }
.seg.seg4{ grid-template-columns:repeat(4,1fr); }
.seg button{
  font-family:'IBM Plex Mono',monospace; font-size:12px; color:var(--muted);
  background:var(--ink); border:1px solid var(--line); border-radius:var(--r-ctl);
  padding:9px 4px; cursor:pointer;
}
.seg button.on{
  color:var(--ink); background:var(--accent); border-color:var(--accent); font-weight:600;
}
.seg button:hover{ border-color:var(--accent); color:var(--text); }

/* ---- hero panel (main viz card) ---- */
.hero{
  background:linear-gradient(180deg,var(--panel-2),var(--ink-2));
  border:1px solid var(--line); border-radius:var(--r-panel); padding:16px;
}
.hero-head{ display:flex; align-items:center; gap:var(--sp-3); margin-bottom:8px; flex-wrap:wrap; }
.hero-head h2{ font-family:'Space Grotesk',sans-serif; font-size:15px; font-weight:600; margin:0; }
.hero-head .meta{ margin-left:auto; font-family:'IBM Plex Mono',monospace; font-size:11px; color:var(--muted-2); }

/* ---- play / transport controls ---- */
.playbar, .controls{
  display:flex; align-items:center; gap:var(--sp-3); margin-top:12px; flex-wrap:wrap;
}
.playbtn{
  display:inline-flex; align-items:center; gap:var(--sp-2); justify-content:center;
  font-family:'Inter',sans-serif; font-size:13px; font-weight:500;
  color:var(--ink); background:var(--accent); border:none; border-radius:var(--r-ctl);
  padding:9px 16px; cursor:pointer; min-width:88px;
}
.playbtn.ghost{
  color:var(--accent); background:var(--ink); border:1px solid var(--accent); min-width:auto;
}
.playbtn svg{ width:13px; height:13px; }
.speed{
  display:flex; align-items:center; gap:var(--sp-2); margin-left:auto;
  font-family:'IBM Plex Mono',monospace; font-size:11px; color:var(--muted-2);
}
.speed select{
  width:auto; background:var(--ink); border:1px solid var(--line); border-radius:var(--r-ctl);
  color:var(--text); font-family:'IBM Plex Mono',monospace; font-size:11.5px; padding:5px 8px;
}

/* ---- status pills ---- */
.warn-pill, .verdict{
  font-family:'IBM Plex Mono',monospace; font-size:11px; font-weight:600;
  padding:3px 10px; border-radius:var(--r-hero); border:1px solid currentColor;
}

/* ---- result readouts ---- */
.readouts{ margin-top:16px; }
.ro-label{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600; margin:2px 0 9px;
}
.ro-grid{ display:grid; grid-template-columns:repeat(4,1fr); gap:var(--sp-4); }
.ro-grid.primary{ display:grid; grid-template-columns:repeat(4,1fr); gap:var(--sp-4); margin-bottom:14px; }
.ro-grid.secondary{ display:grid; grid-template-columns:repeat(4,1fr); gap:var(--sp-3); }
@media (max-width:760px){
  .ro-grid,
  .ro-grid.primary,
  .ro-grid.secondary{ grid-template-columns:repeat(2,1fr); }
}
.ro{
  background:linear-gradient(180deg,var(--panel),var(--ink-2));
  border:1px solid var(--line); border-radius:var(--r-card); padding:13px 14px;
  position:relative; overflow:hidden; min-width:0;
}
/* Long unbreakable values must wrap inside grid tracks */
.ro-grid .ro .v{ overflow-wrap:anywhere; }
.ro.sm{ padding:11px 12px; }
.ro .k{
  font-size:10px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600;
}
.ro.sm .k{ font-size:9px; }
.ro .v{
  font-family:'IBM Plex Mono',monospace; font-weight:600; margin-top:7px;
  font-size:clamp(15px,2vw,20px); letter-spacing:-.01em; line-height:1; color:var(--text);
}
.ro.sm .v{ font-size:clamp(13px,1.8vw,16px); margin-top:6px; }
.ro .u{ font-size:12px; color:var(--muted); font-weight:500; margin-left:3px; }
.ro.sm .u{ font-size:10px; }
.ro .alt{
  font-family:'IBM Plex Mono',monospace; font-size:10.5px; color:var(--muted-2); margin-top:6px;
}
.ro.sm .alt{ font-size:9.5px; margin-top:4px; }

/* Lead accent bar on readouts */
.ro.lead:before{
  content:""; position:absolute; left:0; top:0; bottom:0; width:3px; opacity:.95;
}
.ro.am:before{ background:var(--amber); box-shadow:0 0 12px var(--amber); }
.ro.rd:before{ background:var(--red); box-shadow:0 0 12px var(--red); }
.ro.cy:before{ background:var(--cyan); box-shadow:0 0 12px var(--cyan); }
.ro.gr:before{ background:var(--green); box-shadow:0 0 12px var(--green); }
.ro.vi:before{ background:var(--violet); box-shadow:0 0 12px var(--violet); }
.ro.in:before{ background:var(--indigo); box-shadow:0 0 12px var(--indigo); }
.ro.pk:before{ background:var(--pink); box-shadow:0 0 12px var(--pink); }
.ro.tl:before{ background:var(--teal); box-shadow:0 0 12px var(--teal); }
.ro.cr:before{ background:var(--coral); box-shadow:0 0 12px var(--coral); }
.ro.lm:before{ background:var(--lime); box-shadow:0 0 12px var(--lime); }
.ro.az:before{ background:var(--azure); box-shadow:0 0 12px var(--azure); }

.ro.am .v{ color:var(--amber); }
.ro.rd .v{ color:var(--red); }
.ro.cy .v{ color:var(--cyan); }
.ro.gr .v{ color:var(--green); }
.ro.vi .v{ color:var(--violet); }
.ro.in .v{ color:var(--indigo); }
.ro.pk .v{ color:var(--pink); }
.ro.tl .v{ color:var(--teal); }
.ro.cr .v{ color:var(--coral); }
.ro.lm .v{ color:var(--lime); }
.ro.az .v{ color:var(--azure); }

/* ---- content blocks / glossary ---- */
section.block{ margin-top:76px; }
.rhead{ display:flex; align-items:baseline; gap:var(--sp-4); margin-bottom:24px; flex-wrap:wrap; }
.rhead .ey{
  font-size:10px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--accent); font-weight:600; font-family:'IBM Plex Mono',monospace;
}
.rhead h2{
  font-family:'Space Grotesk',sans-serif; font-size:19px; font-weight:600;
  margin:0; letter-spacing:-.01em;
}
.rhead .hint{ margin-left:auto; font-size:11.5px; color:var(--muted-2); font-style:italic; }
.gloss{ display:grid; grid-template-columns:repeat(2,1fr); gap:var(--sp-4); }
@media (max-width:760px){ .gloss{ grid-template-columns:1fr; } }
.gcard{
  background:linear-gradient(180deg,var(--panel),var(--ink-2));
  border:1px solid var(--line); border-radius:var(--r-card); padding:15px 17px;
}
.gcard h3{ font-family:'Space Grotesk',sans-serif; font-weight:600; font-size:14px; margin:0 0 6px; }
.gcard p{ font-size:12.5px; color:var(--muted); line-height:1.6; margin:0; }
.gcard b{ color:var(--text); font-weight:600; }
.gcard code{
  font-family:'IBM Plex Mono',monospace; font-size:11.5px; color:var(--accent);
  background:var(--ink); padding:1px 5px; border-radius:var(--r-chip);
}
.gcard .fx{
  font-family:'IBM Plex Mono',monospace; font-size:12px; color:var(--accent);
  background:var(--ink); border:1px solid var(--line-soft); border-radius:var(--r-ctl);
  padding:7px 10px; margin:8px 0; display:inline-block;
}

footer{
  margin-top:34px; padding-top:18px; border-top:1px solid var(--line-soft);
  font-size:11.5px; color:var(--muted-2); line-height:1.6;
}
:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }


/* ---- Quick scenarios — one-tap setups (same chrome as .presets) ---- */
.scenario-demos{ margin:0 0 14px; }
.scenario-demos .sc-label{
  font-family:'IBM Plex Mono',monospace; font-size:10px; font-weight:600;
  letter-spacing:.06em; text-transform:uppercase; color:var(--muted-2);
  margin:0 0 7px;
}
.scenario-demos .sc-row{ display:flex; flex-wrap:wrap; gap:var(--sp-2); }
.scenario-demos button{
  font-family:'IBM Plex Mono',monospace; font-size:11px; color:var(--muted);
  background:var(--ink); border:1px solid var(--line); border-radius:var(--r-ctl);
  padding:7px 10px; cursor:pointer; text-align:left;
  transition:border-color var(--t-1) var(--ease-out), color var(--t-1) var(--ease-out), background var(--t-1) var(--ease-out);
}
.scenario-demos button:hover{ border-color:var(--accent,var(--amber)); color:var(--text); }
.scenario-demos button:focus-visible{ outline:2px solid var(--accent,var(--amber)); outline-offset:2px; }
.scenario-demos button .sc-kicker{
  display:block; font-size:9px; font-weight:600; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted-2); margin-bottom:2px;
}
.scenario-demos button:hover .sc-kicker{ color:var(--muted); }
@media (max-width:560px){
  .scenario-demos button{ flex:1 1 calc(50% - 6px); min-height:40px; }
}

/* ---- viz hosts: theme-aware simulator / chart plates ----
   Prefer these tokens over hardcoded #0a111e / #fff so light mode stays legible. */
.hero svg,
.panel svg,
#anim, #springViz, #forceChart, #step, #dLoad, #dShear, #dMoment, #dDefl,
svg[id$="Svg"], svg[id$="Chart"], svg[id$="Viz"], canvas.viz {
  background: var(--viz-bg, var(--ink));
}
/* ...but NOT the little inline icons inside buttons. `.hero svg` and `.panel svg` are broad
   enough to catch every icon in a play/pause/reset/share control, painting an --ink plate behind
   a glyph that is itself filled with currentColor (= --ink on an accent-coloured button). The
   result was a solid dark square where the icon should be — on every simulator's play button.
   Charts are never inside a button, so this exclusion cannot affect them. */
.hero button svg,
.panel button svg,
button svg[id$="Svg"], button svg[id$="Chart"], button svg[id$="Viz"] {
  background: none;
}
/* Common inline style on viz wrappers */
.viz-host, .sim-stage, .chart-wrap {
  background: var(--viz-bg, var(--ink));
  border: 1px solid var(--viz-border, var(--line-soft));
  border-radius: var(--r-card);
}

/* Soften pure-black fills that some pages still hardcode when light theme is on */
:root[data-theme="light"] svg [fill="#0a111e"],
:root[data-theme="light"] svg [fill="#0e1830"],
:root[data-theme="light"] svg [fill="#111"],
:root[data-theme="light"] svg [fill="#000"],
:root[data-theme="light"] svg [fill="black"] {
  fill: var(--viz-bg);
}
:root[data-theme="light"] svg [stroke="#0a111e"],
:root[data-theme="light"] svg [stroke="#0e1830"] {
  stroke: var(--viz-border);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) svg [fill="#0a111e"],
  :root:not([data-theme="dark"]) svg [fill="#0e1830"],
  :root:not([data-theme="dark"]) svg [fill="#111"],
  :root:not([data-theme="dark"]) svg [fill="#000"],
  :root:not([data-theme="dark"]) svg [fill="black"] {
    fill: var(--viz-bg);
  }
  :root:not([data-theme="dark"]) svg [stroke="#0a111e"],
  :root:not([data-theme="dark"]) svg [stroke="#0e1830"] {
    stroke: var(--viz-border);
  }
}

/* Text drawn as SVG that used near-white fills: map to --text in light mode */
:root[data-theme="light"] svg [fill="#e9f0fb"],
:root[data-theme="light"] svg [fill="#fff"],
:root[data-theme="light"] svg [fill="#ffffff"],
:root[data-theme="light"] svg [fill="white"] {
  fill: var(--text);
}
:root[data-theme="light"] svg [stroke="#e9f0fb"],
:root[data-theme="light"] svg [stroke="#fff"],
:root[data-theme="light"] svg [stroke="#ffffff"],
:root[data-theme="light"] svg [stroke="white"] {
  stroke: var(--text);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) svg [fill="#e9f0fb"],
  :root:not([data-theme="dark"]) svg [fill="#fff"],
  :root:not([data-theme="dark"]) svg [fill="#ffffff"],
  :root:not([data-theme="dark"]) svg [fill="white"] {
    fill: var(--text);
  }
  :root:not([data-theme="dark"]) svg [stroke="#e9f0fb"],
  :root:not([data-theme="dark"]) svg [stroke="#fff"],
  :root:not([data-theme="dark"]) svg [stroke="#ffffff"],
  :root:not([data-theme="dark"]) svg [stroke="white"] {
    stroke: var(--text);
  }
}


/* ============================================================
   --- interaction polish ---

   Every control on a tool page reacted instantly or not at all: `.playbtn` — the play/pause/reset
   control on around forty pages, and the most-pressed thing on the site — had no hover state at all,
   and the segmented and preset buttons snapped between colours with no transition.

   Two rules followed here:

   1. Nothing that moves may change LAYOUT. The range thumb in particular is centred on its track by
      a hand-computed `margin-top:-6.5px` for its 17px size, so growing the thumb on hover would
      shift it off the track. The glow ring grows instead — it is a box-shadow, so it costs no space
      and cannot push anything out of alignment.
   2. All of it is off under `prefers-reduced-motion`, at the bottom of this block.
   ============================================================ */

.playbtn, .presets button, .seg button, .ctrlbtn, select, .catseg button{
  transition:background-color var(--t-1) var(--ease-out), border-color var(--t-1) var(--ease-out), color var(--t-1) var(--ease-out),
             box-shadow var(--t-1) var(--ease-out), filter var(--t-1) var(--ease-out), transform var(--t-1) var(--ease-out);
}

/* the primary transport button: lift very slightly, brighten, then press in */
.playbtn:hover{ filter:brightness(1.08); transform:translateY(-1px); }
.playbtn:active{ transform:translateY(0) scale(.985); filter:brightness(.97); }
.playbtn.ghost:hover{
  background:color-mix(in srgb,var(--accent) 12%,var(--ink));
  filter:none;
}
.playbtn.ghost:active{ background:color-mix(in srgb,var(--accent) 18%,var(--ink)); }

/* segmented / preset buttons: a hint of the accent behind them before they are chosen, and a
   real pressed state so a tap on a phone visibly registers */
.presets button:hover, .seg button:hover, .catseg button:hover{
  background:color-mix(in srgb,var(--accent) 9%,var(--ink));
}
.presets button:active, .seg button:active, .catseg button:active{ transform:scale(.98); }
/* the already-selected one should not pretend to be selectable */
.presets button.on:hover, .seg button.on:hover, .catseg button.on:hover{
  filter:brightness(1.06); background:var(--accent);
}

select:hover{ border-color:color-mix(in srgb,var(--accent) 55%,var(--line)); }

/* slider: widen the glow on hover and again while dragging. Size is untouched on purpose —
   see note 1 above. */
input[type="range"]{ transition:box-shadow var(--t-1) var(--ease-out); }
input[type="range"]:hover::-webkit-slider-thumb{
  box-shadow:0 0 0 5px color-mix(in srgb,var(--c,var(--accent)) 30%,transparent);
}
input[type="range"]:active::-webkit-slider-thumb{
  box-shadow:0 0 0 7px color-mix(in srgb,var(--c,var(--accent)) 34%,transparent);
}
input[type="range"]:hover::-moz-range-thumb{
  box-shadow:0 0 0 5px color-mix(in srgb,var(--c,var(--accent)) 30%,transparent);
}
input[type="range"]:active::-moz-range-thumb{
  box-shadow:0 0 0 7px color-mix(in srgb,var(--c,var(--accent)) 34%,transparent);
}
input[type="range"]::-webkit-slider-thumb{ transition:box-shadow var(--t-1) var(--ease-out); }
input[type="range"]::-moz-range-thumb{ transition:box-shadow var(--t-1) var(--ease-out); }

/* a disabled control should look unavailable rather than merely unresponsive */
.playbtn:disabled, .ctrlbtn:disabled, .seg button:disabled, .presets button:disabled{
  opacity:.45; cursor:default; filter:none; transform:none;
}
.playbtn:disabled:hover, .ctrlbtn:disabled:hover{ filter:none; transform:none; }

/* links inside prose: underline on hover only, so body copy stays quiet until pointed at */
.rot-content a:not(.rot-btn), .rot-fieldnotes a{
  text-decoration-color:color-mix(in srgb,var(--accent) 45%,transparent);
  transition:text-decoration-color var(--t-1) var(--ease-out), color var(--t-1) var(--ease-out);
}
.rot-content a:not(.rot-btn):hover, .rot-fieldnotes a:hover{
  text-decoration-color:var(--accent);
}

/* Respect the reader's setting. Colour changes stay — they carry meaning and do not move —
   but nothing translates, scales or eases. */
@media (prefers-reduced-motion:reduce){
  .playbtn, .presets button, .seg button, .ctrlbtn, select, .catseg button,
  input[type="range"], input[type="range"]::-webkit-slider-thumb,
  input[type="range"]::-moz-range-thumb,
  .rot-content a, .rot-fieldnotes a{ transition:none; }
  .playbtn:hover, .playbtn:active,
  .presets button:active, .seg button:active, .catseg button:active{ transform:none; }
}

/* Chain backlinks — the reverse edge of the chain pages, generated by build-registry.py.
   Deliberately quiet: it is a wayfinding aid at the foot of a tool, not a call to action. */
.rot-chainlink{
  display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--sp-3) var(--sp-4);
  margin:22px 0 0; padding:12px 15px; background:var(--ink);
  border:1px solid var(--line-soft); border-radius:var(--r-card);
  font-size:13px; color:var(--muted);
}
.rot-chainlink .cl-ey{
  font-size:10px; letter-spacing:.09em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600;
}
.rot-chainlink a{ color:var(--accent); text-decoration:none; }
.rot-chainlink a:hover{ text-decoration:underline; }
.rot-chainlink .cl-all{ margin-left:auto; font-size:12px; }

/* Unusual-units toggle on the converter. Quiet by design: the units behind it are real and
   correctly converted, they are just not what anyone reaches for by accident. */
.oddbar{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--sp-3) var(--sp-4); margin-top:22px; }
.oddchk{ display:inline-flex; align-items:center; gap:var(--sp-3); font-size:13px; color:var(--muted); cursor:pointer; }
.oddchk input{ width:auto; accent-color:var(--accent); }
/* opt-in rounding controls sit on the same bar as the unusual-units toggle */
.roundbar{ margin-top:12px; }
.roundsel{ width:auto; min-width:58px; padding:5px 7px; font-size:12px; }
.roundsel:disabled{ opacity:.45; cursor:not-allowed; }
.oddnote{ font-family:'IBM Plex Mono',monospace; font-size:11px; color:var(--muted-2); }
.allcell.odd{ border-style:dashed; }
.allcell.odd .sym{ color:var(--amber); }

/* A landmark above the modelled atmosphere. Its height is real and shown; the arrow says the
   chart cannot reach it, which is honest about a limit rather than hiding the number. */
.lmbtn.beyond{ border-style:dashed; opacity:.9; }
.lmbtn.beyond .v{ color:var(--amber); }

/* ---------- paint page: picker and colour converter ---------- */
.pickwrap{ display:grid; grid-template-columns:minmax(220px,1fr) minmax(260px,1.2fr); gap:var(--sp-5); }
@media (max-width:760px){ .pickwrap{ grid-template-columns:1fr; } }
.svfield{ position:relative; width:100%; aspect-ratio:1.25; border-radius:var(--r-card);
  border:1px solid var(--line); cursor:crosshair; touch-action:none; }
.svdot{ position:absolute; width:16px; height:16px; border-radius:50%; border:2px solid #fff;
  box-shadow:0 0 0 1px rgba(0,0,0,.55); transform:translate(-50%,-50%); pointer-events:none; }
.huerail{ position:relative; height:18px; margin-top:10px; border-radius:var(--r-ctl);
  border:1px solid var(--line); cursor:pointer; touch-action:none; }
.huedot{ position:absolute; top:50%; width:14px; height:22px; border-radius:var(--r-chip); background:transparent;
  border:2px solid #fff; box-shadow:0 0 0 1px rgba(0,0,0,.55); transform:translate(-50%,-50%); pointer-events:none; }
.pickbtns{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--sp-3); margin-top:12px; }
.nativelab{ display:inline-flex; align-items:center; gap:var(--sp-2); font-size:12px; color:var(--muted); }
.nativelab input[type="color"]{ width:34px; height:28px; padding:0; border:1px solid var(--line);
  border-radius:var(--r-ctl); background:var(--ink); cursor:pointer; }
.pbtn{ font-family:'Inter',sans-serif; font-size:12.5px; color:var(--text); background:var(--ink);
  border:1px solid var(--line); border-radius:var(--r-ctl); padding:8px 13px; cursor:pointer; }
.pbtn:hover{ border-color:var(--accent); color:var(--accent); }
/* `.pbtn` sets color:var(--text), and --accent-dim is not dim enough to carry it: 2.56:1 on an
   amber-accented page, 2.64 on green, 3.56 on coral - four of the seven accents fail AA, and
   only the coral one happened to be on a page the audit covered. Mixing 65% of the accent into
   the ink keeps the tint and the accent border while putting the worst case at 4.95:1. */
.pbtn.primary{ background:color-mix(in srgb,var(--accent-dim) 65%,var(--ink)); border-color:var(--accent); }
.cfprev{ height:46px; border-radius:var(--r-card); border:1px solid var(--line); margin-bottom:12px; }
.cffields{ display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:var(--sp-3); }
.cffields label{ display:flex; flex-direction:column; gap:var(--sp-2); font-size:10.5px;
  letter-spacing:.07em; text-transform:uppercase; color:var(--muted-2); font-weight:600; }
.cffields input{ background:var(--ink); border:1px solid var(--line); border-radius:var(--r-ctl);
  color:var(--text); font-family:'IBM Plex Mono',monospace; font-size:13px; padding:8px 10px; outline:none; }
.cffields input:focus{ border-color:var(--accent); }
.cffields input[readonly]{ color:var(--muted); }
.cfmeta{ display:flex; flex-wrap:wrap; gap:var(--sp-3) var(--sp-4); margin-top:11px;
  font-family:'IBM Plex Mono',monospace; font-size:11px; color:var(--muted-2); }
.cfmeta b{ color:var(--text); }
.cfnote{ font-size:11.5px; color:var(--muted-2); line-height:1.6; margin-top:11px; }
.cfnote code{ font-family:'IBM Plex Mono',monospace; color:var(--muted); }
.cfnote b{ color:var(--muted); }

/* the gamut verdict — the answer to "why does the tin look duller than my screen" */
.gamut{ margin-top:18px; padding:14px 16px; border-radius:var(--r-card); border:1px solid var(--line-soft);
  background:var(--ink); font-size:13px; color:var(--muted); }
.gamut.close{ border-color:color-mix(in srgb,var(--green) 45%,transparent);
  background:color-mix(in srgb,var(--green) 8%,transparent); }
.gamut.near{ border-color:color-mix(in srgb,var(--amber) 45%,transparent);
  background:color-mix(in srgb,var(--amber) 8%,transparent); }
.gamut.far{ border-color:color-mix(in srgb,var(--red) 45%,transparent);
  background:color-mix(in srgb,var(--red) 8%,transparent); }
.gamut .g-head{ font-family:'Space Grotesk',sans-serif; font-weight:600; font-size:15px;
  color:var(--text); margin-bottom:10px; }
.gamut .g-sw{ display:flex; gap:0; margin-bottom:11px; border-radius:var(--r-ctl); overflow:hidden;
  border:1px solid var(--line); }
.gamut .g-sw span{ flex:1; height:42px; }
.gamut .g-rows div{ display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--sp-3) var(--sp-4); padding:5px 0;
  border-top:1px dashed var(--line-soft); font-family:'IBM Plex Mono',monospace; font-size:12px; }
.gamut .g-rows div:first-child{ border-top:0; }
.gamut .g-rows span{ min-width:118px; color:var(--muted-2); font-size:10.5px;
  letter-spacing:.06em; text-transform:uppercase; }
.gamut .g-rows b{ color:var(--text); }
.gamut .g-rows i{ color:var(--muted-2); font-style:normal; }
.gamut .g-why{ margin-top:11px; font-size:12.5px; line-height:1.65; }
.gamut .g-why b{ color:var(--text); }

/* hue reachability strip — screen against mixable, hue by hue */
.huestrip{ margin-top:16px; }
.huestrip svg{ width:100%; height:auto; display:block; }
.huestrip .hs-axis{ stroke:var(--line); stroke-width:1; }
.huestrip .hs-lab{ fill:var(--muted-2); font-family:'Inter',sans-serif; font-size:11px;
  text-anchor:middle; }
.huestrip .hs-mark{ stroke:var(--text); stroke-width:1.5; stroke-dasharray:3 3; opacity:.75; }
.huestrip .hs-you{ fill:var(--text); font-family:'Inter',sans-serif; font-size:10px;
  text-anchor:middle; letter-spacing:.05em; }
.huestrip .hs-key{ display:flex; flex-wrap:wrap; gap:var(--sp-3) var(--sp-5); margin-top:6px;
  font-size:11.5px; color:var(--muted-2); }
.huestrip .hs-key span{ display:inline-flex; align-items:center; gap:var(--sp-2); }
.huestrip .hs-key i{ width:15px; height:11px; border-radius:var(--r-mark); }
/* outlined vs filled, because two chips at different opacities of the same grey read as one */
.huestrip .sw-ghost{ background:transparent; border:1px dashed var(--muted-2); }
.huestrip .sw-solid{ background:var(--muted-2); }
.huestrip .hs-cap{ font-size:12px; line-height:1.65; color:var(--muted); margin-top:9px; }
.huestrip .hs-cap b{ color:var(--text); }

/* ---- colour library, saved list, gamut outline ---- */
.libsearch{ width:100%; background:var(--ink); border:1px solid var(--line); border-radius:var(--r-ctl);
  color:var(--text); font-family:'Inter',sans-serif; font-size:12.5px; padding:7px 10px;
  outline:none; margin-bottom:8px; }
.libsearch:focus{ border-color:var(--accent); }
.libfams{ display:flex; flex-wrap:wrap; gap:var(--sp-1); margin-bottom:9px; }
.libfams button{ font-family:'IBM Plex Mono',monospace; font-size:10px; color:var(--muted-2);
  background:var(--ink); border:1px solid var(--line); border-radius:var(--r-chip); padding:4px 7px;
  cursor:pointer; }
.libfams button:hover{ border-color:var(--accent); color:var(--text); }
.libfams button.on{ color:var(--ink); background:var(--accent); border-color:var(--accent);
  font-weight:600; }
.libgrid{ display:grid; grid-template-columns:repeat(9,1fr); gap:var(--sp-1); }
@media (max-width:520px){ .libgrid{ grid-template-columns:repeat(7,1fr); } }
.libsw{ aspect-ratio:1; border-radius:var(--r-chip); border:1.5px solid var(--line); cursor:pointer;
  padding:0; }
.libsw:hover{ border-color:var(--text); transform:translateY(-1px); }
.libempty{ font-size:11.5px; color:var(--muted-2); line-height:1.6; padding:4px 0; }

.savedlist{ display:flex; flex-direction:column; gap:var(--sp-2); margin-bottom:10px; }
.savedrow{ display:flex; align-items:center; gap:var(--sp-3); padding:6px 8px; border:1px solid var(--line);
  border-radius:var(--r-ctl); background:var(--ink); }
.savedrow .sw{ width:20px; height:20px; border-radius:var(--r-chip); flex:none;
  border:1px solid var(--line-soft); }
.savedrow .nm{ font-size:12px; color:var(--text); flex:1; min-width:0; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }
.savedrow code{ font-family:'IBM Plex Mono',monospace; font-size:10.5px; color:var(--muted-2); }
.savedrow .rm{ flex:none; width:20px; height:20px; border-radius:var(--r-chip); border:1px solid var(--line);
  background:var(--panel); color:var(--muted-2); cursor:pointer; font-size:12px; line-height:1; }
.savedrow .rm:hover{ border-color:var(--red); color:var(--red); }
.savedfoot{ display:flex; flex-wrap:wrap; gap:var(--sp-2); }
.savedfoot .pbtn{ text-decoration:none; display:inline-flex; align-items:center; }

.svgamut{ position:absolute; inset:0; width:100%; height:100%; border-radius:var(--r-card);
  pointer-events:none; }
.gamtoggle{ display:flex; align-items:center; gap:var(--sp-2); margin-top:9px; font-size:11.5px;
  color:var(--muted-2); cursor:pointer; }
.gamtoggle input{ accent-color:var(--accent); }
