/* ===========================================================================
   Designer v3 — editor chrome design system
   ===========================================================================

   Linked deliberately, page by page — not swept into the site-wide bundle.
   show.html.erb emits the link into `yield :head`, and
   app/views/layouts/_stylesheets.html.erb lists this file as page-scoped so
   every layout leaves it out; see the comment there for why
   `stylesheet_link_tag :app` stops working once a page has its own stylesheet.

   ONE PAGE STILL PICKS IT UP BY ACCIDENT: app/views/tracks/drive.html.erb is a
   standalone HTML document that calls `stylesheet_link_tag :app` directly
   rather than rendering the shared partial, so the drive view still downloads
   this file. Nothing here targets anything outside `.rt-*`/`.rt-editor`, so it
   is inert there — but the `@layer` statement below does set cascade-layer
   order for whatever loads after it on that page. Point drive.html.erb at
   "layouts/stylesheets" and this file is genuinely editor-only.

   WHY A CASCADE-LAYER STATEMENT IS THE FIRST THING IN THIS FILE
   -------------------------------------------------------------------------
   This stylesheet is linked BEFORE the Tailwind bundle (designer.html.erb
   yields :head above `stylesheet_link_tag :app`). Tailwind v4 emits every
   rule inside a cascade layer, and an UNLAYERED rule beats every layered rule
   regardless of source order or specificity. So if the rules below were
   unlayered they would silently beat Tailwind's `hidden` utility — and the
   shell controller hides `emptyHint`, `facilityPalette`, `mapSearch`,
   `selectedPoint`, `pitLaneWarning` and the modals by toggling exactly that
   class. Panels would stop hiding, which is a functional break, not a visual
   one.

   Declaring the layer order here fixes it. The order below reproduces today's
   effective order (`properties` stays lowest, exactly where Tailwind's own
   output puts it) and inserts one new layer, `rt`, between `components` and
   `utilities`. Net effect:
     • preflight / theme / components  <  rt      → our chrome beats Preflight
     • rt  <  utilities                           → `hidden` still wins
   A layer named here but never filled is a no-op, and Tailwind's own
   `@layer components;` re-declaration later in the cascade is idempotent.
   ------------------------------------------------------------------------- */
@layer properties, theme, base, components, rt, utilities;

@layer rt {
  /* ── Tokens ─────────────────────────────────────────────────────────────
     A six-step NEUTRAL grey ladder, and — this is the part that was missing —
     ONE SURFACE ROLE PER STEP, so the ladder is used rather than merely
     declared.

     WHY THE STEPS GOT BIGGER (measured, not taste). The previous ladder was
     20/28/35/43/51/61 — 7-8 of 255 apart — and the rail used only two of its
     six steps: every docked region, every section header and every read-only
     row sat on s1, and every control sat on s2. Measured over the right rail
     with every section open (450x2180 crop of tmp/editor_shots/B8, 16-wide
     luminance bins holding >1% of pixels): 2 steps, 72% of the panel on one
     value, against Blender's Properties editor at 6. The spectators panel was
     worse — 96.4% of its pixels on a single grey, 1 step, against Cities:
     Skylines II's population panel at 10. A panel where every surface is the
     same grey is not restraint, it is flat: nothing groups, nothing recedes,
     and a section header does not read as a header.

     Blender's own panel does NOT solve this by being brighter everywhere; its
     histogram is 2.65% dark gutter / 14.7% mid / 65% panel body / 10.9% light
     widgets. It is the SPREAD, not the mean, that builds hierarchy. So each
     step below is 13-16 of 255 (5-6%, the spacing the craft rule calls for)
     from its neighbour, and each has exactly one job:

       s0  gutter behind the canvas, and the trough inside a segmented control
       s1  a docked panel's base — topbar, tool rail, right rail, status strip
       s2  a section HEADER band, and the surface of a note or an open group
       s3  a control at rest — button, action row, list row
       s4  an EDITABLE field at rest — numeric row, text input
       s5  the filled portion of a gauge, a raised well, a floating edge

     The greys are deliberately achromatic (R=G=B). The audit metric this
     workstream is graded on is "toolbar and status strip measure ~0%
     saturated pixels"; a slate/blue-tinted ladder cannot pass it. Colour in
     this editor belongs to the canvas — racing line, kerbs, run-off, corner
     chips — never to the chrome.

     ONE accent (emerald) carries interactive/selected state. Warning and
     danger are semantic-only: warning marks unsaved/blocking states, danger
     marks destructive actions and errors. Nothing else may introduce a hue.
     ---------------------------------------------------------------------- */
  .rt-editor {
    --rt-s0: #121212;   /*  18 — app gutter, deepest surface        */
    --rt-s1: #1c1c1c;   /*  28 — docked panel base                  */
    --rt-s2: #2c2c2c;   /*  44 — section header band, note, group   */
    --rt-s3: #3a3a3a;   /*  58 — control at rest                    */
    --rt-s4: #4a4a4a;   /*  74 — editable field at rest             */
    --rt-s5: #5e5e5e;   /*  94 — gauge fill, raised well, edge      */

    /* Hover is a LIFT, not a ladder step: a translucent white wash that adds
       the same ~11/255 to whatever surface it lands on (11 over the 28 panel,
       10 over a 74 field). Craft rule 13 puts hover at a single 8-13/255
       lift, and with 14-16/255 between adjacent ladder steps, "hover = the
       next step up" would overshoot it and — worse — make a resting control
       and a hovered surface one step below it render the same grey, which is
       the exact confusion the old +7 step produced. */
    --rt-lift: linear-gradient(#ffffff0e, #ffffff0e);

    /* THE seam. A boundary between two docked regions is a single-pixel
       HIGHLIGHT, never a dark stroke: it must be lighter than both surfaces
       it separates (the canvas gutter at 18 and the panel base at 28), or it
       reads as a gap cut out of the chrome rather than a join. Every region
       border and every in-panel divider uses this one token. */
    --rt-seam: #2c2c2c;   /*  44 — the header-band step               */

    /* Text steps move with the surfaces they sit on. A list row's key and meta
       are --rt-text-3, and a list row is now a 58 control rather than the 28
       panel it used to be; the numeric row it can also sit on is 74. At the old
       110 that is a 2.1:1 contrast against the row and 1.9:1 against the field
       — unreadable. These three keep >= 3:1 against --rt-s4, the brightest
       surface a row is drawn on, which designer_v3_chrome_test.rb asserts. */
    --rt-text:   #dbdbdb;  /* 219 — body text, same as Blender's     */
    --rt-text-2: #b8b8b8;  /* 184 — row labels                       */
    --rt-text-3: #999999;  /* 153 — section meta, disabled           */

    --rt-accent:     #10b981;
    --rt-accent-dim: #0d8f66;
    --rt-accent-fg:  #06140e;  /* knocked-out glyph on an accent fill */

    --rt-warn:   #d99a2b;  /* unsaved / blocking-precondition ONLY   */
    --rt-danger: #e05a52;  /* destructive action / error ONLY        */

    /* One row height drives every label row, numeric row and section head. */
    --rt-row: 28px;

    /* Tool rail. The width is a token because the labels have to FIT it: at
       56px (4px of padding each side, so a 48px cell) the "Measure" label
       overflowed its own accent block — measured 52.0px of ink at 10px /
       0.05em uppercase in headless Chromium, i.e. the M and the E sat past
       both edges of the emerald fill whenever Measure was the active tool.
       designer_v3_chrome_test.rb holds the labels against this width. */
    --rt-tools-w: 64px;
    --rt-tools-pad: 4px;
    --rt-gutter: 2px;
    --rt-pad: 8px;

    /* Two chrome type sizes. Nothing else. */
    --rt-fs-body: 11px;
    --rt-fs-label: 10px;

    --rt-font-num: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;

    background: var(--rt-s0);
    color: var(--rt-text);
    font-size: var(--rt-fs-body);
    line-height: 1.35;
  }

  /* ── Primitives ─────────────────────────────────────────────────────── */

  /* Docked chrome regions. No drop shadows anywhere in here: elevation is
     reserved for genuinely floating transient surfaces. Seams are a single
     1px highlight from the ladder, not a dark decorative stroke. */
  .rt-topbar,
  .rt-tools,
  .rt-rail,
  .rt-status {
    background: var(--rt-s1);
    box-shadow: none;
  }
  .rt-tools { width: var(--rt-tools-w); padding: 4px var(--rt-tools-pad); }
  .rt-topbar { border-bottom: 1px solid var(--rt-seam); }
  .rt-tools  { border-right: 1px solid var(--rt-seam); }
  .rt-rail   { border-left: 1px solid var(--rt-seam); }
  .rt-status { border-top: 1px solid var(--rt-seam); }

  .rt-label {
    font-size: var(--rt-fs-label);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--rt-text-3);
  }

  .rt-num-text {
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    font-size: var(--rt-fs-body);
  }

  /* ── The numeric row ────────────────────────────────────────────────────
     ONE component for every number in the sidebar: track width, pit-lane
     width, per-point width, smoothness, banking, run-off depth, elevation,
     rotation. Full panel width, one row height, label left, value right in
     tabular figures with the unit inside the value string.

     The drag-to-scrub affordance is the native <input type=range> stretched
     invisibly across the whole row. That is deliberate rather than a custom
     pointer handler: it keeps click-to-jump, drag-to-scrub, arrow keys, Home
     /End and screen-reader semantics for free, and — critically — it keeps
     the exact same target elements the panels controller already binds, so a
     restyle cannot silently drop a control's wiring.

     `--rt-fill` is set by the panels controller (or inline at render time for
     the generated rows) and paints the "how far through the range am I" bar.
     ---------------------------------------------------------------------- */
  .rt-num {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    height: var(--rt-row);
    padding: 0 var(--rt-pad);
    margin-bottom: var(--rt-gutter);
    background: var(--rt-s4);
    border-radius: 2px;
    overflow: hidden;
    cursor: ew-resize;
    user-select: none;
  }
  .rt-num::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--rt-fill, 0%);
    background: var(--rt-s5);
    pointer-events: none;
  }
  .rt-num:hover { background-image: var(--rt-lift); }
  .rt-num:focus-within { box-shadow: inset 0 0 0 1px var(--rt-accent); }

  .rt-num__label {
    position: relative;
    color: var(--rt-text-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .rt-num__value {
    position: relative;
    flex: none;
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    color: var(--rt-text);
    white-space: nowrap;
  }
  .rt-num__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    opacity: 0;
    cursor: ew-resize;
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
  }

  /* Typed-entry variant.
     -------------------------------------------------------------------------
     A quantity with no meaningful upper bound cannot be scrubbed on a slider —
     an elevation can be 4 m or 1,400 m, a facility coordinate is unbounded in
     both directions — so these rows are typed. That is the ONLY thing about
     them that differs. Same height, same gutter, same full panel width, same
     label on the left, and the value in the SAME right-hand slot, in the same
     tabular figures, with the unit in the same place.

     It used to be a 76px boxed well floating part-way across the row, holding
     a bare unit-less integer. In the three-row Point block (Width "12.0 m",
     Smoothness "1.00×", Elevation "0") that read as a different control, and
     it is the one criterion the chrome lost a blind comparison against
     Blender's five-row Principled BSDF panel on.

     The well now appears on hover/focus only, so the row still advertises that
     it is editable at the moment the user reaches for it. */
  .rt-num--field { cursor: default; }
  .rt-num--field:hover { background-image: var(--rt-lift); }
  .rt-num__field {
    position: relative;
    flex: none;
    /* Sized in glyphs, not pixels, so the digits sit on the same tabular grid
       as every scrubbed value. 7 covers the widest quantity these rows carry
       ("-500.0" m of elevation, a five-figure map coordinate). */
    width: 7ch;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: 2px;
    background: transparent;
    color: var(--rt-text);
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    font-size: var(--rt-fs-body);
    text-align: right;
    cursor: text;
    /* The row sets `user-select: none` so a drag-to-scrub never selects the
       label. An editable field inside it has to opt back in, or you cannot
       drag-select the digits you are about to retype. */
    user-select: text;
  }
  .rt-num--field:hover .rt-num__field,
  .rt-num__field:focus { background: var(--rt-s5); }
  /* Two controls that are not numbers but must READ as the same row: the
     corner-name field and the run-off type select. They used to be full-width
     controls with their value hard LEFT, sandwiched between "Banking 0°" and
     "Run-off depth 15.0 m" with their values hard right. The control now sits
     in the row's value slot, so every row in the corner editor has its label
     on the left and its value on the right. The select keeps its own internal
     alignment (`text-align` on a <select> is not honoured everywhere) — it is
     the BLOCK that is right-aligned. */
  .rt-num__text {
    flex: 1;
    min-width: 0;
    height: 20px;
    padding: 0 2px;
    border: 0;
    border-radius: 2px;
    background: transparent;
    color: var(--rt-text);
    font: inherit;
    font-size: var(--rt-fs-body);
    text-align: right;
    user-select: text;
  }
  .rt-num--field:hover .rt-num__text,
  .rt-num__text:focus { background: var(--rt-s5); }
  .rt-num__text:focus { outline: 1px solid var(--rt-accent); outline-offset: -1px; }
  .rt-num__select {
    height: 20px;
    padding: 0 2px;
    border: 0;
    border-radius: 2px;
    background: transparent;
    color: var(--rt-text);
    font: inherit;
    font-size: var(--rt-fs-body);
  }
  .rt-num--field:hover .rt-num__select,
  .rt-num__select:focus { background: var(--rt-s5); }
  .rt-num__value--grow { flex: 1; min-width: 0; display: flex; justify-content: flex-end; }

  /* The unit belongs to the value, not to the row: it sits hard against the
     digits so "12.0 m" reads as one string whether it was typed or scrubbed. */
  .rt-num__unit { padding-left: 3px; }
  /* Secondary derived figure (the per-segment gradient beside an elevation).
     Same tabular grid, one ladder step quieter, so it cannot be mistaken for
     the value the row edits. */
  .rt-num__meta {
    position: relative;
    flex: none;
    margin-left: auto;
    padding-right: 8px;
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    color: var(--rt-text-3);
  }
  .rt-num__field:focus { outline: 1px solid var(--rt-accent); outline-offset: -1px; }
  .rt-num__field::-webkit-outer-spin-button,
  .rt-num__field::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
  .rt-num__field[type="number"] { -moz-appearance: textfield; appearance: textfield; }

  /* Read-only readout row — same ladder position, no interaction. Opts out of
     the pointer cursor on purpose: it is not a control. */
  .rt-read {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    height: var(--rt-row);
    padding: 0 var(--rt-pad);
    margin-bottom: var(--rt-gutter);
    color: var(--rt-text-2);
    cursor: default;
  }
  .rt-read__value {
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    color: var(--rt-text);
  }

  /* Advisory readout — a MODEL'S OPINION, not a measurement of the track.
     "Run-off built 0 m" is a fact; "Run-off required 229 m" and "Barrier
     TecPro · 4 rows" are advice about a corner that has not been built yet,
     and all three used to be plain right-aligned values in the identical
     component with no cue at all, so telling a measurement from a
     recommendation meant recognising the wording.

     A MODIFIER ON THE VALUE SLOT, not a second row. The row keeps its height,
     its label position and its tabular figures; the cue is a dimmed
     lower-case tag after the value. No hue — the chrome is graded on
     saturated-pixel count, and advice is not a warning. */
  .rt-read__value--advice::after {
    content: " advised";
    color: var(--rt-text-3);
  }

  /* ── Action row ─────────────────────────────────────────────────────────
     The empty-state row, and the row every list action uses. Same height and
     gutter as the numeric row so a panel scans as one even ladder whether it
     is showing parameters or offering a first step.

     It sits on s3, a full 30/255 above the rail it is drawn on. On the old
     ladder it was s2 on an s1 rail — a +7 lift, which is definitionally the
     hover step the stylesheet documented — so "Author suggested kerbs on every
     corner", "Remove this kerb run" and "Clear every authored kerb" read as
     static paragraph text beside a segmented control that was a genuine +9.
     Measured off B4-kerbs-authored.png: Clear-row srgb(36,36,36) against a
     Pin-button srgb(52,52,52). An action row is a button; it now looks it. */
  .rt-act {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    height: var(--rt-row);
    padding: 0 var(--rt-pad);
    margin-bottom: var(--rt-gutter);
    background: var(--rt-s3);
    border: 0;
    border-radius: 2px;
    color: var(--rt-text);
    font: inherit;
    font-size: var(--rt-fs-body);
    text-align: left;
  }
  .rt-act:hover { background-image: var(--rt-lift); }
  /* The action row has exactly TWO right-hand slots, and each carries ONE
     kind of thing.
       __hint — how you invoke the row: a key or a short uppercase token
                (D, MAP, DEL, CLOSE, AUTO, DEM).
       __meta — a number describing the thing the row acts on (a template's
                12.0 m track width), in tabular figures like every other
                number in the rail.
     Prose belongs in the row's own label. The slot used to hold all three at
     once, so "D", "12.0 m" and "marshals · medical" rendered in the same
     place in the same style and the column stopped meaning anything. */
  .rt-act__hint {
    flex: none;
    color: var(--rt-text-3);
    font-family: var(--rt-font-num);
    font-size: var(--rt-fs-label);
  }
  .rt-act__meta {
    flex: none;
    color: var(--rt-text-3);
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
  }
  /* A destructive row is marked AT REST, not on hover. It used to take the
     danger colour only on :hover, so "Remove this kerb run" was pixel-identical
     to the neutral row above it until the pointer was already on it — which is
     one moment too late to tell someone a click destroys their work. The FILL
     stays the neutral control step (every inactive control looks identical);
     only the label carries the semantic hue. */
  .rt-act--danger { color: var(--rt-danger); }
  .rt-act--danger:hover { background-image: var(--rt-lift); }
  .rt-act[disabled] { color: var(--rt-text-3); background: var(--rt-s3); cursor: default; }

  /* An upgrade row. Deliberately the SAME neutral fill, height and type as
     every other action row: it is one more row in the ladder, not an advert
     pasted into a panel, and it must not be the loudest thing on screen in a
     chrome that is graded on saturated-pixel count. The only difference from a
     plain .rt-act is the anchor reset — .rt-act is written for <button> and an
     <a> brings its own colour and underline — plus the PRO token in the
     existing __hint slot, which is exactly the slot for "how you invoke this
     row". No new hue: the chrome keeps its single accent. */
  .rt-act--pro {
    text-decoration: none;
    color: var(--rt-text-2);
    cursor: pointer;
  }
  .rt-act--pro:hover { background-image: var(--rt-lift); color: var(--rt-text); }
  .rt-act--pro .rt-act__hint { color: var(--rt-accent); }

  /* Two-up action row, for paired actions that share one ladder step. */
  .rt-act-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--rt-gutter);
    margin-bottom: var(--rt-gutter);
  }
  .rt-act-pair > .rt-act { margin-bottom: 0; }

  /* ── Section ────────────────────────────────────────────────────────────
     Collapsed by default, and a collapsed section still SAYS something: the
     meta slot on the right carries the summary (corner count, elevation
     delta, placed facilities) so the rail is scannable without opening nine
     disclosures to find out which ones have content.

     The head is a BAND (s2), one step above the rail it divides. It used to be
     s1 — the rail's own fill — so a section header was invisible as a surface
     and the whole rail read as one undifferentiated grey column with text on
     it. This is the single change that makes the rail scan: twelve header
     bands at 44 on a 28 base are the grouping structure. */
  .rt-sec { margin-bottom: var(--rt-gutter); }
  .rt-sec__head {
    display: flex;
    align-items: center;
    gap: 6px;
    height: var(--rt-row);
    padding: 0 var(--rt-pad);
    background: var(--rt-s2);
    border-radius: 2px;
    list-style: none;
    user-select: none;
  }
  .rt-sec__head::-webkit-details-marker { display: none; }
  /* A head with nothing to disclose (START, PLACE, LOCATION, the shortcut
     sheet's group heads) carries no caret, so its title used to start 14px to
     the LEFT of every other title in the same column — the caret's 8px box
     plus the 6px gap. It keeps the indent and drops the fill instead: a flat
     head is a label, not a control. */
  .rt-sec__head--flat {
    padding-left: calc(var(--rt-pad) + 8px + 6px);
    background: transparent;
  }
  .rt-sec__head:hover { background-image: var(--rt-lift); }
  /* Declared AFTER the hover lift above, and at the same specificity, so it
     wins on source order: a flat head discloses nothing, so it must not light
     up under the pointer as though it did. (It previously sat before that
     rule and therefore lost, which is why a caret-less head still highlighted
     on hover despite declaring `background: transparent`.) */
  .rt-sec__head--flat:hover { background-image: none; }
  .rt-sec__caret {
    flex: none;
    width: 8px;
    height: 8px;
    border-right: 1.5px solid var(--rt-text-3);
    border-bottom: 1.5px solid var(--rt-text-3);
    transform: rotate(-45deg);
    margin-left: 1px;
  }
  .rt-sec[open] > .rt-sec__head > .rt-sec__caret { transform: rotate(45deg); }
  .rt-sec__title {
    flex: 1;
    min-width: 0;
    font-size: var(--rt-fs-label);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--rt-text-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .rt-sec[open] > .rt-sec__head > .rt-sec__title { color: var(--rt-text); }
  .rt-sec__meta {
    flex: none;
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    font-size: var(--rt-fs-label);
    color: var(--rt-text-3);
  }
  .rt-sec__body { padding: var(--rt-gutter) 0 6px; }

  /* Contextual selection panel — the one place the accent marks a REGION
     rather than a control, because "this panel is about the thing you have
     selected" is the single most important state in the editor. */
  .rt-sec--sel > .rt-sec__head { background: var(--rt-s3); }
  .rt-sec--sel > .rt-sec__head > .rt-sec__title { color: var(--rt-accent); }

  /* ── List rows (corners, facilities, history, drive issues) ────────────
     A list row is a control — clicking one selects the corner, the stand or
     the history step it names — so it takes the control step and the 2px
     gutter every other row in the rail has. Transparent was what made the
     Spectators panel measure 96.4% of its pixels on ONE grey: two readouts
     and every stand row were all the panel's own fill with text on top. */
  .rt-item {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    height: var(--rt-row);
    padding: 0 var(--rt-pad);
    margin-bottom: var(--rt-gutter);
    background: var(--rt-s3);
    border: 0;
    border-radius: 2px;
    color: var(--rt-text-2);
    font: inherit;
    font-size: var(--rt-fs-body);
    text-align: left;
  }
  /* A corner row is the header of its own .rt-group, which carries the gutter
     for the pair; without this the open corner editor floats 2px off the row
     that opened it. */
  .rt-group > .rt-item { margin-bottom: 0; }
  .rt-item:hover { background-image: var(--rt-lift); }
  .rt-item__key {
    flex: none;
    width: 26px;
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    color: var(--rt-text-3);
  }
  .rt-item__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .rt-item__name--empty { color: var(--rt-text-3); font-style: italic; }
  .rt-item__meta {
    flex: none;
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    color: var(--rt-text-3);
  }
  /* Selected list row: accent fill, text knocked out. Not a border, not
     coloured text — the same treatment as an active tool. */
  .rt-item.is-selected,
  .rt-item.is-selected:hover { background: var(--rt-accent); color: var(--rt-accent-fg); }
  .rt-item.is-selected .rt-item__key,
  .rt-item.is-selected .rt-item__meta { color: var(--rt-accent-fg); opacity: 0.75; }
  /* An unnamed corner's placeholder is --rt-text-3 on the panel; on the accent
     fill that is barely legible, so it inherits the knocked-out colour too. */
  .rt-item.is-selected .rt-item__name--empty { color: var(--rt-accent-fg); opacity: 0.7; }

  .rt-group { border-radius: 2px; overflow: hidden; margin-bottom: var(--rt-gutter); }
  .rt-group.is-open { background: var(--rt-s2); }
  .rt-group__body { padding: var(--rt-gutter) 4px 4px; }

  /* NESTING RULE — a control must never sit at the same ladder step as the
     surface it sits on, or it stops looking like a control at all.
     This used to need a whole override block, because the open corner editor
     was s2 and so was every control inside it. On the re-spaced ladder the
     group surface is s2 (44), a button on it is s3 (58) and a numeric row is
     s4 (74), so the nesting is already legible from the base rules and the
     overrides are gone — one fewer place for a control's fill to be defined. */

  /* ── Buttons / toggles ──────────────────────────────────────────────────
     Every inactive control looks identical to every other inactive control.
     Active is a solid accent fill with the glyph knocked out. Hover is the
     shared lift. Nothing else changes: no border appears, no weight shifts,
     no transform, no shadow. */
  .rt-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    height: var(--rt-row);
    padding: 0 8px;
    background: var(--rt-s3);
    border: 0;
    border-radius: 2px;
    color: var(--rt-text-2);
    font: inherit;
    font-size: var(--rt-fs-body);
    white-space: nowrap;
  }
  .rt-btn:hover { background-image: var(--rt-lift); color: var(--rt-text); }
  .rt-btn.active,
  .rt-btn[aria-pressed="true"] { background: var(--rt-accent); color: var(--rt-accent-fg); }
  .rt-btn[disabled] { background: var(--rt-s3); color: var(--rt-text-3); cursor: default; }
  /* Hover-only, unlike .rt-act--danger. `.rt-btn--danger` is currently applied
     to the corner editor's "Not a corner" toggle (_cornerDetectionRows in the
     panels controller), which reclassifies a corner rather than destroys it, so
     marking it at rest would paint a non-destructive control red. Once that
     call site moves off the modifier this should match .rt-act--danger. */
  .rt-btn--danger:hover { color: var(--rt-danger); }
  .rt-btn--primary { background: var(--rt-accent); color: var(--rt-accent-fg); font-weight: 600; }
  .rt-btn--primary:hover { background: var(--rt-accent-dim); }
  .rt-btn--primary[disabled] { background: var(--rt-s3); color: var(--rt-text-3); }

  .rt-seg { display: inline-flex; gap: 1px; background: var(--rt-s0); padding: 1px; border-radius: 3px; }
  .rt-seg > .rt-btn { border-radius: 2px; }
  /* Same decision as the visualisation cells, one surface up — see the
     ACCENT-BLOCK DECISION below. The top bar's coordinate-mode segment lit a
     second filled accent block ("Blank"), which is 2.0 of the strip's measured
     5.6% saturation against Godot's 0.523%. The mode is a state you can read
     off the canvas (tiles or no tiles) and off the status strip's own MODE
     slot, so it takes the marker; the filled block stays with the tool. */
  .rt-seg > .rt-btn.active {
    background: var(--rt-s4);
    color: var(--rt-text);
    box-shadow: inset 0 -2px 0 var(--rt-accent);
  }

  /* A text field is EDITABLE, so it takes the field step (s4) rather than the
     control step (s3): the one control in the chrome you put a caret into
     should not be the same surface as the button beside it. */
  .rt-input {
    height: var(--rt-row);
    padding: 0 6px;
    background: var(--rt-s4);
    border: 0;
    border-radius: 2px;
    color: var(--rt-text);
    font: inherit;
    font-size: var(--rt-fs-body);
  }
  .rt-input:focus { outline: 1px solid var(--rt-accent); outline-offset: -1px; }
  .rt-input::placeholder { color: var(--rt-text-3); }

  select.rt-input { width: 100%; }

  /* ── Tool rail ──────────────────────────────────────────────────────── */
  .rt-tool {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    width: 100%;
    padding: 5px 0;
    background: transparent;
    border: 0;
    border-radius: 2px;
    color: var(--rt-text-2);
    font: inherit;
  }
  .rt-tool:hover { background-image: var(--rt-lift); color: var(--rt-text); }
  .rt-tool.active { background: var(--rt-accent); color: var(--rt-accent-fg); }
  /* 2D-only tools while the 3D view is up (designer_v3_controller
     _reflect3DToolRail). Dimmed, not hidden — the rail keeps its shape and
     the tooltip explains why. Opts out of the global pointer-cursor rule the
     same way any non-interactive look-alike does. */
  .rt-tool:disabled { opacity: 0.35; cursor: default; }
  .rt-tool:disabled:hover { background-image: none; color: var(--rt-text-2); }

  /* 3D editing coach line — one sentence over the canvas, first visit only.
     Floating chrome, so it borrows .rt-float's treatment rather than growing
     a new surface style. The editor root anchors it. */
  .rt-editor { position: relative; }
  .rt-coach {
    position: absolute;
    left: 50%;
    bottom: 18px;
    transform: translateX(-50%);
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 6px 6px 12px;
    background: color-mix(in srgb, var(--rt-s1) 92%, transparent);
    border: 1px solid var(--rt-s5);
    border-radius: 3px;
    color: var(--rt-text-2);
    font-size: 12px;
    white-space: nowrap;
  }

  /* ── THE ACCENT-BLOCK DECISION (craft rule 5 vs craft rule 2) ────────────
     Craft rule 5 wants a filled accent block for the ACTIVE TOOL. Craft rule 2
     wants a toolbar that measures ~0% saturated pixels. They genuinely
     conflict, this rail lost the same blind pair three rounds running, and the
     measured cause was never the rule — it was that TWO blocks were lit at
     once: DRAW as the active tool and LAYOUT as the active visualisation
     (measured on B4-kerbs-authored.png, rail crop 132x2280+0+72: two bands
     where >=30% of the rail width is saturated, rows 8-85 and 864-941; rail
     saturation 5.568% against Krita's 0.136%).

     DECIDED, for the two PERMANENT strips — the tool rail and the top bar,
     the surfaces every cell of which is on screen at all times and which are
     the ones graded at ~0% saturation. Across those two, exactly ONE filled
     accent block may be lit for a piece of state, and it belongs to the active
     TOOL. Every other selected state on them — the visualisation cells here,
     the coordinate-mode segment in the top bar — takes a 2px accent marker
     over the neutral control fill instead.

     Two things in the chrome keep a filled accent and are NOT covered by that
     rule, deliberately:
       • `.rt-btn--primary` (Save & Analyse) is an ACTION, not a state. It is
         the product's only commit, it must be findable, and it is the one
         accent that goes grey when it is unavailable.
       • `.rt-item.is-selected` is the answer to "which corner am I editing".
         At most one exists, it exists only while something is selected, and
         every reference tool fills the selected list row.

     The tool changes what every click on the
     canvas does — it is the most consequential piece of state in the editor,
     and it is the state a user must be able to read without looking. A
     visualisation only re-colours the ribbon, and its state is already legible
     from the ribbon itself: the canvas is either grey or a speed heatmap. So
     the active visualisation takes a THIN MARKER — a 2px accent bar on the
     rail's inboard edge over the neutral control fill — which says "this one"
     without spending a second block of hue on it.

     This is enforced against the rendered DOM by designer_v3_chrome_test.rb
     ("the tool rail lights exactly one filled accent block"), not by a
     constant asserting its own value. */
  .rt-tool--viz.active {
    background: var(--rt-s3);
    color: var(--rt-text);
    box-shadow: inset 2px 0 0 var(--rt-accent);
  }
  /* Applied to the rail's Clear cell. Destructive, but the rail is the surface
     graded at ~0% saturation, so the hue arrives on hover — at the moment the
     pointer is on the control — rather than sitting in the column at rest. */
  .rt-tool--danger:hover { color: var(--rt-danger); }
  /* ONE icon grid and ONE stroke weight for every glyph in the chrome. The
     facility palette used to hard-code width="14" height="14" on the same
     0 0 24 24 viewBox, so its glyphs rendered at 1.5 x 14/24 = 0.875px of
     stroke beside the rail's 1.5 x 16/24 = 1.00px — two effective weights in
     one panel. Size icons with this class, never with SVG width/height
     attributes; designer_v3_chrome_test.rb fails on the attributes. */
  .rt-icon { width: 16px; height: 16px; stroke-width: 1.5; flex: none; }
  .rt-tool__label {
    font-size: var(--rt-fs-label);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    line-height: 1;
  }
  .rt-tools__seam { width: 100%; height: 1px; background: var(--rt-seam); margin: 6px 0; }

  /* ── Status strip ───────────────────────────────────────────────────────
     Fixed slots in a fixed order that never reflow and never conditionally
     hide. Each slot reserves its width, so a value going from "0 m" to
     "8.80 km" cannot shove the slots to its right sideways. */
  .rt-status { display: flex; align-items: center; height: 24px; padding: 0 10px; gap: 0; }
  .rt-stat { display: flex; align-items: baseline; gap: 5px; flex: none; padding-right: 14px; }
  .rt-stat__k {
    font-size: var(--rt-fs-label);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--rt-text-3);
  }
  .rt-stat__v {
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    font-size: var(--rt-fs-label);
    color: var(--rt-text);
    display: inline-block;
    min-width: var(--rt-stat-w, 44px);
  }
  .rt-stat--w2 { --rt-stat-w: 62px; }
  .rt-stat--w3 { --rt-stat-w: 92px; }

  /* ── Empty state ────────────────────────────────────────────────────────
     Named concrete actions in the same row component as everything else. No
     illustration, no explanatory paragraph. */
  .rt-empty { display: block; }
  .rt-empty__note {
    display: flex;
    align-items: center;
    height: var(--rt-row);
    padding: 0 var(--rt-pad);
    color: var(--rt-text-3);
  }

  /* ── Transient floating surfaces ────────────────────────────────────────
     The ONLY things allowed to float. Translucent rather than shadowed, so
     the geometry underneath stays legible (the exact BTB Pro failure mode:
     its Surface Count dialog occludes the road being edited). */
  .rt-float {
    background: color-mix(in srgb, var(--rt-s1) 92%, transparent);
    border: 1px solid var(--rt-s5);
    border-radius: 3px;
    box-shadow: none;
    backdrop-filter: blur(6px);
  }

  .rt-modal { background: var(--rt-s1); border: 1px solid var(--rt-s5); border-radius: 4px; box-shadow: none; }
  .rt-scrim { background: color-mix(in srgb, var(--rt-s0) 82%, transparent); }

  /* Warning / danger blocks. Kept to a tinted 1px rule + text colour so the
     saturated pixel count stays near zero even when one is showing. */
  .rt-note {
    padding: 6px var(--rt-pad);
    border-radius: 2px;
    background: var(--rt-s2);
    border-left: 2px solid var(--rt-s5);
    margin-bottom: var(--rt-gutter);
  }
  .rt-note--warn { border-left-color: var(--rt-warn); }
  .rt-note--danger { border-left-color: var(--rt-danger); }
  .rt-note__title { color: var(--rt-text); }
  .rt-note__body { color: var(--rt-text-2); }

  /* ── Ramp legend ────────────────────────────────────────────────────────
     The key for a canvas ramp, so this is the one component in the rail whose
     swatch carries a saturated colour — and the craft rule is satisfied
     exactly because that colour belongs to the data layer: it is read off
     painters.js's ELEVATION_RAMP_STOPS at render time, never written here, so
     the stylesheet still holds no hue of its own.

     Rows are HALF the shared row height, which is a harmonic of the ladder
     rather than an arbitrary size: five of them stack into the space of two
     and a half normal rows, and each carries one short number. The layout is
     the rail's layout — swatch (the thing being named) hard left, value hard
     right in tabular figures — so the key reads as five more rows of the same
     column and not as a floating colour chart, which is what "five bare
     swatches with numbers" was called out for. */
  .rt-legend { padding: 0 0 var(--rt-gutter); }
  .rt-legend__row {
    display: flex;
    align-items: center;
    gap: 6px;
    height: calc(var(--rt-row) / 2);
    padding: 0 var(--rt-pad);
    color: var(--rt-text-2);
  }
  .rt-legend__swatch {
    flex: none;
    width: 18px;
    height: 8px;
    border-radius: 1px;
  }
  .rt-legend__label {
    margin-left: auto;
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    color: var(--rt-text);
  }

  /* The legend's title and its rows are two elements, because the rows are
     written by the panels controller as innerHTML and would erase a heading
     placed inside them. The block hides itself when the controller hides the
     rows, so a flat track never shows a heading over nothing — that is what
     `:has()` buys here, and it is the reason the two can be separate elements
     without the title outliving the key it names. */
  .rt-legend-block:has(> .hidden) { display: none; }

  /* ── the elevation profile chart ─────────────────────────────────────
     A docked instrument, not a card: no border, no fill, no rounding. It is
     the same column as every other row in the rail, and its two generated
     slots (the head and the gradient readout) are ordinary .rt-read rows —
     only the middle band is a drawn surface.

     THE HEIGHT IS FIXED IN CSS AND NOWHERE ELSE. The canvas has no intrinsic
     size, so the panels controller sizes its BACKING STORE from the box this
     rule gives it (times the device pixel ratio) on every resize. Set the
     height on the element instead and the chart ships stretched on every
     HiDPI screen. 88px is 3.1 rail rows: enough to resolve a crest against
     the corner ticks, and not so much that the chart out-weighs the rail. */
  .rt-profile-block { padding: 0 0 var(--rt-gutter); }
  .rt-chart {
    display: block;
    width: 100%;
    height: 88px;
    /* The one interactive drawn surface in the rail. Dragging it scrubs the
       3D camera along the lap, so it takes the horizontal-scrub cursor the
       numeric rows use rather than the default arrow. */
    cursor: ew-resize;
    touch-action: none;
  }

  /* ── the simulated lap time ─────────────────────────────────────────
     The one number in the rail that is allowed to be BIG, and the reason is
     not emphasis for its own sake: it is the only readout a designer watches
     WHILE their other hand is on the canvas, so it has to be legible in
     peripheral vision. 22px is 2× the body size and still under the rail's
     28px row, so the block reads as a docked instrument rather than as a card
     pasted into the column — the same rule the elevation chart follows.

     ACHROMATIC, LIKE THE REST OF THE CHROME. lap_simulation.js gives every
     vehicle profile a `color`, and none of them appears here. The ladder's one
     accent carries "this is the class you have selected" in the list below,
     exactly as it does for a selected corner, and the chrome keeps its ~0%
     saturated-pixel budget. Colour in this editor belongs to the canvas. */
  .rt-lap-block { padding: 0 0 var(--rt-gutter); }
  .rt-lap {
    display: flex;
    align-items: baseline;
    gap: 6px;
    padding: 2px var(--rt-pad) 4px;
  }
  .rt-lap__time {
    font-family: var(--rt-font-num);
    font-variant-numeric: tabular-nums;
    font-size: 22px;
    line-height: 1.1;
    color: var(--rt-text);
  }
  /* The class picker sits on the number's own baseline and hard right, so the
     row reads as one statement — "1:23.45, Formula 1" — rather than as a
     value and an unrelated control. It is the shared select, not a new one. */
  .rt-lap__class { margin-left: auto; display: flex; }
  .rt-lap__class > .rt-num__select { max-width: 130px; }

  /* THE INTERVAL WHERE THE NUMBER IS ABOUT THE PREVIOUS SHAPE.
     Between an edit and the debounced solve the block is describing a track
     the user has already changed. The whole value of this block is that it
     moves when the road does, so a stale number that still looks live is
     worse than no number at all — it reads as "that corner cost nothing".
     Dimmed rather than blanked: the previous lap is still the best answer
     available for the ~200 ms it takes to get the next one, and a slot that
     empties and refills flickers on every edit. */
  .rt-lap-block.is-stale { opacity: 0.5; }

  /* FINE PRINT UNDER A DERIVED READOUT — what the number assumes.
     NOT `.rt-empty__note`, and this is a measured mistake rather than a taste
     one: that class is a ONE-LINE row with `height: var(--rt-row)` and
     `align-items: center`, so a paragraph in it overflows its 28px box in both
     directions and prints straight through the rows above and below. The first
     capture of this block had the assumptions sentence lying across the
     "Slowest" readout and the TRACK section header. This wraps, has no fixed
     height, and sits at the rail's quietest text colour so it reads as a
     footnote rather than as the warning `.rt-note` is for. */
  .rt-fine {
    padding: 4px var(--rt-pad) 6px;
    color: var(--rt-text-3);
    line-height: 1.5;
  }

  /* A segment INSIDE a .rt-read value slot. The row already owns the label and
     the right alignment, so the segment only has to stop inheriting the row's
     tabular-figure treatment and sit on the row's baseline. */
  .rt-seg--inline { padding: 1px; vertical-align: middle; }
  .rt-seg--inline > .rt-btn { height: calc(var(--rt-row) - 8px); padding: 0 6px; }

  .rt-text-warn { color: var(--rt-warn); }
  .rt-text-danger { color: var(--rt-danger); }
  .rt-text-dim { color: var(--rt-text-2); }
  .rt-text-mute { color: var(--rt-text-3); }
  .rt-text-accent { color: var(--rt-accent); }

  /* Every interactive element gets a pointer. Enforced here as a rule over
     element/role selectors rather than per-element classes, so a control
     added later inherits it. Non-interactive look-alikes (.rt-read, the
     numeric rows, which use a resize cursor) opt out above. */
  .rt-editor a,
  .rt-editor button:not(:disabled),
  .rt-editor [role="button"]:not([aria-disabled="true"]),
  .rt-editor summary,
  .rt-editor label.rt-check,
  .rt-editor select,
  .rt-editor input[type="checkbox"] {
    cursor: pointer;
  }

  .rt-check { display: inline-flex; align-items: center; gap: 5px; color: var(--rt-text-2); }
  .rt-check:hover { color: var(--rt-text); }
  .rt-check input { accent-color: var(--rt-accent); width: 12px; height: 12px; }

  /* Scrollbars — part of the chrome, so they use the ladder too. `.rt-scroll`
     marks a region that scrolls INSIDE a docked surface (the shortcuts
     sheet's body).
     Scope note, measured rather than assumed: where the platform draws a
     PERSISTENT scrollbar (Windows/Linux, macOS set to "always show") this
     paints it from the ladder. Where the platform uses overlay scrollbars
     (macOS default, and headless Chromium — probed: offsetWidth - clientWidth
     = 0 on this container) the OS still only draws the thumb while scrolling,
     and no stylesheet changes that. The fold itself is what says "more
     below": the sheet's body is a multiple of nothing in particular, so a
     partial row shows at the bottom edge. */
  .rt-rail::-webkit-scrollbar,
  .rt-scroll::-webkit-scrollbar { width: 8px; }
  .rt-rail::-webkit-scrollbar-track,
  .rt-scroll::-webkit-scrollbar-track { background: var(--rt-s1); }
  .rt-rail::-webkit-scrollbar-thumb,
  .rt-scroll::-webkit-scrollbar-thumb { background: var(--rt-s4); border-radius: 0; }
  .rt-rail::-webkit-scrollbar-thumb:hover,
  .rt-scroll::-webkit-scrollbar-thumb:hover { background: var(--rt-s5); }
  /* Firefox has no ::-webkit-scrollbar; these two properties are its whole
     scrollbar styling API, so it gets the same treatment where it draws one. */
  .rt-rail, .rt-scroll { scrollbar-width: thin; scrollbar-color: var(--rt-s4) var(--rt-s1); }
}
