/* ═══════════════════════════════════════════════════════════════════════════
   EVERYTHING BELOW IS IN @layer skew, AND THAT IS THE ONE LINE AN ADOPTER
   CARES ABOUT.

   An unlayered rule beats a layered one no matter what the specificity says.
   So an app that has its own `.row`, `.box`, `.key`, `.head`, `.page` or
   `.wrap` — and every app has some of those — keeps its own, without renaming
   anything and without a single `!important`.

   It also disarms the trap this file's own notes describe three times over:
   `[data-skin=light] .sw` out-ranking `.sw.on`, a material rule silently
   un-pressing a key. Layers order by DECLARATION, not by specificity, so the
   state rules an app writes on top can no longer lose to a material rule
   underneath.

   The app declares the order it wants, once, before it loads anything:

       @layer skew, app;

   Omit it and the browser orders layers by first appearance, which puts skew
   first anyway. So the default is already right; declaring it is how you say
   it on purpose.
   ═══════════════════════════════════════════════════════════════════════════ */
@layer skew {

/* ═══════════════════════════════════════════════════════════════════════════
   PHYSICAL UI — an exploration, not the design system.

   This file answers one question: if the targz visual language were rebuilt
   as machined hardware — anodised chassis, backlit keys, jewel LEDs, engraved
   plates — what would the LAYER PANEL of Portrait-Typo look like?

   Nothing here is adopted. index.html is untouched. The layer panel's anatomy
   (rows, switches, colour bar, TONE badge, duplicate/delete, the chevron and
   the stroke strip it opens, the counted Clear-all) is the REAL one, lifted
   from Portrait-Typo/ui.js — so the comparison is like for like and this is
   a re-skin of a shipping component rather than an invented one.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* ── the material palette ─────────────────────────────────────────────── */
  --room:        #0b0b0d;   /* the desk it sits on                          */
  --case:        #191a1d;   /* anodised aluminium chassis                    */
  --case-lo:     #101113;   /* the shaded half of every bevel                */
  --case-hi:     #2e3137;   /* the lit half                                  */
  --bay:         #0e0f11;   /* the recessed well the rows sit in             */
  --plate:       #232529;   /* a raised key / module face                    */
  --plate-hi:    #34373d;
  --groove:      #08090a;   /* milled channel                                */
  --rim:         rgba(255,255,255,.09);  /* the 1px lit top edge             */
  --rim-lo:      rgba(0,0,0,.75);        /* the 1px dark bottom edge         */

  --txt:         #d3d6dc;   /* laser-etched white infill                     */
  --txt-dim:     #7d828c;
  --txt-etch:    #0a0b0c;   /* engraved-into-metal text                      */

  --led:         #FF6A00;   /* the accent is a LAMP here, not a fill         */
  --led-hot:     #FFB273;
  --led-red:     #FF2D2D;

  /* ── THE RECESS, ONCE ─────────────────────────────────────────────────
     Everything that goes IN uses these two, whatever its size. It took the
     whole layer switch to arrive at them and there is no reason for the nav
     bar or a tab to relearn it — worse, five copies is five things that drift
     apart the next time one is tuned. One place to change, one place to read.

       --recess   the four walls, sharp: a hard near-black line under the top
                  edge, the sides tight, the bottom softer because that is the
                  wall light reaches, and a lit lip under it all
       --chamfer  the broken edge of the cutout, lit DIRECTIONALLY and with
                  concave polarity — dark where it turns from the lamp at the
                  top, bright where it turns into it at the bottom. Get this
                  backwards and the control reads as a bump, every time. */
  --recess:
    inset 0 0 0 1px rgba(0,0,0,.73),
    inset 0 2.4px 1.2px rgba(0,0,0,.86),
    inset 0 3.6px 4.8px -1.2px rgba(0,0,0,.51),
    inset 3.6px 0 3.6px -2.4px rgba(0,0,0,.57),
    inset -3.6px 0 3.6px -2.4px rgba(0,0,0,.57),
    inset 0 -3.6px 4.8px -2.4px rgba(0,0,0,.31),
    inset 0 -1px 0 rgba(255,255,255,.15),
    0 1px 0 #101317,
    0 1px 1px rgba(0,0,0,.4);
  --chamfer: linear-gradient(152deg,
    rgba(0,0,0,.50)        0%,
    rgba(0,0,0,.20)       24%,
    rgba(255,255,255,0)   50%,
    rgba(255,255,255,.30) 100%);

  --font: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;

  /* Grain. One texture, reused everywhere — real metal is never flat, and a
     gradient with no grain reads as plastic within about two seconds. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
}

*{ box-sizing:border-box; }
html,body{ margin:0; }
body{
  background:
    radial-gradient(120% 90% at 50% -10%, #1c1d21 0%, var(--room) 62%),
    var(--room);
  color:var(--txt);
  font-family:var(--font-ui);
  -webkit-font-smoothing:antialiased;
  min-height:100vh;
}
/* THE AIR IN THE ROOM, NOT A TEXTURE ON THE PANELS, and `fixed` is what says so:
   an absolute grain swims with the canvas when you pan, and a grain that travels
   with the artwork is read as dirt on the paper.

   THE Z IS A TOKEN BECAUSE 99 IS ONLY RIGHT HERE. This page has no floating
   panels, so the grain can sit over everything. An app has panels, and the grain
   belongs UNDER them and OVER the canvas — Portrait-Typo sets 90 against panels
   at 96 and up. Take this rule at 99 into an app and it lays a haze over your own
   panels, which is an afternoon spent looking for it in the panel's CSS. */
body::after{
  content:''; position:fixed; inset:0; pointer-events:none; z-index:var(--grain-z, 99);
  background-image:var(--grain); opacity:.035; mix-blend-mode:overlay;
}

/* ══════════════════════════════════════════════════════════════════════════
   THE SHEET

   The one rule the whole block exists for: A THEME THAT TINTS THE THING BEING
   DRAWN IS A THEME THAT LIES ABOUT THE DRAWING. The chrome is anodised metal on
   a near-black desk. The paper is paper. Nothing here reaches inside it — no
   grain, no tint, no filter, no blend mode — and the only thing this class does
   is put it on the desk convincingly.

   THE SHADOW IS BIGGER THAN ANY PANEL'S, and it has to be. Every raised thing in
   this language uses one wide soft shadow plus one tight contact shadow; at panel
   size that is 6px and 2px. A white sheet on a near-black desk at those numbers
   reads as a sticker. Same recipe, ten times the throw.
   ══════════════════════════════════════════════════════════════════════════ */
.sheet{
  background:#fff;
  box-shadow:
    0 30px 60px -18px rgba(0,0,0,.9),   /* the throw: how far off the desk it is */
    0 6px 16px -4px rgba(0,0,0,.7);     /* the contact: where it actually touches */
}
/* A CANVAS OR AN SVG IS A REPLACED ELEMENT and sits on the text baseline, so the
   sheet gets a few pixels of white under it that no padding explains. */
.sheet > canvas, .sheet > svg{ display:block; }

/* AND SOMETIMES THE SHEET IS NOT THE SUBJECT. A 3D view, a depth map or a debug
   pass is a WINDOW onto something, not a piece of paper laid on a desk, and a
   window does not throw a shadow. */
.sheet.window{ box-shadow:none; }

/* ══════════════════════════════════════════════════════════════════════════
   THE GRID ON THE SHEET

   Paper you are about to plot on is not blank paper. The grid is the only
   thing on the sheet that is NOT the work, which is the whole difficulty: it
   has to be readable enough to align against and faint enough that you never
   mistake a rule for a stroke. So it is ink, in millimetres, with its own
   colour and its own opacity, and it sits on the sheet rather than in it.

   TWO WEIGHTS, AND THE SECOND IS THE POINT. A single pitch is either too
   coarse to place anything against or so dense it greys the page. Majors
   carry the count, sublines subdivide them, and the sublines are thinner AND
   fainter — a grid you read as a hierarchy rather than as a texture.

   EVERY NUMBER IS A CUSTOM PROPERTY because every one of them is a setting in
   the app: the panel writes them and the CSS draws. Nothing here is hard-coded
   that a Grid bay does not expose.
   ══════════════════════════════════════════════════════════════════════════ */
.grid{
  --grid-pitch:  15px;    /* major spacing. SQUARE: same both ways   */
  --grid-minor:  3px;     /* subline spacing. AN EXACT DIVISOR of it */
  --grid-w:      1px;     /* major weight                            */
  --grid-subw:   1px;     /* subline weight                          */
  --grid-ink:    #7FB2E5;
  --grid-op:     1;
  --grid-subink: color-mix(in srgb, var(--grid-ink) 40%, transparent);

  position:absolute; inset:0; pointer-events:none;
  opacity:var(--grid-op);
  background-image:
    repeating-linear-gradient(to right,  var(--grid-subink) 0 var(--grid-subw), transparent var(--grid-subw) var(--grid-minor)),
    repeating-linear-gradient(to bottom, var(--grid-subink) 0 var(--grid-subw), transparent var(--grid-subw) var(--grid-minor)),
    repeating-linear-gradient(to right,  var(--grid-ink)    0 var(--grid-w),    transparent var(--grid-w)    var(--grid-pitch)),
    repeating-linear-gradient(to bottom, var(--grid-ink)    0 var(--grid-w),    transparent var(--grid-w)    var(--grid-pitch));
}

/* ── THREE RULES THAT LOOK LIKE FUSSING AND ARE NOT ────────────────────────
   SQUARE CELLS. One pitch for both axes, so a cell is a square unit — which is
   what ruled paper is. Dividing each axis into the same COUNT gave 15 x 21
   cells on a 300 x 424 sheet: a grid describing the sheet's proportions rather
   than a unit of measure, and you cannot count a distance on it.

   THE PITCHES ARE WHOLE PIXELS AND THE MINOR EXACTLY DIVIDES THE MAJOR. A
   repeating gradient at 3.04px does not land on the device grid, so the
   renderer rounds each repeat independently and the error walks — which draws
   a staircase across the sheet and looks like a bug in the drawing rather than
   in the ruling. The panel does that arithmetic and hands CSS integers.

   AND THE WEIGHT IS 1px, NOT 0.5. Below one device pixel a line does not get
   thinner, it gets rounded up to one and drawn at full strength, so a 0.2mm
   subline and a 0.5mm major came out identical and the hierarchy vanished at
   exactly the density it matters. Weight below 1px is carried by ALPHA — which
   is what `--grid-subink` is for. */

/* THE RULERS ARE ENGRAVED ON THE DESK, so they are LIGHT lines. This is the one
   place the engraving rule inverts: everywhere else a cut is darker than its
   surface with a lit far wall, but the desk is already near black and a near
   black tick on it is not a tick. */
.ruler-tick{ background:rgba(255,255,255,.34); opacity:1; }
.ruler-label{
  color:#6d727c; font-family:var(--font); font-size:8px; letter-spacing:.1em;
  text-shadow:0 1px 0 rgba(0,0,0,.8);
}


/* ── THE DESK SPECIMEN'S FURNITURE ────────────────────────────────────────
   A ruler is a strip with ticks in it, and the strip is the app's to place —
   Portrait-Typo hangs one on each edge of `#canvas-container` and insets both
   with the drawer. What the language owns is what a tick LOOKS like on this
   ground, above; this is the minimum frame that lets you see it. */
.ruler{ position:absolute; pointer-events:none; }
.ruler-top{ top:-13px; left:0; right:0; height:11px; }
.ruler-left{ left:-13px; top:0; bottom:0; width:11px; }
.ruler-tick{ position:absolute; }
.ruler-top .ruler-tick{ bottom:0; width:1px; height:4px; }
.ruler-top .ruler-tick.long{ height:9px; }
.ruler-left .ruler-tick{ right:0; height:1px; width:4px; }
.ruler-left .ruler-tick.long{ width:9px; }


/* THE SPECIMEN'S STAGE, and it is doc-only — the exclude list in
   tools/build-dist.mjs names `.deskdemo`, so it does not reach an app. The
   sheet itself is a part; the box it is posed in is this page.

   THE SPECIMEN'S OWN SIZE IS INLINE ON THE ELEMENT, not a `.deskdemo .sheet`
   rule here, and that is the exclude list's one sharp edge: a selector naming
   a doc class AND a part is KEPT, because dropping `.mat .knob` would take a
   part's geometry with it. Correct in general, and here it would have shipped a
   dead rule sizing a sheet inside a container an app does not have. */
/* THE SHEET SITS ON A STAGE LIKE EVERY OTHER SPECIMEN, and the loupe needs it:
   a glass overhanging the paper has to overhang ONTO something. */
.deskdemo{
  display:flex; align-items:center; justify-content:center;
  gap:56px; flex-wrap:wrap; padding:18px 0 6px; margin:0; width:100%;
}
/* THE BAY IS THE SPECIMEN'S OTHER HALF. A grid you cannot change is a picture
   of a grid; the panel is what makes the sheet an argument. Doc-only. */
/* ══════════════════════════════════════════════════════════════════════════
   THE LOUPE — a magnifier lying on the paper

   A rectangular inset off to one side is a second picture you have to relate
   to the first. A round glass sitting ON the sheet, overhanging its corner and
   throwing a shadow onto the desk, is the same object as everything else here:
   a thing on a table, at 1:1, that you would pick up.

   IT IS ROUND BECAUSE LENSES ARE, and that is not decoration — a circle is the
   one silhouette on this page that cannot be mistaken for a panel, so nothing
   about it reads as another control to operate.

   THREE PARTS, SAME AS EVERY OTHER OBJECT: a machined bezel with the lit edge
   at the top, glass cut into it, and a specular that runs off the lamp at 158°
   like the rest of the room.
   ══════════════════════════════════════════════════════════════════════════ */
.loupe{
  position:absolute; right:-46px; top:-46px;
  width:146px; height:146px; padding:7px;
  border-radius:50%; pointer-events:none;
  background:linear-gradient(158deg,#3a3e45 0%,#22252a 46%,#15171a 72%,#2b2e34 100%);
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.20),      /* the lit top of the ring   */
    inset 0 -2px 0 rgba(0,0,0,.9),            /* and its shaded underside  */
    0 26px 40px -14px rgba(0,0,0,.95),        /* the throw onto the desk   */
    0 6px 12px -3px rgba(0,0,0,.8),           /* the contact               */
    0 0 0 1px rgba(0,0,0,.85);                /* the lifted edge, all round */
}
/* THE GLASS IS CUT INTO THE BEZEL, so it takes the well's inset and not a
   border: a lens sits below the ring that holds it, never on top of it. */
.loupe-glass{
  position:relative; width:100%; height:100%;
  border-radius:50%; overflow:hidden; background:#fff;
  box-shadow:
    inset 0 3px 7px rgba(0,0,0,.5),
    inset 0 0 0 1px rgba(0,0,0,.55),
    inset 0 -1px 0 rgba(255,255,255,.35);
}
/* THE SPECULAR IS ON THE OUTSIDE OF THE GLASS and does not care what is under
   it, so it runs the full disc rather than stopping at the grid. */
.loupe-glass::after{
  content:''; position:absolute; inset:0; border-radius:50%; pointer-events:none;
  background:
    linear-gradient(158deg,
      rgba(255,255,255,.42) 0%, rgba(255,255,255,.14) 15%,
      rgba(255,255,255,.02) 30%, transparent 46%),
    radial-gradient(120% 120% at 78% 88%, rgba(0,0,0,.16), transparent 52%);
}
/* THE MAGNIFICATION IS ENGRAVED ON THE BEZEL, because it is a property of the
   instrument rather than a value the panel sets. */
/* THE MARK MOVED UNDER THE GLASS WITH IT. At the top-right corner the label
   would otherwise sit on the top ruler. */
.loupe-mark{
  position:absolute; left:0; right:0; bottom:-15px; text-align:center;
  font-family:var(--font); font-size:8px; letter-spacing:.16em; color:#4c515a;
  text-shadow:0 1px 0 rgba(255,255,255,.10);
}

/* THE CONTROLS MOVED AND THE SHEET SAYS SO. A stage that used to carry its
   own panel and now carries nothing reads as unfinished; one line of engraving
   is what a real case does when the knob is on the other side. */
.gridmoved{ margin:10px 0 0; }
.gridbody{ padding:9px 10px 11px; gap:9px; }
.gridrow{ display:grid; grid-template-columns:repeat(2,1fr); gap:10px 8px; }
/* THE VALUE SITS BESIDE THE DIAL. `bare` was the first choice and it showed
   only the pointer, which on a control whose whole subject is tenths of a
   millimetre is a control you cannot read — you could turn Sub mm all the way
   and never learn what you had set it to. */
.gridrow .kwrap.compact{ gap:8px; }
.gridrow .kwrap.compact .kval{ min-width:46px; }
.gridcol{ display:flex; align-items:center; gap:8px; }
/* THE SWATCH IS A CAP, not a colour box: it goes down when you press it and it
   carries the ink on its face, the way every other lit thing here does. */
.gridswatch{
  width:34px; height:22px; flex:0 0 34px; border:0; padding:0; cursor:pointer;
  border-radius:6px; background:var(--sw, #7FB2E5);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.45), inset 0 -1px 0 rgba(0,0,0,.5),
             0 0 0 1px rgba(0,0,0,.65), 0 2px 4px rgba(0,0,0,.6);
  transition:transform .1s;
}
.gridswatch:active{ transform:translateY(1px); }

/* ── page furniture ──────────────────────────────────────────────────────── */
.wrap{ max-width:1160px; margin:0 auto; padding:56px 32px 120px; }

.masthead{ margin-bottom:14px; }
.kicker{
  font-family:var(--font); font-size:10px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--led); margin:0 0 14px;
}
h1{
  font-size:clamp(30px,5vw,52px); line-height:1.02; margin:0 0 16px;
  font-weight:700; letter-spacing:-.02em; color:#f2f4f7;
}
.lede{ font-size:15px; line-height:1.65; color:#9ba0aa; max-width:62ch; margin:0 0 8px; }
.lede b{ color:#d3d6dc; font-weight:600; }

.warn{
  display:flex; gap:12px; align-items:flex-start;
  margin:28px 0 0; padding:14px 16px;
  border:1px solid rgba(255,106,0,.28); border-radius:10px;
  background:linear-gradient(180deg, rgba(255,106,0,.10), rgba(255,106,0,.04));
  font-size:13px; line-height:1.6; color:#c8bdb2; max-width:76ch;
}
.warn svg{ flex:0 0 16px; width:16px; height:16px; margin-top:2px; stroke:var(--led); fill:none; stroke-width:1.8; }

h2{
  font-size:12px; font-family:var(--font); letter-spacing:.18em; text-transform:uppercase;
  color:#6d727c; font-weight:600; margin:78px 0 6px;
  padding-bottom:10px; border-bottom:1px solid #212327;
}
h2 span{ color:#3d4149; }
/* `.sub` IS GONE. Twelve paragraphs across four pages introduced sections that
   introduce themselves — a heading, then a specimen you can put your hands on,
   with a sentence in between explaining what you were about to see. The
   `.stage-note` on each specimen already says what it is, in six words, in the
   place you are already looking. */

/* ═══════════════════════════════════════════════════════════════════════════
   THE STAGE — a lit desk. The light comes from above-left and every bevel,
   glow and cast shadow in the panel agrees with it. One light source is the
   whole difference between "3D" and "a pile of gradients".
   ═══════════════════════════════════════════════════════════════════════════ */
.stage{
  position:relative; margin-top:34px;
  padding:64px 40px; border-radius:18px;
  background:
    radial-gradient(70% 60% at 28% 0%, rgba(255,255,255,.055), transparent 60%),
    linear-gradient(180deg,#141518,#0c0d0f);
  border:1px solid #1e2024;
  display:flex; gap:56px; align-items:flex-start; justify-content:center; flex-wrap:wrap;
}
.stage-note{
  position:absolute; left:18px; top:14px;
  font-family:var(--font); font-size:9.5px; letter-spacing:.16em; text-transform:uppercase;
  color:#3f434a;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE CHASSIS
   Five shadows do the whole job:
     1  a wide soft cast shadow on the desk        (it is above the surface)
     2  a tight contact shadow                     (it TOUCHES the surface)
     3  inset 1px light on the top edge            (the lit chamfer)
     4  inset 1px dark on the bottom edge          (the shaded chamfer)
     5  a broad inset dark from below              (the body has thickness)
   ═══════════════════════════════════════════════════════════════════════════ */
.dock{
  width:318px; flex:0 0 318px; position:relative;
  border-radius:14px;
  background:linear-gradient(176deg,#26282d 0%,#1b1c20 26%,#161719 78%,#1c1d21 100%);
  box-shadow:
    0 42px 60px -24px rgba(0,0,0,.9),
    0 8px 14px -6px rgba(0,0,0,.75),
    inset 0 1px 0 rgba(255,255,255,.14),
    inset 0 -1px 0 rgba(0,0,0,.9),
    inset 0 -18px 26px -20px rgba(0,0,0,.8);
  padding:3px;
  user-select:none;
}
/* Brushed anodising: fine vertical striation + grain. Two cheap layers, and
   without them the chassis is a plastic toy. */
.dock::before{
  content:''; position:absolute; inset:0; border-radius:14px; pointer-events:none;
  background-image:
    repeating-linear-gradient(90deg, rgba(255,255,255,.020) 0 1px, transparent 1px 3px),
    var(--grain);
  background-size:auto, 140px 140px;
  opacity:.5; mix-blend-mode:overlay;
}
/* The specular sweep — one soft diagonal band. Placed high-left, matching the
   room light. Never centred: a centred highlight reads as a sticker. */
.dock::after{
  content:''; position:absolute; left:0; right:0; top:0; height:52%;
  border-radius:14px 14px 40% 40%/14px 14px 100% 100%; pointer-events:none;
  background:linear-gradient(158deg, rgba(255,255,255,.075) 0%, rgba(255,255,255,.016) 42%, transparent 70%);
}

/* Corner screws. Four, real, at the same inset — the cheapest single detail
   that says "this is an object". Each is a countersunk pit + a domed head. */
.screw{
  position:absolute; width:9px; height:9px; border-radius:50%; z-index:3;
  background:
    radial-gradient(circle at 34% 28%, #6a6f78 0%, #3c4046 42%, #202225 100%);
  box-shadow:
    inset 0 -1px 1px rgba(0,0,0,.85),
    inset 0 1px 1px rgba(255,255,255,.35),
    0 1px 2px rgba(0,0,0,.85);
}
.screw::after{  /* the slot, oriented differently on each — nobody torques four screws to the same angle */
  content:''; position:absolute; inset:0;
  background:linear-gradient(var(--a,20deg), transparent 44%, rgba(0,0,0,.85) 44%, rgba(0,0,0,.85) 56%, transparent 56%);
  border-radius:50%;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14);
}
.screw.tl{ left:7px;  top:7px;    --a:24deg;  }
.screw.tr{ right:7px; top:7px;    --a:-58deg; }
.screw.bl{ left:7px;  bottom:7px; --a:82deg;  }
.screw.br{ right:7px; bottom:7px; --a:-12deg; }

/* ── the header: an engraved faceplate ───────────────────────────────────── */
.head{
  display:flex; align-items:center; gap:9px;
  padding:14px 13px 12px 17px; position:relative; z-index:2;
  border-bottom:1px solid rgba(0,0,0,.7);
  box-shadow:0 1px 0 rgba(255,255,255,.05);
}
.head .t{
  flex:1; font-family:var(--font); font-size:10.5px; font-weight:700;
  letter-spacing:.24em; text-transform:uppercase;
  /* ENGRAVED, not printed: the glyph is darker than the metal and carries a
     1px light line UNDER it, which is the lit far wall of the cut. */
  color:#5d626b;
  text-shadow:0 1px 0 rgba(255,255,255,.10), 0 -1px 0 rgba(0,0,0,.6);
}
/* The count is a LAMP — a 7-segment window, dark glass with the digit lit
   behind it. The panel's numbers stop being text and become a readout. */
.readout{
  min-width:30px; height:22px; padding:0 7px;
  display:flex; align-items:center; justify-content:center;
  border-radius:4px;
  background:linear-gradient(180deg,#0a0c0b,#111413);
  box-shadow:
    inset 0 2px 4px rgba(0,0,0,.95),
    inset 0 -1px 0 rgba(255,255,255,.06),
    0 1px 0 rgba(255,255,255,.06);
  font-family:var(--font); font-size:13px; font-weight:700;
  color:#7CFFB0; text-shadow:0 0 7px rgba(124,255,176,.75), 0 0 18px rgba(124,255,176,.35);
  font-variant-numeric:tabular-nums;
}

/* ── a KEY: the physical push button primitive ───────────────────────────── */
/* Everything clickable in this panel is one of these at some size. Raised
   face, chamfered rim, cast shadow; pressed = travel down + the shadow
   collapses + the top highlight moves to the bottom. That last part is what
   sells the travel — a button that only moves 1px still looks flat. */
.key{
  display:flex; align-items:center; justify-content:center;
  border:0; padding:0; cursor:pointer; position:relative;
  border-radius:7px; color:#9aa0aa;
  background:linear-gradient(180deg,var(--plate-hi),var(--plate) 52%,#1a1c20);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.16),
    inset 0 -1px 0 rgba(0,0,0,.7),
    0 2px 0 #0e0f11,
    0 3px 5px rgba(0,0,0,.65);
  transition:box-shadow .09s ease, transform .09s ease, color .12s;
}
.key:hover{ color:#e2e5ea; background:linear-gradient(180deg,#3b3f46,#2a2d32 52%,#1e2024); }
.key:active,.key.pressed{
  transform:translateY(2px);
  background:linear-gradient(180deg,#1a1c20,#232529);
  box-shadow:
    inset 0 2px 4px rgba(0,0,0,.8),
    inset 0 -1px 0 rgba(255,255,255,.07),
    0 0 0 #0e0f11,
    0 1px 2px rgba(0,0,0,.6);
}
.key:disabled{ opacity:.32; cursor:not-allowed; transform:none;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06), 0 1px 0 #0e0f11; }
.key svg{ stroke:currentColor; fill:none; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }

.add{ width:26px; height:26px; border-radius:8px; }
.add svg{ width:14px; height:14px; stroke-width:2.2; }
.add:hover{ color:var(--led-hot); }

/* ── the BAY: the milled well the rows drop into ─────────────────────────── */
.bay{
  position:relative; z-index:2;
  margin:11px 9px; padding:8px 7px;
  border-radius:9px;
  /* THE WELL CAME UP IN VALUE WITH THE CAPS. It was near-black, which was
     fine when the switches were near-black too — now they are grey, and a grey
     cap on a black floor is a cut-out sitting on nothing. A recess is not a
     hole into space; it is the same case seen deeper, so it is the case's
     colour minus some light, not minus all of it. The inset shadow is what
     makes it read as below the surface; the fill only has to be darker than
     the surface, not black. */
  background:linear-gradient(180deg,#17191d,#1e2126);
  box-shadow:
    inset 0 3px 7px rgba(0,0,0,.85),
    inset 0 -1px 0 rgba(255,255,255,.05),
    0 1px 0 rgba(255,255,255,.055);
  display:flex; flex-direction:column; gap:5px;
}

/* ══════════════════════════════════════════════════════════════════════════
   THE INTERLOCK — n latching keys on one bar, one down at a time.

   It is not a new mechanism and it never needed a new part. `pkey` carries the
   interlock (press one, the others pop out) and the bar is `.piano`, the same
   milled floor every bay uses. What the two add together is a CLAIM: these n
   things are ONE setting, and exactly one of them is true right now. A row of
   separate keys says n independent things; sunk into one bar they say one
   thing with n positions, which is what a mode is.

   THREE SHAPES, AND ALL THREE ARE IN SHIPPING APPS. A bar (`.piano`), a
   PALETTE stood on end (`.palette`), and a bar that WRAPS when the names stop
   fitting (`.piano.sm.hb-tabs`). A fourth was written from scratch as `.seg`,
   documented as a new part, and never once rendered upright in four attempts.
   It is gone. The rule it broke is the oldest one here: if a component is not
   in a real app, it does not belong in Skew.

   AGAINST THE ROTARY, and the line is about how you read it rather than how
   many there are: a rotary POINTS at its position, so it is right when the
   positions are an ordered scale you aim along. An interlock LIGHTS one, which
   is right when they are unrelated alternatives and each needs its own name
   and icon at full size. Three tools are not a scale from Pan to Select.

   ── THE RIG. Doc-only, and it is three widths and nothing else. `.piano` and
   `.palette` size themselves; the wrapped bar is `width:100%` because in the
   hatch bay it fills a panel, so here it needs a number to fill. That was the
   whole of what kept collapsing: a bar sized by its keys while its keys are
   sized by the bar has no answer on a stage, and every previous fix supplied
   the number from a chassis wrapped round the outside — so the specimen only
   stood up inside a case it is never inside in an app. */
.intrig{
  display:flex; flex-wrap:wrap; align-items:flex-start;
  justify-content:center; gap:34px 44px;
}
.intcell{ display:flex; flex-direction:column; align-items:flex-start; gap:8px; }
.intcell-cap{ align-self:stretch; }
.intcell-wrap{ width:412px; }

/* ── THE TOGGLE RIG. Doc-only, and it is one width and nothing else. A `.tog`
   is `width:100%` because on a panel it fills the bay, so on a stage it needs
   a number to fill — the same trap the interlock rig spent four rebuilds on.
   258px is the settings panel's body width, which is where these actually
   live. */
.togrig{
  display:flex; flex-wrap:wrap; align-items:flex-start;
  justify-content:center; gap:34px 44px;
}
.togcell{ display:flex; flex-direction:column; align-items:flex-start; gap:8px; width:258px; }
.togcell > .bay-cap{ align-self:stretch; }

/* THE HEADING IS THE PANEL'S, NOT THE BAR'S. An interlock sits under a `.bay-cap`
   like anything else, so it carries no title of its own and cannot disagree
   with the one above it. */

/* ── a ROW: a module card seated in the bay ──────────────────────────────── */
.item{ display:flex; flex-direction:column; }
.row{
  display:flex; align-items:center; gap:5px;
  height:40px; padding:0 8px 0 0; width:100%;
  border:0; border-radius:7px; cursor:pointer; text-align:left;
  font-family:inherit; position:relative; overflow:hidden;
  background:linear-gradient(180deg,#31353b,#25282d 60%,#202328);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.10),
    inset 0 -1px 0 rgba(0,0,0,.7),
    0 1px 2px rgba(0,0,0,.6);
  transition:box-shadow .12s, background .12s, transform .09s;
}
.row::after{ /* faceplate grain, per module */
  content:''; position:absolute; inset:0; pointer-events:none;
  background-image:var(--grain); background-size:140px; opacity:.05; mix-blend-mode:overlay;
}
.row:hover{ background:linear-gradient(180deg,#3a3f46,#2c3036 60%,#26292f); }
.row:active{ transform:translateY(1px); }

/* THE LAYER'S COLOUR IS A LIGHT PIPE, not a painted bar. An edge-lit acrylic
   strip: a solid core, a bloom on the metal beside it, and a hot white line
   up its middle where the LED is brightest. This is the single biggest win of
   the physical language — the list stops being a legend and starts glowing in
   the layer's own ink. */
.pipe{
  flex:0 0 4px; align-self:stretch; margin:6px 7px 6px 4px;
  border-radius:3px; position:relative;
  background:linear-gradient(180deg, var(--lyr), color-mix(in srgb, var(--lyr) 72%, #000));
  box-shadow:
    0 0 5px color-mix(in srgb, var(--lyr) 85%, transparent),
    0 0 14px color-mix(in srgb, var(--lyr) 45%, transparent),
    inset 0 0 3px rgba(255,255,255,.55);
}
.pipe::after{
  content:''; position:absolute; left:1px; right:1px; top:14%; bottom:14%;
  border-radius:2px; background:rgba(255,255,255,.55); filter:blur(1px);
}
.row.off .pipe{ background:#2a2c30; box-shadow:inset 0 1px 1px rgba(0,0,0,.8); }
.row.off .pipe::after{ display:none; }

/* ── a SWITCH: backlit key, 24px. Off is a dead lamp behind smoked plastic;
   on is the lamp lit, and the light SPILLS onto the surrounding metal. ───── */
/* ── THE SWITCH IS AN OBJECT WITH A SKIRT, AND IT TRAVELS ────────────────
   The cap had light on it but no BODY: an inset chamfer and nothing outside,
   so it was a painted square rather than a thing standing on a panel. Three
   additions fix that, and they are the same three that make the big keys work.

     A SKIRT      0 2px 0 of near-black — the wall of the cap you can see
                  because it is raised off the face
     A CAST       a soft shadow under that wall, on the panel
     A DOMED FACE a radial sheen from the top-left, so the face is curved
                  rather than a flat ramp

   And then it MOVES. Off is up; on is driven 2px down and the skirt collapses
   to nothing, so the cap ends where its own wall used to be. Mixed sits at
   1px with half a skirt, which is the only honest place for "some". Three
   physical positions, readable from across the panel and readable in a
   greyscale screenshot — the state is in the geometry, not in a colour.

   ON keeps the CAP'S OWN COLOUR with the gradient inverted (dark at the top
   where the bezel now shades it, light at the bottom where the well bounces),
   and lights the GLYPH. Same recipe as the big keys' :active, at 24px. */
.sw{
  width:24px; height:24px; flex:0 0 24px; border-radius:7px;
  display:flex; align-items:center; justify-content:center;
  border:0; padding:0; cursor:pointer; position:relative;
  color:#a3aab4;                             /* a printed legend, not a dimmed one */
  /* A GREY CAP, NOT A BLACK ONE. Shadow is a difference, and the cap was
     already so dark that a hole cut into it had almost nowhere to go — the
     recess could only be drawn by taking away light that was not there. Lift
     the unpressed face into real grey and the same shadow suddenly has two
     stops of room to fall through. This is the one change that makes the
     pressed state read, and it is a change to the UNPRESSED one. */
  background:
    radial-gradient(125% 95% at 32% 16%, rgba(255,255,255,.14), transparent 62%),
    linear-gradient(180deg,#474d57 0%,#3a3f48 36%,#2c3138 68%,#22262c 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.28),
    inset 0 -2px 3px rgba(0,0,0,.6),
    inset 0 0 0 1px rgba(0,0,0,.55),
    0 2px 0 #101317,
    0 3px 6px rgba(0,0,0,.7),
    0 0 0 1px rgba(0,0,0,.45);
  transition:color .12s, box-shadow .1s, background .12s, transform .1s;
}
/* NO HIT PAD ON A SWITCH CAP, and that is a decision rather than an omission.
   `::before` is this grain and `::after` is the chamfer, so a pad would have to
   share one of them — and the only way to share this one is to clip the grain
   to a content box, which changes what the corner of a 24px cap looks like.
   A switch travels 1px. Trading the grain on every cap in the kit against 1px
   of edge on the one control that is usually pressed in the middle of a `.tog`
   row is not a trade. If it ever is, it needs a third pseudo, not this one. */
.sw::before{
  content:''; position:absolute; inset:0; pointer-events:none; border-radius:7px;
  background-image:var(--grain); background-size:90px; opacity:.09; mix-blend-mode:overlay;
}
.sw svg{ position:relative; width:14px; height:14px; stroke:currentColor; fill:none;
         stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round;
         transform-origin:50% 50%; transition:transform .1s; }
/* AND THE PLATE ANSWERS FOR THE CAP. In a `.tog` the button is the whole line
   and the cap is a span inside it, so `.sw:hover` and `.sw:active` never fire
   when the pointer is on the name — which is most of the line. The two states
   are added to the rules that already own them rather than written again next
   to them: a second copy of this gradient is a second thing to tune. */
.sw:hover,
.tog:hover .sw{ color:#eef1f5;
  background:
    radial-gradient(125% 95% at 32% 16%, rgba(255,255,255,.18), transparent 62%),
    linear-gradient(180deg,#525965 0%,#434955 36%,#343a42 68%,#282d33 100%); }
/* pressed by a finger: the cap drops onto its own skirt */
/* ── A SUNK CAP IS A CAP IN SHADOW, and at 24px that has to be overdrawn.

   The recess was there in the geometry and almost invisible in the paint: one
   soft 4px inset spread across a 24px face is a suggestion, and a suggestion
   at this size is nothing. What sells a hole is CONTRAST ACROSS A SHORT
   DISTANCE, so the shadow is now two layers doing two jobs — a tight, nearly
   black 2px line immediately under the top edge (the wall of the cutout, hard
   because it is close), and a wider soft one below it (the light that does get
   in, falling off). Then the opposite wall: a bright lip along the bottom,
   where the well bounces light back up into the cap.

   Top almost black, bottom lit, a hard dark ring round the whole thing. Big
   keys can be subtle about this because they have room for a gradient to
   describe the fall-off; a 24px cap has about six pixels to say "deep" in. */
/* SHADOW ON ALL FOUR WALLS — SHARP, NOT HEAVY.

   The walls were right and the rendering of them was not: 6–12px of blur at
   near-full black is a cap sitting in fog, and it swallowed the grey it was
   supposed to be sitting on. A recess this small has HARD edges — the wall is
   two or three pixels from the face, so the shade it throws cannot have
   softened over that distance. Blur radii are 1–4px now and the alphas are
   roughly half what they were. Less ink, placed more exactly, and the cap
   stays visibly grey underneath it.

   ALSO ONLY 1px OF TRAVEL.

   Two corrections that go together. The recess only had a top wall, so it read
   as a cap with a dark stripe rather than a cap down a hole — a hole has
   sides, and at 24px the sides are most of what you see of it. There are four
   inner shadows now: the top hard and near-black, the two sides tight against
   the edge, the bottom softer, because that is the wall light actually
   reaches. The lit lip stays under it so the bottom does not just go muddy.

   And the drop came back to 1px. 2px was a third of the gap between rows —
   enough that toggling a switch looked like the row twitched. With the walls
   drawn properly the depth no longer needs the travel to carry it, so the
   movement can be the small confirming one it should have been. */
/* DOWN IS THE SAME CAP. It declares no fill of its own — it inherits the
   raised cap's, so the two cannot drift apart when that one is tuned. All that
   changes is the box-shadow: the skirt goes (it is resting on it now) and the
   four walls of the recess come on. Plus 1px of travel, and the legend lights.
   Three declarations, one object. */
.sw:active,
.tog:active .sw{ transform:translateY(1px); box-shadow:var(--recess); }
/* ── DOWN IS ONE STATE. THE LEGEND SAYS WHICH KIND OF DOWN. ─────────────
   ON and MIXED are the same cap in the same position with the same recess,
   and they differ only in the colour of what is printed on it:

     up   + grey legend     nothing is on
     down + WHITE legend    some of them are
     down + ORANGE legend   all of them are

   That is the honest reading of a master over eight switches. A master whose
   column is half on is NOT half-pressed — the thing it commands is genuinely
   engaged, just not everywhere — so parking it at its own intermediate height
   was inventing a mechanical position to stand for a logical one. Down means
   "this is happening"; the colour says how completely. And white against
   orange is a difference you can still see with no colour vision at all,
   because it is a difference in brightness, which the old dim-orange was not. */
.sw.on,
.sw.mixed{ transform:translateY(1px); box-shadow:var(--recess); }
/* NO FILL ON EITHER, DELIBERATELY. Every attempt at a separate pressed colour
   was a way of drawing shade with paint, and it kept killing the shadow: a
   darker cap has nothing left for a shadow to take away. The moulding is one
   piece of grey plastic in two positions, so it gets ONE fill — the raised
   cap's, inherited — and the recess is drawn entirely by the walls above.
   A knock-on worth having: tune .sw's background and every state follows. */
/* ── THE CHAMFER, AS A RING WITH A DIRECTION ────────────────────────────
   A 1px white line along the top was the right idea drawn the wrong way. The
   broken edge of a cutout runs all the way round, and it is not equally lit
   anywhere: it FLARES where it faces the lamp, fades as it turns away, and
   goes to shadow on the far side. A box-shadow cannot say that — it is the
   same value the whole way round — so it always reads as a drawn outline.

   This is the gradient-border trick: a 1.6px ring made by padding a
   pseudo-element and masking its own middle out, filled with a gradient
   running from the light. Bright at 152° (top-left, where every other
   highlight in this panel comes from), through nothing at the equator, into a
   soft dark on the bottom-right where the lip turns away. Half a pixel of
   blur, because an edge that has been broken is round, and a round edge has
   no hard line on it anywhere.

   Only on the pressed states: when the cap is up it fills its hole and there
   is no lip to see. */
.sw.on{ color:var(--led); }
.sw.mixed{ color:#eef1f5; }
.sw.on:hover{ color:var(--led-hot); }
.sw.mixed:hover{ color:#fff; }

/* THE LEGEND SHRINKS WITH THE CAP. A face that has dropped into the panel is
   a face further from the eye, so what is printed on it subtends a smaller
   angle — 10% at this depth. Too small to notice as a change, which is the
   point: one more thing agreeing with the others, so the state reads as an
   object that moved rather than as a colour swap. */
/* …AND THE SHRINK IS ESSENTIALLY GONE: .9 × 1.1 lands on .99, so the pressed
   legend is now the same size as the raised one to within a rounding error.
   Kept as a literal .99 rather than deleted, because the rule is still the
   place the depth-versus-legend-size relationship lives — if the cap ever goes
   deeper than 1px, this is the number that has to follow it. */
.sw.on svg,
.sw.mixed svg,
.sw:active svg{ transform:scale(.99); }
.sw.on svg{ filter:drop-shadow(0 0 3px rgba(255,130,30,.7)); }
/* white is a legend, not a lamp — it gets no bloom */
.sw.mixed svg{ filter:none; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE SLIDE SWITCH — a machined actuator standing proud of a milled slot
   ═══════════════════════════════════════════════════════════════════════════
   A CAP IS FOR A BOOLEAN WITH A LEGEND. Most have none: "Invert tone", "Apply
   edits", "Weave crossings" — nineteen in the app this was written for, three
   with an honest glyph. A cap with nothing printed on it is not a blank legend
   but a missing one, and a lamp in the middle of it is not the answer either:
   it says "lit" where this panel already has jewels, pipes and lit arcs saying
   that, and it turns every switch on the plate into a dot.

   SO THE LEGEND IS THE POSITION, and the part is the switch a console has most
   of: a knurled actuator thrown left or right in a slot milled through the
   faceplate. You read it from the far side of the room without reading
   anything.

   ── WHY IT IS FIVE SURFACES AND NOT TWO ────────────────────────────────────
   The first two passes drew it with the slot's own ::before and ::after and it
   read as cheap, which is a specific failure and not a vague one. A small part
   looks cheap when its shading does not describe a SOLID: stripes instead of
   ridges, one flat fill instead of a chamfer and a face, nothing cast anywhere,
   and every highlight pure white. Pure white is the tell — a lamp has a colour
   and metal has a colour, and their product is never #fff.

   So the slot builds two children and they carry three more pseudo-surfaces:

     .slide      the CUT: the walls of the slot, dark at the top where the wall
                 overhangs, lit along the bottom where the floor bounces
     .sl-bed     the FLOOR: painted, and darkened into its own corners because
                 a floor meets its walls in shadow. This is the part that
                 lights, and what lights is a painted surface catching a lamp,
                 not a coloured rectangle
     .sl-act     the ACTUATOR: a moulded grip, chamfered top and left where the
                 lamp is, falling to shade bottom and right, standing on a
                 skirt and casting onto the plate
     ::before    the KNURL, cut into the middle of its face
     ::after     the SPECULAR, one anisotropic streak across the crowns

   ── AND IT IS WIDER THAN THE SLOT ──────────────────────────────────────────
   The single biggest thing that stopped it reading as an object. A rectangle
   inside a rectangle is two rectangles; an actuator with a FLANGE — 24px in a
   22px opening, overhanging a pixel top and bottom — is a part sitting IN
   something, and its shadow falls on the slot's own edge. Every real slide
   switch has this flange, because the actuator has to hold itself in the
   panel.

   ── THE MATERIAL IS THE KIT'S OWN, AND THERE IS NO METAL IN IT ─────────────
   THIS KIT HAS NO ALUMINIUM ANYWHERE and this part does not get to be the
   exception. The plates are anodised and the keys are moulded, and a bright
   machined bar dropped into that is a part borrowed from another machine —
   which is exactly what the first two passes at this looked like. It is a
   moulded grip: a dark face, a hard bright chamfer along the broken edge, deep
   groove shadows, and one soft streak across the crowns. The contrast lives in
   the EDGE, not in the face, which is also how a real one works. */
.slide{
  position:relative; flex:0 0 46px; width:46px; height:22px; border-radius:7px;
  /* THE CUT ITSELF. The top wall overhangs and is nearly black under it; the
     bottom wall is the one the floor throws light back onto. */
  background:linear-gradient(180deg,#08090b 0%,#0e1114 48%,#0a0c0e 100%);
  /* EVERY INSET HERE CARRIES BLUR, and that is not a taste. An inset shadow
     with an offset and NO blur cannot follow a radius: it is the shape pushed
     one pixel sideways, so at each rounded corner the offset copy pulls away
     from the edge and leaves a bright or dark spot sitting in the turn. Two of
     those on the actuator were visible as dots. One pixel of blur closes the
     gap; where an edge has to stay crisp, it is drawn as a clipped background
     band instead, because a background IS clipped by the radius. */
  box-shadow:
    inset 0 2px 3px rgba(0,0,0,.95),
    inset 0 1px 1px rgba(0,0,0,.9),
    inset 1px 0 2px rgba(0,0,0,.75),
    inset 0 -1px 1px rgba(255,255,255,.11),
    0 1px 0 rgba(255,255,255,.055);
}
/* ── THE FLOOR ─────────────────────────────────────────────────────────────
   DARK IN ITS OWN CORNERS, both states. A floor meets its walls in shadow, and
   the version without that corner darkening was the single flattest thing in
   the part — a coloured rectangle with a border-radius. The radial goes FIRST
   in the list because background layers paint first-on-top. */
.sl-bed{
  display:block; position:absolute; inset:3px; border-radius:4px;
  background-image:
    radial-gradient(120% 200% at 50% 45%, transparent 34%, rgba(0,0,0,.55) 100%),
    linear-gradient(180deg,#06070a,#0c0f12 55%,#080a0c);
  box-shadow:
    inset 0 1px 2px rgba(0,0,0,.95),
    inset 0 -1px 1px rgba(255,255,255,.05);
  transition:background-image .18s, box-shadow .18s;
}
/* LIT IS A PAINTED FLOOR CATCHING A LAMP, so it is brightest across its middle
   and turns down at both walls, with a hard specular line just under the top
   wall. It keeps its inset shadow — it is a hole either way, and a lit floor
   that loses its shadow becomes a sticker laid on the panel. */
.slide.on .sl-bed{
  background-image:
    radial-gradient(120% 200% at 50% 45%, transparent 30%, rgba(0,0,0,.5) 100%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--lyr, var(--led)) 52%, #000) 0%,
      color-mix(in srgb, var(--lyr, var(--led)) 90%, #fff) 34%,
      color-mix(in srgb, var(--lyr, var(--led)) 88%, #000) 62%,
      color-mix(in srgb, var(--lyr, var(--led)) 50%, #000) 100%);
  box-shadow:
    inset 0 1px 2px rgba(0,0,0,.6),
    inset 0 0 0 1px rgba(0,0,0,.4),
    0 0 4px color-mix(in srgb, var(--lyr, var(--led)) 8%, transparent),
    0 0 9px color-mix(in srgb, var(--lyr, var(--led)) 4%, transparent);
}
/* ── THE ACTUATOR ──────────────────────────────────────────────────────────
   SIX STOPS, NOT TWO. An extrusion has a chamfer, a face, a turn and an
   under-edge, and a two-stop gradient can only say "lighter at the top". The
   bottom stop comes back UP a little: that is the floor of the slot bouncing
   into the underside, and it is the difference between a part sitting in a
   hole and a part pasted over one. */
.sl-act{
  display:block; position:absolute; top:2px; left:2px; width:22px; height:18px;
  border-radius:4.5px;
  /* ── THE CLEARANCE IS THE PART ───────────────────────────────────────
     ONE PIXEL OF SLOT AROUND A CAP IS NOT A GAP, IT IS A DRAWING ERROR.
     At 24×20 in a 46×22 cut the actuator very nearly WAS the slot, and the
     only thing separating them was a 1px black outline — which is a
     stroke, and a stroke around a shape is the single loudest way to say
     "this is a picture of a switch". Every physical control in §05 has
     air around it: the knob's cap is inset NINETEEN PER CENT of its
     collar, and the shadow that falls in that gap is most of why it reads
     as an object sitting in a fitting rather than as a disc on a circle.
     22×18 leaves two pixels on every side. The travel is untouched — 2 +
     22 + 20 + 2 = 46 — so the throw, the timing and the end stops are
     exactly what they were and only the air is new. */
  /* THE CHAMFERS ARE BACKGROUND BANDS, NOT INSET HAIRLINES. A background is
     clipped by the border-radius, so a 1px bright band across the top simply
     STOPS where the corner rolls away from the lamp — which is what a chamfer
     does. The same line drawn as `inset 0 1px 0` is the shape offset downward,
     and it left a lit dot in each top corner and a pale one at the bottom
     right. Those were the two dots.

     AND IT IS PLASTIC, NOT ALLOY, WHICH TOOK THREE GOES TO ADMIT. It ran
     #646b76 to #202429 — a warm blue-grey whose top end is the lightest object
     on the panel, which a 24px cap has no business being. Darkening it once
     helped and left the real problem standing: a SIX-STOP 180° LINEAR is an
     EXTRUSION. It says "lighter at the top, darker at the bottom", which is
     true of a bar of metal cut to length and false of anything moulded. §05's
     cap is the material this system already has for dark plastic, and what
     makes it read is not its colour but its RADIAL — brighter in the middle,
     falling away in every direction, because that is what a dome does and a
     dome is what a moulding tool makes.

     SO THE BODY IS A DOME IN THE KNOB'S OWN FOUR VALUES, and the chamfer
     bands stay exactly as they were, because their reasoning never depended
     on the material: a background is clipped by the radius, so a 1px bright
     band across the top STOPS where the corner rolls away from the lamp. They
     go neutral rather than warm — the warmth was there to sell alloy. */
  background-image:
    linear-gradient(180deg, rgba(255,255,255,.15) 0 1px, transparent 1px),
    linear-gradient(90deg,
      rgba(0,0,0,.26) 0%, transparent 16%,
      transparent 82%, rgba(0,0,0,.34) 100%),
    linear-gradient(180deg,
      #34383f 0%, #2e3239 30%, #26292f 68%, #1f2227 100%);
  /* ── THREE TIERS OF SHADOW, AND NO OUTLINE ──────────────────────────
     THE 1px HARD RING WENT WITH THE CLEARANCE. `0 0 0 1px` at .62 is a
     STROKE — zero blur, uniform on all four sides, indifferent to where
     the lamp is — and it was the only thing holding the cap off the slot
     floor. A stroke can separate two shapes; it cannot seat an object,
     because nothing in a lit room has an even dark line all the way round
     it.
     What does the seating is depth, in three tiers: CONTACT at 1px, tight
     and nearly black, which is the line where the part meets what it
     stands on; FORM at 3px, the body of the shadow; AMBIENT at 6px, wide
     and weak, the room. Small objects read as sitting on a surface when
     all three are present and as floating on it when only one is. */
  box-shadow:
    inset 0 -2px 4px rgba(0,0,0,.75),
    inset 0 -1px 1px rgba(255,255,255,.10),
    0 1px 2px rgba(0,0,0,.85),
    0 3px 5px rgba(0,0,0,.7),
    0 6px 10px rgba(0,0,0,.42);
  /* IT SNAPS. A slide switch is spring-loaded over centre — it does not ease
     into place, it arrives and stops hard, which is what the overshoot on this
     curve is. 160ms is the whole throw. */
  transition:transform .16s cubic-bezier(.34,1.25,.5,1), filter .12s,
             box-shadow .18s;
}
.slide.on .sl-act{ transform:translateX(20px); }
/* AND WHEN THE FLOOR IS LIT IT BOUNCES ONTO THE ACTUATOR — FROM THE LEFT.

   THE BOUNCE HAS TO COME FROM WHERE THE LIGHT IS, and the light is not under
   the part. Throwing the switch on puts the actuator at the RIGHT end and
   uncovers the floor to its LEFT: that is the whole lit surface, it is beside
   the actuator rather than beneath it, and it is what any of this bounces off.
   The first version lit the underside, which is a second light source nothing
   on the panel can account for — the exact failure this language spends its
   whole shadow budget avoiding.

   So it is a warm wash coming in through the left flank, hottest at the edge
   nearest the floor and gone by the middle of the face, with a lit lip on the
   left edge itself. It is the only place in this language where a colour
   reaches something it is not printed on.

   AND IT IS THE PART OF THAT IDEA THAT KEEPS OVERSHOOTING. The lip is one
   pixel of orange on the actuator's own edge, which is a correct observation —
   the cap is standing in a lit slot and its near edge catches it — and at 58%
   it stopped being a catch and became a STROKE, an orange outline down the
   side of a grey part. A pixel of colour reads twice as loud as a hundred
   pixels of the same colour, because it has a hard edge on both sides and
   nothing to average against.

   AND THE PERCENTAGE WAS NEVER THE BRIGHTNESS, which is why turning it down
   twice barely moved it. `color-mix(var(--lyr) 22%, #e6dbcd)` is not a dim
   orange — it is SEVENTY-EIGHT PER CENT CREAM with a little orange in it, an
   opaque near-white hairline down the side of a charcoal part, and lowering
   the first number made it WHITER. The number reads like an opacity and is a
   ratio between two colours, so the whole scale ran the wrong way.

   THE OTHER COLOUR IS THE FIX. Mixed into #2b2f35 — the cap's own mid grey —
   40% lands a warm edge a shade above the material instead of a bright line
   across it, and now the number does what it looks like it does. The bleed
   inboard of it is a genuine alpha and comes down to 12%.

   THE BLOOM CAME DOWN WITH IT, twice, and is now roughly a third of where it
   started — 5px at 15% and 11px at 7% on the machined dress. What a lamp under
   a 40x18 window throws onto the plate around it is not much. */
.slide.on .sl-act{
  box-shadow:
    inset 0 -2px 4px rgba(0,0,0,.42),
    inset 6px 0 8px -3px color-mix(in srgb, var(--lyr, var(--led)) 12%, transparent),
    inset 1px 0 1px color-mix(in srgb, var(--lyr, var(--led)) 40%, #2b2f35),
    0 1px 2px rgba(0,0,0,.85),
    0 3px 5px rgba(0,0,0,.7),
    0 6px 10px rgba(0,0,0,.42);
}
/* ── THE KNURL ─────────────────────────────────────────────────────────────
   THREE RIBS, ROUNDED, CUT INTO THE MIDDLE OF THE FACE. The first cut was a
   3px repeating gradient across the whole part: seven one-pixel ridges, which
   at this size the eye reads as NOISE rather than as grip, and which on a dark
   face turns to grey mush. A real grip has a countable number of grooves and
   they stop short of the edges, where the moulding radius is.

   AND EACH GROOVE IS A CROSS-SECTION, NOT A STRIPE. A ridge lit from above-left
   is bright on its left flank, falls through the crown, drops into a dark floor
   and comes back up — five stops per 5.2px, ramped rather than stepped, so the
   ribs read as round. Stepped stops give flat bands, which is exactly what
   "cheap" looked like.

   The overlay on top of it is the band's own vertical shading: the crowns
   catch the lamp at the top and the whole knurl sits in shade at the bottom,
   because the face it is cut into is curving away there. */
/* THE PITCH WAS RE-OPENED AND PUT STRAIGHT BACK. Halving it to 2.6px for six
   ribs is exactly the seven-ridge version described above, which was tried,
   turned to grey mush and got replaced by this. The note is why the note is
   here. What was actually cheap was never the count — it was that the ribs
   were painted in WARM WHITE on a face with no dome under it, so they read as
   ink rather than as cut. Same three ribs, same 5.2px, neutral now and a stop
   quieter, over a body that curves. */
.sl-act::before{
  content:''; position:absolute; left:50%; top:50%; width:15px; height:11px;
  transform:translate(-50%, -50%); border-radius:2px;
  background-image:
    linear-gradient(180deg,
      rgba(255,255,255,.11) 0%, rgba(255,255,255,.02) 24%,
      rgba(0,0,0,.05) 58%, rgba(0,0,0,.26) 100%),
    repeating-linear-gradient(90deg,
      rgba(255,255,255,.11) 0px,
      rgba(255,255,255,.025) 1.5px,
      rgba(0,0,0,.14) 2.3px,
      rgba(0,0,0,.40) 3.3px,
      rgba(0,0,0,.14) 4.1px,
      rgba(255,255,255,.11) 5.2px);
  box-shadow:inset 0 1px 1px rgba(0,0,0,.3);
}
/* ── THERE IS NO SPECULAR, AND THAT IS THE ANSWER ────────────────────────
   THIS HAS NOW BEEN A STREAK, A BLOB AND A DOT, and all three were the same
   mistake at different sizes: a separate layer of white put on top of the part
   to say "this is shiny". Every reference for it — the toggles, the knob
   collar, the two recessed keys — has NONE. What they have is a face whose own
   gradient does the whole job, over a very narrow range, with the light at the
   top and the value falling steadily to the bottom, and a single hairline of
   rim where the moulding radius breaks.

   THE RANGE IS THE POINT: #34383f to #1f2227 is about a stop and a half, and
   the parts read as SOLID because nothing on them is far from anything else.
   Wide-range shading is what makes small plastic look like a rendering of
   plastic. A moulded matte face has almost no specular to give — it scatters —
   and the light you do see on one is the ambient of the room arriving at a
   surface that is tilted slightly toward it.

   THE SIDES TURN AWAY INSTEAD. The 90° layer used to put a light pixel on the
   leading edge; it darkens both ends now, hard at the far one and soft at the
   near, because that is what a rounded edge does and it is the only shading
   here that is about form rather than finish. */
.tog:hover .sl-act{ filter:brightness(1.07); }

/* ═══════════════════════════════════════════════════════════════════════════
   VARIANT B — THE LIT PILL. Same part, same markup, same 46×22, one class
   ═══════════════════════════════════════════════════════════════════════════
   A CANDIDATE, NOT A SECOND PART. `.slide` and `.slide.pill` are the same
   object in two dresses and only one of them will survive; they are up
   together so the choice can be made by looking rather than by describing.
   Whichever loses gets deleted, not kept as an option — an option is a
   decision nobody made.

   WHAT IT ARGUES. The machined version puts the material in the ACTUATOR: a
   knurled moulded grip with the light on its broken edges, and the colour is a
   floor behind it.
   This one inverts that. The actuator goes matte charcoal and nearly vanishes,
   and the lit half becomes the whole object — a glowing window with a word in
   it, blooming out onto the plate. It is louder, it is legible from further
   away, and it is one more thing on a dark panel that GLOWS, which is either
   the point or the problem.

   THE LEGENDS ARE CUT INTO THE FLOOR AT BOTH ENDS AND THE ACTUATOR COVERS ONE.
   That is the mechanism the reference is drawing and it is honest: nothing
   changes the word, nothing swaps it, there is no state in it. ON is engraved
   at the left end and OFF at the right, permanently, and which one you can see
   is which end the actuator is not standing on.

   THE GRIP IS A DOT MATRIX rather than ribs, and that is the other half of the
   comparison: dimples read as moulded, ribs read as machined. Each dot is a
   PIT, not a bump — dark rim on the side facing the lamp, lit crescent on the
   far wall — which is the same reasoning as the recess under a key cap. */
.slide.pill{ border-radius:999px; }
.slide.pill .sl-bed{ inset:2px; border-radius:999px; }
.slide.pill.on .sl-bed{
  box-shadow:
    inset 0 1px 2px rgba(0,0,0,.45),
    inset 0 0 0 1px rgba(0,0,0,.35),
    0 0 5px color-mix(in srgb, var(--lyr, var(--led)) 10%, transparent),
    0 0 11px color-mix(in srgb, var(--lyr, var(--led)) 5%, transparent);
}
/* ── THE ENGRAVED ENDS ─────────────────────────────────────────────────────
   Silkscreened, not backlit: the word is INK on the floor, so it goes dark
   where the floor is lit and stays a dim grey where it is not. A light word on
   a lit panel is a word cut through to a lamp, which is a different part. */
.slide.pill .sl-bed::before,
.slide.pill .sl-bed::after{
  position:absolute; top:50%; transform:translateY(-50%);
  font-family:var(--font); font-size:7px; font-weight:700; letter-spacing:.08em;
  line-height:1; pointer-events:none;
  color:rgba(226,232,240,.13); text-shadow:0 1px 0 rgba(0,0,0,.7);
  transition:color .18s, text-shadow .18s;
}
.slide.pill .sl-bed::before{ content:'ON';  left:5.5px; }
.slide.pill .sl-bed::after { content:'OFF'; right:4.5px; }
.slide.pill.on .sl-bed::before{
  color:color-mix(in srgb, var(--lyr, var(--led)) 26%, #000);
  text-shadow:0 1px 0 color-mix(in srgb, var(--lyr, var(--led)) 55%, #fff);
}
/* ── THE ACTUATOR, MATTE ───────────────────────────────────────────────────
   Charcoal, four stops, no specular streak and no bright chamfer. It is the
   piece that is NOT lit, and everything about it stands back so the window
   does the talking. */
.slide.pill .sl-act{
  border-radius:999px;
  background-image:
    linear-gradient(180deg, rgba(255,255,255,.11) 0 1px, transparent 1px),
    linear-gradient(90deg,
      rgba(0,0,0,.24) 0%, transparent 16%,
      transparent 84%, rgba(0,0,0,.30) 100%),
    linear-gradient(180deg,
      #31353c 0%, #2b2f35 30%, #24272d 68%, #1d2025 100%);
  box-shadow:
    inset 0 -2px 4px rgba(0,0,0,.7),
    inset 0 -1px 1px rgba(255,255,255,.07),
    0 1px 2px rgba(0,0,0,.85),
    0 3px 5px rgba(0,0,0,.7),
    0 6px 10px rgba(0,0,0,.45);
}
.slide.pill.on .sl-act{
  box-shadow:
    inset 0 -2px 4px rgba(0,0,0,.5),
    inset 7px 0 9px -4px color-mix(in srgb, var(--lyr, var(--led)) 13%, transparent),
    inset 1px 0 1px color-mix(in srgb, var(--lyr, var(--led)) 44%, #2b2f35),
    0 1px 2px rgba(0,0,0,.85),
    0 3px 5px rgba(0,0,0,.7),
    0 6px 10px rgba(0,0,0,.45);
}
/* THE DIMPLES. Two circles per cell: the pit, and the crescent of its far wall
   catching the lamp half a pixel below. Five across, three down, in a patch
   that stops short of the ends. */
.slide.pill .sl-act::before{
  width:16px; height:10.5px; border-radius:0;
  background-image:
    radial-gradient(circle at 1.2px 1.2px, rgba(0,0,0,.62) .62px, transparent .72px),
    radial-gradient(circle at 1.2px 1.9px, rgba(226,232,240,.17) .62px, transparent .72px);
  background-size:3.5px 3.5px, 3.5px 3.5px;
  background-position:center, center;
  background-repeat:repeat, repeat;
  box-shadow:none;
}
/* THE ORIGINAL NOTE HERE SAID "NO SPECULAR ON A MATTE PART" and it was right
   the first time. It got argued out of on the grounds that matte scatters
   BROADLY rather than not at all — true of a big surface and useless at 22px,
   where a broad dim highlight is indistinguishable from the part simply being
   lighter. Both dresses have none now; the rule moved up to §A where it
   belongs, and B needs no override to say the same thing. What B still does
   differently is start a stop lower, so the cap stands back and the window is
   the object. */

/* ═══════════════════════════════════════════════════════════════════════════
   VARIANT C — THE ROCKER · ALTERNATIVE, WIP, NOT VALIDATED
   ═══════════════════════════════════════════════════════════════════════════
   NOTHING BELOW THIS LINE IS SETTLED. A and B are two dresses of one part and
   one of them is going to be deleted; C is a third thing that arrived after
   them and has not been picked. It has been rebuilt four times — an SVG
   trapezoid, one flat slab under a perspective, two folded faces, and finally
   the rigid bent slab that is here now — and every one of those rewrites
   changed the geometry rather than the finish, which is what an unsettled part
   looks like.

   IT IS FLAGGED ON THE PAGE, on its own cap, in §06. Do not copy it into an
   app yet: the class names, the element count and the state mapping have all
   moved inside a single afternoon and they may move again. What IS worth
   keeping out of it is written in the notes below — the projection arithmetic,
   the two-value crease, the wall normal, the rule about foreshortening pulling
   marks toward the seam. Those are true whatever this ends up being.
   ───────────────────────────────────────────────────────────────────────────
   THE FIRST TWO ATTEMPTS BOTH DREW THE WRONG MACHINE. Eight passes of stacked
   gradients gave a light pill in a dark slot, and the SVG that replaced them
   gave a trapezoid that MIRRORED — one geometry flipped about its centre, so
   throwing it read as a card turning over rather than a part moving. Both were
   solving the same problem the same wrong way: BAKING THE PERSPECTIVE INTO THE
   DRAWING. A shape with the foreshortening painted on has only two states and
   no way to be between them, and the in-between is the entire thing you feel
   when a switch throws.

   SO THE PERSPECTIVE IS REAL AND THE MOTION IS THE GEOMETRY:

     THE HOLE IS A HOLE. `.slide.rocker` is the CUT — its own black, its own
     overhang, and `overflow:hidden` on the well inside it, so the end that
     goes down is swallowed by the plate rather than drawn shorter. A rocker is
     a part you can see less of, not a part that shrinks.
     THE PADDLE IS ONE FLAT SLAB, `rotateY` about its own middle, under a
     120px perspective. The near end genuinely comes at you and the far end
     genuinely goes away — the taper, the difference in height end to end, the
     way the sunk end loses its edge, all of it falls out of the projection
     instead of being approximated by a path.
     THE PIVOT IS BEHIND THE FACE (`transform-origin` z of -7px), because the
     pin in a real one is inside the housing and not on the surface you touch.
     IT PASSES THROUGH FLAT. Halfway through the throw the paddle IS flat and
     the lighting IS even, which is exactly what the part does at the moment
     the spring goes over centre. Nothing mirrors, so nothing can flip.

   THE LIGHTING IS TWO OVERLAYS CROSS-FADING, not one gradient reversing. A
   gradient that swaps direction has to swap at some frame and that frame is a
   pop; two of them at opposing weights sum to even light exactly when the
   paddle is level. Same for the marks: they are printed on the panel's own
   left and right, and the one on the end that has turned away dims, because a
   surface out of the light does not show its legend either.

   AND THE CORNERS ARE SQUARE. It went 11px, then 6, then 4, and each step
   helped, which was the signal that the answer was 0 and the steps were
   flinching. A radius on a part this small is a TAX ON EVERYTHING ELSE: the
   end wall has to be inset to stay behind it, the seam cannot be a clean
   butt joint, the cut's own edge has to curve away where the light is
   strongest, and every one of those compromises spends detail on softening a
   corner nobody asked to be soft. The reference part is square. Take the
   radius out and the walls, the seam and the crease all get simpler at once
   — which is what SQUARE BUYS: not a look, a budget. The cut keeps 1.5px,
   because a hole drilled in a plate is not razored either. */
.slide.rocker{
  flex:0 0 auto; width:60px; height:30px; border-radius:1.5px;
  /* THE WALL OF THE CUT, blackest thing in the assembly, and darkest right
     under the top lip where nothing reaches. */
  background:linear-gradient(180deg,#020304 0%,#07090b 52%,#0c0f13 100%);
  box-shadow:
    inset 0 3px 4px rgba(0,0,0,.95),
    inset 1px 0 3px rgba(0,0,0,.7),
    inset 0 -1px 1px rgba(255,255,255,.07),
    0 1px 0 rgba(255,255,255,.06);
}
/* the CSS children are the other dresses' — this one has its own parts */
.slide.rocker .sl-bed,
.slide.rocker .sl-act{ display:none; }
/* ── THE WELL: the clip, and the eye ──────────────────────────────────────
   THE CLIP IS WHAT MAKES IT A HOLE. Without it the paddle's sunk end hangs
   out past the plate and the part is a tile lying on the panel. And the
   perspective origin sits ABOVE centre because a console is seen from above:
   you look slightly down into the cut, so the top wall is the one you see the
   inside of. */
/* ── WHAT STANDS PROUD THROWS ONTO WHAT IT STANDS ON ─────────────────────
   THE RAISED HALF IS OUT OF THE PLATE, so it has to darken the plate. Every
   other shadow on this part is INSIDE the cut and the well clips them all,
   which is right for them and wrong for this one: the shadow of something
   sticking up lands on the panel, outside the hole entirely, and a part that
   is proud of a surface and leaves that surface clean is a part pasted on.

   IT IS ITS OWN ELEMENT, ahead of the well and outside its clip, transparent
   with nothing but an OUTER box-shadow — a shadow drawn outside a border box
   never paints inside it, so this covers the plate and never the switch.
   `::before` is the lamp and `::after` is the cut's near lip; both are spoken
   for, and neither is outside the clip anyway.

   AND IT IS NOT THE SAME SHADOW IN BOTH POSITIONS, which is the whole reason
   it is worth having. The lamp is up and to the left, so what is thrown goes
   down and to the RIGHT — always, whichever end is up. With the right half
   raised that lands on open plate and reads long and dark. With the LEFT half
   raised it falls across the cut's own mouth, into a hole that is already
   black, and almost nothing reaches the panel. Same light, same part, two
   different marks, and drawing them the same is the tell. */
.slide.rocker .rk-cast{
  position:absolute; inset:0; border-radius:inherit; z-index:0;
  pointer-events:none;
  box-shadow:12px 6px 11px -3px rgba(0,0,0,.62);
  transition:box-shadow .16s;
}
.slide.rocker.on .rk-cast{ box-shadow:3px 6px 9px -3px rgba(0,0,0,.40); }

.slide.rocker .rk-well{
  position:absolute; inset:0; border-radius:inherit; overflow:hidden;
  perspective:100px; perspective-origin:50% 34%;
  pointer-events:none;
}
/* ── THE PADDLE IS A BENT SLAB ────────────────────────────────────────────
   FROM A SIDE-VIEW SKETCH OF THE REAL PART, which settled three arguments at
   once and killed the two versions before it.

   THE CROSS-SECTION IS `___/` — a slab of CONSTANT THICKNESS, level for its
   first half, kinked at the middle, rising for its second. Not a plane. Not
   two planes fanned symmetrically about a centre. A moulding with a permanent
   26° bend in it, and the bend never changes, because the part is RIGID.

   WHICH MAKES THE THROW OBVIOUS AND MAKES THE OLD ONE WRONG. If the bend is
   fixed and one half comes to rest LEVEL — which is what the sketch shows, and
   what the photograph shows, the sunk half lying flat in the plate — then the
   two positions are: left flat and right up 26°, or left up 26° and right
   flat. Never both leaning. Every version before this rotated the whole part
   and folded the halves symmetrically on top, so neither half was ever level
   and the thing read as a card creased down the middle.

   SO THERE IS NO PAD ROTATION AT ALL. Each half is either 0° or 26°, and each
   animates between its own two numbers:

       state        left face      right face
       ─────────────────────────────────────────
       off           0° flat       −26° raised
       on          +26° raised       0° flat

   THE DIFFERENCE IS ALWAYS 26°, at rest and on every frame in between, which
   is the definition of a rigid body and is why the throw looks like a part
   moving rather than two parts negotiating. Halfway across, the two are at
   +13° and −13° — the symmetric trough — and that is the moulding passing
   level, not a shape being interpolated.

   AND IT HAS THICKNESS, which is the other thing the sketch is about: the
   raised half shows its END WALL, a 4px cut edge square to its own face,
   hinged off the outer edge and folded back into the part. It is the only
   surface here facing the lamp head-on and it is the whole of what says
   SOLID. The flat half has one too and you never see it — at 0° that wall is
   exactly edge-on, which is correct and costs nothing.

   THE PART SITS 3px INTO THE PLATE (`translateZ` on the pad), so the level
   half is BELOW the rim rather than flush with it. A rocker whose sunk half
   is level with the panel has not been pressed, it has been drawn. */
.slide.rocker .rk-pad{
  position:absolute; inset:3px 2px;
  transform-style:preserve-3d;
  transform:translateZ(-4px);
  /* THE SHADOW STAYS ON THE PAD, not on the faces. Put it on each face and
     the two shadows meet at the seam and draw a dark seam of their own, which
     is the one line on the part that has to be built out of value. */
  box-shadow:0 4px 7px rgba(0,0,0,.8);
}

/* ── THE TWO HALVES ───────────────────────────────────────────────────────
   EACH HINGES ON THE SEAM: the left one's origin is its RIGHT edge, the right
   one's its LEFT, so the two share one line and cannot drift apart however
   far either turns.

   A SPRING GOES OVER CENTRE AND THE PART HITS ITS STOP. Fast, and just enough
   overshoot to be a stop being hit rather than a value being eased to — a long
   soft curve here is the single thing that made the first version feel like
   software.

   AND THERE IS NO RADIUS ON EITHER, which is what lets the end wall run the
   FULL height of its half rather than being inset to hide behind a corner it
   would otherwise poke through. Square corners are why the thickness can be
   the thickness. */
/* THE PLASTIC IS THE KNOB'S PLASTIC, and not a family of its own. It had been
   running #171c23 to #525a65 — a blue-grey whose light end is a full stop
   above anything else on the panel, which is what made a moulded switch arrive
   as a machined one. §05's cap is the material this system already has for
   dark plastic and it tops out at #3a3e45, so the face is that cap's own four
   values top-lit. A part invented its own palette; the fix was to stop. */
.slide.rocker .rk-f{
  position:absolute; top:0; width:50%; height:100%;
  transform-style:preserve-3d;
  background:linear-gradient(180deg,#3a3e45 0%,#34383f 8%,#23262b 62%,#1c1e22 100%);
  transition:transform .16s cubic-bezier(.34,1.28,.5,1);
}
.slide.rocker .rk-f-l{
  left:0;
  transform-origin:100% 50%; transform:rotateY(0deg);
}
.slide.rocker .rk-f-r{
  right:0;
  transform-origin:0 50%; transform:rotateY(-42deg);
}
.slide.rocker.on .rk-f-l{ transform:rotateY(42deg); }
.slide.rocker.on .rk-f-r{ transform:rotateY(0deg); }
/* THE FINGER GETS AN ANSWER BEFORE THE STATE DOES. Pressing takes the raised
   half down about 40% of its travel and brings the level one up by the same
   amount — the part still rigid, still 26° across the bend, just short of the
   detent. That is the travel before the spring goes, which is the half of the
   action a click handler cannot show you. */
.tog:active .slide.rocker .rk-f-l{ transform:rotateY(17deg); }
.tog:active .slide.rocker .rk-f-r{ transform:rotateY(-25deg); }
.tog:active .slide.rocker.on .rk-f-l{ transform:rotateY(25deg); }
.tog:active .slide.rocker.on .rk-f-r{ transform:rotateY(-17deg); }

/* ── THE END WALL: the cut edge of the moulding ───────────────────────────
   HINGED OFF THE OUTER EDGE AND FOLDED BACK, which is the only way to get a
   wall that stays square to a face that is turning. 90° about that edge sends
   its 5px straight into -Z, so the wall is the part's thickness and nothing
   else has to know about it.

   A WALL HAS TWO THINGS TO GET RIGHT AND THEY PULL AGAINST EACH OTHER: where
   its body goes, and which way its face LOOKS. The first build got the body
   right and the normal backwards, and the whole thing was invisible — a strip
   whose front pointed back at the middle of the paddle, so what you had a line
   of sight on was its unlit reverse, behind the face that was casting it.
   Nothing renders and nothing errors, which is the worst kind of wrong.

   An element's face looks along +Z, and rotateY(90°) turns that to +X while
   sweeping the element's own width into -Z. So the strip has to START at the
   outer edge and run OUTWARD — `left:100%` on the right half, `right:100%` on
   the left — with the origin on the edge it hinges about. Put it inside the
   face instead and the same rotation lands the body in the same place with the
   normal reversed, which is exactly the bug.

   IT NEEDS NO STATE. At 0° the wall is edge-on and projects to nothing; at 42°
   it opens to about 6.7px of lit cut edge. The geometry hides and shows it, so
   there is no rule here that can fall out of step with the rule above.

   AND THE NUMBER TO WATCH IS NOT THE DEPTH, IT IS THE PROJECTION. Nine pixels
   of wall at 26° looks like four until you work it through the lens, and then
   it is 1.44 — the far edge of the wall is a third of the way to the eye
   further back than the near one, so perspective eats nearly two thirds of it
   before it reaches the screen. That is why three passes of `make the wall
   thicker` moved almost nothing. What opens a wall is the ANGLE, because the
   angle turns it toward you and shortens the run away from you at the same
   time. 26°/9px gives 1.44px. 42°/13px gives 6.7px — four and a half times the
   edge for less than half again the material. Measure the projection, never
   the part. */
/* AND THE CUT EDGE IS THE SAME PLASTIC AS THE FACE. It was #7b8490 at its top
   — brighter than any surface in §05 and the single loudest thing on the
   panel, which is a lot to spend on 6px of a switch. It is the brightest
   surface HERE because it faces the lamp square, and that is a ranking within
   one material, not a licence for a second one. */
.slide.rocker .rk-w{
  position:absolute; top:0; height:100%; width:13px;
  background:linear-gradient(180deg,#3a3e45 0%,#2b2f35 50%,#1c1e22 100%);
}
.slide.rocker .rk-f-l .rk-w{ right:100%; transform-origin:100% 50%; transform:rotateY(-90deg); }
.slide.rocker .rk-f-r .rk-w{ left:100%;  transform-origin:0 50%;    transform:rotateY(90deg); }

/* ── WHICH HALF IS IN THE LIGHT ───────────────────────────────────────────
   TWO LAYERS PER HALF, CROSS-FADING, and every half carries both: `::before`
   is how it looks RAISED, `::after` how it looks lying flat in the plate.
   They swap together with the tilt, and because they are opacities rather
   than one gradient reversing, the moment halfway through the throw is both
   at half — even light on a level paddle, exactly right, and impossible to
   get from a gradient that has to flip on some frame.

   THE CREASE IS THE VALUE STEP BETWEEN THEM, not a line drawn on either. A
   half tilted 26° toward the lamp against a half lying flat in a hole is
   already the largest difference on the part; the seam is where those two
   meet and it needs no help. What it gets is one hard band, 3% of a half
   wide, at the RAISED side's inner edge — the bottom of the bend, the one
   place on that half the light cannot reach. Dark against dark, then bright
   above it. That is a fold. */
.slide.rocker .rk-f::before,
.slide.rocker .rk-f::after{
  content:''; position:absolute; inset:0; border-radius:inherit; z-index:0;
  transition:opacity .16s linear;
}
/* THE TWO HALVES ARE THE SAME PIECE OF PLASTIC, and the first pass forgot it:
   the raised half went to +20% white and the level one under a 78% black
   veil, which is a two-stop range on a part that has one material and one
   lamp. It read as a lit tile beside a dead one. Tilt buys a MODEST amount of
   extra light — enough to tell which half is up, nowhere near enough to change
   what the half is made of. What carries the read is the crease and the cut
   edge, which is the right way round: geometry says which half is up, shading
   only agrees.

   AND THE LIFT IS A SPECULAR, NOT A WASH. A broad white ramp along the raised
   half is how it went grey: spread thin enough to cover a face, white stops
   being a highlight and becomes the colour of the material, and dark plastic
   lit across its whole width is aluminium. The knob had already solved this —
   ONE tight bright spot out to 46% of a radius, everything else left alone —
   so the raised half gets the same thing at the same .30, sitting over its
   outer end. Small and bright reads as a lamp on plastic. Wide and pale reads
   as metal, every time. */
/* RAISED: brightest at the outer end, which is the end nearest the lamp and
   the end the cut edge is on, falling away to the fold */
.slide.rocker .rk-f-l::before{
  background:
    radial-gradient(circle at 16% 24%, rgba(255,255,255,.20) 0%, transparent 46%),
    linear-gradient(90deg,
      rgba(0,0,0,0) 0%, rgba(0,0,0,0) 50%,
      rgba(0,0,0,.18) 86%, rgba(0,0,0,.24) 97%,
      rgba(0,0,0,.44) 97.01%, rgba(0,0,0,.44) 100%);
}
.slide.rocker .rk-f-r::before{
  background:
    radial-gradient(circle at 84% 24%, rgba(255,255,255,.20) 0%, transparent 46%),
    linear-gradient(270deg,
      rgba(0,0,0,0) 0%, rgba(0,0,0,0) 50%,
      rgba(0,0,0,.18) 86%, rgba(0,0,0,.24) 97%,
      rgba(0,0,0,.44) 97.01%, rgba(0,0,0,.44) 100%);
}
/* FLAT: level, in the plate, under its overhang. EVEN, and that evenness is
   the point — a flat plane has no reason to grade across itself, and the
   gentle turn-down at the outer end is the hole's own top wall reaching over
   it, not the part doing something. */
.slide.rocker .rk-f-l::after{
  background:linear-gradient(90deg,
    rgba(0,0,0,.42) 0%, rgba(0,0,0,.30) 46%, rgba(0,0,0,.34) 100%);
}
.slide.rocker .rk-f-r::after{
  background:linear-gradient(270deg,
    rgba(0,0,0,.42) 0%, rgba(0,0,0,.30) 46%, rgba(0,0,0,.34) 100%);
}
/* OFF is the right half raised, which is the half the ring is on. */
.slide.rocker .rk-f-l::before{ opacity:0; }
.slide.rocker .rk-f-l::after { opacity:1; }
.slide.rocker .rk-f-r::before{ opacity:1; }
.slide.rocker .rk-f-r::after { opacity:0; }
.slide.rocker.on .rk-f-l::before{ opacity:1; }
.slide.rocker.on .rk-f-l::after { opacity:0; }
.slide.rocker.on .rk-f-r::before{ opacity:0; }
.slide.rocker.on .rk-f-r::after { opacity:1; }
/* ── THE LEGENDS: A GRIP AND A LENS ───────────────────────────────────────
   `|` AND `○` WERE THE WRONG PAIR, and the reason is not that they are the
   wrong symbols — they are the right ones, on a part that is not asking that
   question. IEC's pair says MAINS ON and MAINS OFF, and half of it is always
   the mark for the state you are NOT in, sitting there in the dark being read
   as an option. At 11px a side there is no room to also explain that one of
   them is a label and the other is a leftover.

   SO THERE IS ONE LEGEND, NOT TWO, AND IT IS A LAMP. The right half carries a
   round lens in a black bezel: dark when the switch is off, lit through when
   it is on. Nothing has to be read — a lit thing is on. That is the whole of
   why this beat the bar, which lit perfectly well and still looked like a
   glowing dash rather than a light behind glass. A ring reads as a LENS
   because a bezel is a thing that holds glass, and a dash is a thing that is
   printed.

   AND THE OTHER HALF IS NOT BLANK, IT IS THE GRIP. A rocker's other end is
   where your thumb goes, so it gets the knurl §04's pill switch already uses,
   at the same numbers — a pit and the crescent of its far wall catching the
   lamp half a pixel below, 3.5px on centre. Texture, not a symbol: it says
   PRESS HERE, which is true of that end in both states, rather than naming a
   state that is not the one you are in.

   ON IS THE RIGHT HALF LYING FLAT, AND THAT IS WHERE THE LAMP IS. It was
   built the other way up first — lamp on the end that stands proud, on the
   theory that a light aimed at you beats a light down in the plate. That
   theory is about lamps and this is a SWITCH: you press a side down and it
   STAYS down for as long as the thing is on, so the pressed side IS the state
   and the lit legend belongs on it. Every illuminated rocker on a wall works
   this way, and the giveaway that the other version was wrong is that its
   light moved to whichever end happened to be up — which is a lamp with an
   opinion about geometry rather than about the circuit.

   WHICH ALSO PUTS THE LIT LENS ON THE FLAT FACE, so it is seen square and
   whole rather than foreshortened 42° away — the glow reads as a round lamp
   instead of an ellipse, and it stops moving under your eye as the part
   throws. What the earlier version gained in aim it lost twice over in shape.

   AND BOTH MARKS SIT NEAR THEIR OUTER ENDS, NOT MID-HALF, which is a
   consequence of the 42° and not a taste. A mark centred in its half is at 14
   of 28, and once that half turns 42° it projects to 68.7% of the switch —
   four per cent from where the same mark sits when the half is level. It read
   as a ring near the middle that changes colour rather than as the right-hand
   lamp. Out at 18 of 28 it lands at 74.6% turned against 79.2% flat: still
   barely any travel, but unmistakably at one END. Foreshortening pulls
   everything toward the seam, so anything that has to stay in its half has to
   start further out than looks right on the flat. */
.slide.rocker .rk-mk{
  position:absolute; top:50%; z-index:1; box-sizing:border-box;
  transform:translateY(-50%);
  transition:opacity .16s;
}
/* the thumb pad — §04's knurl, same pit, same crescent, same 3.5px pitch */
.slide.rocker .rk-grip{
  left:5px; width:14px; height:11px;
  background-image:
    radial-gradient(circle at 1.2px 1.2px, rgba(0,0,0,.62) .62px, transparent .72px),
    radial-gradient(circle at 1.2px 1.9px, rgba(226,232,240,.17) .62px, transparent .72px);
  background-size:3.5px 3.5px, 3.5px 3.5px;
  background-position:center, center;
  background-repeat:repeat, repeat;
  opacity:.52;
}
/* THE MARK IS THE RING, AND THE RING IS WHAT LIGHTS. First pass lit the disc
   inside it and kept the ring black around the outside — a lit lens in a
   bezel, which is a real object and the wrong one. It fills in the O. What is
   on this paddle is a legend CUT THROUGH the moulding, and the cut is the
   stroke: light comes through the shape you drew and nowhere else, so the
   middle stays plastic. A filled circle is a different symbol from an O, and
   at 9.5px it is the only difference there is room for. */
.slide.rocker .rk-lens{
  right:5px; width:9.5px; height:9.5px; border-radius:50%;
  border:2px solid #04060a; box-shadow:0 .6px 0 rgba(255,255,255,.15);
  opacity:1;
}
/* LIT IS THE STROKE GOING HOT AND THROWING. The border takes the lamp colour
   with a little white in it — the core of a lit edge is always paler than the
   light it is made of — and three shadows carry it out to 20px. No inset: an
   inner glow is colour in the middle of the O, which is the thing this is not.
   The glows are §04's jewel radii walked in one step, because a 2px stroke
   throws less far than a 11px lamp and a bloom wider than its source reads as
   fog rather than as light. */
.slide.rocker.on .rk-lens{
  opacity:1;
  border-color:color-mix(in srgb,var(--lyr,var(--led)) 84%,#fff);
  box-shadow:
    0 0 4px color-mix(in srgb,var(--lyr,var(--led)) 80%,transparent),
    0 0 11px color-mix(in srgb,var(--lyr,var(--led)) 45%,transparent),
    0 0 20px color-mix(in srgb,var(--lyr,var(--led)) 22%,transparent);
}
/* THE LEGEND ON THE HALF THAT HAS GONE LEVEL DIMS — a surface out of the
   light does not show its legend either. WITH ONE EXCEPTION, and it is the
   whole point of the part: the lens on the flat half does not dim when it is
   LIT, because a lamp is not a surface catching light, it is a source. The
   dimming rule is about reflectance and it does not apply to something making
   its own. */
.slide.rocker.on .rk-grip{ opacity:1; }
/* ── THE BROKEN EDGE OF THE CUT, over the part that sits in it ────────────
   The plate is thick and the paddle is inside it, so the near lip of the hole
   is IN FRONT of the switch and has to be drawn after it. Every inset here
   carries blur for the reason the top of this file gives: an offset with none
   pulls away from the radius at each corner and leaves a dot in the turn. */
.slide.rocker::after{
  content:''; position:absolute; inset:0; border-radius:inherit; z-index:3;
  pointer-events:none;
  box-shadow:
    inset 0 2.5px 3px rgba(0,0,0,.75),
    inset 1.5px 0 3px rgba(0,0,0,.45),
    inset 0 -2px 2px rgba(0,0,0,.4),
    inset 0 -1px 1px rgba(255,255,255,.10);
}

/* ── THERE IS NO PILOT LAMP. THE LENS IS THE LAMP ─────────────────────────
   A SEPARATE LED ABOVE THE SWITCH IS A SECOND OBJECT SAYING THE SAME THING
   the switch has already said with its own position, and the assembly was
   paying for it twice: once in the 15px of plate it needed above the cut, and
   once in the reading — eye goes to the lamp, then down to the switch, then
   back up to check they agree. They always agree. That is the definition of a
   part that is not needed.

   SO THE LIGHT MOVED INTO THE PART, and it went into the LENS rather than into
   a legend — see the block above. Every illuminated rocker ever made works the
   same way: the mark is not printed on the plastic and lit from outside, it is
   a hole in the plastic with a lamp behind it. The rule lives with the lens
   now, which is where the numbers are. */

/* ── THE ASSEMBLY — the row stands up ─────────────────────────────────────
   `.tog` is a line: switch, then name. A rocker is a COLUMN, so the same
   button turns ninety degrees and the name becomes an engraved plate under the
   switch — which is what a console does, because a bank needs its names in a
   row you can read across.

   AND THE 20px ABOVE IT WENT WITH THE LAMP. That padding was clearance for a
   part that no longer exists, and clearance for nothing is just a bank that
   does not stack as tight as it should. */
.tog.stack{
  flex-direction:column; gap:7px; width:auto;
  min-height:0; padding:5px;
}
.tog.stack .name{
  max-width:64px; text-align:center;
  font-size:8px; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  color:#6d727a; text-shadow:0 1px 0 rgba(255,255,255,.08);
}
.tog.stack.off .name{ color:#585d65; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE TOGGLE — a switch mounted on the faceplate, its name engraved beside it
   ═══════════════════════════════════════════════════════════════════════════
   THERE IS NO PLATE, AND THAT IS THE PART. A `.row` is a module card, and a
   card is a claim: this is a thing with contents, a colour and an editor
   behind it. A boolean has none of that. A settings bay engraves its names
   straight onto the metal and mounts the control on the face, and nineteen
   booleans each on their own raised card is a stack of pallets rather than a
   panel.

   THE WHOLE LINE IS THE BUTTON, so the hit target is the name — which is what
   you were aiming at — rather than a 24px cap floating at the end of it. The
   only thing saying the line is live before you press it is the metal warming
   under the pointer, which is what a faceplate does and what a card cannot do
   without moving. */
.tog{
  display:flex; align-items:center; gap:9px; width:100%;
  min-height:30px; padding:2px 6px; border:0; border-radius:6px;
  background:transparent; cursor:pointer; text-align:left;
  font-family:inherit; position:relative;
  transition:background .12s;
}
.tog:hover{ background:rgba(255,255,255,.035); }
.tog:active{ background:rgba(0,0,0,.20); }
/* THE NAME IS THE ROW'S NAME, ONE SIZE DOWN. A setting sits under a bay cap
   that has already said what the bay is, so its own name does not need the
   12px a layer's does — and at 11.5px a column of them lines up with the
   engraved captions beside it instead of shouting over them. */
.tog .name{ font-size:11.5px; }
.tog.off .name{ color:#8b9099; }
/* OFF IS DIMMER, NOT GREYED OUT. `.row.off` takes the name down to #6a6f77
   because a hidden layer is a layer you have parked; a boolean that is off is
   still the control you are about to press, and at that grey a column of
   mostly-off switches reads as a panel of disabled ones. */

/* THE PIPE IS A LAYER'S OWN INK, and it only appears when the switch was given
   a colour. A pipe on every boolean is a column of light saying nothing, which
   is the fastest way to spend the one signal in this language that carries
   across a room. */
.tog .pipe{ flex:0 0 3px; align-self:stretch; margin:4px 2px 4px 0; }
.tog.off .pipe{ background:#2a2c30; box-shadow:inset 0 1px 1px rgba(0,0,0,.8); }
.tog.off .pipe::after{ display:none; }

/* ── the jewel LED: the layer's colour as a lens ─────────────────────────── */
.jewel{
  width:11px; height:11px; flex:0 0 11px; border-radius:50%; position:relative;
  background:
    radial-gradient(circle at 34% 28%, #fff 0%, color-mix(in srgb,var(--lyr) 60%,#fff) 16%, var(--lyr) 52%, color-mix(in srgb,var(--lyr) 55%,#000) 100%);
  box-shadow:
    0 0 6px color-mix(in srgb,var(--lyr) 75%,transparent),
    0 0 15px color-mix(in srgb,var(--lyr) 40%,transparent),
    inset 0 -1px 2px rgba(0,0,0,.55),
    0 0 0 1px rgba(0,0,0,.55);
}
.jewel.all{ background:var(--all-grad); }
/* the ALL row is the only jewel left in the dock */
.row.off .jewel{ filter:grayscale(1) brightness(.42); box-shadow:inset 0 -1px 2px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.6); }

/* ── the name: laser-etched infill on the faceplate ──────────────────────── */
.name{
  flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-family:var(--font); font-size:12px; font-weight:700; letter-spacing:.02em;
  color:#dfe2e7; text-shadow:0 1px 0 rgba(0,0,0,.85);
}
.sum{
  flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-family:var(--font); font-size:9.5px; letter-spacing:.04em; color:#61666f;
}
.row.off .name{ color:#6a6f77; }

/* TONE — an etched brass plate. A different METAL is how hardware says "a
   different kind of thing"; a second colour would have fought the light pipe. */
.tone{
  flex:0 0 auto; font-family:var(--font); font-size:8.5px; font-weight:700; letter-spacing:.09em;
  padding:2px 5px; border-radius:3px; color:#2a1c05;
  background:linear-gradient(180deg,#E8B45C,#B8862F);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55), inset 0 -1px 0 rgba(0,0,0,.35), 0 1px 2px rgba(0,0,0,.6);
  text-shadow:0 1px 0 rgba(255,255,255,.28);
}

/* micro keys — duplicate / delete. Recessed, not raised: they are destructive
   or duplicative and should not invite the thumb the way the switches do. */
.micro{
  width:22px; height:22px; flex:0 0 22px; border-radius:5px;
  display:flex; align-items:center; justify-content:center;
  border:0; padding:0; cursor:pointer; color:#575c65;
  background:linear-gradient(180deg,#151719,#1e2024);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.8), inset 0 -1px 0 rgba(255,255,255,.06);
  transition:color .12s, box-shadow .12s;
}
.micro svg{ width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
.micro:hover{ color:#d7dae0; box-shadow:inset 0 1px 2px rgba(0,0,0,.8), 0 0 0 1px rgba(255,255,255,.09); }
.micro.del:hover{ color:#FF6B6B; box-shadow:inset 0 1px 2px rgba(0,0,0,.8), 0 0 0 1px rgba(255,45,45,.35), 0 0 8px rgba(255,45,45,.2); }
.micro:disabled{ opacity:.3; cursor:not-allowed; }

/* the milled channel between the row's actions and its chevron */
.sep{ width:1px; height:18px; flex:0 0 1px; margin:0 3px;
      background:#0a0b0c; box-shadow:1px 0 0 rgba(255,255,255,.07); }

/* the chevron is a knurled thumbwheel edge — grip texture, so it reads as the
   thing you flick rather than a thing you press */
/* Knurled — but the teeth stay UNDER the glyph. At 20px a 3px knurl and an
   11px chevron are the same spatial frequency, and the eye loses the chevron
   in the texture: the grip has to be a whisper (.05) and the glyph has to be
   brighter than anything around it, with a dark halo holding it off the metal. */
.chev{
  width:20px; height:22px; flex:0 0 20px; border-radius:4px;
  display:flex; align-items:center; justify-content:center;
  border:0; padding:0; cursor:pointer; color:#aeb4be;
  /* POSITIONED, like every other control with a chamfer. Without it the
     .chev.open::after ring resolved against .row — a 2px chamfer drawn around
     the whole row and then clipped away by its overflow, so the one control
     the rule names by name was the one control not getting it. */
  position:relative;
  background-image:repeating-linear-gradient(72deg, rgba(255,255,255,.05) 0 1px, rgba(0,0,0,.16) 1px 3px);
  background-color:#202226;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.10), inset 0 -1px 0 rgba(0,0,0,.6), 0 1px 1px rgba(0,0,0,.5);
  transition:transform .16s, color .12s, box-shadow .12s;
}
.chev svg{ width:11px; height:11px; stroke:currentColor; fill:none; stroke-width:2.4;
  stroke-linecap:round; stroke-linejoin:round; filter:drop-shadow(0 1px 1px rgba(0,0,0,.9)); }
.chev:hover{ color:#fff; }
.chev.open{ color:var(--led); }
.chev.open svg{ transform:scale(.9); filter:drop-shadow(0 0 3px rgba(255,130,30,.65)); }

/* ── the ALL row is a BUS BAR — a different material, once ───────────────── */
.row.all{
  background:
    repeating-linear-gradient(96deg, rgba(255,255,255,.045) 0 1px, transparent 1px 2px),
    linear-gradient(180deg,#3a3e45,#2a2d33 55%,#232629);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.20),
    inset 0 -1px 0 rgba(0,0,0,.75),
    0 2px 4px rgba(0,0,0,.6);
}
.row.all:hover{ background:
    repeating-linear-gradient(96deg, rgba(255,255,255,.055) 0 1px, transparent 1px 2px),
    linear-gradient(180deg,#43474f,#31353b 55%,#282b30); }
/* ALL never truncates — it is three characters and the row's whole subject. */
.row.all .name{ letter-spacing:.14em; flex:0 0 auto; }

/* selected — the module is UNDER power: a rim of its own colour and a lit face */
.row.sel{
  background:linear-gradient(180deg,#34383f,#232629 60%,#1e2023);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.16),
    inset 0 -1px 0 rgba(0,0,0,.7),
    0 0 0 1px color-mix(in srgb, var(--lyr) 55%, transparent),
    0 0 12px -2px color-mix(in srgb, var(--lyr) 45%, transparent),
    0 2px 5px rgba(0,0,0,.65);
}

/* ── the stroke strip: a sub-bay, wired to its row by the light pipe's rail ── */
.strokes{
  display:flex; align-items:center; gap:4px;
  margin:4px 8px 2px 22px; padding:6px 8px;
  border-radius:6px;
  border-left:2px solid var(--lyr);
  background:linear-gradient(180deg,#191c20,#22262b);
  box-shadow:inset 0 2px 5px rgba(0,0,0,.75), 0 1px 0 rgba(255,255,255,.055),
             -3px 0 8px -4px color-mix(in srgb, var(--lyr) 60%, transparent);
}
.strokes.allstrip{ margin-left:16px; border-left-color:#4a4e56; box-shadow:inset 0 2px 5px rgba(0,0,0,.75), 0 1px 0 rgba(255,255,255,.055); }
.gap{ width:8px; flex:0 0 8px; height:16px;
      background:linear-gradient(90deg,transparent,#000,transparent); opacity:.6; }

/* ── the footer: a guarded action. Hazard stripes are the affordance — this
   is the one control in the panel that destroys work. ────────────────────── */
.foot{ padding:0 9px 12px; position:relative; z-index:2; }
.wipe{
  width:100%; height:32px; gap:8px; border-radius:8px;
  font-family:var(--font); font-size:10.5px; font-weight:700; letter-spacing:.06em;
  color:#8d929b;
}
.wipe svg{ width:13px; height:13px; }
.wipe:hover:not(:disabled){
  color:#FFD3D3;
  background:linear-gradient(180deg,#5c1414,#3a0d0d 55%,#280909);
  box-shadow:
    inset 0 1px 0 rgba(255,140,140,.3),
    inset 0 -1px 0 rgba(0,0,0,.8),
    0 2px 0 #180505,
    0 0 12px rgba(255,45,45,.35);
}
.wipe:hover:not(:disabled)::before{
  content:''; position:absolute; inset:0; border-radius:8px; pointer-events:none; opacity:.14;
  background:repeating-linear-gradient(45deg,#FF2D2D 0 6px, transparent 6px 12px);
}

.key.skirt{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.16),
    inset 0 -1px 0 rgba(0,0,0,.7),
    0 3px 0 #0e0f11,
    0 5px 8px rgba(0,0,0,.7);
}
/* ── EVERY CONTROL THAT IS IN, DRAWN THE ONE WAY ─────────────────────────
   Finger-held and mechanism-held are the same physical position, so they are
   the same rule. None of them declares a fill: each inherits its own raised
   face, which is what keeps a pressed cap recognisably the same object as the
   cap next to it, and means tuning a surface tunes both its states at once.

   Travel is 2px on the big keys and 1px on the small ones, down from 3-4px.
   With the walls and the chamfer doing the work the drop no longer has to
   carry the message, and a tab that fell 4px was moving further than the gap
   to its neighbour. */
.key.skirt:active,
.key.skirt.is-down,
.pkey:active,
.pkey.is-down,
.chip:active,
.chip.is-down,
.chev.open{
  transform:translateY(2px);
  box-shadow:var(--recess);
}
/* the small ones travel half as far — 2px on a 24px cap is a lurch */
.sw:active, .sw.on, .sw.mixed{ transform:translateY(1px); }
.chev.open{ transform:rotate(90deg) translateY(1px); }

/* THE CHAMFER, on everything with a hole to sit in. --ring-r is the control's
   own radius plus the 2px the ring sits outside it. */
.sw.on::after, .sw.mixed::after, .sw:active::after,
.key.skirt.is-down::after, .key.skirt:active::after,
.pkey.is-down::after, .pkey:active::after,
.chip.is-down::after, .chip:active::after,
.chev.open::after{
  content:''; position:absolute; inset:-2px; border-radius:var(--ring-r,9px);
  pointer-events:none; padding:1.6px; background:var(--chamfer);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  filter:blur(.5px);
}
.sw{ --ring-r:9px; }
.key{ --ring-r:9px; }
.pkey{ --ring-r:10px; }
.chip{ --ring-r:9px; }
.chev{ --ring-r:6px; }
/* LATCHED: down AND lit. The lamp is what separates "held by a finger" from
   "held by the mechanism". */
/* LATCHED: down AND lit. The lamp is what separates "held by a finger" from
   "held by the mechanism" — but the cap being 3px lower than its neighbours is
   already most of the message, so the lamp is a warm glyph and a little light
   in the well, not a bloom on the chassis. */
/* LATCHED IS EXACTLY WHAT :active ALREADY DRAWS, held. That state was right
   all along and the latched one kept inventing a darker cap instead of reusing
   it — which is why a lit key read as a hole rather than as a key that is in.

   The recipe, and it is the whole of it: the cap keeps ITS OWN COLOUR, the
   gradient INVERTS (dark at the top, light at the bottom — the bezel is now
   shading the top of a face that has dropped below it, and the light that
   reaches it bounces off the well), a hard black inset runs along the top
   edge, and a hairline of light sits along the bottom. Nothing is tinted, so
   the cap you press and the cap that is held down are recognisably the same
   object in two positions.

   Then the LEGEND lights: orange, with just enough glow to read as backlit
   rather than as printed. That is where all the colour in this state lives. */
.key.is-down{ color:var(--led); }
.key.is-down svg{ filter:drop-shadow(0 0 3px rgba(255,130,30,.7)); }
.key.is-down:hover{ color:var(--led-hot); background:linear-gradient(180deg,#1d1f24,#282b30); }
/* MOMENTARY actuation: the lamp pulses once and dies. Added for 260ms on
   click. A key that does something and shows nothing is a key you press twice. */
@keyframes actuate{
  0%   { box-shadow:inset 0 2px 5px rgba(0,0,0,.85), 0 0 0 1px rgba(255,106,0,.65), 0 0 16px rgba(255,106,0,.55); color:#FFD9BC; }
  100% { box-shadow:inset 0 1px 0 rgba(255,255,255,.16), 0 3px 0 #0e0f11, 0 5px 8px rgba(0,0,0,.7); }
}
.key.flash{ animation:actuate .26s ease-out; }

/* ── THE SAME SURGE WHEN A TOGGLE GOES IN ────────────────────────────────
   A momentary key pulses once when you hit it, and that pulse turned out to
   be the most convincing thing on the panel — it is the only moment the UI
   behaves like something with current in it. A toggle deserves it more, not
   less: a momentary key's pulse reports an event, a toggle's reports that a
   circuit has just closed and is now staying closed.

   IT IS LITERALLY THE MOMENTARY KEY'S ANIMATION. I wrote a second one for
   toggles — a filter with a brightness ramp — on the theory that a latching
   control wanted its own gesture. It did not. Two surges that are nearly the
   same read as one done badly, and the filter version was the worse of the
   two: brightness lifts the whole cap, including the shadow inside it, so the
   recess washes out at exactly the moment the eye is on it.

   The good one puts the light OUTSIDE — a hot rim on the cutout and a 16px
   bloom on the panel around it — and leaves the cap alone. Same keyframe shape
   as :active, same 260ms, and the only difference is where it lands: the
   momentary key settles back to its raised shadow, this one settles into the
   recess, because that is where a toggle stops. */
/* THE LEGEND IS ORANGE FROM FRAME ONE. It was starting at #FFD9BC — near
   white — and animating to the accent, so for a quarter second the icon was
   pale and only arrived at orange as the glow died: the switch appeared to
   turn on AFTER the surge rather than because of it. That washed-out start
   works on the momentary key, where the lamp is the whole event and there is
   no lit state waiting at the end. Here there is one, so the colour is simply
   already there and the animation only carries the light around it. */
@keyframes engage{
  0%   { box-shadow:var(--recess),
                    0 0 0 1px rgba(255,106,0,.65),
                    0 0 16px rgba(255,106,0,.55); }
  100% { box-shadow:var(--recess); }
}
.engage{ animation:engage .26s ease-out; }

/* ── NO FOCUS RING, AT ALL ───────────────────────────────────────────────
   Skew paints no focus indicator and lets no browser paint one either.
   `*:focus` alone is not enough: deleting our outlines does not remove a
   focus ring, it removes the one we control — Chrome's default comes back
   underneath, 2-3px of white-blue. `:focus-visible` has to be answered
   explicitly, not ignored. Same for Firefox's inner border on buttons and
   the highlight flash on touch.

   Honest note, and it is in the bill: this removes the WCAG focus indicator
   entirely. Keyboard focus moves invisibly across every control on the
   panel. Deliberate — a rectangle floating off a moulded cap is the one
   thing here that is unmistakably a web page, and it lands hardest exactly
   where the illusion is most expensive.

   Menu and listbox row highlights are NOT focus rings and stay: they are
   how you see which row you are on in an open dropdown. See .menu-row and
   .sel-row below, and .skip, which needs :focus to appear at all. */
html{ -webkit-tap-highlight-color:transparent; }
*:focus, *:focus-visible{ outline:none; }
::-moz-focus-inner{ border:0; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE NAV BAR — a control strip screwed to the desk
   ═══════════════════════════════════════════════════════════════════════════ */
.strip{
  display:inline-flex; align-items:center; gap:7px;
  padding:9px 13px; border-radius:15px; position:relative;
  background:linear-gradient(176deg,#26282d 0%,#1b1c20 30%,#161719 80%,#1d1e22 100%);
  box-shadow:
    0 30px 44px -20px rgba(0,0,0,.9),
    0 6px 12px -5px rgba(0,0,0,.7),
    inset 0 1px 0 rgba(255,255,255,.14),
    inset 0 -1px 0 rgba(0,0,0,.9);
}
.strip::before{
  content:''; position:absolute; inset:0; border-radius:15px; pointer-events:none;
  background-image:
    repeating-linear-gradient(90deg, rgba(255,255,255,.018) 0 1px, transparent 1px 3px),
    var(--grain);
  background-size:auto,140px; opacity:.5; mix-blend-mode:overlay;
}
.strip .key{ width:34px; height:34px; border-radius:9px; }
.strip .key svg{ width:16px; height:16px; }
/* A MILLED CHANNEL, not a border. Two 1px lines: the cut, and its lit far wall. */
.chan{
  width:2px; height:26px; flex:0 0 2px; margin:0 5px; border-radius:1px;
  background:#08090a; box-shadow:1px 0 0 rgba(255,255,255,.08), inset 0 0 2px rgba(0,0,0,.9);
}
/* the status readout — a lit window, not a label */
.lcd{
  height:26px; padding:0 11px; margin-left:3px; display:flex; align-items:center; gap:7px;
  border-radius:5px;
  background:linear-gradient(180deg,#080a09,#0f1211);
  box-shadow:
    inset 0 2px 5px rgba(0,0,0,.95),
    inset 0 -1px 0 rgba(255,255,255,.06),
    0 1px 0 rgba(255,255,255,.07);
  font-family:var(--font); font-size:10.5px; letter-spacing:.09em;
  color:#7CFFB0; text-shadow:0 0 6px rgba(124,255,176,.7);
  font-variant-numeric:tabular-nums;
}
.lcd.dim{ color:#3f6d55; text-shadow:none; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE PAGE SWITCHER — piano keys with a mechanical interlock
   Pressing one POPS THE OTHERS OUT. That is what a segmented control has
   always been pretending to be, and in metal it is literally true: three
   latching keys on one bar, and the bar can only hold one down.
   ═══════════════════════════════════════════════════════════════════════════ */
.piano{
  display:inline-flex; gap:5px; padding:6px;
  border-radius:12px; position:relative;
  /* SAME FLOOR AS THE BAY, and for the same reason it was raised there: the
     caps came up in value and the well did not. A key's skirt is #0d0e10, so
     on a #0a0b0d floor the one part that proves the cap has a BODY was
     invisible: the cap read as a grey rectangle printed on black. The floor
     only has to be darker than the surface it is cut into, not black. The
     inset shadow is what puts it below; the fill just has to stay out of the
     way of the skirt. */
  background:linear-gradient(180deg,#17191d,#1e2126);
  box-shadow:
    inset 0 3px 8px rgba(0,0,0,.8),
    inset 0 -1px 0 rgba(255,255,255,.05),
    0 1px 0 rgba(255,255,255,.07);
}
.pkey{
  height:36px; padding:0 22px; min-width:78px;
  display:flex; align-items:center; justify-content:center;
  border:0; border-radius:8px; cursor:pointer;
  font-family:var(--font); font-size:11.5px; font-weight:700; letter-spacing:.12em;
  color:#8d929b;
  background:linear-gradient(180deg,#34373d,#232529 55%,#1b1d20);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.16),
    inset 0 -1px 0 rgba(0,0,0,.7),
    0 4px 0 #0d0e10,
    0 6px 10px rgba(0,0,0,.7);
  transition:transform .1s cubic-bezier(.3,1.4,.5,1), box-shadow .1s, color .12s, background .12s;
}
.pkey:hover{ color:#d6dae0; }
.pkey:active{ transform:translateY(4px); }
/* A piano key that is DOWN is already saying so with 4px of travel, and it
   carries a lamp bar under the engraving. That is two signals; a halo round
   the cap would be a third saying the same thing. */
.pkey.is-down{ color:var(--led); text-shadow:0 0 6px rgba(255,130,30,.5); }
/* the lamp bar under the engraved label — the key is lit from inside */
.pkey{ position:relative; }
/* ── THE GUTTER BELONGS TO THE KEYS ──────────────────────────────────────
   A GROUP WITH `gap` AND `padding` HAS PIXELS INSIDE IT THAT BELONG TO NO
   BUTTON, and every one of them is a small lie: the thing looks like one
   continuous control, the pointer is over it, and the click goes nowhere.
   Worse on this part than most, because the keys carry 4px of travel and a
   chamfer that reaches 2px OUTSIDE them — so the lit edge of a key is
   already standing in the gutter, and the gutter is exactly where a click
   does nothing.

   THE HIT AREA IS A PSEUDO-ELEMENT AND THE PAINTED KEY IS NOT TOUCHED. A
   pseudo is not its own event target — a hit on it is a hit on the element
   that owns it — so `::before` stretched past the cap gives the key the
   gutter without moving, resizing or restyling anything you can see. Doing
   it with padding or a negative margin would have shifted the layout; doing
   it by removing the gap would have welded the keys into one slab.

   THE REACH IS HALF THE GAP, DECLARED PER GROUP, and it has to be: a flat
   3px would close the nav's 6px gutter exactly and OVERLAP the hatch tabs'
   4px row gap by two pixels each way, so the key below would claim the
   bottom of the key above and you would press the wrong tab by aiming at the
   right one. Half a gap always meets its neighbour at the midline and never
   crosses it. Every group that overrides `gap` owes this variable a value;
   the defaults are `.piano`'s own 5px gap and a vertical reach that fits
   inside its 6px padding.

   THE OUTER FRAME STAYS INERT ON PURPOSE. The group's padding is the case
   the keys are set into, not a gutter between two of them, and the edge of a
   case is not a button.

   IT MUST STAY TRANSPARENT AND MUST NOT SET `pointer-events:none`. Those
   are the two ways to write this rule and have it do nothing at all.

   AND IT IS A RECTANGLE. `border-radius:inherit` was the third way, and the
   one that survived two rounds of looking straight at it: browsers hit-test
   the ROUNDED box, so a pad that copies the cap's 8px corner is dead in the
   crescent outside every one of its four curves. At the top of a seam the
   crescents of two neighbouring pads meet and make a notch about 8px wide —
   which is the exact spot a pointer arrives at when it aims between two keys,
   and the exact spot this was reported from, twice.

   A PAD IS NOT DRAWN, SO IT HAS NO SHAPE TO KEEP. Rounding it inherits the
   look of the cap and none of the reason for it, and hands back at the corners
   the area the reach was added to claim. Square, and the seam is straight all
   the way to the top of the strip. */
.pkey::before,
.key::before,
.chip::before{
  content:''; position:absolute; border-radius:0;
  inset:calc(var(--pkey-reach-y, 4px) * -1) calc(var(--pkey-reach-x, 2.5px) * -1);
}
.nav-pages{ --pkey-reach-x:3px; }                            /* gap 6 */
.piano.sm{ --pkey-reach-x:3.5px; }                           /* gap 7 */
.piano.sm.hb-tabs{ --pkey-reach-x:2.5px; --pkey-reach-y:2px; } /* 5 / row 4 */
.keypad{ --pkey-reach-y:5.5px; }                             /* row gap 11 */
.strip{ --pkey-reach-x:3.5px; --pkey-reach-y:9px; }          /* gap 7, padding 9 13 */

/* ── AND THE PRESS MUST NOT TAKE THE HIT AREA WITH IT ─────────────────────
   A CAP THAT TRAVELS MOVES OUT FROM UNDER THE POINTER THAT PRESSED IT, and
   that is a whole class of click that animates and then does nothing. `:active`
   lands on pointerDOWN and drops the cap 2px; if the press landed in the top
   2px, the pointer is now above the element, pointerUP goes to the chassis, and
   `click` fires on the nearest common ancestor of the two — the group. You get
   the full press and release, and no handler. It is worst where it is least
   visible: the dead band is exactly the travel, so it is 2px on every big cap
   and it hugs the top edge, which is the edge a pointer arrives at.

   THE PAD MOVES THE OTHER WAY BY THE SAME AMOUNT, so it stays where it was in
   screen space while the cap falls through it. Nothing visible moves — the pad
   was never drawn — and the pointer never leaves the element it pressed. This
   is the whole fix, and it has to be stated for the LATCHED states too: a key
   sitting `.is-down` is a key holding 2px of travel permanently, and its top
   2px would be dead at rest.

   THE NUMBER IS COPIED, NOT DERIVED, and that is deliberate. It must equal the
   travel in the rule above it or it makes a second dead band pointing the other
   way, so it is written next to nothing else and changed with that rule. */
.key:active::before,        .key.is-down::before,
.key.skirt:active::before,  .key.skirt.is-down::before,
.pkey:active::before,       .pkey.is-down::before,
.chip:active::before,       .chip.is-down::before{
  transform:translateY(-2px);
}
/* `.sw` is absent here on purpose — see the note on its grain pseudo above. */
/* THE SMALL STRIPS DO NOT TRAVEL, so their pads must not compensate for travel
   that never happens — that would open the same dead band along the BOTTOM. */
.piano.sm .pkey:active::before,
.piano.sm .pkey.is-down::before{ transform:none; }

/* ── THE OUTER FRAME BELONGS TO THE END KEYS ──────────────────────────────
   THE CASE IS NOT A BUTTON WAS THE WRONG CALL, and it was made about a part
   nobody had pressed yet. In use the frame reads as dead, not as the edge of
   the object: the strip looks like one continuous control, the pointer is over
   it, and the click goes nowhere. That is the same sentence the gutter rule
   above opens with, and the gutter and the rim are the same complaint.

   HALF A GAP INWARD, THE WHOLE PADDING OUTWARD. A key meets its neighbour at
   the midline and never crosses it; toward the case there is no neighbour to
   meet, so it takes the lot. A single-row strip has no neighbour above or below
   either, which is why the vertical reach here is the padding rather than the
   4px default — in a one-row group every key touches the top and bottom frame.

   THE CORNER NEEDS NO RADIUS AT ALL, and giving it one was the mistake that
   cost a round: the pad is square everywhere now, so an end pad reaching the
   case fills the chassis corner completely rather than leaving the crescent
   the chassis's own 12px would have cut out of it. */
/* EACH GROUP NAMES ITSELF AND EXCLUDES THE OTHERS. `.nav-pages` and
   `.piano.sm` both carry `.piano`, so an unscoped `.piano` rule reaches into
   them at equal specificity and the winner is whichever was written last —
   a padding value decided by source order. */
.piano:not(.sm):not(.nav-pages) > .pkey::before{ top:-6px; bottom:-6px; }
.piano:not(.sm):not(.nav-pages) > .pkey:first-child::before{
  left:-6px;
}
.piano:not(.sm):not(.nav-pages) > .pkey:last-child::before{
  right:-6px;
}
/* the two single-row 5px strips */
.piano.sm:not(.hb-tabs) > .pkey::before,
.nav-pages > .pkey::before{ top:-5px; bottom:-5px; }
.piano.sm:not(.hb-tabs) > .pkey:first-child::before,
.nav-pages > .pkey:first-child::before{
  left:-5px;
}
.piano.sm:not(.hb-tabs) > .pkey:last-child::before,
.nav-pages > .pkey:last-child::before{
  right:-5px;
}

/* THE TAB STRIP IS A GRID AND `:first-child` IS NOT ITS LEFT EDGE. Six keys
   auto-flow into three columns across two rows, then `.chan` and ALL are placed
   in columns 4 and 5 — so the LAST DOM CHILD IS NOT THE LAST KEY, and the key
   at the left of row two is child four. What the rim depends on is column and
   row position, so that is what this asks for. `nth-child` is safe here for the
   same reason the grid is: the count is fixed at six by the same rule that
   fixes the template at three columns.

   IT ALSO MUST NOT TAKE THE SINGLE-ROW RULE ABOVE, which is why that one is
   scoped away from `.hb-tabs`: a -5px reach off the top AND the bottom of a
   grid whose row gap is 4px would put row one's pad 6px inside row two, and
   the row you pressed would depend on which pad painted last.

   COLUMN 3 HAS THE MILLED CHANNEL TO ITS RIGHT, not the case — so it keeps the
   half-gap default and owes nothing here. */
.hb-tabs > .pkey:nth-child(-n+3)::before{ top:-5px; }        /* row 1 → the frame */
.hb-tabs > .pkey:nth-child(n+4)::before{ bottom:-5px; }      /* row 2 → the frame */
.hb-tabs > .pkey:nth-child(3n+1)::before{ left:-5px; }       /* column 1 → the frame */

/* THE DOCK STRIP IS THE SAME OBJECT WITH A WIDER FRAME — 13px of padding
   either end against a piano's 6, and a 15px corner. The `.chan` is a child of
   it, so `:first-child`/`:last-child` are the end keys only while it stays
   between them, which is what §09's slot list fixes. */
.strip > .key:first-child::before{
  left:-13px;
}
.strip > .key:last-child::before{
  right:-13px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE LIGHT DIRECTION SETTER — a hemisphere, flattened, with the lamp on it
   ═══════════════════════════════════════════════════════════════════════════
   IT IS A WELL, NOT A DIAL, and the difference decides every value below. A
   dial is a part you turn and it has a cap, a collar and a pointer; this is a
   HOLE cut in the plate with the sky drawn on its floor and one lit thing
   sitting in it. So it takes the slot's language — the same near-black floor,
   the same overhanging top wall, the same bounce off the bottom — rather than
   the knob's, even though the two sit side by side in the same group.

   THE FLOOR IS DARKER AT ITS RIM THAN AT ITS MIDDLE, which is doing two jobs
   at once and is the only reason a flat disc reads as a bowl: it is what a
   round well does under a lamp, and it is also what the CONTENT means, since
   the rim is the horizon and the middle is the zenith. The one gradient says
   both. */
.lwrap{ display:flex; flex-direction:column; gap:9px; align-items:center; }
.lwrap .lcap{ align-self:flex-start; }
.ldisc{
  position:relative; width:var(--ld,132px); height:var(--ld,132px);
  border-radius:50%; flex:0 0 auto; cursor:crosshair; touch-action:none;
  box-shadow:
    inset 0 3px 7px rgba(0,0,0,.9),
    inset 0 -1px 1px rgba(255,255,255,.06),
    0 1px 0 rgba(255,255,255,.06);
}
.ld-well{
  position:absolute; inset:0; border-radius:50%; display:block;
  background:
    radial-gradient(circle at 50% 42%, #1b1e23 0%, #131519 46%, #0a0c0e 82%, #06080a 100%);
}
/* ── THE MARKS ────────────────────────────────────────────────────────────
   THE 45° RING IS DRAWN AND THE OTHERS ARE NOT, and that is the whole of the
   scale. A dial with a ring every fifteen degrees is a protractor, and nobody
   aims a lamp by counting rings — but 45° is a place people ASK for by name,
   so it gets a line and the rest of the hemisphere gets none. The rim is the
   second ring and it is the horizon, which needs no naming.

   THE LEGENDS SIT AT THE FOUR QUARTERS, INSIDE the rim, because a number
   outside the cut is printed on the plate and would have to move when the
   control is resized. Everything here scales with the viewBox. */
.ld-face{
  position:absolute; inset:0; width:100%; height:100%;
  overflow:visible; pointer-events:none;
}
.ld-ring{ fill:none; stroke:rgba(255,255,255,.075); stroke-width:.7; }
.ld-45{ stroke:rgba(255,255,255,.05); stroke-dasharray:1.6 2.4; }
.ld-cross{ stroke:rgba(255,255,255,.045); stroke-width:.6; }
.ld-deg{
  fill:#5a606a; font-family:var(--font);
  font-size:5.4px; letter-spacing:.04em;
}
/* ── THE LAMP ─────────────────────────────────────────────────────────────
   THE ONE LIT THING, and it is §04's jewel at the numbers §04 uses. A light
   picker whose light is a different light from the panel's other lights is
   two lamps, and this document has spent long enough learning that. What it
   adds is a hairline of dark UNDER it — the puck is standing on the floor of
   the well and something has to say so, or a glowing dot at the middle of a
   dark disc is a hole rather than an object.

   IT WAS A SHADED 3D ARROW FOR ONE VERSION, and the note is worth keeping
   because the arrow is the obvious idea and somebody will have it again. On
   paper it wins: a vector says WHICH WAY and a dot only says WHERE. On a
   118px disc it loses, and for a reason that is not a matter of taste — the
   head has to scale with the length or it eats the shaft, so above about 60°
   of elevation the arrow is a stub, and the one shape that has to stay
   recognisable is changing shape across exactly the range you care about. A
   dot is the same mark at every elevation, and the rings already carry the
   reading the arrow was there to give. */
.ld-puck{
  position:absolute; width:13px; height:13px; margin:-6.5px 0 0 -6.5px;
  border-radius:50%; display:block; pointer-events:none;
  background:radial-gradient(circle at 34% 28%, #fff 0%,
    color-mix(in srgb, var(--lyr, var(--led)) 60%, #fff) 16%,
    var(--lyr, var(--led)) 52%,
    color-mix(in srgb, var(--lyr, var(--led)) 55%, #000) 100%);
  box-shadow:
    0 0 6px color-mix(in srgb, var(--lyr, var(--led)) 70%, transparent),
    0 0 15px color-mix(in srgb, var(--lyr, var(--led)) 36%, transparent),
    inset 0 -1px 2px rgba(0,0,0,.55),
    0 1px 2px rgba(0,0,0,.85),
    0 0 0 1px rgba(0,0,0,.55);
  /* IT FOLLOWS THE FINGER AND EASES EVERYWHERE ELSE. A transition on a puck
     being dragged is lag; the same transition when a host calls `set` or an
     arrow key fires is the lamp swinging rather than teleporting. */
  transition:left .12s ease-out, top .12s ease-out;
}
.ldisc.dragging .ld-puck{ transition:none; }

/* ── THE TWO NUMBERS ──────────────────────────────────────────────────────
   STILL THERE, AND STILL TYPEABLE. The picker replaces the arithmetic, not
   the value: 325 is a thing you copy out of one panel into another, and a
   control that can only be dragged cannot be handed a number somebody read
   to you over the phone. */
.lrow{ display:flex; gap:7px; width:100%; }
.lfield{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:3px; }
.lfield .kval{ width:100%; }

/* ═══════════════════════════════════════════════════════════════════════════
   LAUNCHPAD — one rack module per project
   ═══════════════════════════════════════════════════════════════════════════ */
.console{
  border-radius:16px; padding:18px;
  background:linear-gradient(176deg,#1d1f23,#141517 60%,#17181b);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.10),
    inset 0 -1px 0 rgba(0,0,0,.85),
    0 26px 44px -22px rgba(0,0,0,.9);
  position:relative;
}
.console::before{
  content:''; position:absolute; inset:0; border-radius:16px; pointer-events:none;
  background-image:var(--grain); background-size:140px; opacity:.05; mix-blend-mode:overlay;
}
.con-top{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:14px; position:relative; z-index:1; }
/* folder chips are latching radio keys, same interlock as the page switcher */
.chip{
  height:28px; padding:0 12px; display:inline-flex; align-items:center; gap:7px;
  /* POSITIONED, OR THE HIT PAD AND THE CHAMFER BOTH GO SOMEWHERE ELSE. This
     was missing and only worked by accident: `:active` puts a `transform` on
     the chip, and a transform makes a containing block — so the chamfer, which
     exists only while pressed, always found one. The hit pad has to be there at
     REST, where there is no transform, so it needs the real thing. */
  position:relative;
  border:0; border-radius:7px; cursor:pointer;
  font-family:var(--font); font-size:10.5px; font-weight:700; letter-spacing:.08em;
  color:#8d929b;
  background:linear-gradient(180deg,#2c2f34,#1e2024);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.13), 0 2px 0 #0e0f11, 0 3px 5px rgba(0,0,0,.6);
  transition:transform .1s, box-shadow .1s, color .12s, background .12s;
}
.chip:hover{ color:#dfe2e7; }
.chip:active{ transform:translateY(2px); }
.chip.is-down{ color:var(--led); text-shadow:0 0 6px rgba(255,130,30,.5); }
.chip .ct{
  font-size:9.5px; padding:1px 5px; border-radius:3px;
  background:rgba(0,0,0,.45); color:#7CFFB0; text-shadow:0 0 5px rgba(124,255,176,.5);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.8);
}
.chip.is-down .ct{ color:#FFD9BC; text-shadow:0 0 5px rgba(255,140,60,.6); }
.chip.new{ color:#6d727c; background:none;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.09), inset 0 1px 0 rgba(255,255,255,.05); }
.chip.new:hover{ color:#c6cad1; box-shadow:inset 0 0 0 1px rgba(255,255,255,.18); }

/* the search slot — a milled well with a real input in it */
.slot{
  display:flex; align-items:center; gap:8px; height:30px; padding:0 11px;
  border-radius:7px; flex:1 1 190px; max-width:280px;
  background:linear-gradient(180deg,#0a0b0d,#131518);
  box-shadow:inset 0 2px 5px rgba(0,0,0,.9), inset 0 -1px 0 rgba(255,255,255,.05), 0 1px 0 rgba(255,255,255,.06);
}
.slot svg{ width:13px; height:13px; stroke:#5b6069; fill:none; stroke-width:1.8; flex:0 0 13px; }
.slot input{
  flex:1; min-width:0; background:none; border:0; outline:0; padding:0;
  font-family:var(--font); font-size:11px; letter-spacing:.06em; color:#7CFFB0;
  text-shadow:0 0 6px rgba(124,255,176,.5);
}
.slot input::placeholder{ color:#3f444c; text-shadow:none; letter-spacing:.1em; }

.rack{ display:grid; grid-template-columns:repeat(auto-fill,minmax(272px,1fr)); gap:14px; position:relative; z-index:1; }

.module{
  position:relative; display:flex; flex-direction:column; gap:9px;
  padding:14px 14px 12px 17px; border-radius:11px;
  background:linear-gradient(178deg,#282b30,#1e2023 55%,#1a1c1f);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.11),
    inset 0 -1px 0 rgba(0,0,0,.75),
    0 3px 6px rgba(0,0,0,.6);
  overflow:hidden;
}
.module::after{
  content:''; position:absolute; inset:0; pointer-events:none;
  background-image:var(--grain); background-size:140px; opacity:.05; mix-blend-mode:overlay;
}
/* THE RUNNING RAIL — the green bar down the left of the shipping card, as a
   light pipe. Dark and dead when the project is not up. */
.module::before{
  content:''; position:absolute; left:0; top:8px; bottom:8px; width:4px;
  border-radius:0 3px 3px 0; background:#26282c;
  box-shadow:inset -1px 0 2px rgba(0,0,0,.8);
  transition:background .2s, box-shadow .2s;
}
.module.up::before{
  background:linear-gradient(180deg,#7CFFB0,#22C55E);
  box-shadow:0 0 7px rgba(60,230,140,.8), 0 0 18px rgba(60,230,140,.35), inset 0 0 3px rgba(255,255,255,.7);
}
.mod-head{ display:flex; align-items:center; gap:9px; }
/* the status LED — a jewel, dark until the process is alive */
.pilot{
  width:9px; height:9px; flex:0 0 9px; border-radius:50%;
  background:radial-gradient(circle at 34% 28%, #4a4f57, #26282c 70%);
  box-shadow:inset 0 -1px 1px rgba(0,0,0,.7), 0 0 0 1px rgba(0,0,0,.6);
  transition:background .2s, box-shadow .2s;
}
.module.up .pilot{
  background:radial-gradient(circle at 34% 28%, #fff 0%, #B6FFD4 18%, #2BE07A 55%, #128C45 100%);
  box-shadow:0 0 6px rgba(43,224,122,.85), 0 0 15px rgba(43,224,122,.4), inset 0 -1px 1px rgba(0,0,0,.4), 0 0 0 1px rgba(0,0,0,.5);
}
.mod-name{
  flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-family:var(--font); font-size:12.5px; font-weight:700; letter-spacing:.01em;
  color:#e3e6eb; text-shadow:0 1px 0 rgba(0,0,0,.85);
}
/* NODE — a stamped plate, not a coloured pill */
.stamp{
  font-family:var(--font); font-size:8.5px; font-weight:700; letter-spacing:.14em;
  padding:2px 5px; border-radius:3px; color:#0d1a12;
  background:linear-gradient(180deg,#8FE9B4,#3FA96C);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5), inset 0 -1px 0 rgba(0,0,0,.3), 0 1px 2px rgba(0,0,0,.6);
}
.mod-desc{ font-size:11.5px; line-height:1.5; color:#7e838d; margin:0; min-height:34px; }

/* the port display — a lit strip. It is a LINK, so it also has a key on it. */
.port{
  display:flex; align-items:center; gap:8px; height:30px; padding:0 10px;
  border-radius:6px; cursor:pointer; border:0; width:100%; text-align:left;
  background:linear-gradient(180deg,#06110c,#0c1a13);
  box-shadow:inset 0 2px 5px rgba(0,0,0,.9), inset 0 0 12px rgba(60,230,140,.10),
             inset 0 -1px 0 rgba(255,255,255,.05), 0 1px 0 rgba(255,255,255,.06);
  font-family:var(--font); font-size:11px; letter-spacing:.06em;
  color:#7CFFB0; text-shadow:0 0 7px rgba(124,255,176,.7);
}
.port:hover{ box-shadow:inset 0 2px 5px rgba(0,0,0,.9), inset 0 0 16px rgba(60,230,140,.2), 0 0 8px rgba(60,230,140,.2); }
.port span:last-child{ margin-left:auto; font-size:9px; color:#3f7a5c; text-shadow:none; letter-spacing:.1em; }
.port svg{ width:12px; height:12px; stroke:currentColor; fill:none; stroke-width:1.8; }

.mod-acts{ display:flex; align-items:center; gap:7px; }
/* LAUNCH — an illuminated green key. The lamp lens is INSIDE the cap, left of
   the label, the way a real illuminated pushbutton is built. */
.act{
  height:32px; padding:0 13px; gap:8px; border-radius:8px; flex:0 0 auto;
  font-family:var(--font); font-size:10.5px; font-weight:700; letter-spacing:.08em;
}
.act svg{ width:12px; height:12px; }
.act.go{
  color:#CFFFE3;
  background:linear-gradient(180deg,#1E7A47,#12613A 55%,#0C4529);
  box-shadow:
    inset 0 1px 0 rgba(190,255,220,.35),
    inset 0 -1px 0 rgba(0,0,0,.6),
    0 3px 0 #06301c,
    0 5px 9px rgba(0,0,0,.65),
    0 0 12px rgba(43,224,122,.22);
}
.act.go:hover{ background:linear-gradient(180deg,#279456,#167544 55%,#0F5533);
  box-shadow:inset 0 1px 0 rgba(190,255,220,.45), 0 3px 0 #06301c, 0 5px 9px rgba(0,0,0,.65), 0 0 18px rgba(43,224,122,.4); }
.act.go:active{ transform:translateY(3px);
  box-shadow:inset 0 2px 6px rgba(0,0,0,.8), 0 0 0 #06301c, 0 1px 2px rgba(0,0,0,.5), 0 0 14px rgba(43,224,122,.35); }
/* STOP — red, and it reads as red BEFORE you read the word. The lens is lit
   because the thing it stops is running; a dark stop button is a lie. */
.act.halt{
  color:#FFD6D6;
  background:linear-gradient(180deg,#8E1F1F,#661616 55%,#490F0F);
  box-shadow:
    inset 0 1px 0 rgba(255,190,190,.35),
    inset 0 -1px 0 rgba(0,0,0,.6),
    0 3px 0 #2c0808,
    0 5px 9px rgba(0,0,0,.65),
    0 0 12px rgba(255,45,45,.25);
}
.act.halt:hover{ background:linear-gradient(180deg,#A82626,#7C1B1B 55%,#5A1313);
  box-shadow:inset 0 1px 0 rgba(255,190,190,.45), 0 3px 0 #2c0808, 0 5px 9px rgba(0,0,0,.65), 0 0 18px rgba(255,45,45,.45); }
.act.halt:active{ transform:translateY(3px);
  box-shadow:inset 0 2px 6px rgba(0,0,0,.85), 0 0 0 #2c0808, 0 1px 2px rgba(0,0,0,.5), 0 0 14px rgba(255,45,45,.4); }
/* the lamp lens inside an illuminated key */
.lens{ width:8px; height:8px; border-radius:50%; flex:0 0 8px; }
.act.go .lens{ background:radial-gradient(circle at 35% 30%, #fff, #7CFFB0 40%, #1FA85C);
  box-shadow:0 0 6px rgba(124,255,176,.9), inset 0 -1px 1px rgba(0,0,0,.4); }
.act.halt .lens{ background:radial-gradient(circle at 35% 30%, #fff, #FF9B9B 40%, #C21B1B);
  box-shadow:0 0 6px rgba(255,80,80,.9), inset 0 -1px 1px rgba(0,0,0,.4); }
/* secondary keys — unlit, plain metal. They open something elsewhere. */
.act.aux{ padding:0 11px; color:#9aa0aa; }
/* …unless one of them LATCHES. `.act.aux` and `.key.is-down` have the same
   specificity and this file declares aux later, so a latched aux key was
   quietly keeping its grey glyph — the one thing the lit state is made of. */
.act.is-down{ color:var(--led); }

.mod-tags{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
/* an engraved tag: cut into the module face, so it is a MARK not a control */
.tagcut{
  font-family:var(--font); font-size:9px; letter-spacing:.1em; padding:2px 7px;
  border-radius:4px; color:#797e88;
  background:linear-gradient(180deg,#171a1c,#1e2124);
  box-shadow:inset 0 1px 3px rgba(0,0,0,.85), 0 1px 0 rgba(255,255,255,.06);
  text-shadow:0 1px 0 rgba(255,255,255,.06);
}
.tagcut.folder{ color:#C99A5B; }
/* the drag grip — knurled, bottom right, exactly where the shipping card has it */
.grip{
  position:absolute; right:9px; bottom:9px; width:14px; height:14px; border-radius:3px;
  background-image:radial-gradient(circle, rgba(255,255,255,.22) 1px, transparent 1.2px);
  background-size:5px 5px; opacity:.35; cursor:grab;
}
.grip:hover{ opacity:.7; }

.empty{
  grid-column:1/-1; padding:38px; text-align:center; border-radius:11px;
  font-family:var(--font); font-size:10.5px; letter-spacing:.18em; color:#4a4e56;
  background:linear-gradient(180deg,#0a0b0d,#131518);
  box-shadow:inset 0 3px 7px rgba(0,0,0,.9), 0 1px 0 rgba(255,255,255,.05);
  text-shadow:0 1px 0 rgba(255,255,255,.06);
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE KNOB — what a slider becomes when it has to exist as an object
   ───────────────────────────────────────────────────────────────────────────
   A slider is a track and a lump. A knob is FOUR parts and each one earns it:

     1  THE COLLAR      a milled ring the cap sits in, so it looks fitted
     2  THE ARC         the value, drawn as a lit trace around the collar from
                        the minimum to where you are. This is the readout you
                        actually use — the number is for confirming, the arc is
                        for judging. A slider's filled track does the same job
                        badly, because it is straight and everything else is not
     3  THE CAP         knurled aluminium, conic-gradient teeth, domed with an
                        off-centre specular that stays put as the cap turns
     4  THE POINTER     one cut line, and it ROTATES. The knob's angle is the
                        value, which is why you can read it from across the room

   270° of travel, -135° to +135°, the way every real knob is built. Drag up or
   right to raise it; the wheel works too; shift for fine.

   WHERE A KNOB IS WRONG: a range with two ends you compare (Zone from / Zone
   to) still wants a track, because "from here to there" is a LENGTH and a
   length is a straight thing. Those stay faders — see .fader below.
   ═══════════════════════════════════════════════════════════════════════════ */
.knob{
  width:var(--kd,54px); height:var(--kd,54px); position:relative; flex:0 0 auto;
  touch-action:none; cursor:ns-resize;
}
/* the collar it is fitted into */
.knob-collar{
  position:absolute; inset:0; border-radius:50%;
  background:radial-gradient(circle at 50% 50%, #0c0d0f 56%, #1d1f23 57%, #101113 100%);
  box-shadow:inset 0 2px 5px rgba(0,0,0,.9), inset 0 -1px 0 rgba(255,255,255,.07), 0 1px 0 rgba(255,255,255,.06);
}
/* THE VALUE ARC IS AN SVG STROKE, and it has to be.
   It was a conic-gradient behind a CSS mask, and both halves of that alias:
   a conic hard stop is a hard pixel edge with no antialiasing, and a
   radial-gradient mask has two more hard edges at the ring's inner and outer
   walls. Three stair-stepped boundaries on a 54px circle — which is exactly
   what a stepped orange arc looks like.
   A stroked <circle> with stroke-dasharray is antialiased by the renderer,
   scales to any size, gets a round cap for free, and the glow is one
   drop-shadow on currentColor instead of a colour-mix per state. */
.knob-ring{
  position:absolute; inset:0; width:100%; height:100%; overflow:visible;
  transform:rotate(135deg);            /* put the 90° dead zone at the bottom */
  color:var(--arc,#FF6A00);
}
/* AND AN ENDLESS KNOB HAS NO DEAD ZONE TO PUT ANYWHERE. An SVG circle starts
   at three o'clock, so the ring turns back to −90° to start the trace at
   twelve — which is where zero is on a knob that reads an angle. */
.knob.endless .knob-ring{ transform:rotate(-90deg); }
/* ── AN ENDLESS KNOB'S POINTER IS ITS TRACE ─────────────────────────────────
   A POT PAINTS `--arc` ROUND THE RING because the arc is its readout. An
   endless knob draws no arc — the length of one on a closed ring is
   meaningless — so the same colour goes on the pointer, which is what carries
   the value here. One option, `arc`, used where each kind of knob actually
   says its number.

   AND IT IS ALWAYS ON, because it is not a state: it is what the knob IS.
   Colouring it only while something is being turned makes the dial go dark the
   moment you let go, and a dial that is only readable while you are touching
   it is not a readout. */
.knob.endless .knob-cap::after{
  background:var(--arc);
  box-shadow:0 0 0 1px color-mix(in srgb, var(--arc) 40%, transparent),
             0 0 5px color-mix(in srgb, var(--arc) 70%, transparent);
}

/* A BIPOLAR knob reads from the CENTRE, not from the minimum — Yaw at 0° with
   a half-full arc says "half way up the range", which is true and useless;
   what you want to see is how far off centre you are, and which side. So the
   trace grows out of 12 o'clock in whichever direction you turned, and the
   collar carries an etched centre mark for it to grow out of. */
.knob.bi .knob-collar::after{
  content:''; position:absolute; left:50%; top:-2px; width:2px; height:5px; margin-left:-1px;
  border-radius:1px; background:#767d88; box-shadow:0 1px 1px rgba(0,0,0,.85);
}
/* ── AND THE MARK ON THE COLLAR IS THE SAME CHANNEL, ARRIVING ───────────────
   IT IS ALREADY IN LINE WITH THE WIRE — a tick at twelve o'clock directly
   under where the trace comes down — so drawing it as a bright etched notch
   while the trace is a dark groove makes two different things out of one. Cut
   it the same way and the wire does not stop above the dial; it runs under the
   name and comes out on the collar, which is what the eye was going to assume
   anyway. Scoped to this panel: everywhere else the etched mark is right,
   because everywhere else there is no wire arriving at it. */
.orbay .knob.bi .knob-collar::after{
  height:7px; top:-3px; border-radius:0;
  background:#0d1013;
  box-shadow:0 1px 0 rgba(255,255,255,.085), 0 -1px 0 rgba(0,0,0,.55);
  transition:background .16s linear, box-shadow .16s linear;
}
.kr-track,.kr-val{ fill:none; stroke-linecap:round; stroke-width:7; }
.kr-track{ stroke:rgba(255,255,255,.075); }
.kr-val{ stroke:currentColor; filter:drop-shadow(0 0 3px currentColor); transition:stroke-dasharray .05s linear; }
.knob.dragging .kr-val{ transition:none; }
/* the cap: knurled edge, domed face, fixed specular */
.knob-cap{
  position:absolute; inset:19%; border-radius:50%;
  background:
    radial-gradient(circle at 36% 26%, rgba(255,255,255,.30) 0%, transparent 46%),
    conic-gradient(#3a3e45 0deg, #23262b 3deg, #3a3e45 6deg, #23262b 9deg),
    radial-gradient(circle at 50% 50%, #34383f 55%, #1c1e22 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.28),
    inset 0 -2px 4px rgba(0,0,0,.75),
    0 2px 4px rgba(0,0,0,.8);
  transform:rotate(var(--deg,0deg));
  transition:transform .05s linear;
}
/* the pointer — a CUT, so it is dark with a lit lower wall */
.knob-cap::after{
  content:''; position:absolute; left:50%; top:9%; width:2px; height:34%;
  margin-left:-1px; border-radius:1px;
  background:#0a0b0c; box-shadow:0 0 0 1px rgba(255,255,255,.13), 0 0 5px rgba(255,255,255,.10);
}
.knob.dragging .knob-cap{ transition:none; }

/* ── the furniture, and it is the part that was too big ──────────────────
   A caption line, a knob and a readout line stacked is THREE rows for ONE
   control — a 54px knob in a 100px box. Stacked is kept only for a BANK,
   where three of them side by side is the point (Yaw / Pitch / Roll).
   Everything else is a ROW: knob, engraved label, readout, on one 42px line
   — the same height a slider row already was, so the knob costs nothing. */
.kwrap{ display:flex; flex-direction:column; align-items:center; gap:4px; }
/* The inline form is a PARAM STRIP: its own small plate, so a column of them
   reads as a rack of channels rather than as controls floating on the body.
   (The modifier is `inline`, not `row` — `.row` is the layer module, and a
   knob strip was silently inheriting its plate, its hover and its grain.) */
.kwrap.inline{
  flex-direction:row; align-items:center; gap:12px; width:100%;
  /* THE DIAL IS BIGGER THAN ITS BOX. The value arc sits at r=43 of 50 with a
     3px glow in rendered pixels, so on a 40px knob the light spills about
     1.6px outside the element. At 4px of padding against a 9px corner radius
     the plate's own edge was cutting diagonally through that spill, which is
     what a knob crammed into a slot looks like. 9px of air and a radius that
     follows the knob rather than fighting it. */
  padding:9px 13px 9px 10px; border-radius:13px;
  /* A STRIP HAS AN INTRINSIC WIDTH AND MUST NOT GO UNDER IT. width:100% with
     nothing holding the floor lets a narrow parent squeeze the row until the
     label and the value climb onto the dial, which reads as a broken knob
     rather than as a container that is too small. */
  min-width:178px;
  background:linear-gradient(180deg,#25282c,#1c1e21);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07), inset 0 -1px 0 rgba(0,0,0,.65),
             0 1px 2px rgba(0,0,0,.5);
}
.kwrap.inline .klab{ flex:1; text-align:left; }
.kwrap.inline .kval{ min-width:58px; }

/* TILE — the stack on a PROUD PLATE, which makes it a module rather than a
   control. Same plate recipe as the inline strip, because they are the same
   decision in two orientations: a lit hairline on top, a dark one under, and a
   short cast shadow. The plate is the edge that says where one channel stops
   and the next begins, so this is what a GRID of knobs wants. */
.kwrap.tile{
  gap:7px; padding:11px 13px 10px; border-radius:11px;
  background:linear-gradient(180deg,#25282c,#1c1e21);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07), inset 0 -1px 0 rgba(0,0,0,.65),
             0 2px 4px rgba(0,0,0,.55);
}

/* COMPACT — dial and value, no caption, and no plate under it either: a strip
   earns its plate by being one of several in a column, and a single control
   sitting inside a row that already names it does not. */
.kwrap.compact{ flex-direction:row; align-items:center; gap:9px; }
.kwrap.compact .kval{ min-width:52px; }

/* BARE — the dial alone. Not a stack with its parts deleted: a caller that
   removes .klab and .kval by hand is a caller that has to be updated when the
   markup changes, and there were two of those before this existed. */
.kwrap.bare{ gap:0; }
.klab{
  font-family:var(--font); font-size:8.5px; letter-spacing:.16em; text-transform:uppercase;
  color:#5d626b; text-shadow:0 1px 0 rgba(255,255,255,.09); line-height:1;
}
.kval{
  min-width:50px; height:18px; padding:0 7px; border-radius:4px;
  display:flex; align-items:center; justify-content:center;
  background:linear-gradient(180deg,#080a09,#0f1211);
  box-shadow:inset 0 2px 4px rgba(0,0,0,.95), 0 1px 0 rgba(255,255,255,.06);
  font-family:var(--font); font-size:10.5px; color:#7CFFB0;
  text-shadow:0 0 6px rgba(124,255,176,.65); font-variant-numeric:tabular-nums;
}

/* THE FIELD IS THE WINDOW, not a box drawn inside it. A readout being typed
   into is the same lit glass with a cursor in it — same phosphor, same tabular
   figures, no border and no white ground, because nothing about it has become
   a form. Only the caret says it is live. */
/* NO I-BEAM ON THE WINDOW. A text cursor over a readout says "this is a text
   field", and it is not one — it is a display that will take a value if you
   ask it twice. The pointer stays a pointer until the caret is actually in
   there. */
.kedit-able{ cursor:default; }
.kval input.kedit,
.lcd input.kedit{
  width:100%; min-width:0; border:0; padding:0; margin:0; background:none;
  text-align:center; font:inherit; color:inherit; text-shadow:inherit;
  font-variant-numeric:tabular-nums; letter-spacing:inherit;
  caret-color:#7CFFB0;
}
.kval input.kedit:focus,
.lcd input.kedit:focus{ outline:none; }
/* the window brightens while it is taking input, the way a lit display does */
.kval:has(input.kedit),
.lcd:has(input.kedit){
  box-shadow:inset 0 2px 4px rgba(0,0,0,.95), 0 0 0 1px rgba(124,255,176,.35),
             0 0 7px rgba(124,255,176,.20);
}

/* ── the FADER, built like one ───────────────────────────────────────────
   Kept only where the value is a LENGTH. The first pass was a rounded bar and
   a rounded lump, which is the flat slider with a bevel painted on. A real
   fader is four things:

     THE SLOT     cut, not drawn — dark core, lit far wall at the bottom, and
                  the ends CAPPED so the travel has a physical limit
     THE SCALE    engraved ticks at 0 · ¼ · ½ · ¾ · 1 under the slot. Ticks are
                  what make a track readable without a number, and they are the
                  reason a fader can be a fader instead of a bar with a lump
     THE TRACE    the lit part of the slot, glowing onto the metal around it
     THE CAP      a moulded thumb: chamfered top, a knurled grip, a WAIST where
                  the finger sits, and one bright indicator line on the exact
                  value — the line is the pointer, the cap is just what holds it
   ─────────────────────────────────────────────────────────────────────────── */
.fader{ display:flex; align-items:center; gap:12px; padding:3px 0; }
.fader .flab{ flex:0 0 62px; font-family:var(--font); font-size:8.5px; letter-spacing:.16em;
  text-transform:uppercase; color:#5d626b; text-shadow:0 1px 0 rgba(255,255,255,.09); }
/* The scale sits BELOW the cap's travel, not under it — a 24px cap on a 26px
   slot covers its own ticks, which is the scale and the pointer fighting for
   the same 4px. */
.fslot{ flex:1; position:relative; height:30px; display:flex; align-items:center; }
.ftrack{
  position:absolute; left:0; right:0; top:7px; height:9px; border-radius:5px;
  cursor:pointer; touch-action:none;
  background:linear-gradient(180deg,#060708 0%,#0d0f11 55%,#191c20 100%);
  box-shadow:
    inset 0 2px 4px rgba(0,0,0,.98),
    inset 0 -1px 0 rgba(255,255,255,.09),
    inset 2px 0 3px -2px rgba(0,0,0,.9),
    inset -2px 0 3px -2px rgba(0,0,0,.9),
    0 1px 0 rgba(255,255,255,.07);
}
/* the engraved scale, under the slot */
.fticks{
  position:absolute; left:0; right:0; top:25px; height:4px; pointer-events:none;
  background-image:linear-gradient(90deg, #000 0 1px, transparent 1px);
  background-size:calc(25% - .25px) 3px; background-repeat:repeat-x;
  box-shadow:0 1px 0 rgba(255,255,255,.05);
  opacity:.85;
}
.fticks::after{ content:''; position:absolute; right:0; top:0; width:1px; height:3px; background:#000;
  box-shadow:1px 0 0 rgba(255,255,255,.06); }
.ffill{
  position:absolute; left:2px; top:2px; bottom:2px; border-radius:3px;
  background:linear-gradient(180deg,#FFC58F,var(--led) 55%,#C24E00);
  box-shadow:0 0 5px rgba(255,106,0,.7), 0 0 13px rgba(255,106,0,.35),
             inset 0 1px 0 rgba(255,255,255,.45);
}
/* THE BAND between two caps is a PIECE, not a fill that starts late. A fine
   hatch is what says so — the same reason the caps are knurled. */
.ffill.band{
  top:1px; bottom:1px; border-radius:4px;
  background-image:
    repeating-linear-gradient(74deg, rgba(0,0,0,.26) 0 1px, transparent 1px 3px),
    linear-gradient(180deg,#FFC58F,var(--led) 55%,#C24E00);
  box-shadow:0 0 6px rgba(255,106,0,.6), 0 0 14px rgba(255,106,0,.3),
             inset 0 1px 0 rgba(255,255,255,.5), inset 0 0 0 1px rgba(0,0,0,.35);
}
.kval.wide{ min-width:78px; }
/* the cap — waisted, knurled, with the indicator line on the value */
.fcap{
  position:absolute; top:50%; width:17px; height:24px; margin:-12px 0 0 -8.5px;
  border-radius:5px; cursor:grab;
  background:
    linear-gradient(180deg,#585d66 0%,#3a3e45 16%,#22252a 46%,#1a1c20 54%,#33373d 86%,#4c5158 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.45),
    inset 0 -1px 0 rgba(255,255,255,.22),
    inset 1px 0 0 rgba(255,255,255,.10),
    inset -1px 0 0 rgba(0,0,0,.6),
    0 3px 6px rgba(0,0,0,.9),
    0 0 0 1px rgba(0,0,0,.65);
}
.fcap:active{ cursor:grabbing; }
/* knurl: fine teeth on the two shoulders, none across the waist */
.fcap::before{
  content:''; position:absolute; inset:2px 3px;
  background-image:repeating-linear-gradient(0deg,
    rgba(255,255,255,.13) 0 1px, rgba(0,0,0,.30) 1px 2.5px);
  -webkit-mask:linear-gradient(180deg,#000 0 34%, transparent 36% 64%, #000 66% 100%);
          mask:linear-gradient(180deg,#000 0 34%, transparent 36% 64%, #000 66% 100%);
  border-radius:3px;
}
/* THE INDICATOR — this is the part that carries the value */
.fcap::after{
  content:''; position:absolute; left:2px; right:2px; top:50%; height:2px; margin-top:-1px;
  border-radius:1px; background:var(--led-hot);
  box-shadow:0 0 5px var(--led), 0 0 10px rgba(255,106,0,.55), 0 1px 0 rgba(0,0,0,.8);
}


/* ── THE JAW: the range fader's end, and why it is not a cap ──────────────
   A cap is centred on its value, so two of them merge into one lump as soon as
   the span drops under a cap's width — see the note in the kit. A jaw puts its
   MASS on the far side of the piece that carries the value, so the two bodies
   cannot overlap however close the values get.

       [▓▓]━┃■■■■■■■■■■■■┃━[▓▓]     grip · neck · blade · band · blade · neck · grip

   NO TRIANGLE. Two of them shipped and both were wrong, the second for the
   same reason as the first: a tapering shape has its least material exactly
   where you look at it hardest, so there is nothing there to shade and it
   reads as a DRAWING of a part. There is no lighting fix for a point. The
   EQ-200's caps are the reference and they are square, solid blocks — bright
   top rim, black waist, hard shadow underneath, mass everywhere. So the jaw is
   built out of that block, which is the one shape this panel already knows how
   to make.

   AND EVERY PIECE IS A ROUNDED RECTANGLE, WHICH IS HALF THE WIN. `clip-path`
   was the whole source of the trouble on the pointed versions: it clips
   pointer events, so the part was ungrabbable exactly at the tip where the two
   ends meet; it clips inset shadows, so the diagonals — the only edges that
   made the shape anything — were the two edges with no light on them; and it
   is applied AFTER filters, so a drop-shadow standing in for the bevel got
   trimmed off at the very silhouette it was meant to sit outside. Every one of
   those went away with the taper. The three pieces below take the cap's own
   six inset shadows unmodified, because they ARE the cap's shape.

   THE BOX IS STILL THE HIT TARGET and the three pieces are inert, because a
   19px part with a 3px waist in it is not a thing to ask a hand to hit
   precisely. It is also taller than anything it draws — the jaws sit on
   opposite sides of their own blades, so growing them cannot make them
   collide. */
.fend{
  position:absolute; top:50%; width:19px; height:28px; margin-top:-14px;
  cursor:grab; touch-action:none;
}
.fend:active{ cursor:grabbing; }

/* ONE MOULDING FOR ALL THREE PIECES — the cap's gradient and the cap's six
   shadows, copied and not adapted. Waist dark, both shoulders lit, a white
   hairline along the top, a black one down the inboard side, and the part
   sitting on its own shadow. */
.fend > i{
  position:absolute; top:50%; pointer-events:none;
  background:linear-gradient(180deg,#585d66 0%,#3a3e45 16%,#22252a 46%,#1a1c20 54%,#33373d 86%,#4c5158 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.45),
    inset 0 -1px 0 rgba(255,255,255,.22),
    inset 1px 0 0 rgba(255,255,255,.10),
    inset -1px 0 0 rgba(0,0,0,.6),
    0 3px 6px rgba(0,0,0,.9),
    0 0 0 1px rgba(0,0,0,.65);
}

/* THE GRIP — the square cap, outboard, and the only piece a thumb is invited
   to. 12 x 20 with the cap's 4px corners: near enough square to read as the
   button it is borrowed from, short enough that the blade beside it is clearly
   the taller thing. */
.fend-grip{ width:12px; height:20px; margin-top:-10px; border-radius:4px; }
.fend-a .fend-grip{ left:0; }
.fend-b .fend-grip{ right:0; }
/* the cap's knurl, same 1px/2.5px pitch, teeth on the two shoulders and none
   across the waist — a grip texture over the thin part is a grip on nothing */
.fend-grip::before{
  content:''; position:absolute; inset:2px 3px;
  background-image:repeating-linear-gradient(0deg,
    rgba(255,255,255,.13) 0 1px, rgba(0,0,0,.30) 1px 2.5px);
  -webkit-mask:linear-gradient(180deg,#000 0 34%, transparent 36% 64%, #000 66% 100%);
          mask:linear-gradient(180deg,#000 0 34%, transparent 36% 64%, #000 66% 100%);
  border-radius:3px;
}
/* AND THE LIT LINE ACROSS ITS WAIST, the cap's own indicator, because a grip
   that looks like the cap and is missing the one mark the cap wears reads as a
   cap with its lamp out. On the plain fader that line IS the value; here the
   BLADE carries the value and this is the mark that says the piece is live —
   the same part doing the lighter job, which is why it sits on the grip and
   never on the blade. Orange on the blade would be orange against the orange
   band, and cutting that band is the blade's whole job. The knurl's mask has
   already cleared the waist for it, exactly as it does on the cap. */
.fend-grip::after{
  content:''; position:absolute; left:2px; right:2px; top:50%; height:2px; margin-top:-1px;
  border-radius:1px; background:var(--led-hot);
  box-shadow:0 0 5px var(--led), 0 0 10px rgba(255,106,0,.55), 0 1px 0 rgba(0,0,0,.8);
}

/* THE BLADE — the vertical bar standing at the value, and the tallest piece
   because it is the one that means something: its INNER face is where the band
   stops. It is NOT lit, and that is the whole reason its edge reads — the band
   it terminates is orange, so a dark blade cuts it clean, where an orange
   indicator would dissolve into the very thing it is there to bound. The plain
   cap's lit hairline was marking a value against bare track and had nothing to
   lose to. This one does. */
.fend-blade{ width:4px; height:24px; margin-top:-12px; border-radius:2px; }
/* THE BLADE EASES OUTWARD TO KEEP THE SEAM OPEN when the two ends close on
   each other — `--nudge`, set from the kit, never more than a pixel and a half
   and zero for any span with room to be exact. It moves the DRAWN blade only:
   the jaw's box, which is the hit target, stays where the value put it. */
.fend-a .fend-blade{ right:var(--nudge, 0px); }
.fend-b .fend-blade{ left:var(--nudge, 0px); }

/* THE NECK — the thin section that carries the load between them, 6 wide and
   tucked 2px under the grip and 1px under the blade so both seams close. It
   goes down FIRST in the kit so the two thick pieces paint over its ends:
   three parts in a row is three parts, but a thin section under two thick ones
   is one moulding with a waist in it. Its own shadows are lighter than the
   others' for the same reason a waist is darker than a shoulder — less of it
   is facing the lamp. */
.fend-neck{ width:6px; height:8px; margin-top:-4px; border-radius:1.5px;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.26),
    inset 0 -1px 0 rgba(255,255,255,.12),
    0 2px 4px rgba(0,0,0,.85),
    0 0 0 1px rgba(0,0,0,.6);
}
.fend-a .fend-neck{ left:10px; }
.fend-b .fend-neck{ right:10px; }

/* THE HELD JAW CATCHES MORE LIGHT, and it is told to from the kit rather than
   read off `:active` — the TRACK captures the pointer, so the jaw never sees
   the press. With the two ends a pixel apart this is the only feedback left
   that says which one you have. */
.fend[data-hold] > i{ filter:brightness(1.22); }

/* THE BAND IS A GRIP NOW. It always claimed to be a piece — that is what the
   hatch is for — and it was the one part of this control you could not pick up,
   because every pointer event on the line was already spoken for by a cap.
   Dragging it slides the window with its span held. */
.ffill.band{ cursor:grab; }
.ftrack[data-on="band"] .ffill.band{ cursor:grabbing; }
/* A CLOSED BAND IS A LEGAL STATE, AND IT IS LIT. The two blades hold a three
   pixel seam open between them however far the values close, right down to
   a === b, and what shows in it is the band itself — so a shut span reads as a
   hairline of light where the two ends have met, rather than as a dark join
   you have to read the numbers to interpret. The values are not touched by it;
   only the drawn blades move.
   AT THAT WIDTH THE BAND IS A GLOW, NOT A PIECE. The hatch says "solid thing
   you can pick up" and cannot say it in three pixels; the inset black ring
   would eat two of the three. Both come off and the light is turned up. */
.ffill.band.tight{
  background-image:none;
  background:linear-gradient(180deg,#FFD9B4,var(--led-hot) 55%,#FF7A1A);
  box-shadow:0 0 7px var(--led), 0 0 17px rgba(255,106,0,.5);
}

/* THE SCALE MOVES IN BY A WHOLE JAW AT EACH END. A jaw is one-sided, so the
   value cannot reach the last 19px of groove at either end without walking its
   grip out of the slot — and a tick engraved where the value cannot go is a
   tick that lies. */
.fader.range .fticks{ left:19px; right:19px; }

/* ── THE CENTRE-DETENT SCALE ──────────────────────────────────────────────
   A trim fader's zero is a PLACE, so it is marked like one: a full-height
   tick at the middle where the others are stubs, and the cap notches into it.
   The quarter marks stay, because the scale still has to say how far. */
.fader.bi .fticks::before{
  content:''; position:absolute; left:50%; top:-1px; width:1px; height:5px;
  margin-left:-.5px; background:#000; box-shadow:1px 0 0 rgba(255,255,255,.07);
}

/* ── THE SAME FADER, STOOD UP ─────────────────────────────────────────────
   Everything that was horizontal turns: the slot, the groove's lighting (a
   vertical cut is lit on one WALL, not on its top edge), the scale, the
   cap's waist and its knurl, and the indicator line, which has to stay
   across the travel to point at a value. Column-reverse puts the readout at
   the top and the legend at the foot, which is where a mixer has always put
   them: you read the number while your hand is on the cap, and the channel
   name is under the strip. */
.fader.vert{ flex-direction:column-reverse; align-items:center; gap:7px; }
.fader.vert .flab{ flex:0 0 auto; text-align:center; }
.fader.vert .fslot{ flex:0 0 auto; width:34px; height:136px; }
.fader.vert .ftrack{
  left:12px; right:auto; top:0; bottom:0; width:9px; height:auto;
  background:linear-gradient(90deg,#060708 0%,#0d0f11 55%,#191c20 100%);
  box-shadow:
    inset 2px 0 4px rgba(0,0,0,.98),
    inset -1px 0 0 rgba(255,255,255,.09),
    inset 0 2px 3px -2px rgba(0,0,0,.9),
    inset 0 -2px 3px -2px rgba(0,0,0,.9),
    1px 0 0 rgba(255,255,255,.07);
}
.fader.vert .fticks{
  left:auto; right:1px; top:0; bottom:0; width:4px; height:auto;
  background-image:linear-gradient(180deg, #000 0 1px, transparent 1px);
  background-size:3px calc(25% - .25px); background-repeat:repeat-y;
  box-shadow:1px 0 0 rgba(255,255,255,.05);
}
.fader.vert .fticks::after{ right:auto; left:0; bottom:0; top:auto; width:3px; height:1px; }
.fader.vert.bi .fticks::before{
  left:0; top:50%; width:5px; height:1px; margin:-.5px 0 0;
  box-shadow:0 1px 0 rgba(255,255,255,.07);
}
.fader.vert .ffill{ left:2px; right:2px; top:auto; bottom:2px; }
.fader.vert .fcap{
  top:auto; left:50%; width:24px; height:17px; margin:-8.5px 0 0 -12px;
  background:linear-gradient(90deg,#585d66 0%,#3a3e45 16%,#22252a 46%,#1a1c20 54%,#33373d 86%,#4c5158 100%);
  box-shadow:
    inset 1px 0 0 rgba(255,255,255,.45),
    inset -1px 0 0 rgba(255,255,255,.22),
    inset 0 1px 0 rgba(255,255,255,.10),
    inset 0 -1px 0 rgba(0,0,0,.6),
    0 3px 6px rgba(0,0,0,.9),
    0 0 0 1px rgba(0,0,0,.65);
}
.fader.vert .fcap::before{
  inset:3px 2px;
  background-image:repeating-linear-gradient(90deg,
    rgba(255,255,255,.13) 0 1px, rgba(0,0,0,.30) 1px 2.5px);
  -webkit-mask:linear-gradient(90deg,#000 0 34%, transparent 36% 64%, #000 66% 100%);
          mask:linear-gradient(90deg,#000 0 34%, transparent 36% 64%, #000 66% 100%);
}
.fader.vert .fcap::after{
  left:50%; right:auto; top:2px; bottom:2px; height:auto; width:2px; margin:0 0 0 -1px;
}
/* a bank: the reason vertical exists at all is that the caps line up into a
   profile you read across, so the sample has to be more than one */
.fbank{ display:flex; gap:10px; align-items:flex-end; }

/* ── the ROTARY SELECTOR: a dropdown with detents ────────────────────────
   A <select> is a list that hides. A selector switch shows every position at
   once and points at the one you are on.

   THE POSITIONS ARE PIPS ON THE DIAL, which is where a rotary switch has
   always put them: one small dot per detent, ringed round the collar at the
   angle the pointer stops at. Two things fall out of that. You can see how
   many positions there are without reading anything — four dots is four
   choices. And the pointer LANDS ON ONE, so the knob is not merely "turned a
   bit", it is at a position, which a bare pointer over an unmarked collar can
   never say. The dots are dark until the pointer reaches them; the one under
   the pointer is lit, so the dial answers on its own and the label list beside
   it is only there to say what the positions are called. */
/* THE DIAL SITS IN A BOSS THAT STANDS PROUD OF THE CASE.

   A control does not have to live inside the rectangle. On real gear the big
   selector gets its own raised boss moulded into the shell, half off the face,
   because the switch body is deeper than the panel and the case grows to meet
   it. Three things follow, and all three are why it is worth doing here:

     the dial stops competing for panel width — the boss hangs ~20px outside
     the edge, and the width it would have eaten is now free for the labels;
     the panel gets a SILHOUETTE, so it reads as an object with a front and a
     side rather than as a rounded rectangle;
     and the one control that is not like the others stops looking like a knob
     that wandered in — it is bolted through a different part of the case.

   The positions then all fan to ONE side, which is what a boss on the left
   edge forces and what a rotary switch does anyway: a tick per detent, a
   leader line out to its name, the four names stacked in a column you read
   top to bottom. */
/* THE BAY: dial on the left, its two neighbours on the right, one band.
   Moving the boss onto the panel edge frees the width the dial used to eat,
   and the two param strips go into it at the same height — so what was three
   stacked blocks is one. */
.placebay{ display:flex; align-items:center; gap:6px; min-height:116px; }
.placebay .knobcol{ flex:1 1 auto; min-width:0; }
/* the place bay is tighter than the default strip, but not tighter than the
   glow: same 9px of vertical air, less on the sides */
/* THE PLACE BAY IS THE ONE TIGHT CALLER, so it opts out of the floor: it
   already runs a narrower value window and it shares a 318px box with a
   rotary, so 178 would push it out of its own panel. */
.placebay .kwrap.inline{ gap:9px; padding:9px 9px 9px 8px; min-width:0; }
.placebay .kwrap.inline .kval{ min-width:52px; }

.rotary{ position:relative; height:116px; flex:0 0 106px; --rcx:-5px; }
/* THE LEGEND IS ON THE CASE, ABOVE THE WHOLE BAY — not tucked over the first
   position. Inside the dial block it landed 8px above "Reference": two lines
   of small grey caps, the same size and colour, which reads as a five-item
   list whose first item happens to be the heading. Out here it is a heading
   over three controls, with a hairline under it doing the separating that the
   gap could not. */
.bay-cap{
  display:flex; align-items:center; gap:9px; padding:2px 2px 0;
  font-family:var(--font); font-size:8.5px; letter-spacing:.16em; text-transform:uppercase;
  color:#5d626b; text-shadow:0 1px 0 rgba(255,255,255,.09); white-space:nowrap;
}
.bay-cap::after{
  content:''; flex:1; height:1px; background:#0a0b0c; box-shadow:0 1px 0 rgba(255,255,255,.05);
}
/* THE BOSS WEARS THE CASE'S OWN FACE, not a darker disc laid on top of it.
   That is the whole trick: it is opaque and it paints above the panel's
   background, so the 1px rim running down the panel's left edge disappears
   behind it and the SILHOUETTE becomes panel ∪ circle — the case bulging out
   to meet a switch body deeper than the panel, which is why real enclosures
   have these. Get the gradient wrong and it is a coin sitting on a box. */
/* THE PANEL'S OUTLINE HAS TO FOLLOW IT, or it is a coin lying on a box.

   Three things had to change for that. The boss is a child of the CASE now,
   not of the dial — it paints on the panel's own background, above it and
   below the body, so the 1px rim running down the panel's left edge simply
   disappears behind it. Its centre sits 10px INSIDE the edge rather than on
   it, so the circle crosses the straight edge at about 11° off vertical
   instead of meeting it square: a soft junction where a half-disc gives you
   two corners. And the panel's drop shadow is a FILTER rather than a
   box-shadow — a box-shadow is cast by the border-box, so it would run
   straight down the old rectangle and cut the bulge in half, while
   filter: drop-shadow() is cast by the rendered ALPHA and therefore by the
   union of rectangle and circle. One silhouette, one shadow. */
/* THE BUMP IS THE PANEL'S OWN SHAPE. Nothing is added on top of it.

   Two goes at this failed the same way, and it was the same mistake twice: a
   separate disc sat over the panel, and a disc is a disc. Matching its grey
   was never going to work, because the panel's face is ONE gradient run down
   its whole height and the disc had a gradient of its own; and even with the
   fill aligned there were two elements, two edges and two shadows to keep in
   agreement, plus a blend mode inside a filtered box that blew the whole thing
   out white.

   So the panel is ONE element and the silhouette is a clip-path: the rounded
   rectangle unioned with a circular lobe, joined by a TANGENT FILLET at each
   end so the straight edge does not butt the curve — the flare the case would
   have where a moulded boss grows out of a flat face. The background is the
   panel's, painted once, clipped to that outline, so a seam cannot exist: it
   is not two surfaces meeting, it is one surface with a bump in it.

   The clip-path is generated from measured layout — the lobe's centre is
   wherever the dial ended up — and the drop shadow goes on a WRAPPER, because
   filters are applied before clipping and a shadow cast by the unclipped box
   would be clipped away with it. */
.bulge-wrap{
  filter:
    drop-shadow(0 26px 30px rgba(0,0,0,.75))
    drop-shadow(0 6px 8px rgba(0,0,0,.6));
}
.box.bulge{
  width:370px; flex:0 0 370px;               /* 318 of panel + 52 of lobe */
  padding:3px 3px 3px 55px;                  /* content stays inside the face */
  border-radius:0;                           /* the clip-path owns the corners now */
  background-image:
    radial-gradient(circle 46px at 44px var(--boss-y,50%), rgba(255,255,255,.055), transparent 74%),
    radial-gradient(circle 46px at 54px calc(var(--boss-y,50%) + 12px), rgba(0,0,0,.24), transparent 68%),
    linear-gradient(176deg,#26282d 0%,#1b1c20 26%,#161719 78%,#1c1d21 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.14),
    inset 0 -1px 0 rgba(0,0,0,.9),
    inset 0 -18px 26px -20px rgba(0,0,0,.8);
  clip-path:var(--panel-shape, none);
}
/* the brushed overlay is full-bleed now — the clip-path is what shapes it */
.box.bulge::before{ border-radius:0; }
/* --rcx IS MEASURED FROM THE BODY'S CONTENT BOX, NOT THE PANEL'S EDGE, and
   that is two paddings deep: the case's 55px on this side plus .box-body's own
   12px. Offsetting by only the first left the dial sitting 12px right of the
   lobe it is supposed to be concentric with — which is exactly the "the knob
   is not in the middle of its bump" that no amount of moving the bump fixes.
   Lobe centre 44 − (55 + 12) = −23. */
.box.bulge .rotary{ --rcx:-23px; }
/* the parting line where the moulded boss meets the flat face */
.box-boss::after{
  content:''; position:absolute; inset:6px; border-radius:50%;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05), inset 0 -1px 0 rgba(0,0,0,.4);
}
/* the parting line where the boss meets the face */
/* THE POINTER IS THE KNOB. A round cap with a cut line has to be read twice —
   once to find the line, once to see where it points. A teardrop IS the arrow:
   the body is what your fingers turn, the tip is the reading, and the whole
   shape rotates. It is also the one LIGHT part of the panel, which is how a
   selector on real gear is built — a pale lever on a dark plate, because where
   it points is the thing you must see from across the room. */
.rot-knob{
  position:absolute; left:var(--rcx); top:50%; width:62px; height:62px; margin:-31px 0 0 -31px;
  cursor:ns-resize; touch-action:none; border-radius:50%; z-index:2;
}
.rot-seat{
  position:absolute; left:var(--rcx); top:50%; width:74px; height:74px; margin:-37px 0 0 -37px;
  border-radius:50%;
  background:radial-gradient(circle, #0c0d0f 60%, #1c1e22 62%, #0f1012 100%);
  box-shadow:inset 0 2px 7px rgba(0,0,0,.92), 0 1px 0 rgba(255,255,255,.07);
}
/* THE CAP DOES NOT TURN — only the pointer does. That is the whole reason the
   lever became a disc: a circle has no orientation, so its lighting can be
   nailed to the room like every other cap here instead of swinging round with
   the value. */
.tear{
  width:100%; height:100%; overflow:visible; display:block;
  filter:drop-shadow(0 4px 7px rgba(0,0,0,.78));
}
.tear-ptr{
  transform:rotate(var(--deg,0deg));
  transform-box:view-box; transform-origin:50% 50%;
  transition:transform .18s cubic-bezier(.3,1.5,.5,1);
}
.rot-knob.dragging .tear-ptr{ transition:none; }
/* the leaders: tick end, elbow, then flat into the name. Engraved hairlines,
   so they are strokes — a 1px div rotated to an angle is a grey smear. */
.rot-leads{
  position:absolute; left:var(--rcx); top:50%; margin-top:-65px; overflow:visible;
  pointer-events:none;
}
.rot-lead{
  fill:none; stroke:#4b515b; stroke-width:1; stroke-linejoin:round; stroke-linecap:round;
  transition:stroke .14s, filter .14s;
}
.rot-lab{
  position:absolute; left:var(--rcx); top:50%; border:0; background:none; cursor:pointer;
  padding:4px 6px; margin:-4px -6px;      /* a real hit target, not just the word */
  font-family:var(--font); font-size:9.5px; letter-spacing:.1em; white-space:nowrap;
  color:#6b7079; transition:color .14s;
}
.rot-lab:hover{ color:#c2c7cf; }
.rot-lab.on{ color:#FFD9BC; text-shadow:0 0 8px rgba(255,140,60,.6); }

/* ═══════════════════════════════════════════════════════════════════════════
   .eng — THE ENGRAVED ICON, and it is the primitive this language was missing.

   There was engraved TYPE (.demo-etch) and nothing engraved that is a SHAPE,
   so every icon in the document is a flat monoline glyph laid ON the metal.
   That is the sticker failure the file already names about a centred
   highlight, and it is worse for icons because there are more of them.

   Same recipe as the etched legend, moved from text-shadow to drop-shadow
   because the subject is a stroke rather than a glyph: the line is the
   PANEL'S SHADOW COLOUR, not a grey, and one 1px light line sits under it —
   the lit far wall of the cut. Nothing in here is white, and nothing in here
   is currentColor: an engraving does not change colour when its row is
   selected, because it is a hole.
   ═══════════════════════════════════════════════════════════════════════════ */
.eng{
  width:14px; height:14px; flex:0 0 14px; display:block;
  fill:none; stroke:#0d0f12; stroke-width:1.7;
  stroke-linecap:round; stroke-linejoin:round;
  filter:drop-shadow(0 1px 0 rgba(255,255,255,.11));
}
/* Cut into a recess floor rather than the faceplate: less light reaches the
   far wall down there, so the highlight drops with it. */
.hb-deep .eng{ stroke:#08090b; filter:drop-shadow(0 1px 0 rgba(255,255,255,.055)); }

/* ═══════════════════════════════════════════════════════════════════════════
   PANEL BODIES — the editor and the viewport share one chassis
   ═══════════════════════════════════════════════════════════════════════════ */
.box{
  width:318px; flex:0 0 318px; position:relative; padding:3px; border-radius:14px;
  background:linear-gradient(176deg,#26282d 0%,#1b1c20 26%,#161719 78%,#1c1d21 100%);
  box-shadow:
    0 42px 60px -24px rgba(0,0,0,.9),
    0 8px 14px -6px rgba(0,0,0,.75),
    inset 0 1px 0 rgba(255,255,255,.14),
    inset 0 -1px 0 rgba(0,0,0,.9),
    inset 0 -18px 26px -20px rgba(0,0,0,.8);
  user-select:none;
}
.box::before{
  content:''; position:absolute; inset:0; border-radius:14px; pointer-events:none;
  background-image:
    repeating-linear-gradient(90deg, rgba(255,255,255,.020) 0 1px, transparent 1px 3px),
    var(--grain);
  background-size:auto,140px; opacity:.5; mix-blend-mode:overlay;
}
/* ── THE HEAD IS A LABEL, NOT A ROOM ──────────────────────────────────────
   IT WAS 46px TALL: 13 above and 11 below a 22px button. That is fine for one
   box standing on a documentation page and wrong the moment six of them are
   stacked on a desk, which is what the adopters actually do — 46px each is a
   quarter of a small screen's height spent saying which panel you are in, six
   times, in a system whose whole argument is that a faceplate is mostly the
   controls on it.

   28px, AND THE BUTTON HAD TO COME DOWN WITH IT. Padding alone bottoms out at
   36 because a 22px cap is the floor, and 36 was still a title bar with room in
   it. So the cap is 18px and the padding is 5 and 5. Eighteen is small for a
   target and it is the right kind of small: it is a title-bar control, the same
   class of thing as a window's own corner buttons, which every system draws at
   about this size and nobody misses — and the whole head is draggable, so the
   only pixels that have to be hit precisely are the ones that reduce it.

   THE NAME KEEPS ITS 12px. A title small enough to be overlooked is a title
   that has stopped working, and the height came out of the air round it rather
   than out of the type. */
.box-head{
  display:flex; align-items:center; gap:9px; padding:5px 12px 5px 16px; position:relative; z-index:2;
  border-bottom:1px solid rgba(0,0,0,.7); box-shadow:0 1px 0 rgba(255,255,255,.05);
}
.box-head .nm{ flex:1; font-family:var(--font); font-size:12px; font-weight:700; color:#e3e6eb;
  text-shadow:0 1px 0 rgba(0,0,0,.85); }
.box-body{ padding:12px; position:relative; z-index:2; display:flex; flex-direction:column; gap:12px; }

/* tab keys — the same interlock as the page switcher, one size down */
.piano.sm{ padding:5px; gap:7px; width:100%; }
/* A SIZE OVERRIDE MUST NOT OUT-RANK THE STATE. These say how big a key is,
   and they were also re-declaring its box-shadow — at the same specificity as
   the shared pressed rule but later in the file, so a small key that went down
   kept its RAISED shadow and only the label changed colour. Scoped to :not()
   so the raised shadow is only claimed while the key is actually up. */
.piano.sm .pkey{ height:26px; min-width:0; flex:1; padding:0 6px; font-size:9px; letter-spacing:.06em; }
.piano.sm .pkey:not(.is-down):not(:active){
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14), inset 0 -1px 0 rgba(0,0,0,.7),
             0 2px 0 #0d0e10, 0 3px 6px rgba(0,0,0,.6); }
/* THE TAB DOES NOT TRAVEL. A 26px cap dropping 2px in a strip this tight
   lands its shadow on the neighbour and the whole row reads as unstable —
   and unlike the big keys there is no room for the fall to be legible as a
   fall. It still goes IN: the recess and the chamfer say pressed, the lamp
   says which one. What is removed is the movement, not the state. */
.piano.sm .pkey:active,
.piano.sm .pkey.is-down{ transform:none; }

/* a screen: the reference image behind glass */
.screen{
  border-radius:8px; padding:9px; position:relative; overflow:hidden;
  background:linear-gradient(180deg,#08090b,#111316);
  box-shadow:inset 0 3px 8px rgba(0,0,0,.95), inset 0 -1px 0 rgba(255,255,255,.05), 0 1px 0 rgba(255,255,255,.06);
}
.screen canvas, .screen .thumb{ display:block; width:100%; border-radius:4px; filter:contrast(1.05) brightness(.96); }
/* the glass: one diagonal reflection, and a scanline haze */
.screen::after{
  content:''; position:absolute; inset:0; pointer-events:none; border-radius:8px;
  background:
    linear-gradient(148deg, rgba(255,255,255,.10) 0%, rgba(255,255,255,.02) 26%, transparent 46%),
    repeating-linear-gradient(0deg, rgba(0,0,0,.16) 0 1px, transparent 1px 3px);
}
.screen .cap{
  display:block; text-align:center; margin-top:7px;
  font-family:var(--font); font-size:8.5px; letter-spacing:.14em; color:#3f7a5c;
}
/* A SCREENSHOT IS A SCREEN AND NOT A THUMBNAIL, so it keeps the well and the
   diagonal reflection and loses the scanlines. The haze is a 3px repeat: right
   on a 56px reference where it reads as one texture, and at full width it is a
   grille laid over somebody's actual interface. The glass has to stay — without
   it a bright rectangle on this page is a hole rather than a display. */
.screen.shot{ padding:7px; }
.screen.shot::after{
  background:linear-gradient(148deg, rgba(255,255,255,.09) 0%, rgba(255,255,255,.02) 24%, transparent 44%);
}
.screen.shot img{ display:block; width:100%; border-radius:5px; filter:none; }
.filmrow{ display:flex; align-items:center; gap:10px; }
.filmrow .thumb{ width:56px; height:56px; object-fit:cover; border-radius:5px; flex:0 0 56px;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.1), 0 2px 5px rgba(0,0,0,.7); }
.filmrow .meta{ flex:1; min-width:0; }
.filmrow .fn{ font-family:var(--font); font-size:11px; color:#dfe2e7; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.filmrow .fs{ font-family:var(--font); font-size:9px; letter-spacing:.08em; color:#61666f; margin-top:3px; }
.filmrow .stack{ display:flex; flex-direction:column; gap:5px; }
.filmrow .stack .key{ height:24px; padding:0 10px; font-family:var(--font); font-size:9px; letter-spacing:.1em; }
/* THREE KEYS ARE TALLER THAN THE PICTURE, so the picture grows. 3 x 24 + 2 x 5
   is 82 against the thumb's 56, and at that point either the stack overhangs a
   small image or the image comes up to meet it. It comes up: an 82px preview of
   a map is a better preview anyway, and the row stays ONE block rather than a
   picture with something taller floating beside it. Set from the key COUNT in
   `assetRow`, never by the caller — a row that is tall because somebody passed
   a flag is a row that will be tall with two keys in it. */
.filmrow.three .thumb{ width:82px; height:82px; flex:0 0 82px; }

/* EMPTY IS A WELL, NOT A GAP. Every one of these rows starts with nothing in
   it, so the state gets a part rather than a hole: the same recess a slot is
   cut with, and no border to suggest a frame around a picture that is not
   there. */
.filmrow .thumb.empty{
  background:linear-gradient(180deg,#0c0d0f,#141619);
  box-shadow:inset 0 3px 7px rgba(0,0,0,.9), inset 0 0 0 1px rgba(255,255,255,.05);
}

.knobrow{ display:flex; align-items:flex-start; justify-content:space-around; gap:8px; padding:2px 0; }
/* a settings bay is not a viewport — the knobs sit tight and share the row */
.set-bay .knobrow{ padding:0; justify-content:space-evenly; }
/* single knobs stack as ROWS — one 40px line each, the height a slider was */
.knobcol{ display:flex; flex-direction:column; gap:8px; padding:1px 2px; }
.divider{ height:1px; background:#0a0b0c; box-shadow:0 1px 0 rgba(255,255,255,.06); margin:1px 0; }
.actrow{ display:flex; align-items:center; gap:7px; }
.actrow .act{ flex:1; }
.act.fire{
  color:#FFE0C8;
  background:linear-gradient(180deg,#C25411,#8E3B0B 55%,#6A2C08);
  box-shadow:
    inset 0 1px 0 rgba(255,210,170,.4), inset 0 -1px 0 rgba(0,0,0,.6),
    0 3px 0 #451c05, 0 5px 9px rgba(0,0,0,.65), 0 0 14px rgba(255,106,0,.3);
}
.act.fire:hover{ background:linear-gradient(180deg,#DC6314,#A5450D 55%,#7C340A);
  box-shadow:inset 0 1px 0 rgba(255,210,170,.5), 0 3px 0 #451c05, 0 5px 9px rgba(0,0,0,.65), 0 0 20px rgba(255,106,0,.5); }
.act.fire:active{ transform:translateY(3px);
  box-shadow:inset 0 2px 6px rgba(0,0,0,.85), 0 0 0 #451c05, 0 1px 2px rgba(0,0,0,.5), 0 0 16px rgba(255,106,0,.4); }
.act.fire .lens{ background:radial-gradient(circle at 35% 30%, #fff, #FFC08A 40%, #E05A00);
  box-shadow:0 0 7px rgba(255,140,50,.9), inset 0 -1px 1px rgba(0,0,0,.4); }

/* ═══════════════════════════════════════════════════════════════════════════
   THE GIZMO — an axis ball under a glass dome
   ═══════════════════════════════════════════════════════════════════════════ */
/* ═══════════════════════════════════════════════════════════════════════════
   THE ORBIT BALL — three draggable rings

   THE SVG DOES THE DRAWING AND THIS DOES THE MATERIAL, which is the split the
   knob ring already uses: geometry is arithmetic and belongs in the factory,
   but how thick a near edge is against a far one is a decision about light and
   belongs here.

   NEAR IS THICK AND OPAQUE, FAR IS THIN AND FAINT, and that ratio is the only
   thing making three ellipses read as a sphere. Two stops of opacity is not
   enough on a dark ground — the far half has to look like it is behind
   something, and at .30 it does.

   THE GRAB CURSOR IS ON THE WHOLE BOX and not on the rings, because an SVG
   stroke is a hit area two pixels wide and a cursor that only appears when you
   are already on target is a cursor that never appears. The factory's own pick
   radius is 13px; the cursor says "this thing turns" from anywhere on it.
   ═══════════════════════════════════════════════════════════════════════════ */
/* NO FACEPLATE. It had one — a round disc of metal with the socket sunk into
   the middle of it — and that is one plate too many: a panel adopting this got
   somebody else's metal pasted onto its own, and a plate on a plate reads as a
   BOSS rather than as a cut. This part is the HOLE. The collar's knife line is
   at the edge of the box, the plate is whatever the box is sitting in, and
   that plate belongs to the host. */
.orbit{
  position:relative; flex:0 0 auto; margin:0 auto;
  cursor:grab; touch-action:none; user-select:none;
}
.orbit.turning{ cursor:grabbing; }

/* ══════════════════════════════════════════════════════════════════════════
   AND NONE OF IT HAPPENS UNDER GLASS — hence `:not(.glazed)` on all three.

   A DOME OVER THE BORE CONTAINS THE LIGHT. The three rules below are the
   socket answering back while an axis is being turned: the collar's machined
   edge takes the colour, the mouth's shade warms, the seat with it. All three
   are the plate and the well — which is to say, they are what a glazed ball
   has a LID over. Light coming off something inside a covered well does not
   arrive on the rim of the cover; it stays in the well.

   THE FEEDBACK IS NOT LOST, IT MOVES INBOARD. Everything that says "this axis
   is live" and is not the plate still runs: the arc itself, the live colour on
   the well lips, and the cast light on the ball's underside — all of them
   inside the raster, which is to say under the glass, which is where a lit
   thing in a covered well is. What goes is the wash on the plate, and on a
   glazed ball that wash was the one mark with nothing physical behind it.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── THE SOCKET'S RIM TAKES THE COLOUR, BUT ONLY WHILE SOMETHING IS TURNING ──
   THE COLLAR'S LOWER EDGE IS THE BRIGHTEST FIXED THING ON THIS PART — the one
   machined line that does not move when the ball does, which is exactly why it
   reads as the plate rather than as the object. Colouring it is loud, so it is
   spent on the fact rather than the question: turning an axis, not pointing at
   one. Hover lights the arc and the wire and leaves the metal alone. */
.orbit:not(.glazed)[data-drive] .orbit-collar{
  box-shadow:
    inset 0 calc(2px * var(--osc,1)) calc(3px * var(--osc,1)) rgba(0,0,0,.9),
    inset 0 calc(-3px * var(--osc,1)) calc(7px * var(--osc,1)) color-mix(in srgb, var(--axc) 43%, transparent),
    0 0 0 1px rgba(0,0,0,.72),
    0 1px 0 rgba(255,255,255,.07);
}
/* ── AND THE SOCKET'S SHADOW IS TINTED, NOT BLACK ───────────────────────────
   A SHADOW IS NOT AN ABSENCE OF LIGHT, IT IS WHAT IS LEFT OF IT. The rim is
   lit red; the dark inside the socket is that same red minus almost all of it,
   which is a very dark red — not black. Leaving it neutral is the giveaway
   that the rim's colour was painted on rather than shone, because nothing that
   is actually lit casts a grey shadow.

   A QUARTER OF THE COLOUR INTO BLACK, so it is still unmistakably shadow. The
   difference is barely nameable in isolation and obvious in place, which is
   what tinted shade is supposed to be. */
.orbit:not(.glazed)[data-drive] .orbit-mouth{
  --shade: color-mix(in srgb, var(--axc) 6%, #000);
  box-shadow:
    inset 0 0 calc(13px * var(--osc,1)) calc(7px * var(--osc,1)) color-mix(in srgb, var(--shade) 88%, transparent),
    inset 0 calc(7px * var(--osc,1)) calc(16px * var(--osc,1)) calc(1px * var(--osc,1)) color-mix(in srgb, var(--shade) 72%, transparent),
    inset 0 0 calc(28px * var(--osc,1)) calc(2px * var(--osc,1)) color-mix(in srgb, var(--shade) 45%, transparent),
    inset 0 calc(-2px * var(--osc,1)) calc(8px * var(--osc,1)) color-mix(in srgb, var(--axc) 30%, transparent);
}
.orbit:not(.glazed)[data-drive] .orbit-seat{
  --shade: color-mix(in srgb, var(--axc) 5%, #000);
  box-shadow:
    inset 0 calc(7px * var(--osc,1)) calc(14px * var(--osc,1)) color-mix(in srgb, var(--shade) 95%, transparent),
    inset 0 calc(-2px * var(--osc,1)) calc(3px * var(--osc,1)) rgba(255,255,255,.10),
    0 1px 0 rgba(255,255,255,.06);
}
.orbit-seat{ transition:box-shadow .16s linear; }
.orbit-collar, .orbit-mouth{ transition:box-shadow .16s linear; }

/* NO HALO ROUND THE SOCKET. A glow on the seat and the collar lit the whole
   plate, which is a selection ring wearing a costume — it says "this control"
   when the thing that changed is one arc of it, and at that size it is the
   loudest object on the page. The light belongs INSIDE the cut, behind the
   wall of the well, where it is a hint that something in there is on. That is
   the raster's job, not the stylesheet's. */
/* THE BALL IS A RASTER NOW, and the classes that were here — arc, grid, cap,
   seam — are gone with the strokes that wore them. Every one of them was a
   line pretending to be a surface. The material lives in a map, the map is
   sampled per pixel against the pixel's own normal, and there is nothing left
   for CSS to colour: what a mark looks like is a consequence of where it sits
   on a lit sphere, which is not a thing a stylesheet can know.

   IT SITS BETWEEN THE SEAT AND THE MOUTH, in the same order the strokes did —
   socket floor under, ball, then the socket's own shadow over the top of it,
   so a ball edge runs into the rim shadow rather than being cut out against
   it. */
.orbit-cv{
  position:relative; z-index:1; display:block;
  width:100%; height:100%; border-radius:50%;
}

.orbit-seat{
  position:absolute; inset:0.8%; border-radius:50%; pointer-events:none;
  background:
    linear-gradient(178deg,
      #07080a 0%, #0b0d10 34%, #14181d 62%, #262b32 88%, #303640 100%),
    #0d0e10;
  box-shadow:
    inset 0 calc(7px * var(--osc,1)) calc(14px * var(--osc,1)) rgba(0,0,0,.95),
    inset 0 calc(-2px * var(--osc,1)) calc(3px * var(--osc,1)) rgba(255,255,255,.10),
    0 1px 0 rgba(255,255,255,.06);
}
/* THE RIM STEALS LIGHT FROM EVERY SIDE AT ONCE — not directional like the
   lamp — so the outer few millimetres go dark whichever way you look. This is
   what dissolves the sphere's own circular edge into the socket wall behind
   it, and it sits OVER the rings so a ring running out to the rim goes into
   the same shadow the ball does. */
.orbit-mouth{
  position:absolute; z-index:2; inset:2.0%; border-radius:50%; pointer-events:none;
  box-shadow:
    inset 0 0 calc(13px * var(--osc,1)) calc(7px * var(--osc,1)) rgba(0,0,0,.88),
    inset 0 calc(7px * var(--osc,1)) calc(16px * var(--osc,1)) calc(1px * var(--osc,1)) rgba(0,0,0,.72),
    inset 0 0 calc(28px * var(--osc,1)) calc(2px * var(--osc,1)) rgba(0,0,0,.45),
    inset 0 calc(-2px * var(--osc,1)) calc(2px * var(--osc,1)) rgba(255,255,255,.05);
}
/* ── AND IT GOES UNDER THE CANVAS ONCE THERE IS GLASS IN THE HOLE ──────────
   OVER THE BALL IS RIGHT FOR AN OPEN BORE: the socket's shadow is in FRONT of
   a ball sitting down in a hole. It is not in front of the glass that CAPS
   that hole. The dome's edge lift sits at 0.93 of the radius, where these three
   shadows measure about 90% opaque — so left on top they do not dim that ring,
   they delete it.

   THE BALL DOES NOT LOSE ITS EDGE BY THIS. That darkening was always doubled:
   `VIG` in the raster does the same job from the inside, per pixel, and its own
   note says so in nearly the same words. What the mouth still does, and can
   only do from down here, is shade the socket wall between the ball's
   silhouette and the hole. */
.orbit.glazed .orbit-mouth{ z-index:0; }
/* ── AND NEITHER OF THEM KEEPS ITS BOTTOM HIGHLIGHT UNDER GLASS ────────────
   BOTH THE SEAT AND THE MOUTH CARRY A WHITE LINE ALONG THEIR LOWER INSIDE —
   `inset 0 -2px … rgba(255,255,255,…)` — and it is right for an open bore: it
   is the far wall of the well catching the room, which is what stops a hole
   reading as a printed circle. It is also the last light left pooling under
   the ball once the dome is on, and a pool of light under a covered thing is
   the one place a viewer knows there should be none.

   THE DOME IS OVER BOTH OF THEM. Same rule as the collar's wash and the cast
   light on the ball's underside: light belonging to the plate does not arrive
   on something the plate has a lid over. The dark half of each shadow stays —
   a well is still a well — only the highlight goes. */
.orbit.glazed .orbit-seat{
  box-shadow:
    inset 0 calc(7px * var(--osc,1)) calc(14px * var(--osc,1)) rgba(0,0,0,.95),
    0 1px 0 rgba(255,255,255,.06);
}
.orbit.glazed .orbit-mouth{
  box-shadow:
    inset 0 0 calc(13px * var(--osc,1)) calc(7px * var(--osc,1)) rgba(0,0,0,.88),
    inset 0 calc(7px * var(--osc,1)) calc(16px * var(--osc,1)) calc(1px * var(--osc,1)) rgba(0,0,0,.72),
    inset 0 0 calc(28px * var(--osc,1)) calc(2px * var(--osc,1)) rgba(0,0,0,.45);
}
/* THE HARD EDGE IS THE PLATE'S AND ONLY THE PLATE'S. It sits at the CUT IN THE
   FACEPLATE, not at the ball's rim — a machined edge is a plate that stops and
   a sphere never stops, it turns away. Two objects sharing one contour is what
   makes a ball look stamped into a hole. Everything inboard of this is tone. */
.orbit-collar{
  position:absolute; z-index:2; inset:0; border-radius:50%; pointer-events:none;
  box-shadow:
    inset 0 calc(2px * var(--osc,1)) calc(3px * var(--osc,1)) rgba(0,0,0,.9),
    inset 0 calc(-2px * var(--osc,1)) calc(3px * var(--osc,1)) rgba(255,255,255,.09),
    0 0 0 1px rgba(0,0,0,.72),
    0 1px 0 rgba(255,255,255,.07);
}
/* `--ow` IS THE RING WEIGHT AND THE FACTORY DERIVES IT FROM `size`. Everything
   below is a ratio of it, so a ball at 450px is the same drawing as one at 132
   rather than the same drawing with wire for rings. */
.orbcell{ display:flex; flex-direction:column; align-items:center; gap:13px; }
/* ── THE GLASS BENCH — DOC-ONLY, AND IT IS IN THE EXCLUDE LIST ─────────────
   A row of knobs under the specimen, for tuning the dome's material while it
   is being decided. It is not part of `orbit` and no app ships it: a control
   whose material is a knob row is a control nobody can adopt without settling
   ten numbers first. Sits in a shallow tray so it reads as bench equipment
   rather than as more of the specimen. */
.orbench{
  display:flex; flex-wrap:wrap; justify-content:center; align-items:flex-start;
  gap:18px 22px; margin:26px auto 2px; padding:16px 20px 12px; max-width:940px;
  border-radius:10px;
  background:linear-gradient(178deg, rgba(0,0,0,.34), rgba(255,255,255,.014));
  box-shadow:
    inset 0 2px 5px rgba(0,0,0,.55),
    inset 0 -1px 0 rgba(255,255,255,.05),
    0 1px 0 rgba(255,255,255,.04);
}

/* ── THE FRAME METER — DOC-ONLY, AND IT IS IN THE EXCLUDE LIST ────────────
   The same shallow tray as the bench above it, shorter, because it is the same
   kind of thing: equipment for deciding something rather than part of anything.
   Every selector here names `.fpsmeter` or `.fpscell` and nothing else — pair
   either with `.kval` and the rule stops being doc-only and ships, which is the
   whole reason the readouts are addressed as bare children. */
.fpsmeter{
  display:flex; flex-wrap:wrap; justify-content:center; align-items:flex-start;
  gap:8px 20px; margin:12px auto 2px; padding:11px 20px 9px; max-width:940px;
  border-radius:10px;
  background:linear-gradient(178deg, rgba(0,0,0,.30), rgba(255,255,255,.012));
  box-shadow:
    inset 0 2px 5px rgba(0,0,0,.5),
    inset 0 -1px 0 rgba(255,255,255,.045),
    0 1px 0 rgba(255,255,255,.035);
}
.fpscell{ display:flex; flex-direction:column; align-items:center; gap:6px; }
.fpscell > *{ margin:0; }
/* the copy button — a key, near enough, and the selector names `.fpsmeter`
   and nothing else so the rule strips out of dist with the rest of the tray */
.fpsmeter button{
  font-family:var(--font); font-size:8px; letter-spacing:.14em; color:#aab1bc;
  padding:6px 10px; border:0; border-radius:5px; cursor:pointer; white-space:nowrap;
  background:linear-gradient(180deg,#4a4f57 0%,#2a2d33 52%,#3c414a 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.30),
    inset 0 -1px 0 rgba(255,255,255,.12),
    0 2px 4px rgba(0,0,0,.8),
    0 0 0 1px rgba(0,0,0,.6);
}
.fpsmeter button:active{ background:linear-gradient(180deg,#33373d 0%,#22252a 52%,#2e3238 100%); }

/* ── THE THREE BALLS, AND THE SMALL ONE STEPS OUT OF LINE ────────────────────
   IT IS THE ONE BEING DRIVEN, so it is the one that should not sit in the row
   like a third sample. Pushed right and dropped down, with its knobs beneath
   it, it reads as the working control and the other two as what the same part
   looks like at other sizes — which is what they are. */
.orbrow{
  display:flex; gap:44px; align-items:center;
  flex-wrap:wrap; justify-content:center;
}
/* WHERE THE BAY SITS IN THE THREE-BALL ROW — the page's decision, not the
   part's. Pushed right and dropped down so the one being driven does not sit
   in line like a third sample. */
.orbay.orbspec{ margin-left:40px; margin-top:38px; }
/* THE THREE DIALS GO ACROSS, NOT DOWN. `.knobcol` is a column because that is
   what a bay of them is elsewhere; under a ball they are one row of three
   axes, and stacked they are taller than the ball they belong to — which puts
   the last one so far from it that it stops looking like its knob. Overridden
   here rather than changed at the source: every other user of `.knobcol` still
   wants a column. */
/* ── THE BAY: BALL, WIRES, DIALS ─────────────────────────────────────────────
   The kit builds this; the page only decides where it sits. Dials as close
   under the ball as the wires need — they are its knobs, and distance between
   them is distance between a thing and its own controls. */
.orbay{
  position:relative;
  display:flex; flex-direction:column; align-items:center; gap:22px;
}
.orbay-knobs{ display:flex; flex-direction:row; gap:20px; padding:1px 2px; }

/* ── THE TRACES, AND THEY ARE GROOVES BEFORE THEY ARE SIGNALS ────────────────
   CUT, NOT PRINTED. Same argument as the ball's own arcs: a line drawn on a
   surface is a decal, and what makes this read as machined is that it is dark
   with a lit edge under it — a channel in the panel with the light catching
   its lower wall. At rest that is all it is, and all it says is that these
   dials are wired to that ball.

   LIT ONLY WHILE ITS AXIS IS THE ONE MOVING. Three permanently glowing traces
   would be three claims competing at once, which is the state this panel is in
   least often; the useful moment is the one where exactly one thing is being
   turned, and then the panel can afford to say so loudly. */

.orbay-trace{
  position:absolute; left:0; top:0; width:100%; pointer-events:none; overflow:visible;
  /* OVER THE BALL, because the last few pixels of every channel are cut INTO
     it — a wire that stops at the silhouette is touching the sphere, one that
     crosses it is going in. The canvas cannot draw these: it knows nothing
     about where the dials ended up. */
  z-index:3;
}
.ot{
  fill:none; stroke-linejoin:miter; stroke-linecap:butt;
}
/* THE CHANNEL — always there, and it is metal, so it never emits */
.ot-cut{
  stroke-width:5; stroke:#0d1013;
  /* TWO WALLS, NOT ONE. A single light edge below reads as a line with a
     highlight under it; a dark edge ABOVE as well is what says the channel has
     a near side too, and depth is the pair — one wall catching the lamp, one
     losing it. Same argument the ball's own cuts make. */
  filter:drop-shadow(0 1px 0 rgba(255,255,255,.085))
         drop-shadow(0 -1px 0 rgba(0,0,0,.55));
}
/* THE FILAMENT — a third the width, lying at the bottom of it, and nothing at
   all until its axis is carrying. It is not a lit version of the groove: it is
   a different object, further away, seen through the groove. */
.ot-lit{
  stroke-width:3.4; stroke:transparent;
  transition:stroke .16s linear, filter .16s linear;
}
/* AND EACH ONE ANSWERS ONLY TO ITS OWN NAME — while it is being turned, and
   also while the hand is merely OVER its dial. Hover is not a lesser version
   of the drag state, it is the question the drag answers: "which of these
   three am I about to move." Showing the wire then is the whole reason the
   wire is drawn, and it means the connection is visible without committing to
   a turn — which is also the only way to see it in a still picture.

   `:has` rather than a JS hover handler: the trace sits BEFORE the dials in
   the DOM and no amount of sibling selector reaches backwards, but the parent
   knows about both. */
.orbay:has(.orbay-knobs > :nth-child(1):hover) .ot-lit.ot-yaw,
.orbay:has(.orbay-knobs > :nth-child(2):hover) .ot-lit.ot-pitch,
.orbay:has(.orbay-knobs > :nth-child(3):hover) .ot-lit.ot-roll,
.orbay[data-live="yaw"]   .ot-lit.ot-yaw,
.orbay[data-live="pitch"] .ot-lit.ot-pitch,
.orbay[data-live="roll"]  .ot-lit.ot-roll{
  stroke:var(--lit);
  /* THE BLOOM HUGS THE LINE, BUT IT HAS TO BE THERE TO HUG IT. Two passes at
     2 and 5 was so tight it was indistinguishable from the stroke itself —
     technically correct restraint, visually nothing. Three passes now: the
     core escaping the slot, what lands on the metal beside it, and a wide
     faint one that is the only part reading as light in the air rather than on
     a surface. Still short of a halo — the falloff is done inside fifteen
     pixels, so the plate two centimetres away is untouched. */
  /* FULL — dial back from here */
  filter:drop-shadow(0 0 4px var(--lit))
         drop-shadow(0 0 10px var(--lit))
         drop-shadow(0 0 22px var(--lit))
         drop-shadow(0 0 40px var(--lit));
}
.ot-yaw{   --lit:#4ADE80; }
.ot-pitch{ --lit:#FF4A4A; }
.ot-roll{  --lit:#5AA9FF; }

/* THE LABEL GOES WITH IT. The knob already wears its axis on the arc; while it
   is the one being turned, its NAME says so too — which is the half you can
   read without looking at the dial. */
.orbay .orbay-knobs > :nth-child(1):hover .klab,
.orbay .orbay-knobs > :nth-child(2):hover .klab,
.orbay .orbay-knobs > :nth-child(3):hover .klab,
.orbay[data-live="yaw"]   .orbay-knobs > :nth-child(1) .klab,
.orbay[data-live="pitch"] .orbay-knobs > :nth-child(2) .klab,
.orbay[data-live="roll"]  .orbay-knobs > :nth-child(3) .klab{
  color:var(--klit); text-shadow:0 0 7px var(--klit);
}
/* ── THE POINTER LIGHTS, NOT THE NOTCH ──────────────────────────────────────
   THE NOTCH IS AT TWELVE O'CLOCK AND STAYS THERE. Lighting it put the colour
   on the one mark that never moves — a lamp on the datum, which says the knob
   is live but not a word about where it is. The POINTER is the knob's answer,
   and it is already the thing your eye is on: light that and the lit part and
   the informative part are the same part.

   The notch keeps its cut, unlit, because it is still the last few pixels of
   the wire arriving — a channel, not a signal. */
/* THE ALWAYS-ON COLOUR IS THE KIT'S NOW — `.knob.endless` above paints the
   pointer with the knob's own `arc`, and these three already pass their axis
   colour as `arc`, so the panel does not have to say it twice. What is left
   here is only the BRIGHTENING, which is a state and belongs to the panel. */
.orbay .orbay-knobs > :nth-child(1):hover .knob-cap::after,
.orbay .orbay-knobs > :nth-child(2):hover .knob-cap::after,
.orbay .orbay-knobs > :nth-child(3):hover .knob-cap::after,
.orbay[data-live="yaw"]   .orbay-knobs > :nth-child(1) .knob-cap::after,
.orbay[data-live="pitch"] .orbay-knobs > :nth-child(2) .knob-cap::after,
.orbay[data-live="roll"]  .orbay-knobs > :nth-child(3) .knob-cap::after{
  box-shadow:0 0 0 1px color-mix(in srgb, var(--klit) 55%, transparent),
             0 0 7px var(--klit), 0 0 15px var(--klit);
}
.knob-cap::after{ transition:background .16s linear, box-shadow .16s linear; }
/* THE COLOUR IS THE DIAL'S OWN, whichever way it came to be lit */
.orbay .orbay-knobs > :nth-child(1){ --klit:#4ADE80; }
.orbay .orbay-knobs > :nth-child(2){ --klit:#FF4A4A; }
.orbay .orbay-knobs > :nth-child(3){ --klit:#5AA9FF; }
.klab{ transition:color .16s linear, text-shadow .16s linear; }
/* NO AXIS BADGE. A small orange X/Y/Z appeared in the corner while a ring was
   held — a label naming the thing you already have hold of, in a colour that
   belongs to none of the three rings and means "fault" everywhere else on this
   panel. The ring itself lights in its own colour, and where this part sits
   beside dials the trace and the dial's name light too; that is three
   statements of the same fact before the badge adds a fourth. `data-ax` stays
   on the element as a hook for a host that wants one. */

.gizmo{
  width:132px; height:132px; margin:0 auto; position:relative; border-radius:50%;
  background:
    radial-gradient(circle at 38% 26%, rgba(255,255,255,.13) 0%, transparent 42%),
    radial-gradient(circle at 50% 60%, #191b1f 0%, #0c0d0f 78%);
  box-shadow:
    inset 0 4px 10px rgba(0,0,0,.95),
    inset 0 -2px 0 rgba(255,255,255,.06),
    0 1px 0 rgba(255,255,255,.07);
}
.gizmo::after{  /* the dome */
  content:''; position:absolute; inset:0; border-radius:50%; pointer-events:none;
  background:linear-gradient(152deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,.03) 28%, transparent 52%);
}
.gz-arm{ position:absolute; left:50%; top:50%; height:2px; transform-origin:0 50%;
  background:linear-gradient(90deg, color-mix(in srgb,var(--ax) 30%, transparent), var(--ax));
  box-shadow:0 0 5px color-mix(in srgb,var(--ax) 55%, transparent); border-radius:1px; }
.gz-node{
  position:absolute; width:15px; height:15px; margin:-7.5px 0 0 -7.5px; border-radius:50%;
  border:0; padding:0; cursor:pointer;
  background:radial-gradient(circle at 34% 28%, #fff 0%, color-mix(in srgb,var(--ax) 55%,#fff) 18%, var(--ax) 55%, color-mix(in srgb,var(--ax) 50%,#000) 100%);
  box-shadow:0 0 7px color-mix(in srgb,var(--ax) 70%,transparent), inset 0 -1px 2px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.55);
  transition:transform .12s;
}
.gz-node:hover{ transform:scale(1.22); }
.gz-node.hollow{ background:radial-gradient(circle at 50% 50%, #14161a 52%, color-mix(in srgb,var(--ax) 70%,#000) 53%);
  box-shadow:inset 0 0 4px rgba(0,0,0,.9), 0 0 0 1px rgba(0,0,0,.6); }
/* THE ONE THAT IS THE VIEW, AND IT IS LIT IN ITS OWN COLOUR. Everything else
   that latches on this panel goes amber; these six are already colour-coded by
   axis, and an orange ring round the red node would be a third colour arguing
   with the two that mean something. So it is the axis turned up — a white
   keyline, a wider halo, and the bead sitting proud of the ball.

   HOLLOW STAYS HOLLOW WHEN IT IS SELECTED. Filled and ringed is which side of
   the ball a node is on; lit is which one you picked. Two facts, two channels
   — fill the ring in on selection and the ball starts claiming that choosing
   Back moved it to the front. */
.gz-node.on{
  transform:scale(1.18);
  box-shadow:
    0 0 0 1.5px rgba(255,255,255,.9),
    0 0 13px color-mix(in srgb,var(--ax) 85%,transparent),
    inset 0 -1px 2px rgba(0,0,0,.6);
}
.gz-node.on:hover{ transform:scale(1.28); }
.gz-hub{
  position:absolute; left:50%; top:50%; width:11px; height:11px; margin:-5.5px 0 0 -5.5px;
  border-radius:50%;
  background:radial-gradient(circle at 34% 28%, #cfd4db, #5f656e 60%, #26292e);
  box-shadow:inset 0 -1px 1px rgba(0,0,0,.7), 0 0 0 1px rgba(0,0,0,.6), 0 0 6px rgba(255,255,255,.14);
}
/* A KEY THAT TRAVELS NEEDS ROOM UNDER IT. On a 5px grid the pressed key drops
   4px and its glow lands on the cap below — the two read as one welded block,
   and the thing that was supposed to say "this one is down" instead says "these
   two are joined". So the pad gets a taller ROW gap than column gap (they are
   not the same problem: nothing travels sideways), and its keys get a 3px
   skirt, which is what a short-travel key on a real pad has anyway. */
.keypad{ display:grid; grid-template-columns:repeat(3,1fr); gap:11px 5px; }
.keypad .pkey{ min-width:0; padding:0 4px; height:30px; font-size:9px; letter-spacing:.08em; }
.keypad .pkey:not(.is-down):not(:active){
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16), inset 0 -1px 0 rgba(0,0,0,.7),
             0 3px 0 #0d0e10, 0 5px 8px rgba(0,0,0,.7);
}
/* ── THE TOOLS ARE NOT PART OF THE VIEWPORT ──────────────────────────────
   Pan, Turn and Select were the third row of the viewport panel, under the
   view keys, and that is a filing error you can feel: everything else in that
   box answers WHERE THE CAMERA IS — a view, an angle, a fit. These three
   answer WHAT THE MOUSE DOES, which is true of the whole application and stays
   true while you are not looking at the viewport at all. Two different
   questions do not share a chassis.

   So they get their own palette, and it is a tool palette in the old sense: a
   narrow strip, one key per tool, stacked, small enough to sit anywhere on the
   canvas. Being a separate object also lets it be MOVED, which a row inside a
   panel can never be — and being three keys on their own bar makes the
   interlock obvious, where in a stack of rows it read as one more row. */
.palette{
  width:118px; flex:0 0 118px; align-self:flex-start;
  /* 10px, not 5: every raised cap lays a 3px hard skirt plus blur under
     itself, so a 5px gap is really ~2px of daylight — and a pressed cap
     drops 2px further into it. The keypad's stacked rows are 11px apart
     for the same reason. */
  display:flex; flex-direction:column; gap:10px;
  padding:8px; border-radius:12px; position:relative;
  background:linear-gradient(176deg,#26282d 0%,#1b1c20 30%,#161719 80%,#1d1e22 100%);
  box-shadow:
    0 28px 40px -20px rgba(0,0,0,.9),
    0 6px 12px -5px rgba(0,0,0,.7),
    inset 0 1px 0 rgba(255,255,255,.14),
    inset 0 -1px 0 rgba(0,0,0,.9);
}
.palette::before{
  content:''; position:absolute; inset:0; border-radius:12px; pointer-events:none;
  background-image:
    repeating-linear-gradient(90deg, rgba(255,255,255,.018) 0 1px, transparent 1px 3px),
    var(--grain);
  background-size:auto,140px; opacity:.5; mix-blend-mode:overlay;
}
.pal-cap{
  font-family:var(--font); font-size:8px; letter-spacing:.2em; text-transform:uppercase;
  color:#5d626b; text-shadow:0 1px 0 rgba(255,255,255,.09);
  padding:1px 0 6px 3px;
}
.palette .pkey{ height:34px; min-width:0; padding:0 10px; font-size:9.5px; justify-content:flex-start; }
.palette .pkey svg{ width:14px; height:14px; margin-right:8px; stroke:currentColor; fill:none; stroke-width:1.7; }

/* ═══════════════════════════════════════════════════════════════════════════
   .plate — THE FLOATING PLATE, and it is the language's first one.

   Everything else in this document is bolted into a chassis. This is the first
   surface that OPENS: it positions itself against the thing that summoned it,
   sits above the panel, and goes away. The answer has to be reusable, because
   menus, tooltips and the rotary's own popup all want it.

   A plate is the same metal as a .box with two differences, and both are about
   elevation. The cast shadow is HARDER and SHORTER — a plate 8mm off the panel
   throws a tighter shadow than a panel 40mm off the desk — and there is a
   1px black line all the way round, because a lifted edge has a dark side no
   matter where the lamp is. Nothing else changes: same gradient, same grain,
   same edge highlight. It is a piece of the same machine, held up.
   ═══════════════════════════════════════════════════════════════════════════ */
.plate{
  position:fixed; z-index:80; padding:3px; border-radius:12px;
  background:linear-gradient(176deg,#26282d 0%,#1b1c20 26%,#161719 78%,#1c1d21 100%);
  box-shadow:
    0 18px 30px -10px rgba(0,0,0,.92),
    0 5px 10px -3px rgba(0,0,0,.8),
    0 0 0 1px rgba(0,0,0,.9),
    inset 0 1px 0 rgba(255,255,255,.15),
    inset 0 -1px 0 rgba(0,0,0,.9);
  user-select:none;
}
.plate::before{
  content:''; position:absolute; inset:0; border-radius:12px; pointer-events:none;
  background-image:
    repeating-linear-gradient(90deg, rgba(255,255,255,.020) 0 1px, transparent 1px 3px),
    var(--grain);
  background-size:auto,140px; opacity:.5; mix-blend-mode:overlay;
}
.plate-body{ padding:10px; position:relative; z-index:2; display:flex; flex-direction:column; gap:9px; }

/* ═══════════════════════════════════════════════════════════════════════════
   .picker — A COLOUR PICKER MADE OF THE SAME STUFF AS EVERYTHING ELSE.

   input[type=color] renders its popup in browser chrome and no CSS reaches
   it, so the one moment you touch a colour, a dark machined panel produced a
   bright OS sheet. Every primitive in this language exists to avoid exactly
   that. This is not "theme the popup" — it cannot be themed — it is replace
   it, keeping a hidden native input as the form value so nothing downstream
   has to change.

   DARK BY DEFAULT, and here that is not a style preference: a picker is the
   one place the panel must DISAPPEAR so the colour can be judged. Neutral
   metal, no coloured light spilling out of the chassis, no tint in the bezel.
   The only saturated thing inside is the colour being picked.

   NOT WANTED, and left out on purpose: alpha, HSL/LAB tabs, palette
   management. A plotter pen is opaque and has one colour.
   ═══════════════════════════════════════════════════════════════════════════ */
.picker{ width:236px; }

/* the field is behind glass, like every other display in the language */
.pk-field{
  position:relative; height:132px; border-radius:6px; cursor:crosshair;
  background:
    linear-gradient(0deg, #000, transparent),
    linear-gradient(90deg, #fff, hsl(var(--pk-h,20) 100% 50%));
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.75), 0 1px 0 rgba(255,255,255,.07);
  touch-action:none;
}
.pk-field::after{
  content:''; position:absolute; inset:0; border-radius:6px; pointer-events:none;
  background:
    linear-gradient(148deg, rgba(255,255,255,.13) 0%, rgba(255,255,255,.02) 24%, transparent 44%),
    repeating-linear-gradient(0deg, rgba(0,0,0,.12) 0 1px, transparent 1px 3px);
}
/* the crosshair reads as something UNDER the glass: a dark ring with a light
   one inside it, so it survives on white and on black without inverting */
.pk-cross{
  position:absolute; width:13px; height:13px; margin:-6.5px 0 0 -6.5px;
  border-radius:50%; pointer-events:none; z-index:1;
  box-shadow:0 0 0 1.5px rgba(0,0,0,.85), inset 0 0 0 1.5px rgba(255,255,255,.9),
             0 1px 3px rgba(0,0,0,.6);
}

/* HUE IS A FADER — a value with a range like every other value with a range,
   so it gets the slot, the knurled cap and the engraved scale. What changes
   is what is IN the slot and that there is no lit trace: the track is already
   the answer, and a fill over it would be a second reading of the same thing. */
.pk-hue{ padding:0; }
.pk-hue .flab, .pk-hue .kval{ display:none; }
.pk-hue .ftrack{
  background:linear-gradient(90deg,#f00 0,#ff0 16.66%,#0f0 33.33%,#0ff 50%,#00f 66.66%,#f0f 83.33%,#f00 100%);
  box-shadow:inset 0 1px 3px rgba(0,0,0,.7), inset 0 -1px 0 rgba(255,255,255,.12),
             0 1px 0 rgba(255,255,255,.07);
}
.pk-hue .ffill{ display:none; }
/* six primaries, not four quarters — the scale counts what the track holds */
.pk-hue .fticks{ background-size:calc(16.666% - .25px) 3px; }

.pk-row{ display:flex; align-items:center; gap:8px; }
.pk-row .key{ width:28px; height:28px; flex:0 0 28px; border-radius:7px; }
.pk-row .key svg{ width:14px; height:14px; }
.pk-hue{ flex:1; min-width:0; }

/* the readout is ONE LIT WINDOW, not three white boxes borrowed from a form */
.pk-lcd{ gap:0; padding:0 6px; height:28px; }
.pk-lcd input{
  width:30px; border:0; background:none; padding:0 2px; text-align:center;
  font-family:var(--font); font-size:10.5px; letter-spacing:.06em;
  color:inherit; text-shadow:inherit; font-variant-numeric:tabular-nums;
}
.pk-lcd input.hex{ width:62px; text-align:left; letter-spacing:.09em; }
.pk-lcd input:focus{ outline:none; background:rgba(124,255,176,.10); border-radius:3px; }
.pk-lcd .pk-sep{ opacity:.4; }
/* the label strip under the window, engraved on the case */
.pk-legend{
  display:flex; padding:0 6px; margin-top:-4px;
  font-family:var(--font); font-size:7.5px; letter-spacing:.14em; color:#4b5058;
  text-shadow:0 1px 0 rgba(255,255,255,.07);
}
.pk-legend span{ width:34px; text-align:center; }

/* recents are pen caps in a well — the SAME object the ink rail uses, at the
   size a rail of eight needs. One primitive, two places. */
.pk-well{
  display:flex; align-items:flex-end; gap:5px; height:34px; padding:4px 6px 3px;
  border-radius:7px;
  background:linear-gradient(180deg,#131519,#191c20);
  box-shadow:inset 0 2px 6px rgba(0,0,0,.85), inset 0 -1px 0 rgba(255,255,255,.04);
}
/* the recents are the same tube at a smaller size — the clip-path is in
   percentages, so the silhouette comes along for free */
.pk-well{ height:34px; align-items:center; }
.pk-well[hidden]{ display:none; }
.pk-well .pencap{ width:17px; height:17px; flex:0 0 17px; border-radius:6px; --ring-r:8px; }
/* pinned: both selectors weigh the same and the rail's rules come later, so
   without this the selected recent takes the rail's size */
.pk-well .pencap.on{ transform:none; width:17px; height:17px; flex:0 0 17px; border-radius:6px; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE APP DOCK AND ITS MENUS — the other bar

   The nav bar above is a bar of DESTINATIONS and its spec refuses verbs in as
   many words. This is the other half of that sentence: a bar that is ALL verbs
   plus one readout, floating over the canvas at the bottom of the window, and
   it is the bar every Portrait app actually drives from.

   IT IS MADE OF PARTS THAT ALREADY EXISTED — `.strip` for the chassis, `.key`
   for the caps, `.chan` for the channel between the two groups, `.lcd` for the
   readout. What is new here is the placement, and the plate that opens off it.
   ═══════════════════════════════════════════════════════════════════════════ */
.appdock{
  position:fixed; left:0; right:0; bottom:20px; z-index:40;
  display:flex; justify-content:center;
  /* THE DOCK EATS CLICKS, and this is the same trap the nav dock carries: the
     dock spans the viewport and the bar does not, so without this you have an
     invisible full-width lid over the bottom of the canvas — which on a
     drawing app is the strip of paper nearest the hand. */
  pointer-events:none;
}
.appdock > *{ pointer-events:auto; }
/* `fixed:false` — a dock being SHOWN rather than run, on a documentation page
   or inside a settings preview. Same object, sitting in the flow. */
.appdock.inline{ position:static; }

.dock-bar .chan{ margin:0 8px; }
.dock-count{ min-width:104px; justify-content:center; }

/* ── the menu ─────────────────────────────────────────────────────────────
   A `.plate` with rows in it. The plate already brings the metal, the grain,
   the hard cast shadow and the 1px black line round a lifted edge; none of
   that is restated here. */
.menu{ min-width:212px; }
.menu .plate-body{ padding:5px; gap:1px; }

.menu-row{
  display:flex; align-items:center; gap:11px; width:100%;
  height:34px; padding:0 10px; border:0; border-radius:8px;
  background:none; color:#c2c7cf; cursor:pointer;
  font-family:var(--font-ui); font-size:12.5px; letter-spacing:.01em;
  text-align:left;
}
/* THE ROW LIGHTS, IT DOES NOT MOVE. A menu row is not a key — it has no cap,
   no skirt and no travel, and giving it 2px of push makes a list of them
   jitter as the pointer runs down it. What says "this one" is the same thing
   that says it on a faceplate: light. */
.menu-row:hover,
.menu-row:focus-visible{
  background:linear-gradient(180deg,#34383f,#26292e);
  color:#eef1f5;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.10), inset 0 -1px 0 rgba(0,0,0,.7);
  outline:none;
}
.menu-row:active{ background:linear-gradient(180deg,#202226,#1a1c1f); }
.menu-row:disabled{ opacity:.34; cursor:not-allowed; }
.menu-row:disabled:hover{ background:none; box-shadow:none; color:#c2c7cf; }

.menu-ico{ display:flex; flex:0 0 16px; color:#868d97; }
.menu-row:hover .menu-ico,
.menu-row:focus-visible .menu-ico{ color:var(--led); }
.menu-ico svg{ width:16px; height:16px; stroke:currentColor; fill:none;
               stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
.menu-nm{ flex:1; white-space:nowrap; }

/* THE SHORTCUT IS ENGRAVED, NOT PRINTED — a legend cut into the plate beside
   the row, the same recess the `.lcd` window sits in one size down. It is a
   HINT and never a target, so it takes no hover and no pointer. */
.menu-kbd{
  flex:0 0 auto; pointer-events:none;
  font-family:var(--font); font-size:9.5px; letter-spacing:.08em;
  color:#6b727c; background:#121417; padding:2.5px 6px; border-radius:4px;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.75), 0 1px 0 rgba(255,255,255,.05);
}
.menu-row:hover .menu-kbd,
.menu-row:focus-visible .menu-kbd{ color:#98a0ab; }

/* ── THE SELECTOR ─────────────────────────────────────────────────────────
   THE FACE IS A WINDOW, NOT A KEY. It is showing you a value, so it is the
   `.lcd`'s relative and not the `.pkey`'s: a recess with the name lit in it,
   the same engraved slot every readout on this panel sits in. What makes it
   a CONTROL rather than a readout is the chevron at its end and the fact that
   the whole window is the target — a 12px arrow is not something to aim at
   when the plate beside it is 168px wide and means the same thing.

   IT GOES DOWN WHEN IT IS OPEN, which is `.pkey.is-down`'s language, because
   what is open is a lid and the thing you pushed to lift it stays pushed. */
.selwrap{ display:flex; flex-direction:column; gap:5px; }
.sel{
  display:flex; align-items:center; gap:8px;
  width:var(--selw,168px); height:28px; padding:0 8px 0 11px;
  border:0; border-radius:6px; cursor:pointer; text-align:left;
  font-family:var(--font); font-size:9.5px; letter-spacing:.1em;
  color:#c2c7cf;
  background:linear-gradient(180deg,#0a0c0e,#12151a 55%,#0d1013);
  box-shadow:
    inset 0 2px 4px rgba(0,0,0,.95),
    inset 0 -1px 1px rgba(255,255,255,.06),
    0 1px 0 rgba(255,255,255,.06);
  transition:color .12s, box-shadow .14s;
}
.sel:hover{ color:#eef1f5; }
.sel-nm{ flex:1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* THE CHEVRON LIES DOWN AND STAYS DOWN. It points at where the plate will
   appear, which is under the face — and it does NOT flip when the list opens:
   a chevron that turns is saying "this closes", and what closes a plate here
   is Escape, the face, or anywhere else on the page. It is a signpost, not a
   toggle. */
.sel-chev{ display:flex; flex:0 0 12px; color:#5f6670; transform:rotate(90deg); }
.sel-chev svg{ width:12px; height:12px; stroke:currentColor; fill:none;
               stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.sel:hover .sel-chev{ color:#8d949e; }
.sel.is-down{ box-shadow:inset 0 3px 6px rgba(0,0,0,.95), inset 0 0 0 1px rgba(255,106,0,.28); }
.sel.is-down .sel-chev{ color:var(--led); }

/* ── THE LIST: TWO MARKS, FOR TWO DIFFERENT THINGS ────────────────────────
   A MENU HAS ONE MARK BECAUSE IT ONLY HAS ONE THING TO SAY. Its rows are
   commands, so the only state a row can be in is "the pointer is on it". A
   VALUE list has a row that is already true before you touch anything, and if
   that row and the hovered row look the same you cannot tell what you HAVE
   from what you are ABOUT TO GET — which is the one question you opened the
   list to answer.

   SO THE CURRENT ROW IS PRESSED IN and the pointed-at row LIGHTS. Down and up
   are opposite directions, they cannot be confused at a glance, and the pair
   is not invented here: it is exactly what a bank of `.pkey`s already does,
   where the key that is down is the page you are on and the key under your
   finger is merely warm. A selector row makes the same claim a lit key makes,
   so it is made the same way. */
.sellist .plate-body{ gap:1px; }
/* ── THE LIST IS SET LIKE THE ROTARY'S LEGENDS, and it had been set like a
   MENU: 12.5px of UI sans at #c2c7cf, which is the right size for a list of
   commands and a full size too big for a list of VALUES. The two controls
   answer the same question — §05's rotary spells its options out around the
   dial and this spells them down a plate — so a name has to look the same in
   both or they are two vocabularies for one idea.

   THE NUMBERS ARE .rot-lab's own, taken whole: 9.5px of the mono at .1em, dim at
   #6b7079, waking to #c2c7cf under the pointer, and the live one at #FFD9BC
   with the same warm bloom behind it. Nothing here is chosen; it is copied,
   which is the only way two parts stay agreeing after one of them is touched.
   The row comes down to 26px with the type — 34 was leading for 12.5px text
   and at 9.5 it is a gap with a word in it. */
.sel-row{
  gap:9px; height:26px;
  font-family:var(--font); font-size:9.5px; letter-spacing:.1em;
  color:#6b7079;
}
.sel-row:hover,
.sel-row:focus-visible{ color:#c2c7cf; }
.sel-row.is-cur{ color:#FFD9BC; text-shadow:0 0 8px rgba(255,140,60,.6); }
.sel-row.is-cur:hover,
.sel-row.is-cur:focus-visible{ color:#FFE7D4; }
/* THE PLATE HUGS THE NAMES. 212px is the menu's minimum because a command
   list has a shortcut column to keep clear of; a value list has nothing on its
   right and a plate wider than its longest name is a plate with a margin
   nobody asked for. */
.sellist{ min-width:0; }
.sellist .menu-nm{ white-space:nowrap; }
.sel-tick{
  flex:0 0 4.5px; height:4.5px; border-radius:50%; display:block;
  background:#2b3037; box-shadow:inset 0 1px 1px rgba(0,0,0,.8);
  transition:background .12s, box-shadow .12s;
}
/* CURRENT: the row has gone down into the plate. Same recess the panel uses
   for a latched key, and the tick is the lamp that comes on with it. */
.sel-row.is-cur{
  background:linear-gradient(180deg,#0c0e11,#15181c);
  color:#eef1f5;
  box-shadow:
    inset 0 2px 4px rgba(0,0,0,.9),
    inset 0 -1px 0 rgba(255,255,255,.05);
}
.sel-row.is-cur .sel-tick{
  background:var(--led);
  box-shadow:0 0 5px color-mix(in srgb, var(--led) 70%, transparent),
             0 0 0 1px rgba(0,0,0,.5);
}
/* AND HOVER STILL LIGHTS THE CURRENT ROW, it does not replace its recess. The
   row you have can be the row under your hand, and neither mark is allowed to
   swallow the other — the tick stays lit, the cut stays cut, and what changes
   is only that the name comes up. */
.sel-row.is-cur:hover,
.sel-row.is-cur:focus-visible{
  background:linear-gradient(180deg,#101317,#191d22);
  box-shadow:
    inset 0 2px 4px rgba(0,0,0,.9),
    inset 0 -1px 0 rgba(255,255,255,.07),
    inset 0 0 0 1px rgba(255,106,0,.22);
}

/* ── THE INTERLOCKED BANK ─────────────────────────────────────────────────
   NINE KEYS, THREE ACROSS, IN THE WELL THE PAGE SWITCHER ALREADY USES. There
   is almost nothing here, and that is the finding rather than an omission: a
   `.piano` laid out as a grid instead of a row IS this control. Three parts
   were built before it — a plate that opened, a card on a hinge, a barrel with
   a window — and each one was an answer to a question about SPACE that had
   quietly replaced the question about choosing.

   THE ONE THING THE ROW DID NOT NEED: keys that are all the same width. In a
   line they size to their labels and that is correct, because a bar of five
   tabs is read left to right and the widths are information. In a block they
   are a GRID and a ragged column edge is just a ragged column edge, so
   `1fr` each and the longest name sets the pitch. */
.bankwrap{ display:flex; flex-direction:column; gap:6px; }
.piano.sm.bank{
  display:grid; grid-template-columns:repeat(var(--bcols,3), 1fr);
  gap:5px; width:auto;
  /* the gutter belongs to the keys — see the note on `.pkey::before` */
  --pkey-reach-x:2.5px; --pkey-reach-y:2.5px;
}
/* A NAME IS NOT A TAB LABEL. The page switcher's keys carry one word at 9px
   with wide tracking, which is right for four of them across a bar and wrong
   for nine in a block: at that count the tracking is what makes the grid read
   as a wall of letters rather than as nine things. It comes in, the case stays
   upper — engraved legends on this panel are upper everywhere — and the key
   grows 4px so a two-word name has somewhere to sit. */
.bank-key{
  height:30px; min-width:0; padding:0 9px;
  font-size:9px; letter-spacing:.055em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE GATED LEVER — the list is cut into the plate, and nothing opens
   ═══════════════════════════════════════════════════════════════════════════
   TWO DROPDOWNS WERE BUILT BEFORE THIS AND THE SECOND ONE SETTLED IT. A plate
   that flies out over the panel reads as a web control however it is painted,
   because flying out over things is not something a panel DOES — and the
   lighter and softer it gets the worse it reads, because now it is a sheet of
   paper on a machined face. The failure was never the styling. It was the
   CATEGORY.

   A faceplate is a solid, and every real control on one is cut INTO it. So the
   list is milled into the plate: a slot with a detent notch at each position,
   a lever standing in it, the names engraved beside the notches. Everything is
   legible at rest, there is no reveal to design, and the only thing that moves
   is the lever.

   IT IS DARK, IT IS PLASTIC AND IT IS ALL ONE MATERIAL, which the hinged
   legend card that was tried here and deleted was not. The slot is the slide switch's own cut, the lever's grip is the slide
   switch's own knurl at the slide switch's own numbers, and the legends are
   the engraved captions §06 already puts under its switches. Nothing here is a
   new surface. */
.gatewrap{ display:flex; flex-direction:column; gap:6px; }
.gate{
  position:relative; display:flex; gap:10px; align-items:stretch;
  width:var(--gw,150px);
}

/* ── THE SLOT: the cut, and the detents in its walls ─────────────────────
   THE NOTCHES ARE ON BOTH WALLS because that is where a detent plate has
   them — the ball rides between two, and a slot notched down one side only is
   a comb. They are the one part of this that has to be believed: without them
   the lever is a fader cap and the whole argument for a GATE goes with it. */
.gate-slot{
  position:relative; flex:0 0 26px; width:26px; border-radius:7px;
  cursor:pointer; touch-action:none;
  background:linear-gradient(180deg,#06080a 0%,#0c0f13 46%,#080a0c 100%);
  box-shadow:
    inset 0 2px 4px rgba(0,0,0,.95),
    inset 1px 0 2px rgba(0,0,0,.75),
    inset 0 -1px 1px rgba(255,255,255,.07),
    0 1px 0 rgba(255,255,255,.06);
}
.gate-notch{
  position:absolute; left:0; right:0; height:3px; margin-top:-1.5px;
  display:block; pointer-events:none;
  /* A NOTCH IS A STEP IN A WALL, so it is drawn from the walls inward and
     dies before the middle: the lever runs down the centre and a line all the
     way across would be a rung it has to climb through. */
  background:
    linear-gradient(90deg,
      rgba(0,0,0,.85) 0 4px, transparent 7px,
      transparent calc(100% - 7px), rgba(0,0,0,.85) calc(100% - 4px)),
    linear-gradient(90deg,
      rgba(255,255,255,.10) 0 4px, transparent 7px,
      transparent calc(100% - 7px), rgba(255,255,255,.10) calc(100% - 4px));
  background-position:0 0, 0 1.5px;
  background-size:100% 1.5px, 100% 1.5px;
  background-repeat:no-repeat, no-repeat;
}

/* ── THE LEVER ───────────────────────────────────────────────────────────
   IT SITS IN THE SLOT AND STANDS PROUD OF IT, which is the difference between
   a lever and a marker: 2px of clearance at the sides so the cut shows all the
   way round, and a three-tier shadow under it so it is standing on the floor
   rather than printed on it. The same seating the slide switch cap got, for
   the same reason.

   THE KNURL RUNS ACROSS IT because you push this one up and down, and a grip
   is ribbed ACROSS the direction of travel — ribs running the same way as your
   thumb are ribs your thumb slides along. */
.gate-lever{
  position:absolute; left:2px; right:2px; height:17px; margin-top:-8.5px;
  display:block; border-radius:4px; pointer-events:none;
  background-image:
    linear-gradient(180deg, rgba(255,255,255,.15) 0 1px, transparent 1px),
    repeating-linear-gradient(180deg,
      rgba(255,255,255,.10) 0px, rgba(255,255,255,.02) 1.5px,
      rgba(0,0,0,.14) 2.3px, rgba(0,0,0,.38) 3.3px,
      rgba(0,0,0,.14) 4.1px, rgba(255,255,255,.10) 5.2px),
    linear-gradient(180deg,#34383f 0%,#2e3239 30%,#26292f 68%,#1f2227 100%);
  box-shadow:
    inset 0 -2px 4px rgba(0,0,0,.7),
    0 1px 2px rgba(0,0,0,.85),
    0 3px 5px rgba(0,0,0,.7),
    0 6px 10px rgba(0,0,0,.42);
  /* IT DROPS INTO THE NEXT NOTCH, IT DOES NOT GLIDE TO IT. Short, and with
     just enough overshoot to be a ball falling into a detent — the same curve
     the slide switch throws on, because it is the same event. */
  transition:top .13s cubic-bezier(.34,1.25,.5,1);
}
.gate.dragging .gate-lever{ transition:none; }
/* THE LAMP IS ON THE LEVER, not beside the name. A gate tells you where you
   are by WHERE THE LEVER IS; the lamp is only there to find it in the dark,
   which is why it is a hairline down the grip and not a jewel. */
.gate-lever::after{
  content:''; position:absolute; left:50%; top:3px; bottom:3px; width:2px;
  margin-left:-1px; border-radius:1px;
  background:var(--lyr, var(--led));
  box-shadow:0 0 5px color-mix(in srgb, var(--lyr, var(--led)) 70%, transparent);
}

/* ── THE LEGENDS ─────────────────────────────────────────────────────────
   ENGRAVED ON THE PLATE, one per notch, at the same pitch — cut into the
   metal, not printed on it, which is the dark fill with a lit lower wall that
   every caption on this page already uses.

   THE WHOLE ROW IS THE TARGET. The lever is 22px wide and the name beside it
   is the width of the control; making you aim at the small part when the big
   part means the same thing is a tax nobody should pay. What says the row is
   live before you press it is the metal warming under the pointer, which is
   what a faceplate does and what a highlight bar cannot do without becoming a
   menu. */
.gate-legs{
  flex:1 1 auto; min-width:0; padding:7px 0;
  display:flex; flex-direction:column;
}
.gate-leg{
  height:var(--gp,24px); padding:0 8px; border:0; border-radius:4px;
  display:flex; align-items:center; width:100%;
  background:transparent; cursor:pointer; text-align:left;
  font-family:var(--font); font-size:10px; letter-spacing:.11em;
  text-transform:uppercase; color:#5d636c;
  text-shadow:0 1px 0 rgba(255,255,255,.05);
  transition:background .12s, color .14s, text-shadow .14s;
}
.gate-leg:hover{ background:rgba(255,255,255,.035); color:#7d848e; }
.gate-leg:active{ background:rgba(0,0,0,.20); }
/* THE ONE THE LEVER IS STANDING IN. It does not get a bar behind it and it
   does not move — it is lit, which is the same thing the rest of this panel
   means by "this one" and the only mark that does not turn an engraved plate
   into a list of rows. */
.gate-leg.on{
  color:#f0f3f7;
  text-shadow:0 0 7px color-mix(in srgb, var(--lyr, var(--led)) 45%, transparent),
              0 1px 0 rgba(0,0,0,.6);
}
.gate-nm{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* a milled cut between groups of rows — the `.chan` lying down */
.menu-cut{
  height:2px; margin:4px 8px; border-radius:1px; background:#08090a;
  box-shadow:0 1px 0 rgba(255,255,255,.07), inset 0 0 2px rgba(0,0,0,.9);
}

/* ── the console ──────────────────────────────────────────────────────────
   THE READOUT WITH A HISTORY. The `.lcd` in the bar says how many strokes are
   on the sheet now; this says what the app has been doing about it — so it is
   the same green monospace behind the same glass, grown into a window. It is
   NOT a panel with a title bar: a console is a display, and the key that
   opened it is its label.

   NO TAIL AND NO POINTER ON ANY PLATE. The plate is anchored to the key and
   the key is lit — two channels already saying where it came from. A pointer
   stuck to the edge only tells the truth while the plate is directly over its
   key, and a wide one is never over its key: it slides to fit the window, and
   the tail ends up somewhere along the bar aiming at whatever is under it. */
.console{ width:530px; max-width:calc(100vw - 24px); }
.con{ position:relative; z-index:2; display:flex; flex-direction:column; }
.con-log{
  max-height:242px; overflow-y:auto; padding:13px 15px 11px;
  display:flex; flex-direction:column; gap:9px;
}
.con-row{ display:flex; gap:15px; align-items:flex-start; }
/* THE TIME IS A LEFT MARGIN, not a column of data. Dim, tabular, and it does
   not move when a line above it wraps. */
.con-t{
  flex:0 0 auto; font-family:var(--font); font-size:11px; color:#5c636c;
  font-variant-numeric:tabular-nums; line-height:1.55;
}
.con-m{ font-family:var(--font); font-size:11px; line-height:1.55; color:#98a0aa; }
/* a RESULT — what got made, how many, how long. The lamp colour of the readout,
   because it is the same kind of fact. */
.con-m.lit{ color:#7CFFB0; text-shadow:0 0 6px rgba(124,255,176,.30); }

.con-foot{
  display:flex; align-items:center; justify-content:space-between;
  padding:9px 15px 8px; border-top:1px solid #08090a;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.055);
}
.con-n, .con-clear{
  font-family:var(--font); font-size:10px; letter-spacing:.07em; color:#5c636c;
}
.con-clear{ background:none; border:0; padding:0; cursor:pointer; }
.con-clear:hover{ color:var(--led); }

/* a thin dark scrollbar — the only one in the language, because this is the
   only part that can outgrow its own window */
.con-log::-webkit-scrollbar{ width:10px; }
.con-log::-webkit-scrollbar-track{ background:transparent; }
.con-log::-webkit-scrollbar-thumb{
  background:#33373d; border-radius:6px;
  border:3px solid transparent; background-clip:content-box;
}
.con-log::-webkit-scrollbar-thumb:hover{ background:#454a52; background-clip:content-box; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE HATCH BAY — the first COMPOUND control in the language.

   Everything built so far is one control on one row, and a row of five of
   them is mute: same shape, same green number at the end, nothing saying
   which one you turn to change how the drawing looks. Hatching is the one
   place where the parameters are PHYSICAL OBJECTS — a pen has a nib width you
   can hold, spacing is a distance on paper, angle is an angle — so it is the
   block that should prove the language, and it needs a chassis idea the
   language did not have: a bay whose members feed a display INSIDE it.
   ═══════════════════════════════════════════════════════════════════════════ */
/* ── THE BAY IS SIZED TO A LAPTOP, AND THE TAB STRIP IS WHAT PAID FOR IT ───
   405 of face plus the 55 the lobe sticks out to the left. .box.bulge sets 370
   for the editor; this rule is later and more specific to the bay, and both are
   the same arithmetic: face + lobe.

   IT WAS 572, AND THE STRIP WAS THE REASON. Six captions and a master on ONE
   LINE need about 420px before anything else in the bay is consulted — so the
   widest row in the panel was the one carrying no values, and every other row
   was built out to meet it. On a 1280 or 1440 laptop that is a third of the
   screen spent on a panel that sits over the drawing it is editing.

   Two lines of three fixes it at the source: the strip's demand drops to ~230
   and the binding row becomes the one that actually holds the controls — the
   turret beside three knobs, with the pen case under them. That is what 405 is
   measured from, and it is the honest floor. Below it the case starts wrapping.

   THE COST IS 26px OF HEIGHT AND IT IS WORTH IT, because the strip is the one
   block in the bay whose height is not carrying a reading. Everything else that
   came down — the sheets, the case, the well — came down by the same
   proportion the face did, so the panel is the same panel at a smaller size
   rather than a squeezed one. */
.box.hatchbay{ width:460px; flex:0 0 460px; }
/* The rhythm follows the face. 12/12 was set against a 520 face and reads as a
   margin at 405; the bay is a dense instrument and its gutters are what say so. */
.box.hatchbay > .box-body{ padding:10px; }
.hb-body{ display:flex; flex-direction:column; gap:9px; }

/* ── THE TAB STRIP, AND THE ENABLE ON THE SAME BAR ────────────────────────
   Six keys and a switch in one well. The keys share the width evenly because
   they are peers; the switch does not, because it is not one — it is what the
   channel to its left says it is, a different question on the same bar.

   ── THREE ACROSS, TWICE, AND ALL DOWN THE SIDE ────────────────────────────
   ONE BAR IS NOT THE SAME CLAIM AS ONE LINE. What the strip has to say is that
   these six are ALTERNATIVES and the seventh is a reach — and a well with six
   keys in it says that whether they are in one row or two. What one line
   additionally said was "the bay is at least 420px wide", which is not a fact
   about hatching.

   ALL SPANS BOTH ROWS, past the same milled channel it always sat behind. That
   is the load-bearing part: it is now the one key on the bar with a different
   SHAPE, which is exactly what it is — not a seventh surface but the question
   of how wide the next change goes, asked about all six at once. A half-height
   ALL in the corner would read as the surface nobody named.

   IT IS PLACED, NOT REFLOWED, and nothing in the markup moved. The channel and
   ALL are pinned to columns 4 and 5 across both rows; grid places explicit
   items first, so the six keys auto-flow into the three free columns and skip
   the two occupied ones — 1,2,3 then 4,5,6, in DOM order, reading left to
   right and top to bottom, which is the order the list is written in. Any host
   that builds this strip the reference's way gets the two lines for free. */
.piano.sm.hb-tabs{
  display:grid; grid-template-columns:repeat(3,1fr) auto auto;
  /* THE TWO ROWS ARE EXPLICIT, and that is not tidiness. `grid-row: 1 / span 2`
     is spelled as a span rather than as `1 / -1` because a negative line counts
     back from the end of the EXPLICIT grid — with the rows left implicit there
     is only line 1 to count back to, so `-1` resolves to the start line and the
     span collapses to one row. The channel and ALL would silently sit in the top
     row with the keys flowing round them. */
  grid-template-rows:repeat(2,24px); align-items:stretch;
  column-gap:5px; row-gap:4px; padding:5px; width:100%;
}
/* `.piano.sm .pkey` IS THREE CLASSES AND THIS HAS TO BEAT IT. It was written as
   `.hb-tabs .pkey`, which is two — so the bay's own size override lost to the
   generic small-key rule on every property they share, and the strip has been
   drawing 26px caps at 9px with 6px of padding while this rule asked for
   something else. Silent, because both are plausible sizes for a tab. The
   modifier goes on the same element as the size class it is overriding. */
.piano.sm.hb-tabs .pkey{
  min-width:0; padding:0 4px; height:auto; flex:none;
  font-size:8.5px; letter-spacing:.06em;
}
/* Full height of the well rather than a fixed 20: the channel is a CUT in the
   plate, and a cut that stops short of the two rows it separates is a scratch. */
.hb-tabs .chan{ grid-column:4; grid-row:1 / span 2; margin:0 3px; height:auto; }
/* ALL SAYS ALL. A glyph was a quarter of the width and cost more than it
   saved: every other key on this bar is a WORD, so a lone mark among six names
   reads as a different KIND of thing rather than as the same kind with a wider
   reach — which is the one thing it must not say. It is narrow because it is
   set tight, not because it is abbreviated. */
.hb-tabs .hb-all{
  grid-column:5; grid-row:1 / span 2;
  width:auto; height:auto; min-width:0; padding:0 9px; border-radius:7px;
  font-family:var(--font); font-size:8.5px; font-weight:700; letter-spacing:.08em;
}

/* ── TWO FACTS, TWO CHANNELS ──────────────────────────────────────────────
   DOWN is what the next change reaches. A LIT LEGEND is what gets plotted.
   They are independent, so a tab can be down and dark — you are editing
   something that will not be drawn, which is a real state and a useful one —
   or up and lit. A muted tab keeps its cap and loses its legend: the key is
   still there, the mark it makes is not. */
/* A STRUCK LEGEND, not a faded one. Dimming says "this key is unavailable",
   which is the opposite of true — an undrawn surface is still fully editable,
   and you often want to set one up before you switch it on. A strike says the
   name is still there and the MARK IS NOT. The line takes the key's own
   colour, so an off tab that is down strikes in the lamp colour and one that
   is up strikes in grey. */
.hb-tabs .pkey.mute{
  text-decoration:line-through;
  text-decoration-color:currentColor;
  text-decoration-thickness:1.4px;
  color:#7c828b;
}

/* ── THE OPTION ROW ───────────────────────────────────────────────────────
   One switch, its engraving and its name, on the line under the knob whose
   units it changes. Not a bay of its own: it is a modifier, and a modifier
   that gets its own well reads as a setting in its own right. */
.hb-opt{ display:flex; align-items:center; gap:8px; padding:2px 2px 4px; }
/* THE ONLY MARK ON THE ROW, so it gets the room to be read. */
.hb-opt .sw{ flex:0 0 26px; width:26px; height:26px; border-radius:7px; --ring-r:9px; }
.hb-opt .sw svg{ width:15px; height:15px; }
.hb-optlab{
  font-family:var(--font); font-size:8.5px; letter-spacing:.14em; color:#5d626b;
  text-shadow:0 1px 0 rgba(255,255,255,.09);
}
/* the whole bar warms while it is engaged — the state belongs to the strip,
   not to the one key that set it */
.hb-tabs.all-on{
  box-shadow:inset 0 3px 8px rgba(0,0,0,.8), inset 0 -1px 0 rgba(255,255,255,.05),
             inset 0 0 0 1px rgba(255,106,0,.22), 0 1px 0 rgba(255,255,255,.07);
}
/* A CONTOUR HAS NO AREA TO FILL, so the four controls that fill one go quiet.
   PEN AND INK STAY LIVE, because a contour has both — dimming the whole row
   made the one tab where you set a line's weight the one tab where you could
   not touch anything. The sheets keep drawing: a contour's preview is its
   outline, which is exactly what the tab is for. */
.hatchbay.noline .hb-turret,
.hatchbay.noline .hb-knobs .kwrap:nth-child(2),
.hatchbay.noline .hb-knobs .kwrap:nth-child(3),
.hatchbay.noline .hb-opt,
.hatchbay.noline .hb-deep{ opacity:.3; pointer-events:none; }

/* ── THE SPECIMEN — the readout, and it is not decoration ─────────────────
   The refs both put the display in among the controls that feed it: the
   EQ-200 draws the curve its ten faders make, the K.O.II keeps a lit strip
   over the pads. You judge 0,6 by seeing how dense 0,6 IS, not by reading the
   number — which is the whole argument for showing material instead of value,
   and the reason every other control in this bay is an input to this picture. */
.hb-spec{ padding:8px; }
/* THE GROUND IS PAPER, and it has to be.

   It was near-black, which worked for exactly as long as the ink was orange.
   The pen case has BLACK in it — every plotter case does, it is the pen you
   own most of — and black ink on a black field is nothing at all. Grey is
   worse, because it looks like it is working.

   That is the same argument the colour picker makes about its own panel: the
   surface a colour is judged against cannot compete with it. So the specimen
   is paper under the bay's glass — a lit display showing a document, which is
   what it is — and every pen in the case reads on it, which is the entire
   point of a specimen.

   AND THE STOCK IS TONED, because the case has a WHITE pen in it. White on
   cream is not a subtle result, it is no result — the specimen would show an
   empty sheet and look broken rather than look like white ink. Toned stock is
   what you actually reach for when you plot white, and it is the one ground
   where black, grey and white all read at once. */
.hb-field{
  --paper:#BEB5A3;
  /* TALL ENOUGH FOR A SUBJECT. A 118px strip could only ever hold an even
     field of hatch, which shows the spacing and hides the thing hatching is
     actually for. A ball needs room to be a ball, and its shadow needs room
     to leave it.

     160 RATHER THAN 196, AND THE SHEET KEEPS ITS PROPORTION. This is the one
     block in the bay where a pixel is a MILLIMETRE — the specimen is 1:1 — so
     the honest way to state the change is in paper: each sheet was 63 × 52mm
     and is now 47 × 42mm, and `KIT.scene` sizes the ball `min(w × .34,
     h × .295)`, which is height-bound at both, so a 25mm ball where there was
     a 30mm one. That is a smaller specimen and it is still a specimen: at the
     1–4mm pitches this bay sets, a 25mm ball is ten to twenty strokes across,
     which is what you are reading.

     KEEPING 196 ON A 179-WIDE SHEET WOULD HAVE BEEN THE WORSE ANSWER, and it
     is what a straight width cut would have left: the pair was landscape and
     would have gone portrait, with the ball no bigger than it is here — the
     height is what binds either way — and 36px of margin above and below it. */
  height:160px; border-radius:4px; position:relative; overflow:hidden;
  background:var(--paper);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.55), inset 0 2px 8px rgba(0,0,0,.4);
}
/* Real millimetres: the viewBox is measured from the element and converted at
   96/25.4, so one user unit is one mm and the hatch on screen is the hatch
   that plots. A 0.20 nib is a sub-pixel line and looks like one — that is not
   a bug to tune away, it is what 0.20 is, and the density is still exact. */
.hb-field svg{ display:block; width:100%; height:100%; }
/* TWO SHEETS ON ONE DISPLAY. Not two displays: they are one answer read two
   ways, and a gap wide enough to be a border would make them two exhibits. */
.hb-pair{ display:flex; gap:7px; }
.hb-pair .hb-field{ flex:1 1 0; min-width:0; }
.hb-legend{ color:#3f7a5c; opacity:.75; }
/* THE FLOOD STILL NEEDS NO CODE. Strokes are drawn at the nib width, spacing
   apart; take spacing under the nib and they overlap into solid ink on their
   own. Below the nib there is no hatch, there is ink. */
.hatchbay.off .hb-field{ background:#20242a; box-shadow:inset 0 0 0 1px rgba(255,255,255,.05), inset 0 2px 8px rgba(0,0,0,.7); }
.hatchbay.off .hb-field svg{ visibility:hidden; }
.hatchbay.off .hb-scale{ color:#3c4149; }

/* ── A SURFACE THAT IS NOT DRAWN HAS ITS LAMPS OUT ────────────────────────
   Every lit thing on this bay means one thing: this is going to be plotted.
   On a surface you have struck through none of that is true.

   ONE FILTER, NOT TWENTY OVERRIDES — the lamps are spread across knob arcs,
   fader fills, cap indicators, readouts and the ink lamp, and half of them
   hard-code their colour, so hunting them one by one would miss some and go
   stale. The controls stay LIVE and only their light goes, because editing a
   surface before switching it on is why the strike is a strike and not a
   disable. The tab strip is excluded: you have to be able to reach the tab
   that turns it back on. */
.hatchbay.off .hb-body > :not(.hb-tabs){ filter:saturate(.06) brightness(.6); }

.hb-scale{
  display:flex; justify-content:space-between; align-items:center; margin-top:7px;
  font-family:var(--font); font-size:8.5px; letter-spacing:.14em; color:#3f7a5c;
}

/* ── the two columns under it: the turret, and the three straight controls ── */
.hb-row{ display:flex; align-items:flex-start; gap:4px; }
.hb-stack{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:2px; }
.hb-stack .kwrap.inline .kval{ min-width:52px; }
/* Every caption in this bay carries its engraving, so the captions are rows
   rather than words. The icon leads: on a real panel the symbol is what you
   find first and the word is what confirms it. */
.hatchbay .flab,
.hatchbay .klab{ display:flex; align-items:center; gap:7px; }
.hatchbay .fader .flab{ flex:0 0 96px; }
.hatchbay .kwrap.inline .klab{ flex:0 0 82px; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE DRUM — a cylinder of names in a milled window

   THE WINDOW IS CUT, THE DRUM IS BEHIND IT. That order matters: the recess
   belongs to the CASE and the names belong to the thing turning inside it, so
   the shadow at the top of the window falls ON the names and does not travel
   with them. Get that backwards and the drum reads as a list with a gradient
   over it, which is what every software picker looks like and what this is
   not trying to be.
   ═══════════════════════════════════════════════════════════════════════════ */
/* ── THE WELL IT IS CUT INTO ──────────────────────────────────────────────
   The same milled recess every other bay in this document uses, and the
   MARGIN is the point: a recess is read from its FLOOR, not from its walls, so
   a well with no floor showing is not a well, it is a border. Eight pixels of
   case all the way round is what lets the eye see the drum is standing in
   something rather than butted against it. */
.drum-bay{
  display:inline-block; padding:8px; border-radius:12px;
  background:linear-gradient(180deg,#15181b,#1c2024 55%,#131619);
  box-shadow:
    inset 0 0 0 1px rgba(0,0,0,.6),
    inset 0 3px 7px rgba(0,0,0,.85),
    inset 3px 0 5px -3px rgba(0,0,0,.7),
    inset -3px 0 5px -3px rgba(0,0,0,.7),
    inset 0 -1px 0 rgba(255,255,255,.06),
    0 1px 0 rgba(255,255,255,.07);
}

/* THE DRUM SITS ON THAT FLOOR, so it throws one shadow rather than two:
   a drop-shadow on the pair follows the outline they make together, where a
   box-shadow on each would draw a seam down the join. */
.drum{
  display:flex; align-items:stretch; gap:0; height:76px;
  /* NOTHING IN HERE IS SELECTABLE TEXT. The names are a readout that happens
     to be made of letters; a drag across them is a turn of the drum, and a
     browser that helpfully highlights them has misread the object. */
  user-select:none; -webkit-user-select:none;
  filter:drop-shadow(0 5px 9px rgba(0,0,0,.75)) drop-shadow(0 1px 2px rgba(0,0,0,.6));
}
.drum-win{
  position:relative; flex:1 1 auto; min-width:0; overflow:hidden;
  border-radius:6px 0 0 6px; cursor:default;
  /* the milled well the cylinder sits in */
  background:linear-gradient(180deg,#111417,#191d21 52%,#101316);
  box-shadow:
    inset 0 0 0 1px rgba(0,0,0,.7),
    inset 0 3px 6px rgba(0,0,0,.9),
    inset 0 -3px 6px rgba(0,0,0,.75),
    inset 2px 0 3px -1px rgba(0,0,0,.7),
    inset 0 -1px 0 rgba(255,255,255,.11),
    /* THE CASE ABOVE THE CUT IS LIT, and that edge was missing. A recess is
       dark INSIDE its top wall — but the flat face it is cut into is the part
       of the panel facing the lamp most directly, so there is a bright line
       right where the surface stops and the hole begins. Without it the drum
       has no top: the names just start out of nothing. */
    inset 0 1px 0 rgba(255,255,255,.055),
    0 -1px 0 rgba(255,255,255,.07),
    0 1px 0 rgba(255,255,255,.09);
  /* far enough back that the curve is a drum and not a fisheye */
  perspective:260px;
  perspective-origin:50% 50%;
}
.drum-list{
  position:absolute; inset:0;
  transform-style:preserve-3d;
  pointer-events:none;
}
.drum-item{
  position:absolute; left:0; right:0; top:50%; height:18px; margin-top:-9px;
  display:flex; align-items:center; justify-content:flex-start; padding:0 10px;
  border:0; background:none; cursor:pointer; pointer-events:auto;
  font-family:var(--font); font-size:9.5px; letter-spacing:.1em; white-space:nowrap;
  color:#6b7079;
  backface-visibility:hidden;
  /* THE OVERSHOOT IS THE DETENT. A name that slides to its place has been
     dragged there; one that goes slightly past and settles has DROPPED INTO
     something. That tiny bounce is the only visual a notch has — you normally
     feel it, and here there is nothing to feel. */
  transition:color .12s, transform .17s cubic-bezier(.22,1.7,.36,1), opacity .17s ease-out;
}
.drum-item:hover{ color:#c2c7cf; }
/* THE ONE IN THE WINDOW IS LIT, and it is the only thing that says which is
   selected — the others are simply nearer or further round the drum. */
.drum-item.on{ color:#FFD9BC; text-shadow:0 0 8px rgba(255,140,60,.6); }

/* THE SHADOW LIVES ON THE CASE, NOT ON THE CYLINDER. It is the lip of the
   window cutting the light off top and bottom, so it stays put while the names
   roll under it. */
.drum-win::after{
  content:''; position:absolute; inset:0; pointer-events:none; border-radius:6px 0 0 6px;
  background:
    linear-gradient(180deg, rgba(8,9,11,.95) 0%, rgba(8,9,11,.30) 22%, transparent 40%),
    linear-gradient(0deg,   rgba(8,9,11,.95) 0%, rgba(8,9,11,.30) 22%, transparent 40%);
}
/* the detent line: where the drum reads, engraved on the case */
.drum-win::before{
  content:''; position:absolute; left:0; right:0; top:50%; height:18px; margin-top:-9px;
  pointer-events:none; z-index:1;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07), inset 0 -1px 0 rgba(255,255,255,.05);
}

/* THE GRIP IS THE RIM, and the rim is where a drum is actually turned. Each
   tooth takes the lamp separately, the same alternation the knob's collar has
   — a uniform ridge pattern is a texture, and textures do not turn. */
/* ── THE WHEEL ────────────────────────────────────────────────────────────
   THE BARREL CURVES THE WAY THE NAMES DO. The drum's axis is horizontal, so
   its edge turns away at the TOP and BOTTOM and is broadest across the middle
   — the shading has to run vertically to match. It ran horizontally at first,
   which shades a cylinder lying the other way and quietly contradicts the very
   list rolling beside it.

   AND THE TEETH DIE AT THE ENDS. A knurl at one pitch from top to bottom is a
   striped strip; on a real wheel the teeth crowd together as the surface turns
   out of sight, and it is that crowding — not the stripes — that says round. A
   mask at both ends is the cheap way to say it, and at this size it is the
   honest one too: past about 70° the teeth are finer than a pixel anyway. */
.drum-grip{
  flex:0 0 24px; position:relative; border-radius:0 7px 7px 0;
  cursor:ns-resize; touch-action:none;
  /* THE TEETH ARE ON THE WHEEL, THE LIGHT IS IN THE ROOM. Two background
     layers and only the first one moves: the knurl is a property of the
     cylinder and travels with it, the crown highlight is where the lamp
     happens to fall and stays put however far the thing has turned. Slide
     both and it stops being a wheel and becomes a conveyor belt.

     THE PITCH IS 3.2px AND NOT 3px ON PURPOSE. One detent rolls the surface
     R·θ = 17.9px; at a 3px pitch that is exactly six teeth and the pattern
     lands identical every time, so the only movement you would ever see is
     during the tween. At 3.2 it lands two thirds of a tooth out, so a wheel
     that has been turned once looks turned. */
  background:
    repeating-linear-gradient(180deg,
      rgba(255,255,255,.17) 0 1px, rgba(0,0,0,.44) 1px 3.2px),
    linear-gradient(180deg,
      #191d22 0%, #232830 8%, #2f353e 20%, #3f4650 36%, #454d58 45%,
      #2e343c 63%, #191d22 85%, #0d1013 100%);
  background-position:0 var(--roll, 0px), 0 0;
  /* the same spring the names settle on — one mechanism, one motion */
  transition:background-position .17s cubic-bezier(.22,1.7,.36,1);
  /* THE SLOT IT TURNS IN. The wheel was butted straight against the window,
     which reads as two panels side by side rather than as a cylinder coming up
     through a cut in the case. A wheel that protrudes has to protrude THROUGH
     something: hard dark walls closing over it top and bottom, the far wall
     catching the room at the bottom, and a lit lip on the case below the whole
     slot. Same four hairlines as every other recess here — this one just has
     something turning in it. */
  box-shadow:
    inset 0 3px 5px -1px rgba(0,0,0,.92),
    inset 0 -3px 5px -1px rgba(0,0,0,.85),
    inset -2px 0 3px -1px rgba(0,0,0,.8),
    inset 1px 0 0 rgba(0,0,0,.6),
    inset 0 -1px 0 rgba(255,255,255,.13),
    inset 0 1px 0 rgba(255,255,255,.06),
    0 -1px 0 rgba(255,255,255,.07),
    0 1px 0 rgba(255,255,255,.09);
}
/* the broken edge of the cut, lit directionally — dark where it turns from the
   lamp at the top, bright where it turns into it at the bottom. Concave, like
   every other chamfer in this document, or the wheel reads as a bump. */
.drum-grip::before{
  content:''; position:absolute; inset:-1.5px -1.5px -1.5px 0;
  border-radius:0 8px 8px 0; pointer-events:none; z-index:2;
  padding:1.5px; background:var(--chamfer);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  filter:blur(.4px);
}
/* the ends turning away, and one specular along the crown */
.drum-grip::after{
  content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:
    linear-gradient(180deg,
      rgba(8,10,12,.88) 0%, rgba(8,10,12,.30) 5%, transparent 13%,
      transparent 87%, rgba(8,10,12,.34) 95%, rgba(8,10,12,.9) 100%),
    linear-gradient(180deg,
      transparent 36%, rgba(255,255,255,.18) 42%, rgba(255,255,255,.05) 48%, transparent 54%);
}
/* AND THE CASE TAKES THE KNOCK. The ball dropping into the groove is a small
   impact, so the window flinches: one frame of the detent line brightening and
   the lip shadow deepening, gone in 90ms. Long enough to register, too short
   to be an animation you notice. */
@keyframes drum-knock{
  0%   { box-shadow:inset 0 1px 0 rgba(255,255,255,.30), inset 0 -1px 0 rgba(255,255,255,.22); }
  100% { box-shadow:inset 0 1px 0 rgba(255,255,255,.07), inset 0 -1px 0 rgba(255,255,255,.05); }
}
.drum-win.knock::before{ animation:drum-knock .09s ease-out; }


/* ── THE TYPE SELECTOR ────────────────────────────────────────────────────
   Four ways to fill an area, on the switch rather than on a bar of keys. A
   row of keys shows four labels and makes you infer that they are one choice;
   a selector switch has ONE lever pointing at ONE detent, so the fact that
   these are alternatives is in the object rather than in the reading. The
   names are long — "Hand hatched" is not shortenable without lying about what
   it is — so the column is wider than the dial needs. */
/* 136, NOT 178, AND IT IS MEASURED OFF THE LONGEST NAME. The dial's centre sits
   at --rcx (−23px, out in the lobe) and `rotary` prints its labels at LAB_X=56
   from that centre, so the column is used from x=33 rightward: "Hand random" at
   9.5px monospace with .1em of tracking is ~85px of cap, ending at 118. The 60px
   the column used to carry past that was the panel being built out to a strip
   that no longer needs it. Anything longer than eleven characters wants this
   number checked, not the panel widened. */
.hb-turret{ flex:0 0 136px; display:flex; flex-direction:column; gap:6px; }
.hb-sel.rotary{ flex:1 1 auto; height:132px; }

/* the three knobs share one row */
.hb-knobs{ justify-content:space-between; gap:4px; padding:2px 0 4px; }
/* ONE KNOB, ONE POSITION, AND ONLY BECAUSE IT IS DOCUMENTED. Spacing and angle
   reach every fill on the dial except one: hand random has no register to
   rotate, and 08 says so in as many words — "a fill with no readable direction
   is the whole definition of it". So Angle goes quiet there rather than turning
   and changing nothing, which is the worse of the two. Everywhere else both
   knobs are live, including on the hands. */
.hb-knobs .kwrap.inert{ opacity:.34; pointer-events:none; }
.hb-knobs .kwrap{ flex:1 1 0; min-width:0; align-items:center; gap:5px; }
.hb-knobs .klab{ justify-content:center; font-size:8px; letter-spacing:.12em; }
.hb-knobs .kval{ min-width:0; width:100%; text-align:center; }
.hb-knobs .kval.wide{ min-width:0; font-size:9.5px; letter-spacing:.02em; }

/* SPACING UNDER THE NIB IS NOT AN ERROR AND NOTHING REPORTS IT. The strokes
   overlap and the sheet fills solid, which is a fill — something you ask for.
   An earlier build lit a red legend and turned the spacing arc red, framing a
   legitimate result as a fault. The specimen already shows exactly what
   happens, and that is the whole job of a specimen. */

/* ── THE INK SWATCH — a cap whose face is the colour ──────────────────────
   THREE OBJECTS WERE TRIED AND ALL THREE WERE THE SAME MISTAKE. A pen cap, a
   nib, a glass pipette: each got better at being a picture of a thing on the
   desk, and none of them got better at being a CONTROL. A rail of little
   drawings is an illustration; you cannot tell at a glance which one is
   selected, the colour is fighting a silhouette for attention, and at 17px
   the object it is a picture of is unreadable anyway.

   So the swatch stops depicting the pen and starts behaving like everything
   else on this panel: a cap with a coloured face, seated in the case, that
   goes DOWN when it is the one in use. The colour gets the whole face instead
   of a stem, the state is the same up/down the switches and the piano keys
   use, and it is legible at any size because a rounded square has no detail
   to lose.

   MODELLED, NOT FLAT — which is the only thing separating this from the soft
   pastel swatch it is shaped like. Light from above left at 158° like the rest
   of the document, a hairline of light on the top edge, a dark one under it,
   and the specular OFF CENTRE because a centred highlight reads as a sticker.
   ─────────────────────────────────────────────────────────────────────────── */
/* THE CASE WRAPS RATHER THAN OVERFLOWS, and that is the one thing a rail of
   fixed-width slots must be told. `.pencap` is `flex:0 0 22px` — it cannot
   shrink — so on any face too narrow for the row the caps ran off the panel
   instead of taking a second line. A pen case with two rows of slots is a pen
   case; nine slots trailing off the edge of one is a bug. It still sits on one
   line at the shipped width, and the height follows the content rather than
   being declared, or a wrapped second row would be clipped by the number that
   was right for one. */
.pens{ display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
       gap:4px; padding:5px 0 3px; }
/* GLASS AS A SHEET ON TOP, NOT AS THE BODY — and the distinction is the whole
   of why the first attempt failed and this one does not. That one remodelled
   the cap AS glass: bright walls, a hard specular and the ink as a core seen
   through them, which is the recipe for a tube and is wrong for a 22px square,
   because at that size the two lit walls and the specular are most of the
   object and the colour is what is left over. On a swatch the COLOUR IS THE
   POINT.

   This adds no walls and no core. The modelled cap is untouched — same size,
   same corners, same seating in the case, same colour across the whole face —
   and a pane of glass is laid over it. The only thing that MOVED is the
   specular, off the painted face and onto the sheet, which is the rule this
   language already wrote at the loupe: "the specular is on the outside of the
   glass and does not care what is under it". Two speculars at the same 158°
   in the same corner is one specular drawn twice, and on a light ink it blows
   the top-left corner out. */
.pencap{
  --ink:#FF6A00;
  --ring-r:11px;
  width:22px; height:22px; flex:0 0 22px;
  border:0; padding:0; cursor:pointer; position:relative;
  border-radius:7px;
  /* the painted face, and nothing else on it — the specular is on the glass */
  background:
    linear-gradient(158deg,
      color-mix(in srgb,var(--ink) 74%,#fff) 0%,
      var(--ink) 44%,
      color-mix(in srgb,var(--ink) 86%,#000) 100%);
  /* THE ONES YOU ARE NOT USING SIT DOWN IN THE CASE, and the hairlines are the
     whole of it. An inset shadow laid over the RAISED recipe — light hairline
     above, dark below, a skirt and a cast shadow — is a proud cap with a smudge
     on it, and the eye believes the hairlines every time.

     A recess inverts all four: a hard near-black line INSIDE the top edge, the
     wall shading down from it, a faint light INSIDE the bottom because that is
     the wall the room actually reaches, and a lit lip OUTSIDE below the whole
     thing. And no cast shadow at all — a hole does not throw one.

     THE DIM IS A FILTER, not a second set of colours: nine inks would need
     nine muted twins, and they would drift from the real ones the first time
     either was tuned. */
  filter:brightness(.6) saturate(.82);
  box-shadow:
    inset 0 0 0 1px rgba(0,0,0,.6),
    inset 0 2.4px 1.2px rgba(0,0,0,.88),
    inset 0 7px 11px -3px rgba(0,0,0,.72),
    inset 3.5px 0 4px -3px rgba(0,0,0,.6),
    inset -3.5px 0 4px -3px rgba(0,0,0,.6),
    inset 0 -3px 5px -2px rgba(255,255,255,.10),
    inset 0 -1px 0 rgba(255,255,255,.16),
    0 1px 0 rgba(255,255,255,.085);
  transition:box-shadow .1s, filter .12s;
}
/* ── THE CHAMFER ON THE EDGE ──────────────────────────────────────────────
   The one thing a box-shadow cannot draw: an edge lit DIRECTIONALLY, dark
   where it turns away from the lamp at the top and bright where it turns into
   it at the bottom. Concave polarity — the other way round and every cap on
   the rail reads as a bump. */
.pencap::before{
  content:''; position:absolute; inset:-1.5px; border-radius:8.5px;
  pointer-events:none; padding:1.5px; background:var(--chamfer);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  filter:blur(.4px);
}
/* ── THE GLASS CAP ON THE FACE ────────────────────────────────────────────
   A SMALL LENS INSIDE THE FACE — about 3px in on the 22px cap, with the
   colour running all the way round it. That margin is the whole of what makes
   it an OBJECT SITTING ON the colour rather than a finish on the paint: at
   this size a highlight only reads as a separate part if it is visibly smaller
   than the thing it sits on. It was 5px in, which was a lens with a border
   round it rather than a cap on a face.

   THE WHITE LINE IS LIT ON ONE DIAGONAL HALF AND DARK ON THE OTHER, and that
   is the difference between an edge and an outline. A ring all the way round
   is a drawn shape; light lands on the two sides facing the lamp and the other
   two go quiet, so the top and the left carry the line and it thins away
   through the top-right and bottom-left corners on its own — the offset does
   that, not a stop. Same lamp as everything else here.

   AND A DARK HAIRLINE OUTSIDE IT, so the lens is seated ON the face rather
   than scratched into it, with the cast shadow every raised thing here has.

   THE FACE OF THE LENS IS MILKY, not clear — a flat 20% white across the
   whole of it, under the specular. Clear glass over a colour is the colour,
   which is to say nothing at all happened; the veil is what gives the pane a
   substance of its own and is the reason the lens now reads at a glance rather
   than only at its lit edge. It is FLAT and not graded: the 158° specular over
   it is already the gradient, and a second one under it fights it.

   IT IS ONLY ON THE LENS, WHICH IS THE WHOLE LICENCE FOR IT. The colour is the
   point of a swatch and a veil over the whole face would be a swatch of the
   wrong colour — the margin outside the lens keeps the ink exactly as mixed,
   so there is always true colour on the cap to read.

   THE SPECULAR OVER IT IS THE LOUPE'S PANE, unchanged: the same two layers at
   the same numbers, running off the top-left and dying by 46% with the room
   going dark into the bottom-right. Copied rather than re-tuned — a second set
   of values for the same material is two materials the first time either is
   touched. It is confined to the lens, and that is the only difference.

   PER CENT AND NOT PIXELS, because this rail is built at two sizes: 22px on
   the panel and 17px in the picker's well. In pixels the small one gets a lens
   at the wrong scale with corners that do not follow the case's.

   AN EMPTY SLOT GETS NONE OF IT. That state is a hole in the case with no cap
   in it, and a hole has no face to glaze. */
.pencap:not(.empty)::after{
  content:''; position:absolute; inset:13.5%; border-radius:26%;
  pointer-events:none;
  background:
    linear-gradient(158deg,
      rgba(255,255,255,.42) 0%, rgba(255,255,255,.14) 15%,
      rgba(255,255,255,.02) 30%, transparent 46%),
    radial-gradient(120% 120% at 78% 88%, rgba(0,0,0,.16), transparent 52%),
    linear-gradient(rgba(255,255,255,.20), rgba(255,255,255,.20));
  box-shadow:
    inset 1.1px 1.1px 0 -.1px rgba(255,255,255,.85),
    0 0 0 1px rgba(0,0,0,.28),
    0 1px 2px rgba(0,0,0,.32);
}
/* HOVER COMES PART WAY UP OUT OF THE TRAY — the walls shallow out and the rim
   catches a little light. Not all the way: it is not selected. */
.pencap:hover{
  filter:brightness(.85) saturate(.92);
  box-shadow:
    inset 0 0 0 1px rgba(0,0,0,.45),
    inset 0 1px 2px rgba(0,0,0,.6),
    inset 0 -1px 0 rgba(255,255,255,.12),
    0 0 0 1px rgba(255,255,255,.12),
    0 1px 0 rgba(255,255,255,.06);
}
/* ── SELECTED IS THE LAMP, AND ONLY THE LAMP ──────────────────────────────
   Every cap on the rail is the same object at the same size in the same hole.
   One has a light round it, and that is the whole of the difference.

   THREE OTHER SIGNALS WERE TRIED AND ALL THREE WERE REDUNDANT: the selected
   cap was bigger, it was brighter, and its core was inverted. Each is a second
   thing to notice that means the thing you already noticed, and the cost is
   not clutter — it is that the rail stops being a row of nine like objects. A
   pen case is nine identical slots; which pen is in your hand is a fact about
   you, not about the slot, so it is said with light and nothing else.

   IT IS THE PANEL'S LAMP COLOUR, NOT THE INK'S: an indicator that changed
   colour with the thing it indicates stops being an indicator. */
.pencap.on{
  filter:none;
  box-shadow:
    inset 0 0 0 1px rgba(0,0,0,.6),
    inset 0 2.4px 1.2px rgba(0,0,0,.88),
    inset 0 7px 11px -3px rgba(0,0,0,.72),
    inset 3.5px 0 4px -3px rgba(0,0,0,.6),
    inset -3.5px 0 4px -3px rgba(0,0,0,.6),
    inset 0 -3px 5px -2px rgba(255,255,255,.10),
    inset 0 -1px 0 rgba(255,255,255,.16),
    0 0 0 1.5px rgba(255,106,0,.85),
    0 0 7px rgba(255,106,0,.45),
    0 0 15px rgba(255,106,0,.20),
    0 1px 0 rgba(255,255,255,.085);
}
/* the native input is the value, never the surface — the cap is what you see */
.ink-src{
  position:absolute; inset:0; width:100%; height:100%;
  opacity:0; border:0; padding:0; pointer-events:none; cursor:pointer;
}

/* ── AN EMPTY SLOT IS A HOLE ──────────────────────────────────────────────
   The same footprint with no cap in it: the milled well the cap sits in, on
   its own. Deeper than it is wide-looking, with the two hairlines every other
   recess in this language has, and no colour at all because it has none yet. */
.pencap.empty{
  border-radius:7px;
  background:linear-gradient(180deg,#101317,#181b20);
  box-shadow:inset 0 3px 6px rgba(0,0,0,.9), inset 0 -1px 0 rgba(255,255,255,.05),
             0 1px 0 rgba(255,255,255,.055);
}
/* the invitation: the well lights its own rim rather than lifting, because a
   hole cannot come towards you */
.pencap.empty:hover{
  transform:none;
  box-shadow:inset 0 3px 6px rgba(0,0,0,.9), inset 0 0 0 1px rgba(255,255,255,.15),
             0 1px 0 rgba(255,255,255,.055);
}

/* ── THE RECESS BELOW THE FOLD ────────────────────────────────────────────
   Min stroke and Join reach are cleanup thresholds — set once for a plotter
   and never touched again. Five identical rows is itself most of why the
   current panel is sad, so the two that do not belong in the rhythm of the
   four that matter go down a step, into their own well. */
.hb-deep{
  margin-top:2px; padding:6px 9px 7px; border-radius:8px;
  background:linear-gradient(180deg,#131519,#191c20);
  box-shadow:inset 0 3px 7px rgba(0,0,0,.88), inset 0 -1px 0 rgba(255,255,255,.045),
             0 1px 0 rgba(255,255,255,.05);
}
.hb-deep .fader{ padding:1px 0; }
/* THE SLOT IS THE ONE THING IN THIS WELL THAT DID NOT COME DOWN, and it was
   tried: 30 → 26 looks like 8px of free height across the two rows. It is not
   free. `.fticks` is absolutely positioned at top:25px, so at 26 the scale
   hangs 3px out of its own slot and into the row below, and pulling it up to
   clear that puts it under a 24px cap — which is the exact fault `.fslot` is
   30px to avoid, written four rules above. A fader is a track, a cap AND a
   scale; shrinking the housing without the other two is how a control loses
   the third one silently. */
.hb-deep .fader .flab{ flex:0 0 74px; color:#4e535b; }
.hb-deep .bay-cap{ margin-bottom:5px; }

/* the bay's own lamp: lit only when this layer is actually hatching, so a bay
   full of settings that are doing nothing looks like it */
.hatchbay.off .jewel{ filter:grayscale(1) brightness(.4);
  box-shadow:inset 0 -1px 2px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.6); }

/* ═══════════════════════════════════════════════════════════════════════════
   THE WINDOW — moved, and got out of the way exactly one way
   ═══════════════════════════════════════════════════════════════════════════ */
.win{ position:relative; will-change:transform; }
/* THE WHOLE HEAD IS THE HANDLE, so it says so with the cursor and not with a
   grip glyph in the corner. A title bar is the one strip of a panel with
   nothing in it you can hit by accident. */
.win-head{ cursor:grab; user-select:none; }
.win.dragging .win-head{ cursor:grabbing; }
/* LIFTED WHILE IT MOVES. A panel being dragged is off the desk, so it throws
   the harder, shorter shadow a .plate does — the same elevation vocabulary,
   because it is the same fact. */
.win.dragging{
  box-shadow:
    0 26px 40px -12px rgba(0,0,0,.92),
    0 8px 14px -4px rgba(0,0,0,.8),
    0 0 0 1px rgba(0,0,0,.9),
    inset 0 1px 0 rgba(255,255,255,.16);
}
/* ONE BUTTON, and it is a micro key: recessed, because it removes something.
   The raised caps on this panel invite the thumb; this one should not. */
/* 18px, DOWN FROM 22 — the head is 28px tall and the cap is what sets that
   floor. See the note on `.box-head`. */
.win-btn{
  width:18px; height:18px; flex:0 0 18px; margin-left:auto;
  display:flex; align-items:center; justify-content:center;
  border:0; padding:0; cursor:pointer; color:#575c65; border-radius:5px;
  background:linear-gradient(180deg,#151719,#1e2024);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.8), inset 0 -1px 0 rgba(255,255,255,.06);
  transition:color .12s, box-shadow .12s;
}
.win-btn svg{ width:11px; height:11px; stroke:currentColor; fill:none; stroke-width:2;
              stroke-linecap:round; }
.win-btn:hover{ color:#d7dae0; box-shadow:inset 0 1px 2px rgba(0,0,0,.8),
                0 0 0 1px rgba(255,255,255,.09); }

/* REDUCED IS THE TITLE BAR AND NOTHING ELSE. Not a shrunken panel — a panel at
   50% is a panel you cannot read and cannot use, which is the worst of both.
   The head is already a complete object: a lamp, a name, and the one control
   that puts it back. */
.win.reduced .box-body,
.win.reduced .hb-body{ display:none; }
.win.reduced{ width:auto; min-width:190px; }
.win.reduced .win-head{ border-bottom:0; box-shadow:none; }

/* ── OPENED OUT: THE PANEL TAKES THE SCREEN ───────────────────────────────
   `maximise: true` on `windowise` puts a second cap in the head and toggles
   this class. What it does is the whole part: the window leaves the flow, pins
   to the viewport with a 22px margin all round, and its body becomes the thing
   that scrolls.

   `transform: none` IS NOT TIDINESS. `windowise` drags by
   `transform: translate(dx, dy)` and that offset lives in the style attribute,
   so a panel opened out after being dragged would open out somewhere else —
   however far you last moved it, off centre, with the far edge past the screen.
   A host that pins with INLINE styles has to park them itself; see `onMax`.

   THE MARGIN IS 22px AND IT IS A MARGIN, NOT A GAP. Edge to edge would make the
   panel a page and lose the one thing that says it is still a panel: the case,
   its shadow, and the desk visible round it.

   z-index 400 puts it over every surface a document stacks under it, and the
   way out is the restore cap in its own title bar — which is why the chrome may
   go. Anything a host means to keep reachable from here (a colour picker, a
   modal) sits above 400 by its own number. */
.win.maxed{
  position:fixed; inset:22px; width:auto; height:auto;
  transform:none; z-index:400;
  display:flex; flex-direction:column;
}
/* THE BODY IS WHAT GROWS AND WHAT SCROLLS. The head keeps its height — it is
   the way out — and `min-height:0` is what lets a flex child actually shrink
   inside its parent instead of pushing it open. */
.win.maxed > .box-body{ flex:1 1 auto; min-height:0; max-height:none; overflow:auto; }

/* ── TWO BUTTONS IN A HEAD THAT WAS BUILT FOR ONE ─────────────────────────
   `.win-btn` is `margin-left: auto`, which is how ONE button reaches the
   right-hand end of a flex head. Two of them is two auto margins, and flexbox
   splits the free space BETWEEN them — so the maximise cap would sit marooned
   in the middle of the title bar with the reduce cap at the end. The second
   gives its auto up; the first keeps it and carries the pair right. */
.win-head .win-max{ margin-right:4px; }
.win-head .win-max ~ .win-btn{ margin-left:0; }

/* ── AND THE FURNITURE STANDS DOWN ────────────────────────────────────────
   Raising the panel above the bars is only half of it: they stay behind it and
   go on showing round the 22px margin — chrome you cannot reach, framing a view
   it does not belong to. The kit's own bar and drawer go; a document hides its
   own the same way, off `body.maxed-open`.

   `visibility` RATHER THAN `display`, because the drawer animates its own
   transform and a `display:none` in the middle of that leaves it half open when
   it comes back. Visibility keeps the box and takes the pixels. */
body.maxed-open .dock-bar,
body.maxed-open .drawer,
body.maxed-open .drawer-tab{ visibility:hidden; pointer-events:none; }
/* CLOSED IS GONE, not hidden-but-taking-space. Something else has to bring it
   back, which is the price of being closeable and the reason a panel the app
   needs does not get an ×. */
.win.gone{ display:none; }

/* the dock that brings the closed ones back */
/* the demo readout's own row — it borrowed the settings panel's dimension
   layout, which no longer exists now that those are three knobs */
.win-readout{ display:flex; align-items:center; gap:6px; }
.win-readout .lcd{ flex:1 1 0; min-width:0; justify-content:center; }

.win-dock{
  display:flex; align-items:center; gap:7px; flex-wrap:wrap;
  padding:9px 11px; border-radius:10px; margin-top:16px;
  background:linear-gradient(180deg,#17191d,#1e2126);
  box-shadow:inset 0 3px 7px rgba(0,0,0,.85), inset 0 -1px 0 rgba(255,255,255,.05),
             0 1px 0 rgba(255,255,255,.055);
}
.win-dock .lab{
  font-family:var(--font); font-size:8.5px; letter-spacing:.16em; color:#5d626b;
  text-shadow:0 1px 0 rgba(255,255,255,.09); margin-right:4px;
}
.win-dock .chip.away{ opacity:.42; }

/* the demo floor: panels sit ON something, or "moved it" has no meaning */
.win-floor{
  position:relative; min-height:330px; padding:18px;
  border-radius:14px; overflow:hidden;
  background:
    repeating-linear-gradient(0deg, rgba(255,255,255,.022) 0 1px, transparent 1px 26px),
    repeating-linear-gradient(90deg, rgba(255,255,255,.022) 0 1px, transparent 1px 26px),
    linear-gradient(180deg,#0d0e10,#121417);
  box-shadow:inset 0 3px 10px rgba(0,0,0,.8), inset 0 -1px 0 rgba(255,255,255,.04);
}
.win-floor .box{ position:absolute; }
.win-floor{ min-height:430px; }

/* ── THE OFF-CANVAS MODEL ─────────────────────────────────────────────────
   A screen with a page in it and a drawer beside it. Everything here is the
   real mechanism at a size you can watch: one transform on the page, one on
   the drawer, the same curve on both, and the tab riding on the drawer rather
   than sitting on the page. */
.oc{
  position:relative; height:104px; border-radius:6px; overflow:hidden;
  background:linear-gradient(180deg,#0a0b0d,#101316);
  box-shadow:inset 0 2px 6px rgba(0,0,0,.9), inset 0 -1px 0 rgba(255,255,255,.05),
             0 1px 0 rgba(255,255,255,.06);
}
.oc-page{
  position:absolute; inset:0; padding:11px 12px;
  display:flex; flex-direction:column; gap:7px; justify-content:center;
  transition:transform .28s var(--drawer-ease);
}
.oc-bar{ height:5px; border-radius:3px; background:#242830; }
.oc-bar.lit{ background:linear-gradient(90deg,#FF6A00,#C24E00); box-shadow:0 0 6px rgba(255,106,0,.4); }
.oc-drawer{
  position:absolute; top:0; right:0; bottom:0; width:96px;
  transform:translateX(100%);
  transition:transform .28s var(--drawer-ease);
  padding:11px 9px; display:flex; flex-direction:column; gap:7px; justify-content:center;
  background:linear-gradient(176deg,#26282d,#1b1c20 40%,#161719);
  box-shadow:-1px 0 0 rgba(255,255,255,.10), -2px 0 0 rgba(0,0,0,.9),
             -14px 0 22px -8px rgba(0,0,0,.8);
}
.oc-field{
  height:13px; border-radius:4px;
  background:linear-gradient(180deg,#080a09,#0f1211);
  box-shadow:inset 0 2px 4px rgba(0,0,0,.9), 0 1px 0 rgba(255,255,255,.06);
}
.oc.open .oc-page{ transform:translateX(-96px); }
.oc.open .oc-drawer{ transform:translateX(0); }
/* the one bump of it still showing when it is away */
.oc-tab{
  position:absolute; left:-17px; top:12px; width:17px; height:30px;
  padding:0; border:0; cursor:pointer; border-radius:6px 0 0 6px;
  display:flex; align-items:center; justify-content:center;
  background:linear-gradient(96deg,#2b2e33,#212429 55%,#191b1f);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.13), inset 0 -1px 0 rgba(0,0,0,.8),
             -4px 2px 7px -2px rgba(0,0,0,.75);
}
@media (prefers-reduced-motion: reduce){
  .oc-page, .oc-drawer{ transition:none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE OFF-CANVAS DRAWER — push variant

   OFF-CANVAS NAMES WHERE IT LIVES WHEN IT IS SHUT, which is the useful thing
   to know about it. Three variants exist and only one of them is this: an
   OVERLAY slides over the page, a REVEAL slides the page off something already
   underneath, and a PUSH moves the page aside. Push is the one worth the work
   here — an overlay hides the drawing at the exact moment you adjust it, so
   you slide it out, lose sight of the thing, change a number and only find out
   what you did after sliding it back.

   Z-INDEX: the page runs 1–3, panels 80, the picker plate 99. The drawer is
   200 and its tab 201, because a drawer over a floating plate is right and a
   plate over the drawer that opened it is not.
   ═══════════════════════════════════════════════════════════════════════════ */
:root{ --drawer-w:404px; --drawer-tab:38px; --drawer-ease:cubic-bezier(.22,.68,.24,1); }

.wrap,
.navdock{ transition:transform .28s var(--drawer-ease); will-change:transform; }
/* THE NAV GOES WITH THE PAGE. It is a sibling of .wrap, not a child, so a
   rule that only moves .wrap leaves the bar sitting still while everything
   under it slides, which reads as the bar being part of the drawer. */
body.drawer-open .wrap,
body.drawer-open .navdock{ transform:translateX(calc(-1 * var(--drawer-w))); }

.drawer{
  position:fixed; top:0; right:0; bottom:0; width:var(--drawer-w); z-index:200;
  transform:translateX(100%);
  transition:transform .28s var(--drawer-ease);
  will-change:transform;
  /* THE CASE'S OWN METAL, and a hard edge down the left where it meets the
     page — a rack unit is a slab with a machined face, not a sheet of glass. */
  background:linear-gradient(176deg,#26282d 0%,#1b1c20 26%,#161719 78%,#1c1d21 100%);
  box-shadow:
    -1px 0 0 rgba(255,255,255,.10),
    -2px 0 0 rgba(0,0,0,.9),
    -28px 0 44px -16px rgba(0,0,0,.85);
}
.drawer::before{
  content:''; position:absolute; inset:0; pointer-events:none;
  background-image:
    repeating-linear-gradient(90deg, rgba(255,255,255,.020) 0 1px, transparent 1px 3px),
    var(--grain);
  background-size:auto,140px; opacity:.5; mix-blend-mode:overlay;
}
body.drawer-open .drawer{ transform:translateX(0); }
/* the drawer's own head: a lamp and a name. The way out is the tab. */
.drawer-head{
  position:relative; z-index:2;
  display:flex; align-items:center; gap:9px;
  padding:15px 16px 13px;
  border-bottom:1px solid rgba(0,0,0,.7);
  box-shadow:0 1px 0 rgba(255,255,255,.05);
}
.drawer-head .nm{
  flex:1; font-family:var(--font); font-size:12px; font-weight:700; color:#e3e6eb;
  text-shadow:0 1px 0 rgba(0,0,0,.85); letter-spacing:.06em;
}

.drawer-body{
  position:relative; z-index:2; height:100%; overflow-y:auto; overscroll-behavior:contain;
  padding:14px 16px 26px; display:flex; flex-direction:column; gap:14px;
  height:calc(100% - 50px);
}
/* ── NO PANEL INSIDE A PANEL ──────────────────────────────────────────────
   THE DRAWER IS THE CHASSIS. A .box in here brought a second one with it: its
   own bevel, its own grain, its own cast shadow, sitting on a slab that
   already has all three. Two chassis deep and the wells stop reading as cut
   into anything, because the thing they are cut into is itself floating on
   something else.

   SO THE GROUPS LOSE THEIR CASE AND KEEP THEIR CONTENTS. The bays are still
   milled wells, still cut into metal, still lit from above left. What goes is
   only the box around them, and what is left is what was ever doing the work.

   The head stays, minus the rule under it: a lamp and a name is a caption, and
   a caption does not need a chassis to be one. */
.drawer-body .box{
  width:100%; flex:0 0 auto;
  background:none; box-shadow:none; border-radius:0; padding:0;
}
.drawer-body .box::before{ content:none; }        /* and its grain */
.drawer-body .box-head{
  padding:2px 2px 9px; border-bottom:0; box-shadow:none;
}
.drawer-body .box-head .nm{ font-size:11px; letter-spacing:.12em; color:#b9bec6; }
.drawer-body .box-body{ padding:0; }

/* ── THE PULL TAB ─────────────────────────────────────────────────────────
   IT RIDES ON THE DRAWER, so when the drawer is away the tab is the one bump
   of it still showing — a unit not quite pushed home. A control floating in the
   corner would be a handle that stays put while its drawer moves, which is a
   handle for nothing.

   44px tall: the touch minimum, and it is the only control on the page that
   has to be hittable before anything else is on screen. */
.drawer-tab{
  position:absolute; left:calc(-1 * var(--drawer-tab)); top:20px; z-index:201;
  width:var(--drawer-tab); height:46px; padding:0; border:0; cursor:pointer;
  border-radius:9px 0 0 9px;
  display:flex; align-items:center; justify-content:center;
  background:linear-gradient(96deg,#2b2e33 0%,#212429 55%,#191b1f 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.13),
    inset 0 -1px 0 rgba(0,0,0,.8),
    inset 1px 0 0 rgba(255,255,255,.06),
    -6px 3px 10px -3px rgba(0,0,0,.75);
  transition:background .14s;
}
/* THE SAME TAB, STOOD ON THE PAGE. It is absolutely positioned on the drawer
   because it rides on its edge; here it is the specimen, so it sits still and
   gets both its corners back — same metal, same engraved bars, same size, so
   what you press in the section is what you press at the edge. */
.tab-demo{
  position:static; border-radius:9px; width:44px; height:52px;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.13),
    inset 0 -1px 0 rgba(0,0,0,.8),
    inset 1px 0 0 rgba(255,255,255,.06),
    inset -1px 0 0 rgba(0,0,0,.5),
    0 4px 0 #0d0e10,
    0 7px 12px rgba(0,0,0,.7);
}
.tab-demo:active{ transform:translateY(2px); box-shadow:var(--recess); }

/* ── BOTH POSITIONS, SIDE BY SIDE ─────────────────────────────────────────
   A control whose mark changes has two specimens, or the section documents
   half of it. The left one is LIVE and the right one is the same tab frozen
   open — not a picture of it: the same element, the same class, the same
   engraved tracks, with the fold applied by hand instead of by the drawer.
   Anything else would be a drawing that could drift from the thing it draws.

   THE FROZEN ONE IS NOT A BUTTON. It is a div and it is aria-hidden, because
   a control that looks pressable and does nothing is worse than no control —
   and a screen reader hearing two Open Settings buttons would be told the page
   has two drawers. */
/* SECTION 10 IS TWO COLUMNS: the spec on the left, the control it describes on
   the right. Stacked, the panel was a wall of definitions and the thing they
   are about was below the fold — you read the spec for a button you had not
   seen yet. Side by side you can check a line against the object.
   Doc-only. */
.setrow{ display:flex; align-items:flex-start; gap:26px; }
.setrow > .api{ flex:1 1 0; min-width:0; max-width:none; margin:0; }
.setrow > .stage{ flex:0 0 340px; margin-top:0; padding:44px 24px; }
@media (max-width: 1000px){
  .setrow{ flex-direction:column; }
  .setrow > .stage{ flex:1 1 auto; width:100%; }
}

.tab-pair{ display:flex; gap:52px; align-items:flex-start; }
.tab-cell{ display:flex; flex-direction:column; align-items:center; gap:9px; }
.tab-cell .tab-demo{ cursor:default; }
.tab-cell .tab-demo#setopen{ cursor:pointer; }
.tab-cap{
  font-family:var(--font); font-size:8.5px; letter-spacing:.16em; color:#5d626b;
  text-shadow:0 1px 0 rgba(255,255,255,.09);
}
.tab-sub{ font-size:11px; color:#6b7079; margin-top:-5px; }
.drawer-tab:hover{ background:linear-gradient(96deg,#33373d 0%,#272b31 55%,#1d2024 100%); }

/* ── THE MARK ON THE TAB ──────────────────────────────────────────────────
   A BURGER SAYS "MENU" AND THIS IS NOT ONE. Three equal bars is the mark for a
   list of destinations; behind this tab are knobs, switches and a paper size.
   Sliders say it exactly — three tracks with a handle at a different place on
   each — and they say it as a picture of what is actually in there rather than
   as a convention you have to have learned.

   ENGRAVED, like every other mark on this case: the track is cut and the
   handle sits proud of it, so the two read as different objects the way they
   do on the panel inside.

   AND THE HANDLES MOVE WHEN IT OPENS. Not a swap to a cross — that is two
   icons pretending to be one. The same three handles slide along their own
   tracks, which is the tab showing you it has been operated rather than
   showing you a different symbol. */
/* THE THREE TRACKS ARE PLACED, NOT STACKED. They were block elements with a
   margin, so folding them meant guessing how far apart their centres were —
   and a guess that is out by a pixel gives you two arms at the right ANGLE on
   the wrong LINE, which reads as a chevron pointing left rather than as a
   cross. Absolute positions make the fold exact: both arms travel to the
   middle row's y and land on the same line. */
.sliders{ position:relative; display:block; width:17px; height:14px; }
.sliders i{
  position:absolute; left:0; right:0; height:1.6px; border-radius:1px;
  background:#0c0e11; box-shadow:0 1px 0 rgba(255,255,255,.13);
  transition:transform .24s var(--drawer-ease), opacity .16s;
}
.sliders i:nth-child(1){ top:0; }
.sliders i:nth-child(2){ top:6.2px; }
.sliders i:nth-child(3){ top:12.4px; }
.sliders i b{
  position:absolute; top:-2px; width:4px; height:5.6px; border-radius:1.5px;
  background:linear-gradient(180deg,#5a616b,#2e333a);
  box-shadow:0 1px 0 rgba(0,0,0,.7), inset 0 1px 0 rgba(255,255,255,.35);
  transition:left .24s var(--drawer-ease), opacity .16s;
}
.sliders i:nth-child(1) b{ left:2px; }
.sliders i:nth-child(2) b{ left:10px; }
.sliders i:nth-child(3) b{ left:6px; }

/* OPEN, IT IS A CROSS. The handles go first — a slider with its handle gone is
   already not a slider — then the outer two arms travel 6.2px to the middle
   row and rotate opposite ways, so they cross at one point instead of meeting
   at an end. */
body.drawer-open .sliders i b,
.mark-open .sliders i b{ opacity:0; }
body.drawer-open .sliders i:nth-child(1),
.mark-open .sliders i:nth-child(1){ transform:translateY(6.2px) rotate(45deg); }
body.drawer-open .sliders i:nth-child(2),
.mark-open .sliders i:nth-child(2){ opacity:0; }
body.drawer-open .sliders i:nth-child(3),
.mark-open .sliders i:nth-child(3){ transform:translateY(-6.2px) rotate(-45deg); }

/* the model's tab is smaller — same construction, scaled */
.oc-tab .sliders{ width:11px; height:9px; }
.oc-tab .sliders i{ height:1.2px; }
.oc-tab .sliders i:nth-child(1){ top:0; }
.oc-tab .sliders i:nth-child(2){ top:3.9px; }
.oc-tab .sliders i:nth-child(3){ top:7.8px; }
.oc-tab .sliders i b{ width:3px; height:4px; top:-1.4px; }
.oc-tab .sliders i:nth-child(1) b{ left:1px; }
.oc-tab .sliders i:nth-child(2) b{ left:6px; }
.oc-tab .sliders i:nth-child(3) b{ left:3.5px; }
.oc.open .oc-tab .sliders i b{ opacity:0; }
.oc.open .oc-tab .sliders i:nth-child(1){ transform:translateY(3.9px) rotate(45deg); }
.oc.open .oc-tab .sliders i:nth-child(2){ opacity:0; }
.oc.open .oc-tab .sliders i:nth-child(3){ transform:translateY(-3.9px) rotate(-45deg); }

/* MOTION IS A PREFERENCE. Everything still works with it off — the drawer
   arrives, it just does not travel. */
@media (prefers-reduced-motion: reduce){
  .wrap, .navdock, .drawer, .sliders i, .sliders i b{ transition:none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   09 · THE SETTINGS PANEL

   A FOLDER IS A BAY, not a collapsible header. Five folders that all collapse
   is a panel that has hidden itself: you open one to look at it and close it
   to reach the next, and at no point can you see the machine. These are milled
   wells with an engraved cap and a hairline — the same object the layer rows
   drop into — so the whole panel is legible at once and only the genuinely
   rare stuff goes behind a chevron.
   ═══════════════════════════════════════════════════════════════════════════ */
.set-body{ gap:7px; }
.set-bay{ margin:0; padding:8px 9px 9px; display:flex; flex-direction:column; gap:7px; }
.set-bay > .bay-cap{ margin:0 0 1px; }

/* two switches side by side, because four names each is small enough to sit
   in half a panel and a column of them would be twice the height for nothing */
.set-two{ display:flex; gap:6px; }
.set-two > *{ flex:1 1 0; min-width:0; }

/* THE CAPTION IS ENGRAVED ABOVE THE CONTROL, not floated beside it. A form row
   puts the name and the thing on one line and makes the name a column that has
   to be wide enough for the longest one — which is why every pane like this
   ends up with a third of its width spent on text nobody reads twice. */
.set-sel{ display:flex; flex-direction:column; gap:3px; }
.set-cap{
  font-family:var(--font); font-size:8px; letter-spacing:.16em; color:#5d626b;
  text-shadow:0 1px 0 rgba(255,255,255,.09);
}
/* THE DIAL IS SMALLER IN A SETTINGS BAY than in a viewport, and it has to be:
   a 76px cap plus 56px of leader plus a name is more width than a 318px panel
   has to give five times over. 54px keeps the pointer readable and buys 22px
   back for the names, which is the difference between "Smallest first" fitting
   and not. */
/* THE SWITCH IS A GROUP, AND THE GROUP IS WHAT GETS CENTRED. The rotary fills
   its cell and positions the dial inside it absolutely, so a half-width bay
   left the knob against one edge and a column of air against the other. The
   fix is not to nudge --rcx along until it looks right: it is to give the
   container the width of what is actually in it (a 54px dial, a 48px leader,
   and about 40px of name) and let auto margins do the centring.

   --rcx IS THE DIAL'S CENTRE, so anything under 27 hangs its left half outside
   whatever contains it. At 22 it did, and nobody saw until the .box padding
   came off and there was nothing left to hang into. */
.set-sel .rotary{ height:auto; width:124px; flex:0 0 auto; margin:0 auto; --rcx:30px; }
.set-sel .rot-knob{ width:54px; height:54px; margin:-27px 0 0 -27px; }
.set-sel .rot-seat{ width:64px; height:64px; margin:-32px 0 0 -32px; }
.set-sel .rot-lab{ font-size:9px; letter-spacing:.08em; }
.set-sel.tall .drum-bay{ display:block; }
/* a row carrying a name AND its size needs the width and a touch less letter */
.set-sel .drum-item{ font-size:9px; letter-spacing:.06em; padding:0 9px; }

.set-orient{ width:100%; }
.set-orient .pkey{ flex:1 1 0; min-width:0; height:26px; font-size:8.5px; letter-spacing:.06em; padding:0 4px; }
/* a square has no orientation and a custom size already is one — the bar goes
   quiet rather than offering a choice with one outcome */
/* DEAD, NOT GONE. A control a substroke has no use for dims where it is
   rather than vanishing: hiding it makes the bay change height as you move
   along the strip, which reads as the panel being unstable. */
.set-bay .inert{ opacity:.34; pointer-events:none; }
.set-orient.inert{ opacity:.3; pointer-events:none; }
/* both live in the same slot; exactly one of them is ever in it */

/* THREE KNOBS ON ONE LINE, and the units engraved under them rather than
   repeated in every readout. A knob that says "42.0" three times over with
   "cm" glued on is three copies of a fact that belongs to the row. */
.set-dimrow{ padding:2px 0 0; justify-content:space-evenly; }
.set-units{
  display:flex; justify-content:space-evenly; padding:1px 0 0;
  font-family:var(--font); font-size:8px; letter-spacing:.14em; color:#3f7a5c;
}
.set-units span{ flex:1 1 0; text-align:center; }
.set-orient.inert{ opacity:.3; pointer-events:none; }
/* both live in the same slot; exactly one of them is ever in it */

/* THE DERIVED PAIR. Two lit windows and a unit, reading what the format and
   the orientation make — no track, no thumb, nothing that says "drag me". */
.set-dlab{
  font-family:var(--font); font-size:8.5px; letter-spacing:.14em; color:#5d626b;
  text-shadow:0 1px 0 rgba(255,255,255,.09);
}
.set-dunit{ font-family:var(--font); font-size:8.5px; letter-spacing:.14em; color:#3f7a5c; }


/* ADVANCED IS A DRAWER. It is allowed to hide because what it hides is rare;
   the four bays above it are not, which is the whole argument for their being
   open wells rather than four more of these. */
.set-adv{ padding:2px 9px 8px; }
.set-advcap{ cursor:default; }
.set-adv .chev{ margin-left:2px; }
/* THE DRAWER IS SHUT UNTIL IT IS OPEN, and it is a drawer rather than a bay
   because what is in it is rare. A bay that is always open for four numbers
   you touch twice a year is four numbers in the way of the ones you touch
   every minute. */
.set-advbody{ display:none; flex-direction:column; gap:7px; padding:8px 0 0; }
.set-adv.open .set-advbody{ display:flex; }
.set-seed{ display:flex; align-items:center; gap:8px; }
.set-seed .lcd{ flex:1 1 auto; justify-content:center; }
.set-roll{
  flex:0 0 auto; height:22px; padding:0 10px; border-radius:6px;
  font-family:var(--font); font-size:8.5px; font-weight:700; letter-spacing:.1em;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE PROGRESS TRACK — moved up from the machine, because it was never a
   machine part

   MACHINE 07 wrote this as `.meter` and the argument for it had nothing to do
   with plotters: a progress bar is a rectangle that grows, and the thing it is
   worst at is the question actually being asked of it, which is "is it still
   moving". A fill creeping 0.3% a minute is indistinguishable from a fill that
   has stopped. Segments quantise it — the lit count is a number you can read
   across a room, and the next one arriving is an EVENT. Same data, and it
   ticks.

   THAT IS AS TRUE OF AN UPLOAD AS OF A JOB, and an upload is where it turned
   up next: the NextDraw sender draws exactly this track under a filename with
   a byte count beneath it, and nothing in that panel is a machine part. The
   rules below are byte for byte the ones the machine page has been running;
   what changed is which file they live in, and skew-machine.css now has a note
   where they were rather than a copy.

   THE NAME AND THE CAPTION ARE THE PART, NOT DECORATION ROUND IT. A track on
   its own says how far; it cannot say how far through WHAT, and every app that
   drew one of these drew a name over it and a figure under it. Both lines take
   `.filmrow`'s type unchanged — an 11px name that ellipsises rather than wraps,
   a 9px engraved line under it — because a file being loaded and a file being
   sent are the same fact at two moments.

   AND THE NUMBER IS UNDER THE TRACK, NOT ON IT. The segments are for watching
   and a number is for reporting: MACHINE 07 keeps a lit `.readout` beside its
   meter for exactly that, and this caption is the quiet version of the same
   decision. Nothing is ever printed inside the track, which would put moving
   text on the one part whose whole job is to be readable at a glance.
   ═══════════════════════════════════════════════════════════════════════════ */
.prog{ display:flex; flex-direction:column; gap:8px; min-width:0; }
/* THE NAME ELLIPSISES, AND THAT IS NOT A DETAIL. These are filenames, they are
   long, and a name that wraps to three lines moves the track down the panel
   every time the file changes — a layout that jumps while something is
   uploading reads as a fault in the upload. */
.prog-nm{
  font-family:var(--font); font-size:11px; color:#dfe2e7;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.prog-cap{
  font-family:var(--font); font-size:9px; letter-spacing:.08em; color:#61666f;
  font-variant-numeric:tabular-nums;
}

/* THE METER — the track itself. A recess, and lit segments in it. */
.meter{
  display:flex; gap:2px; height:22px; padding:4px; border-radius:7px;
  background:linear-gradient(180deg,#0a0c0d,#121517);
  box-shadow:inset 0 2px 6px rgba(0,0,0,.92), inset 0 -1px 0 rgba(255,255,255,.05),
             0 1px 0 rgba(255,255,255,.06);
}
.meter i{
  flex:1; border-radius:1.5px;
  background:rgba(255,255,255,.05);
  box-shadow:inset 0 1px 1px rgba(0,0,0,.7);
  transition:background .1s, box-shadow .1s;
}
.meter i.on{
  background:var(--led);
  box-shadow:0 0 6px rgba(255,106,0,.75), inset 0 1px 0 rgba(255,255,255,.35);
}
/* THE LAST LIT SEGMENT IS THE ONE ARRIVING, SO IT IS THE HOT ONE — and it is
   `.hot` rather than `.head`, which is what it was called for as long as this
   part lived on the machine page and is the bug that came with it. `.head` is
   a PART in this language: the engraved faceplate header, forty lines up, with
   `display:flex` and 14px of padding on it. Nothing in `.meter i.on.head`
   overrides a size, so the segment took the header's padding and its border,
   and one `<i>` in the middle of a 22px track blew up to 30 by 26 with a lit
   glow round it — visible on MACHINE 07 for as long as that page has had a
   running job, and only noticed when the part moved somewhere it was looked at.
   A state class on a shared element has to be as specific as the part is. */
.meter i.on.hot{ background:var(--led-hot); box-shadow:0 0 10px rgba(255,178,115,.95); }
.meter.done i.on{ background:#7CFFB0; box-shadow:0 0 7px rgba(124,255,176,.8); }

/* ═══════════════════════════════════════════════════════════════════════════
   THE MODAL — a plate that stops the desk

   THE ONE GAP WHERE THE APPS WERE NOT RE-COMPOSING BUT INVENTING. Nine of them
   have a blocking layer — a question that has to be answered, a render that has
   to finish — and all nine drew their own, because grepping this system for
   "modal", "overlay" and "toast" returned nothing at all. A language exists to
   settle that kind of thing once.

   IT IS A PANEL AND NOT A NEW SURFACE. `.box` is the chassis, `.box-head` is
   the title, `.box-body` is the room, `.actrow` is the foot: every part of this
   was already here and the only thing that had never been written is what
   happens to the DESK while it is up. Anything else would be a second kind of
   card, and a system with two kinds of card has to explain which is which.

   THE DESK GOES DARK AND STAYS THE DESK. The cover is `--room` thickened, not a
   blur and not a wash of black — the grain, the lamp and whatever panel you
   were working on are all still there, dimmer, which is what says the modal is
   ON the desk rather than a new page. A blur would be the one effect on this
   site that no machined object can make.

   z-index 500, WHICH IS THE NUMBER THE WINDOW ALREADY NAMED. `.win.maxed` sits
   at 400 and its comment says anything a host means to keep reachable from
   there — a colour picker, a modal — sits above it by its own number. This is
   that number, and the ladder is now page 1–3 · panels 80 · picker 99 ·
   drawer 200 · maximised window 400 · modal 500.

   440px WIDE, MEASURED OFF THE APP AND NOT CHOSEN. `.box` is 318 because that
   is the width of a panel in a rail of panels. A modal is not in a rail: it has
   a paragraph in it, and 318 turns four lines of prose into eight.

   THE BACKDROP IS NOT A BUTTON. Clicking the desk does nothing. A modal asks a
   question and the answer is a key — dismissing one of these by clicking past
   it is how a confirmation gets answered by accident, and the two things it
   guards here are a file being rewritten and a job being cancelled. Escape is
   the keyboard's way out and it is wired to the CANCEL key, so a modal built
   without one — a render that must not be abandoned halfway — has no accidental
   way out either, deliberately.
   ═══════════════════════════════════════════════════════════════════════════ */
/* THE PAGE UNDER IT DOES NOT SCROLL, and it is a class on the body rather than
   an inline style, the way `body.drawer-open` already is: the stylesheet owns
   what a state looks like, and a factory that writes `style.overflow` has to
   remember what was there before. What the cover blocks is the pointer; a wheel
   over it still reaches the document, and a page sliding about behind a
   question reads as a page that did not notice it was asked. */
body.modal-open{ overflow:hidden; }
.modal-cover{
  position:fixed; inset:0; z-index:500;
  display:flex; align-items:center; justify-content:center;
  /* 22px, the margin `.win.maxed` already uses: edge to edge would make the
     plate a page and lose the case, its shadow and the desk round it. */
  padding:22px;
  background:color-mix(in srgb, var(--room) 78%, transparent);
}
/* THE BODY IS WHAT SCROLLS, and `min-height:0` is what lets a flex child
   actually shrink instead of pushing its parent open — the same three lines
   `.win.maxed` needed, for the same reason: a listing of forty layers must not
   push the two keys off the bottom of the screen. */
.modal{
  /* max-width, because 440 is wider than a phone and a plate that overflows its
     own cover is a plate with its keys off the screen. */
  width:440px; max-width:100%; flex:0 0 auto; max-height:100%;
  display:flex; flex-direction:column;
}
.modal > .box-body{ min-height:0; overflow:auto; }
/* A TITLE IS A LEGEND, so it is tracked out like every other legend on the
   panel. `.box-head .nm` is set for a panel in a rail, where the name sits
   beside controls and the tracking would fight them; this one is alone on its
   bar and reads as engraving. */
.modal .box-head .nm{ letter-spacing:.09em; }

/* A PANEL HAS NO PROSE ANYWHERE ELSE, AND THIS ONE HAS TO HAVE IT. Every other
   piece of text in the language is a legend — tracked, capitalised, mono, cut
   into metal. None of that survives three sentences, so the modal's copy is the
   UI face at reading size and it is the one place in this stylesheet where type
   is set to be read rather than to be labelled. It stays `--txt`, which keeps
   the title above it brighter and the caption below it dimmer. */
.modal-say{
  margin:0; font-family:var(--font-ui); font-size:13px; line-height:1.55;
  color:var(--txt);
}
.modal-say + .modal-say{ margin-top:9px; }

/* THE LISTING IS CUT INTO THE FACE. What goes in one of these is the evidence
   for the question — the five layers about to be renamed, the four files about
   to be overwritten — and evidence is a thing you read, not a thing you press.
   So it takes the well every recess in this language takes, `--recess`, and the
   empty slot's floor from `.filmrow`; nothing here is a new surface. */
.modal-well{
  border-radius:8px; padding:10px 12px;
  display:flex; flex-direction:column; gap:6px;
  background:linear-gradient(180deg,#0c0d0f,#141619);
  box-shadow:var(--recess);
}
/* THREE COLUMNS AND THE ARROW IS ITS OWN, so every arrow in the well is in the
   same place down the column. Two columns with the arrow glued to one of them
   makes a ragged seam the eye reads as misalignment. */
.modal-row{
  display:grid; grid-template-columns:1fr 12px 1fr; gap:9px; align-items:baseline;
  font-family:var(--font); font-size:11px; color:var(--txt);
}
.modal-row.one{ grid-template-columns:1fr; }
/* WHAT IT IS NOW IS DIMMER THAN WHAT IT BECOMES, because the right-hand column
   is the change and the left is the thing being changed. */
.modal-row b{ font-weight:400; color:var(--txt-dim);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.modal-row b.to{ color:var(--txt); }
.modal-row i{ font-style:normal; color:var(--txt-dim); text-align:center; }

/* ── THE PRIMED KEY ────────────────────────────────────────────────────────
   THE RING IS NOT A FOCUS RING, and it matters that it is not: this file paints
   no focus indicator anywhere and says why. This is a MARK ON THE PART — the
   ring an illuminated pushbutton wears when it is the one the panel is waiting
   for — and it is there whether or not anything is focused, which is exactly
   what a focus ring is not.

   IT SAYS WHERE RETURN GOES. One key in a modal is the answer and the rest are
   ways out, and without the ring the only thing separating them is that one of
   them is green — which stops being true the moment the answer is destructive
   and wears `.halt` instead.

   `outline`, NOT A SHADOW, and that is the whole reason it can exist. `.act`
   spends its box-shadow on the skirt, the rim and the lamp glow; a ring added
   to that stack would be a fourth thing competing for the same property and
   would have to be re-declared in every hover and active state. An outline
   follows the border radius, sits outside the cap, and travels with it when the
   key goes down — which is right, because it is painted on the cap and not on
   the panel behind it. */
.act.primed{ outline:2px solid var(--led); outline-offset:3px; }
.act.primed:disabled{ outline-color:#5a4030; }

/* ═══════════════════════════════════════════════════════════════════════════
   MATERIALS — the primitives, so this is a language and not one drawing
   ═══════════════════════════════════════════════════════════════════════════ */

/* NOT A GRID. A grid gives every cell the same width, so a knob 60px across
   and a fader 300px across get the same track and the knob sits in a lake of
   its own column. These are eighteen objects of eighteen different sizes and
   the only layout that respects that is one that asks each of them how wide it
   is: a wrapping row, packed left, tiles at their natural width.

   IT ALSO KILLS THE `wide` / `tall` PROBLEM RATHER THAN MANAGING IT. Column
   spans, dense flow and a per-group column count were all ways of telling a
   grid about sizes it could have measured itself. */
.mats{
  display:flex; flex-wrap:wrap; align-items:stretch;
  gap:26px 30px; width:100%;
}
/* STRETCH, NOT flex-start, AND IT IS THE WHOLE ALIGNMENT FIX. In a wrapping
   row every LINE sizes to its tallest item; stretch makes the rest of that
   line match it, and `margin-top:auto` on the body then drops every name onto
   the same baseline. With flex-start each tile was only as tall as itself and
   the names sat wherever their specimen happened to end. */
/* THE TILES GROW TO FILL THE LINE. flex:0 0 auto packed them left at their
   natural width and left the tail of every row empty, which on a stage reads
   as the group having run out rather than as the group being laid out. They
   share the slack instead, so the specimens spread across the full width and
   the air lands BETWEEN parts rather than all of it after the last one.

   `min-width` is the floor that keeps a seven-part line from squeezing a
   300px fader; below it the line wraps, which is the correct answer. */
.mat{
  flex:1 1 auto; min-width:118px; max-width:100%;
  display:flex; flex-direction:column;
}
/* A WIDE PART ASKS FOR ITS REAL LENGTH AS ITS BASIS, so the slack is shared in
   proportion to what each part actually needs rather than equally between a
   knob and a fader bank. */
.mats .mat.wide{ flex-basis:300px; }

/* THE SPECIMEN AREA HAS A FLOOR so a 12px LED and a 200px fader bank do not
   make two different-looking rows, and it takes the slack in a stretched
   tile so the name stays pinned under it. */
.mat-stage{
  display:flex; align-items:center; justify-content:center; gap:10px;
  min-height:88px; flex:1 0 auto;
}
.mat-body{ margin-top:auto; padding-top:11px; }

/* THE NAME IS ENGRAVED, like every other label on this page. It was a card
   title when the tile was a card; on an open stage a heading in body type at
   15px is the loudest thing in the section and the part comes second. */
.mat-t{
  font-family:var(--font); font-size:8.5px; letter-spacing:.15em;
  text-transform:uppercase; color:#6b7079; margin:0; text-align:center;
  text-shadow:0 1px 0 rgba(255,255,255,.07);
}

/* A FADER IS A LENGTH AND WANTS A REAL ONE. 300px is what the layer editor's
   own Zone row runs at, so the specimen is the size the part is actually used
   at rather than whatever a column happened to be. */
.mats .mat.wide .fader{ width:300px; }
.mats .mat.wide .frange{ width:300px; }
.mats .mat.tall .mat-stage{ min-height:200px; }
/* THE SEATED SWITCH IS A WHOLE PANEL, so its tile asks for a panel's width:
   370 of case-plus-lobe and 22 of air. `wide` alone is 300, and a wrapping row
   would squeeze the one specimen on this page whose entire subject is a
   silhouette. It does not grow either — a bulge stretched to fill a line is a
   lobe in the wrong place, because the lobe's centre is a measured constant
   and the clip-path is computed from the box it was measured in. */
.mats .mat.matbulge{ flex-basis:392px; flex-grow:0; }
.mats .mat.matbulge .mat-stage{ min-height:212px; }
/* The small controls are sampled at the size the row uses them — same 5px gap,
   no scaling. The chevron's knurl is tuned against an 11px glyph; blow it up
   and the tile shows a material that is nowhere in the document. */
.mat-row{ display:flex; align-items:center; gap:5px; }
.mat-keys{ display:flex; gap:11px; }
.mat-keys .key{ width:38px; height:38px; border-radius:9px; }
.mat-keys .key svg{ width:18px; height:18px; }
.demo-plate{ width:90px; height:44px; border-radius:8px;
  background:linear-gradient(176deg,#2b2e33,#1b1d20);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14), inset 0 -1px 0 rgba(0,0,0,.85), 0 6px 12px -4px rgba(0,0,0,.8); }
.demo-well{ width:90px; height:44px; border-radius:8px;
  background:linear-gradient(180deg,#17191d,#1e2126);
  box-shadow:inset 0 3px 7px rgba(0,0,0,.85), inset 0 -1px 0 rgba(255,255,255,.05),
             0 1px 0 rgba(255,255,255,.055); }
/* THE STAGE — the third surface, and the one this documentation was posing the
   other two ON without ever naming it. A chassis is a raised body and a well is
   a cut into one; a stage is neither. It is a FIELD: the same case metal seen
   at low light, a hairline border rather than a bevel, and one soft radial
   from the lamp so it is not flat. Nothing is bolted to it and nothing is cut
   into it — things are simply set down on it, which is why every specimen on
   this site sits on one and why the drawing area of an app is one too. */
.demo-stage{ display:block; width:90px; height:44px; border-radius:8px;
  border:1px solid #1e2024;
  background:radial-gradient(70% 60% at 28% 0%, rgba(255,255,255,.055), transparent 60%),
             linear-gradient(180deg,#141518,#0c0d0f); }
.demo-etch{ font-family:var(--font); font-size:15px; font-weight:700; letter-spacing:.18em; color:#5d626b;
  text-shadow:0 1px 0 rgba(255,255,255,.12), 0 -1px 0 rgba(0,0,0,.7); }



/* ═══════════════════════════════════════════════════════════════════════════
   THE API BLOCK — the interface, not the argument

   This section used to open with eleven paragraphs of reasoning, which is the
   right thing to have and the wrong thing to put in front of someone who wants
   to USE it. The reasoning stayed where it belongs — in the code comments, next
   to the line it explains, where it cannot go stale without the line going with
   it — and what is on the page is the smallest thing you can act on: the one
   contract, the entry points, and a door to the full reference.
   ═══════════════════════════════════════════════════════════════════════════ */
.api{
  max-width:76ch; margin:0 0 4px; padding:16px 18px 14px;
  border:1px solid #212327; border-radius:12px;
  background:linear-gradient(180deg,#141518,#0f1012);
}
.api-rule{ font-size:13px; line-height:1.65; color:#8b909a; margin:0 0 14px; }
.api-rule b{ color:#c6cad1; font-weight:600; }
.api dl{ margin:0; display:grid; grid-template-columns:auto 1fr; gap:5px 16px; align-items:baseline; }
.api dt{
  font-family:var(--font); font-size:11px; letter-spacing:.02em; color:#7CFFB0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.api dd{ margin:0; font-size:12.5px; line-height:1.5; color:#7d828b; }
/* the rows a dev gets wrong: marked, not explained twice */
/* A GROUP HEADING INSIDE THE LIST, because fourteen entries in one column is
   a lookup table you have to read all of to use any of. It spans both columns
   and takes an empty `dd` to keep the grid honest — the alternative is nesting
   four lists, which puts four different baselines down the same page.

   IT NAMES THE THREE LAYERS §02 ALREADY NAMED. A grouping that invents its own
   categories teaches a second vocabulary for the same thing; this one is the
   page agreeing with itself. HATCH splits in two because it is the only one
   big enough to need it, and the split is fills against everything else —
   which is the question you actually arrive with.

   THE EMPTY `dd` IS REMOVED FROM THE GRID, NOT LEFT BLANK. A `dl` needs a `dd`
   after every `dt` to stay a definition list, but this is a two-column grid on
   auto-placement: a heading spanning both columns and then an empty cell puts
   the NEXT term in column two, and every row after it is off by one. Nothing
   throws, the list just quietly stops lining up. `display:none` takes it out
   of the placement entirely, and the markup stays a list. */
.api dt.api-group{
  grid-column:1 / -1; margin-top:16px; padding-bottom:5px;
  border-bottom:1px solid #17191c;
  color:#c6cad1; font-size:10px; letter-spacing:.16em;
  white-space:normal;
}
.api dl > dt.api-group:first-child{ margin-top:0; }
.api dt.api-group span{ color:#575c65; letter-spacing:.10em; margin-left:8px; }
.api dl > dd:empty{ display:none; }
.api dt.warn{ color:#FFB273; }
.api code{ font-family:var(--font); font-size:11px; color:#c6cad1; }
.api-more{
  display:inline-block; margin-top:15px; padding:6px 11px; border-radius:6px;
  font-family:var(--font); font-size:9.5px; letter-spacing:.12em; text-transform:uppercase;
  color:#8b919b; text-decoration:none;
  background:linear-gradient(180deg,var(--plate-hi),var(--plate) 52%,#1a1c20);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14), inset 0 -1px 0 rgba(0,0,0,.7),
             0 2px 0 #0e0f11, 0 3px 5px rgba(0,0,0,.6);
}
.api-more:hover{ color:#e2e5ea; }
.api-more:active{ transform:translateY(2px);
  box-shadow:inset 0 2px 4px rgba(0,0,0,.8), 0 1px 2px rgba(0,0,0,.6); }

/* ═══════════════════════════════════════════════════════════════════════════
   08 · FILLING — the specimen rig

   The bay next door is a compound CONTROL: a specimen with four controls
   pointing at it. This is a compound SPECIMEN, and the only control on either
   panel re-rolls the seed — because there is nothing here to set. The fills
   are answers to one question and what is being asked is which of them is
   right; a knob would invite tuning before judging.

   ONE ROW PER FILL, AND ONE SHEET PER ROW. It was a grid of separate screens,
   which is one display per drawing — and that is the wrong unit, because the
   question a row asks is what ONE hand does to two different problems. So the
   two answers sit on the same sheet, side by side, with a gap: same paper,
   same pen, same run, and the gap is the whole of the separation. Reading DOWN
   the balls compares the hands on tone; down the shapes compares them on
   boundary; across a row is one hand answering both.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── THE PAIRED STAGE ─────────────────────────────────────────────────────
   SIDE BY SIDE IS THE COMPARISON. Stacked, one hand and one machine are two
   things you scroll between, and a fill you have to remember is a fill you are
   not comparing — the whole reason both are on the same ball and the same lamp
   is so the eye can cross between them without moving.

   IT BROKE ITS BANKS FOR ONE VERSION AND DOES NOT NEED TO ANY MORE. At full
   size the pair was about 1260px against a 1160px wrap, so the stage ran to
   the viewport edge and scrolled sideways. Halving the sheets took it to about
   700, which fits the column with room — so it goes back inside, centred, and
   the page keeps one measure down its whole length. A section that breaks the
   grid should be doing it because it cannot fit, not because it once could
   not. */
.stage.pair{ flex-wrap:nowrap; gap:30px; justify-content:center; }
.stage.pair > .box{ flex:0 0 auto; }
.hh-cell .bay-cap{ margin-bottom:1px; }
.hh-cell .bay-cap em{ font-style:normal; color:#4a4f57; letter-spacing:.10em; }
.hh-cell .bay-cap::after{ order:9; }
.hh-spec{ padding:8px; }
/* THE GLASS KEEPS ITS REFLECTION AND LOSES ITS SCANLINES, which is the one
   place this block departs from `.screen` and it is not a preference. The haze
   is a 1-in-3px horizontal rule; the thing behind it is a fill made of rules a
   few tenths apart. Those two beat against each other, and the moire lands
   exactly on the property being judged. A display that adds a texture to the
   specimen is not a display, it is a filter. */
.hh-spec::after{ background:linear-gradient(148deg, rgba(255,255,255,.10) 0%, rgba(255,255,255,.02) 26%, transparent 46%); }
/* WHITE STOCK, AND IT IS NOT THE BAY'S CHOICE. This block took its toned
   #BEB5A3 from `.hb-field` on the grounds that the two should match, which was
   never the bay's actual reason: the BAY has a white pen in its case, and
   white on cream is not a subtle result, it is no result — an empty sheet that
   reads as broken rather than as white ink. Toned stock is what you reach for
   when you plot white, and that constraint is real and stays there.

   IT DOES NOT APPLY HERE. There is exactly one pen in this block — a hatch
   being read for its HAND has no business also being read for its colour — and
   that pen is black. So the ground can be the ground these fills are actually
   for, which is white paper, and the specimen stops being a rendition of the
   plot and starts being the plot. Black on white is 18.4:1 against 8.7:1 on
   the toned stock, and at a 0.35 nib that is the difference between reading a
   pitch and estimating it.

   THE WELL LIGHTENS WITH IT. The inner shadow was 8px at .4 alpha, which sits
   as a well under a toned sheet and as a grey vignette under a white one —
   dirtying the corners of the very thing being judged. The 1px rim stays,
   because that is what says where the sheet ENDS; the glow drops to a sixth of
   its weight, which is enough to seat the paper and not enough to tint it. The
   glass over the top is untouched: same reflection, same bezel, same screen. */
.hh-field{
  --paper:#FFFFFF;
  width:100%; height:240px;
  border-radius:4px; position:relative; overflow:hidden;
  background:var(--paper);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.55), inset 0 2px 6px rgba(0,0,0,.16);
}
.hh-field svg{ display:block; width:100%; height:100%; }

/* ══ THE HATCH BAY ══════════════════════════════════════════════════════
   The instrument. The grid below it is the catalogue, and the difference is
   that this one you turn: it is a `box`, which is what Skew calls a panel, and
   a panel is the right thing for a surface with knobs on it. The specimens
   below are not panels and must not become them — that distinction is the
   whole reason both exist on one page. */
/* THE SHEET WANTS A SHEET'S PROPORTION. At 820 wide and 300 tall the pane was
   a 206 × 79mm strip — two and a half to one — and a teapot fitted into that is
   a small object marooned in a lot of paper. 760 × 430 is 200 × 113mm, which is
   a landscape sheet, and the object fills it. */
.hatchbay{ width:auto; flex:0 0 auto; max-width:760px; margin:26px auto 0; }
.bay-body{ gap:11px; }
/* THE ROW PICKS THE MODEL. It was layer tabs with two of three dead, which is
   a promise on a page that already has enough of them. And it is the ONLY
   place the subject is chosen — it used to be here and again over the grid,
   which is two controls on one key: they do not sync each other, so they drift
   apart until something rebuilds them both. */
.bay-subj{ width:100%; }
.bay-panes{ display:block; }
.bay-pane{ padding:7px; }
/* THE FIELD IS A FIXED HEIGHT AND THE WIDTH IS THE GRID'S. Every pane measures
   its own element at 96/25.4, so the drawing is 1:1 whatever the column comes
   out at — but the HEIGHT has to be pinned or the two panes disagree about how
   much paper they are showing and the same object arrives at two sizes. */
.bay-field{
  --paper:#FFFFFF;
  height:430px; border-radius:4px; position:relative; overflow:hidden;
  background:var(--paper); cursor:grab; touch-action:none;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.55), inset 0 2px 6px rgba(0,0,0,.16);
}
.hatchbay.turning .bay-field{ cursor:grabbing; }
.bay-field svg, .bay-field canvas{ display:block; width:100%; height:100%; }
/* WHILE IT TURNS, THE PLOT IS STALE AND SAYS SO. It cannot keep up — the
   labyrinth is half a second a frame — so rather than let it look merely slow
   it steps back and lets the tone pane lead. Nothing jumps, nothing reflows;
   only the thing that is no longer current stops claiming to be. */
/* WORKING IS DIMMED. TURNING IS NOT. While you drag, the pane is showing the
   silhouette at the angle you are holding, which is CURRENT — dimming it would
   say the opposite. While the fill rebuilds, what is on screen is the last
   angle's drawing, which is stale, and it says so. */
.hatchbay.working .bay-pane{ opacity:.42; }
.bay-pane{ transition:opacity .14s; }
@media (prefers-reduced-motion:reduce){ .bay-pane{ transition:none; } }
.bay-caps{
  display:flex; align-items:center; gap:9px;
  font-family:var(--font); font-size:8px; letter-spacing:.10em; color:#3f7a5c;
}
.bay-caps .right{ margin-left:auto; }
.bay-caps .lcd{ height:19px; padding:0 8px; font-size:8px; }
.bay-controls{ display:grid; grid-template-columns:1fr auto; gap:18px; align-items:start; }
.bay-types{ display:flex; flex-direction:column; gap:1px; margin-top:5px; }
/* A LIST, NOT A DRUM. Nine named things you pick BY NAME want to be readable
   at once; the drum is for when there are too many to show, and nine is not
   too many. The marker is a rule down the left rather than a fill behind the
   row — a highlighted row in a column of nine reads as a selected cell in a
   table, which invites you to look for the other columns. */
.bay-t{
  text-align:left; border:0; background:none; cursor:pointer;
  font-family:var(--font); font-size:11px; letter-spacing:.02em; color:#7d828b;
  padding:3px 0 3px 11px; border-left:2px solid transparent;
  transition:color .12s, border-color .12s;
}
.bay-t:hover{ color:#c6cad1; }
.bay-t.on{ color:#FF8A3D; border-left-color:#FF6A00; }
.bay-knobs{ display:flex; gap:14px; align-items:flex-start; }
.bay-deep{ border-top:1px solid #17191c; padding-top:10px; }
.bay-own{
  margin:0; font-size:10px; line-height:1.5; color:#E3B45E;
}

/* ── ONE FRAME, AND THE GRID IS THE PAGE ────────────────────────────────
   A specimen used to arrive inside a `box` inside a `stage`, and the box put a
   lit head bar, a bevel and a 42px drop shadow around a thing whose only job
   is to be a flat sheet under glass. Three surfaces before the paper, and the
   middle one carried nothing: its head said what the `stage-note` already
   said, and its fixed 318px is what left a 1160px column holding one small
   panel in the middle of a great deal of nothing.

   THE STAGE IS THE FRAME NOW and the tiles sit on it directly: stage, screen,
   paper. The screen is untouched — it is the part that makes a white sheet
   read as being under glass rather than as a hole in the page. */
.stage.rig{ display:block; padding:44px 26px 30px; gap:0; }
.rig-head{
  position:absolute; left:22px; right:22px; top:13px;
  display:flex; align-items:center; gap:12px;
}
.rig-head .stage-note{ position:static; }
.rig-head .hh-note{ margin-left:auto; }
.rig-head .piano.sm{ width:auto; flex:0 0 auto; }

/* ── FIXED COLUMNS, NOT FLUID ONES, AND THAT IS THE MILLIMETRE TALKING ──
   Three across, and they do not stretch. Every tile measures its own element
   and converts at 96/25.4, so a column that grew with the window would
   silently change the size of the drawing being judged — and 1:1 is the whole
   claim this page makes. So the tile is a fixed 336px and the grid centres
   however many fit, dropping to two and then one. 320 x 240 of sheet is
   84.7 x 63.5mm, which is more than twice the paper the paired layout could
   give either subject. */
.rig-cells{
  display:grid; grid-template-columns:repeat(3, 336px);
  justify-content:center; gap:38px 26px;
}
@media (max-width:1120px){ .rig-cells{ grid-template-columns:repeat(2, 336px); } }
@media (max-width:780px){ .rig-cells{ grid-template-columns:336px; } }

/* ── A TILE IS A PICTURE, A NAME, A NUMBER AND A CALL ───────────────────
   In that order, and with no frame of its own. An outline round each one
   would be a fourth surface on a page that already has the sheet, the glass
   and the stage, and in a grid the gap between tiles already says where one
   ends. This is the whole of the card. */
.hh-cell{ display:flex; flex-direction:column; }
.hh-spec{ padding:8px; }
.hh-meta{ display:flex; align-items:center; gap:8px; margin-top:12px; }
.hh-meta .eng{ width:15px; height:15px; flex:0 0 15px; color:#575c65; }
.hh-meta .nm{
  font-family:var(--font); font-size:12px; font-weight:600; letter-spacing:.02em;
  color:#e3e6eb;
}
/* THE COUNT IS THE PRICE, so it sits where a price sits: hard right, lit, and
   the only number on the tile. PEN DOWNS and not lifts, because a fill that is
   one stroke reads as 1 and not as 0. */
.hh-meta .lcd{ margin-left:auto; height:22px; padding:0 9px; font-size:8px; }
.hh-sub{ margin:6px 0 0; font-size:11px; line-height:1.55; color:#7d828b; }
.hh-sub .tag{
  display:block; font-family:var(--font); font-size:8px; letter-spacing:.14em;
  text-transform:uppercase; color:#3f7a5c; margin-bottom:3px;
}
/* ── THE CALL, AND IT IS NOT A PANEL ────────────────────────────────────
   It was a well under the sheet, which is a fourth surface holding one line of
   type. A hairline rule above it says the same thing for the cost of one
   pixel, which is what a rule is for. */
.hh-call{
  display:flex; align-items:flex-start; gap:8px;
  margin-top:11px; padding-top:10px; border-top:1px solid #1e2024;
}
.hh-call code{
  flex:1 1 auto; min-width:0;
  display:block; white-space:pre-wrap; overflow-wrap:anywhere;
  font-family:var(--font); font-size:10px; line-height:1.7; color:#7d828b;
}
.hh-call code b{ color:#c6cad1; font-weight:600; }
.hh-call code i{ color:#3f7a5c; font-style:normal; }
.hh-call .micro{ flex:0 0 22px; }
/* COPIED IS A STATE OF THE CALL, not a toast. There is nowhere on this page a
   toast could go that is not on top of a specimen, and the thing that changed
   is the call — so the call says so, where the eye already is. */
.hh-call.is-copied code, .hh-call.is-copied code b{ color:#3f7a5c; }
.rig-cells .bay-cap{ font-size:8px; letter-spacing:.12em; }
.hh-roll{ padding:0 11px; height:23px; font-family:var(--font); font-size:8.5px;
          letter-spacing:.16em; color:#8b919b; }
.hh-note{ font-family:var(--font); font-size:8.5px; letter-spacing:.14em; color:#5d626b;
          text-shadow:0 1px 0 rgba(255,255,255,.08); }

/* THE BILL IS GONE, AND SO IS `.bill`. Two pages ended in a pair of cards
   listing what the language buys and what it costs — twelve bullets of prose
   apiece, arguing in writing about parts that were live on the same screen a
   scroll above. The specimen is the argument. A page that has just let you
   press every control on it does not then need to tell you how they felt. */

footer{ margin-top:80px; padding-top:22px; border-top:1px solid #1c1e22;
        font-family:var(--font); font-size:10.5px; color:#4a4e56; letter-spacing:.06em; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE NAV DOCK — every page carries it
   ═══════════════════════════════════════════════════════════════════════════ */

/* THE DOCK IS FULL WIDTH AND THE BAR IS NOT, which is the whole reason the
   dock has pointer-events:none. A sticky rail spanning the viewport with a
   180px bar floating in the middle of it is an invisible 1160px lid over the
   top of every page: the first row of anything below it stops taking clicks
   and nothing on screen says why. The bar puts them back on for itself. */
.navdock{
  position:sticky; top:14px; z-index:40;
  display:flex; justify-content:center;
  margin:14px 0 -46px; padding:0 32px;
  pointer-events:none;
}
.navdock > .strip{ pointer-events:auto; gap:8px; padding:8px 11px; }

/* SEATED. Past the first scroll the bar is over content rather than over the
   room, so it deepens its cast shadow and takes an outline. Both halves of
   that matter: the shadow says it is above, the outline stops it dissolving
   into a panel of the same metal passing underneath. */
.navdock > .strip{ transition:box-shadow .18s ease; }
.navdock.seated > .strip{
  box-shadow:
    0 40px 56px -22px rgba(0,0,0,.96),
    0 10px 18px -6px rgba(0,0,0,.85),
    0 0 0 1px rgba(0,0,0,.7),
    inset 0 1px 0 rgba(255,255,255,.15),
    inset 0 -1px 0 rgba(0,0,0,.9);
}
/* the sentinel the observer watches. 1px, at the top, and never seen. */
.nav-sentinel{ height:1px; margin:0; }

/* the wordmark is .demo-etch at nav size: engraved, not printed */
/* NO UNDERLINE, EVER, and it is set on the CLASS rather than on the anchor.
   The mark is a span on the home page and an anchor everywhere else, so a rule
   that only names `a.nav-mark` is a rule that describes half the cases and
   leaves the other half depending on which element it happened to be. Declared
   once, on the thing that is always true: this is an engraved badge, and an
   engraved badge is not underlined. */
.nav-mark{ font-size:12.5px; letter-spacing:.2em; padding:0 8px 0 5px; text-decoration:none; }
.nav-mark:hover, .nav-mark:focus, .nav-mark:active{ text-decoration:none; }
/* AND IT IS THE WAY HOME, so it has to say so on approach. The HOME key that
   used to sit in the bar is gone — the same door twice, and the one that cost
   a slot in a bar that has to fit on a phone. An engraved wordmark gives no
   hint that it is pressable, so the affordance is the hover: it lifts toward
   the lit end of its own range rather than turning into a link, because a blue
   underline on a machine's badge is a web page wearing a panel. */
a.nav-mark{ cursor:pointer; transition:color .14s, text-shadow .14s; }
a.nav-mark:hover{ color:#9aa0aa; text-shadow:0 1px 0 rgba(255,255,255,.2), 0 -1px 0 rgba(0,0,0,.8); }

.nav-pages{ padding:5px; gap:6px; }
/* AN ANCHOR, NOT A BUTTON. These go to documents, so the part underneath has
   to be the part that goes to documents: middle-click opens a tab, the status
   bar shows where it lands, and none of that survives a button with an
   onclick. .pkey is a look; <a> is the mechanism. */
.nav-link{
  height:28px; min-width:0; padding:0 15px;
  font-size:10px; letter-spacing:.14em; text-decoration:none;
}
/* THE PAGE YOU ARE ON IS A SPAN. A link to here is a link to nowhere, and a
   key that is already down is not a control. It keeps .pkey.is-down for the
   travel and the lamp, and loses the cursor and the href. */
span.nav-link{ cursor:default; }
.nav-ver{ margin-left:2px; }

/* THE SECTIONS KEY — a door into the current document, not a destination.
   Sized to the nav's own scale rather than the strip's default 34px cap, so it
   stands exactly as tall as the keys it sits beside and the bar's height does
   not move. Everything else about it is `.key.skirt`, unchanged. */
.strip .nav-sections{ width:28px; height:28px; border-radius:7px; }
.strip .nav-sections svg{ width:15px; height:15px; }
/* THE ROWS HAVE NO ICON COLUMN. A menu of twelve headings is a list of NAMES —
   a mark beside each one would have to be invented per section, and §02 has
   the rule for that: an arbitrary glyph is worse than none. The number keeps
   the chip slot on the right, where a shortcut goes, because on a page you
   know it is the number you aim at. */
.nav-sections-menu{ min-width:248px; max-height:min(70vh, 640px); overflow-y:auto; }
.nav-sections-menu .menu-row{ gap:14px; }

@media (prefers-reduced-motion: reduce){
  .navdock > .strip{ transition:none; }
}

/* Under 620px the version window and the channel go, because the destinations
   are the load-bearing part.

   THE WORDMARK STAYS, AND IT DID NOT USED TO. It went with them, and that was
   correct for exactly as long as HOME was a key in the bar — the bar carried
   the way home and the mark was decoration. It is the way home now, so hiding
   it on a phone leaves a narrow screen with no route back to the index at all.
   The arithmetic works out better than before, too: dropping the HOME key
   freed about seventy pixels and the mark costs about sixty, so the bar has
   more room at 375px than it had when the mark was hidden. */
@media (max-width:620px){
  .navdock{ padding:0 12px; }
  .nav-ver, .navdock .chan{ display:none; }
  .nav-mark{ font-size:11px; letter-spacing:.14em; padding:0 6px 0 2px; }
  .nav-link{ padding:0 11px; }
}

/* ── THE SKIP LINK. A nav-heavy site puts destinations in front of the content
      on every page, and a keyboard has to tab through all of them each time.
      Off screen until focused, then it lands on the metal like anything else. */
.skip{
  position:absolute; left:-9999px; top:0; z-index:60;
  padding:9px 14px; border-radius:0 0 8px 0; text-decoration:none;
  font-family:var(--font); font-size:10px; letter-spacing:.14em;
  color:var(--led); background:linear-gradient(180deg,#26282d,#161719);
  box-shadow:0 6px 14px rgba(0,0,0,.8), inset 0 1px 0 rgba(255,255,255,.14);
}
.skip:focus{ left:0; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE SPEC TABLE — reference rows, not prose
   ═══════════════════════════════════════════════════════════════════════════ */
/* ═══════════════════════════════════════════════════════════════════════════
   THE FOLDED DETAIL — a spec table behind a chevron

   Doc-only: the excluded list in tools/build-dist.mjs names these, because a
   disclosure around a reference table is how this SITE reads, not a part of
   the language an app adopts.

   THE SUMMARY IS A MILLED STRIP rather than a key. It is not a control that
   does something to the machine, it is a lid — so it gets the well's two
   hairlines and no travel, and the only thing that moves is the chevron.
   ═══════════════════════════════════════════════════════════════════════════ */
.detail{ margin:18px 0 26px; }

.detail-tab{
  display:flex; align-items:center; gap:10px;
  padding:7px 11px 7px 8px; border-radius:9px; cursor:pointer;
  list-style:none;                       /* the default triangle, gone */
  background:linear-gradient(180deg,#17191d,#1e2126);
  box-shadow:inset 0 3px 7px rgba(0,0,0,.85), inset 0 -1px 0 rgba(255,255,255,.05),
             0 1px 0 rgba(255,255,255,.055);
  transition:color .12s;
}
.detail-tab::-webkit-details-marker{ display:none; }
.detail-tab:hover .detail-nm{ color:#aeb4be; }

.detail-nm{
  font-family:var(--font); font-size:8.5px; letter-spacing:.16em; text-transform:uppercase;
  color:#6b7079; text-shadow:0 1px 0 rgba(255,255,255,.07); transition:color .12s;
}
/* THE COUNT IS A READOUT, because it is a number the page works out. It also
   answers the only question a shut lid raises: how much is under it. */
.detail-n{
  margin-left:auto; font-family:var(--font-mono, var(--font)); font-size:8.5px;
  color:var(--lcd, #4CE04C); opacity:.55; font-variant-numeric:tabular-nums;
}

/* THE CHEVRON TURNS, and it is the same 90 degrees a layer row's does — same
   class, same knurl, same lamp, so a lid and a row open the same way. */
.detail[open] .detail-tab .chev{ transform:rotate(90deg) translateY(1px); color:var(--led); }
.detail[open] .detail-nm{ color:#aeb4be; }
.detail .spec{ margin-top:2px; }

@media (prefers-reduced-motion: reduce){
  .detail-tab, .detail-tab .chev{ transition:none; }
}

.spec{
  width:100%; border-collapse:collapse; margin-top:26px;
  font-size:13px; line-height:1.6;
}
.spec th{
  text-align:left; padding:9px 12px; border-bottom:1px solid #212327;
  font-family:var(--font); font-size:9px; letter-spacing:.16em; text-transform:uppercase;
  color:#5a5f68; font-weight:600;
}
.spec td{ padding:11px 12px; border-bottom:1px solid #17191c; color:#8b909a; vertical-align:top; }
.spec tr:last-child td{ border-bottom:0; }
.spec td:first-child{
  width:24%; min-width:150px; color:#c6cad1;
  font-family:var(--font); font-size:10px; letter-spacing:.1em; text-transform:uppercase;
}
.spec td b{ color:#c6cad1; font-weight:600; }
.spec code{
  font-family:var(--font); font-size:11.5px; color:#a9b0ba;
  background:#121417; padding:1.5px 5px; border-radius:4px;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.7);
}
/* the rows a dev gets wrong. Amber, because it is a caution and not a failure:
   every one of them is code that runs and then behaves oddly six weeks later. */
.spec tr.trap td{ background:rgba(255,196,0,.045); }
.spec tr.trap td:first-child{ color:#E3B45E; box-shadow:inset 2px 0 0 rgba(255,196,0,.5); }

@media (max-width:620px){
  .spec td:first-child{ width:auto; min-width:0; }
  .spec, .spec td{ font-size:12px; }
}

/* a token swatch, inline in the first cell of a token row */
.tok{ display:inline-block; width:13px; height:13px; border-radius:3px; margin-right:7px;
      vertical-align:-2px; box-shadow:inset 0 1px 0 rgba(255,255,255,.18),
      inset 0 -1px 0 rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.6); }


}   /* @layer skew */
