/* =============================================================================
   domainchaos.com — the paper edition
   -----------------------------------------------------------------------------
   The previous build was a single 30KB file: a dark console, one orange, a
   glow on every edge, and every colour in it a literal. This is its opposite
   and deliberately so, in the register labs.llc uses — white paper, one ink,
   a great deal of air, and type doing the work the glow used to do.

   The whole system is five decisions:

     ink      #1E262B   near-black with the blue left in, never pure #000
     paper    #FFFFFF
     deep     #11161C   the dark band, one step colder than the ink
     line     a 12% ink hairline; there are no heavy borders anywhere
     measure  1265px

   This sheet is the LIGHT edition and the canonical one. The dark edition is
   assets/css/dark.css: an override sheet scoped to html[data-dark], loaded
   after this one, holding nothing but the literals this file could not express
   as tokens. Nothing in this file may be edited to serve the dark edition —
   that is what the override sheet is for, and keeping that boundary is what
   stops the light edition regressing every time the dark one is touched.

   THE ONE RULE when adding to this file: prefer a token. A declaration that
   reads var(--ink) or var(--line) is already correct in both editions and
   needs no second line anywhere. Only a literal has to be answered twice, and
   the literals that cost the most are the ones where a colour is a SURFACE
   rather than text — a band, a panel, a field. Those are the ones the audit in
   tools/audit.js counts.
   ========================================================================== */

/* ------------------------------------------------------------------ webfont
   DM Sans, self-hosted, the same two files labs.llc serves. It used to be an
   @import of fonts.googleapis.com sitting at the top of an inline <style> —
   which is the worst of every option: a render-blocking stylesheet, on a
   third-party origin, discovered only after the HTML parser reached the middle
   of the document, in a page that otherwise made no network request at all
   before first paint.

   Variable across both axes the sheet uses (opsz 9-40, wght 200-700), so the
   four-weight inversion the design rests on is 94KB of woff2 rather than
   twelve separate faces. Split on the same unicode ranges Google splits on, so
   a page of English never downloads the accented block. */
/* ORBITRON — the squared instrument face the flight-deck dials are set in.
   SIL Open Font License 1.1, so it ships with the site rather than being
   fetched: an 11KB latin subset, variable 400-900, the same one Google serves
   and the same unicode range DM Sans is split on. Self-hosted for the same
   reason DM Sans is — a dial that draws its numerals from a canvas cannot wait
   on a third-party stylesheet, and the CSP does not have to be widened for a
   file that is already here. app.js asks document.fonts for it before the
   first dial is drawn; see the note there. */
@font-face {
    font-family: 'Orbitron';
    font-style: normal;
    font-weight: 400 900;
    font-display: swap;
    src: url('../fonts/orbitron-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'DM Sans';
    font-style: normal;
    font-weight: 200 700;
    font-display: swap;
    src: url('../fonts/dm-sans-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'DM Sans';
    font-style: normal;
    font-weight: 200 700;
    font-display: swap;
    src: url('../fonts/dm-sans-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                   U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                   U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
    --ink:        #1E262B;
    --ink-77:     rgba(30, 38, 43, .77);
    /* The secondary ink, at .64 rather than .55. .55 measures 3.6:1 on the
       paper, under the 4.5:1 body-text floor, and this token carries most of
       the small type on the site: every eyebrow, every field label, every
       registrar name, the whole of the results table's second line. At .64 it
       measures 4.7:1 and is still plainly the quieter of the two inks. */
    --ink-55:     rgba(30, 38, 43, .64);
    --paper:      #ffffff;
    --tint:       #FAFAFA;  /* recessed panel ground, added for Network Labs */
    --deep:       #11161C;
    --deep-soft:  #171E26;
    --line:       rgba(30, 38, 43, .12);
    --line-soft:  rgba(30, 38, 43, .07);
    /* The inverse hairline: for rules drawn ON the deep band while the page
       itself is light. The dark edition has to flip this one the other way or
       those rules draw white on white. */
    --line-dark:  rgba(255, 255, 255, .13);
    --muted:      #717171;

    /* DomainChaos was #ff8800 throughout — a screen orange, chosen against
       #0a0a0a. It measures 2.1:1 on white, so on paper it is not an accent, it
       is a highlighter. The lineage survives as the burnt print red of the
       same family; the dark edition takes it back up to #F97316, which is the
       original orange one step calmer and holds on the deep ground. Used for
       hairlines, eyebrows and live indicators only — never as a large fill. */
    --accent:     #C2410C;
    /* The accent as a FILL, with dark type on it. Inking a colour is right for
       type and wrong for a solid block, so the two are separate tokens and the
       fill keeps its saturation. */
    --accent-fill: #FB923C;
    --on-accent:  #2A1206;

    /* ------------------------------------------------------------- semantics
       The status of a domain is the one place this site needs colour to carry
       meaning, so these four are the only hues besides the accent.

       Every one of them is three steps down its scale from where a dark-ground
       palette would put it. #00cc66 — the old build's "AVAILABLE" — measures
       1.8:1 on white; at #15803D it is 4.9:1 and still unmistakably green. The
       dark edition moves all four back up; see dark.css. */
    --free:       #15803D;   /* available            */
    --taken:      #B91C1C;   /* registered           */
    --unsure:     #B45309;   /* could not determine  */
    --info:       #1D4ED8;   /* links, social        */
    --free-wash:   rgba(21, 128, 61, .07);
    --taken-wash:  rgba(185, 28, 28, .06);
    --unsure-wash: rgba(180, 83, 9, .07);

    --measure:    1265px;
    --gutter:     clamp(20px, 5vw, 56px);

    /* The height of the sticky header, published as a token because two things
       have to agree about it: the header, and every anchor target on the page.
       A heading scrolled to the top of the viewport lands underneath a sticky
       bar unless something subtracts the bar first. */
    --nav-h:      74px;
    --band:       clamp(56px, 6.4vw, 96px);
    --ease:       cubic-bezier(.22, .61, .36, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Applied to anything with an id rather than to a hand-kept list of sections:
   a scroll margin costs nothing on an element nobody ever scrolls to, and a
   list is a thing to forget to update. */
[id] { scroll-margin-top: calc(var(--nav-h) + 14px); }

body {
    margin: 0;
    background: var(--paper);
    color: var(--ink-77);
    font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
                 Helvetica, Arial, sans-serif;
    font-size: 17px;
    line-height: 1.62;
    /* R4: the floor, by inheritance. Set the base readable and thin only what
       is deliberately large. The opposite order leaks — anything with no class
       of its own inherits the thin weight from a wrapper, and you end up with
       11px labels at weight 300 that you cannot find a rule for. */
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4 { color: var(--ink); margin: 0; font-weight: 600; }

.d1 {
    font-size: clamp(2.1rem, 5.2vw, 3.9rem);
    font-weight: 200; letter-spacing: -.035em; line-height: 1.09;
}
.d2 {
    font-size: clamp(1.75rem, 3.5vw, 2.77rem);
    font-weight: 600; letter-spacing: -.04em; line-height: 1.06;
}
.d3 {
    font-size: clamp(1.25rem, 2vw, 1.6rem);
    font-weight: 300; letter-spacing: -.028em; line-height: 1.22;
}

/* The eyebrow. Every band opens with one; it is the only place small-caps
   tracking appears, so it reads as a system label rather than as text. 700,
   because tracking thins a face further and 500 at this size on paper is a
   smear. */
.eyebrow {
    display: flex; align-items: center; gap: .7rem;
    font-size: .688rem; font-weight: 700;
    letter-spacing: .19em; text-transform: uppercase;
    color: var(--ink-55);
    margin: 0 0 1.6rem;
}
.eyebrow::before { content: ''; width: 26px; height: 1px; background: var(--accent); flex: 0 0 auto; }
.eyebrow.on-deep { color: rgba(255, 255, 255, .62); }

.lede {
    font-size: clamp(1.02rem, 1.35vw, 1.19rem);
    line-height: 1.6; font-weight: 300; color: var(--ink-77);
    max-width: 62ch;
}
.lede + .lede { margin-top: 1rem; }

a { color: inherit; }

.wrap { max-width: var(--measure); margin: 0 auto; padding-inline: var(--gutter); }
.band { padding-block: var(--band); }
/* ------------------------------------------------------------------ the seam
   --band is a band's internal room, but what a reader sees between two
   sections is the void, and the void is not the padding. Where an unpainted
   band meets another the two paddings stack and the void is 2x--band; where
   one meets the deep band only half of it is white, because the other half is
   painted and reads as the band rather than as a gap. One uniform rule, and
   the visible white on the front page ran 101px to 262px across seven seams.

   So the seam is halved on both sides wherever two unpainted bands touch and
   left whole against a painted one. Every void is then --band, whatever it
   stands next to. The tint counts as unpainted on purpose: #FAFAFA against
   #FFFFFF is not a boundary the eye stops at. :has() is what makes the
   trailing half knowable, and where it is missing the band simply keeps the
   whole padding it shipped with.
   ------------------------------------------------------------------------ */
.band:not(.band--deep) + .band:not(.band--deep) { padding-top: calc(var(--band) * .5); }
.band:not(.band--deep):has(+ .band:not(.band--deep)) { padding-bottom: calc(var(--band) * .5); }
/* A hairline between two bands is a divider drawn IN the void, not a block
   that fills it, so the seam is still doubled and still wants halving. */
.band:not(.band--deep) + .rule + .band:not(.band--deep) { padding-top: calc(var(--band) * .5); }
.band:not(.band--deep):has(+ .rule + .band:not(.band--deep)) { padding-bottom: calc(var(--band) * .5); }

/* ---------------------------------------------------------------------------
   THE DEEP BAND

   The ink tokens are re-declared here rather than a list of `.band--deep .x`
   overrides being kept downstream. Everything on this sheet paints its
   secondary text with var(--ink-55) and its hairlines with var(--line), and
   every one of those would be near-black on a near-black ground the moment the
   component was used inside a deep band.

   --ink stays a FILL colour in a handful of places — the filled button, the
   pressed segment — so those are restated below rather than inheriting a white
   block with white type in it.
   ------------------------------------------------------------------------ */
.band--deep {
    --ink:        #FFFFFF;
    --ink-77:     rgba(255, 255, 255, .72);
    --ink-55:     rgba(255, 255, 255, .58);
    --line:       rgba(255, 255, 255, .16);
    --line-soft:  rgba(255, 255, 255, .09);
    --accent:     #F97316;
    --muted:      #8A9199;
    /* The semantic hues go back up their scale on a dark ground — the same
       three steps, in the other direction. See R6. */
    --free:       #4ADE80;
    --taken:      #F87171;
    --unsure:     #FBBF24;
    --info:       #93C5FD;

    background: var(--deep); color: var(--ink-77);
}
.band--deep h1, .band--deep h2, .band--deep h3, .band--deep h4 { color: #fff; }

/* The places --ink is a background rather than a text colour. */
.band--deep .btn                     { background: #fff; color: var(--deep); }
.band--deep .btn:hover               { background: rgba(255, 255, 255, .86); }
.band--deep .btn--ghost              { background: transparent; color: #fff; border-color: rgba(255,255,255,.38); }
.band--deep .btn--ghost:hover        { background: #fff; color: var(--deep); border-color: #fff; }
.band--deep .seg__b[aria-pressed="true"] { background: #fff; border-color: #fff; color: var(--deep); }

.band--tint { background: #FAFAFA; }
.rule { height: 1px; background: var(--line); border: 0; margin: 0; }

/* =============================================================================
   NAV
   ========================================================================== */

.nav {
    position: sticky; top: 0; z-index: 60;
    background: rgba(255, 255, 255, .86);
    backdrop-filter: saturate(180%) blur(14px);
    -webkit-backdrop-filter: saturate(180%) blur(14px);
    border-bottom: 1px solid var(--line-soft);
}
.nav__in {
    max-width: var(--measure); margin: 0 auto; padding: 0 var(--gutter);
    height: var(--nav-h); display: flex; align-items: center; gap: 1rem;
}
.nav__mark { display: flex; align-items: center; gap: .8rem; text-decoration: none; margin-right: auto; }
/* The mark. No box, no ring, no fill.

   The old build ringed a photographic disc in 4px of orange with a 45px glow;
   the rewrite before this one swapped that for a hairline and a border-radius,
   which was quieter but was still a circle drawn around a logo that already had
   one. Two edges, concentric, neither of them content.

   A wordmark on paper needs no edge at all: the letterforms are the shape, and
   the thing that separates them from the page is the page. Height is set here
   and width is left to the viewBox so the 86:24 proportion cannot be squashed
   the way an <img> with two hard attributes squashed the old one.

   The colour is inherited, not declared — currentColor in the SVG picks up
   whatever --ink resolves to in this edition, which is the entire reason it can
   be one file for both. */
.nav__logo { display: block; height: 17px; width: auto; color: var(--ink); }

/* The mark is a <div> of two links now — the labs.llc plate and the local
   wordmark — so each half re-declares the flex row the single anchor used to
   be. The plate's height is set here and its width left to the 189:80 ratio,
   for the same no-squashing reason as the wordmark above. */
.nav__mark a { display: flex; align-items: center; gap: .8rem; text-decoration: none; }
.nav__plate { display: block; height: 28px; width: auto; }
.nav__mark b {
    font-size: .95rem; font-weight: 600; letter-spacing: -.03em; color: var(--ink);
}
.nav__mark span {
    font-size: .6rem; font-weight: 700; letter-spacing: .32em;
    text-transform: uppercase; color: var(--ink-55);
    padding-left: .8rem; border-left: 1px solid var(--line);
}
@media (max-width: 980px) { .nav__mark span { display: none; } }

.nav__links { display: flex; align-items: center; gap: 1.9rem; }
@media (max-width: 1100px) { .nav__links { display: none; } }
.nav__links a {
    text-decoration: none; font-size: .875rem; font-weight: 500;
    color: var(--ink-77); letter-spacing: -.01em; position: relative; padding-block: .4rem;
}
.nav__links a::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: 0;
    height: 1px; background: var(--ink); transform: scaleX(0); transform-origin: left;
    transition: transform .32s var(--ease);
}
.nav__links a:hover::after, .nav__links a[aria-current]::after { transform: scaleX(1); }
.nav__links a[aria-current] { color: var(--ink); }

/* ---------------------------------------------------------------------------
   THE EDITION PILL

   Lifted whole from labs.llc, because it is the same control doing the same
   job and a visitor who has met one should not have to learn the other. Two
   editions of this page — this one, paper, and indexdark.html — and the pill
   is where a visitor moves between them.

   Deliberately a segmented control rather than another nav link: the two are
   alternatives to each other, not destinations alongside Generator and
   Registrars, and a segmented control is the one shape that says so without a
   word of explanation.

   Exactly one segment carries .is-on, and it is written into the markup rather
   than worked out at runtime, because on any given page the answer is a
   constant: this file is always the light one. tools/mkdark.py moves it.

   The moving part is a single thumb behind both segments, not a background on
   whichever segment is lit. That is what lets it slide: on hover it travels to
   the segment under the cursor and previews the switch before the click.
   ------------------------------------------------------------------------ */
.mode-pill {
    position: relative; flex: 0 0 auto;
    display: inline-flex; align-items: center;
    padding: 3px; border-radius: 999px;
    border: 1px solid var(--line);
    /* A shallow well, so the thumb reads as sitting in the track rather than
       floating on the navbar. Both values are ink at single digits of alpha. */
    background: linear-gradient(180deg, rgba(30,38,43,.045), rgba(30,38,43,.025));
    box-shadow: inset 0 1px 2px rgba(30, 38, 43, .055);
}
.mode-pill__thumb {
    position: absolute; z-index: 0;
    top: 3px; bottom: 3px; left: 3px; width: calc(50% - 3px);
    border-radius: 999px; background: var(--ink);
    box-shadow: 0 1px 2px rgba(30, 38, 43, .26), 0 6px 18px rgba(30, 38, 43, .14);
    transition: transform .42s var(--ease);
}
/* Which half is lit. The class is on the container and written into the
   markup: on any one page the answer never changes. */
.mode-pill--dark .mode-pill__thumb { transform: translateX(100%); }

/* Hover preview. There are exactly two segments, so "the unlit one is hovered"
   already says which side the thumb belongs on and the rule needs no position
   selector — which matters, because the lit segment is a <span> and the unlit
   one an <a>, and a :first-of-type / :last-of-type pair counts types. Where
   :has is unsupported the pill simply does not preview, which costs nothing. */
.mode-pill:has(.mode-pill__seg:not(.is-on):hover) .mode-pill__thumb { transform: translateX(100%); }
.mode-pill--dark:has(.mode-pill__seg:not(.is-on):hover) .mode-pill__thumb { transform: none; }

.mode-pill__seg {
    position: relative; z-index: 1;
    display: inline-flex; align-items: center; justify-content: center;
    /* Both segments hold the same width, because the thumb is half the track
       and an uneven split would leave it short of one end. */
    min-width: 2.5rem; padding: .4rem .45rem; border-radius: 999px;
    text-decoration: none; color: var(--ink-55);
    transition: color .32s var(--ease);
}
/* The lit segment is a <span>, not a link — it is the page you are already on,
   and a link to here is a reload that costs a round trip and returns the same
   document. */
.mode-pill__seg.is-on { color: var(--paper); cursor: default; }
/* The fallback where :has is missing and the thumb therefore cannot move: the
   unlit segment darkens in place. It must not go to paper — with no thumb
   underneath, paper on the track is a label that vanishes. */
.mode-pill__seg:not(.is-on):hover { color: var(--ink); }
/* While a segment is hovered the thumb is under IT, so the lit colour follows
   the thumb rather than the class. Equal specificity, later wins. */
.mode-pill:has(.mode-pill__seg:hover) .mode-pill__seg.is-on { color: var(--ink-55); }
.mode-pill:has(.mode-pill__seg:hover) .mode-pill__seg:hover { color: var(--paper); }
.mode-pill__seg:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* 13px, and the same 13 is written on the <svg> tag itself as width and
   height. That duplication is the point: an SVG with a viewBox and no
   dimensions of its own falls back to 300x150 when no stylesheet reaches it. */
.mode-pill__i { width: 13px; height: 13px; flex: 0 0 auto; display: block; }

@media (prefers-reduced-motion: reduce) { .mode-pill__thumb { transition: none; } }

/* The hamburger. At every width — it opens the whole menu rather than a fold
   of the header, so on a wide screen the navbar's section links and the menu's
   tools are two different offers rather than the same one twice. */
.nav__burger {
    display: flex; width: 40px; height: 40px; padding: 0;
    background: transparent; border: 1px solid var(--line); border-radius: 8px;
    cursor: pointer; align-items: center; justify-content: center;
    color: var(--ink);
    transition: border-color .24s var(--ease);
}
.nav__burger:hover { border-color: var(--ink); }
.nav__burger svg { display: block; }
/* transform-box: view-box puts the origin in the icon's own coordinate space
   rather than on the line's own zero-height box, and the translate has to come
   second — CSS applies transform functions right to left. */
.nav__burger line {
    transform-box: view-box; transform-origin: 9px 6px;
    transition: transform .28s var(--ease);
}
.nav__burger[aria-expanded="true"] .nav__burger-t { transform: rotate(45deg) translateY(2.5px); }
.nav__burger[aria-expanded="true"] .nav__burger-b { transform: rotate(-45deg) translateY(-2.5px); }
@media (prefers-reduced-motion: reduce) { .nav__burger line { transition: none; } }

/* THE HEADER ON A PHONE. Three things have to fit inside 335 usable pixels at
   375 and 280 at 320: the mark, the edition pill, and the way into the rest of
   the site. Everything here is measured against those two numbers. */
@media (max-width: 1100px) { .nav__in { gap: .9rem; } }
@media (max-width: 640px) {
    :root { --nav-h: 62px; }
    .nav__in { gap: .55rem; }
    .nav__mark b { font-size: .86rem; }
    /* A segment is a glyph in a box, and the box is the tap target. The glyph
       stays at 13px — it is meant to be small — and what grows is the box. */
    .mode-pill__seg { min-width: 1.9rem; padding: .58rem .4rem; }
    /* No ring on a phone: the burger is the only thing in that corner and the
       outline was the heaviest object in the bar for no work it was doing.
       44px is the smallest a finger reliably hits. */
    .nav__burger { width: 44px; height: 44px; border-color: transparent; }
    .nav__burger:hover { border-color: transparent; }
    .nav__in > .btn { display: none; }
}
@media (max-width: 380px) { .nav__mark b { display: none; } }

/* =============================================================================
   THE MENU
   ========================================================================== */
.menu[hidden] { display: none; }
.menu {
    position: fixed; inset: 0; z-index: 200; display: block;
    /* Clipped: on the way out the scroll lock is released the instant close()
       runs while the panel is still travelling, and an unclipped panel sitting
       outside the viewport for those 360ms is a horizontal scrollbar that
       appears and leaves for no visible reason. */
    overflow: hidden;
}
.menu__scrim {
    position: absolute; inset: 0;
    /* A scrim exists to dim what is behind it, so it stays dark in both
       editions — see R2. It is the one black wash on this sheet that the dark
       edition does not re-polarise. */
    background: rgba(17, 22, 28, .38);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    opacity: 0; transition: opacity .32s var(--ease);
}
.menu.is-open .menu__scrim { opacity: 1; }
.menu__panel {
    position: absolute; top: 0; right: 0; height: 100%;
    width: 100%; max-width: 404px;
    background: var(--paper);
    border-left: 1px solid var(--line);
    box-shadow: -24px 0 60px rgba(30, 38, 43, .10);
    display: flex; flex-direction: column;
    transform: translateX(100%);
    transition: transform .36s var(--ease);
}
.menu.is-open .menu__panel { transform: none; }
/* The panel takes focus itself on open so a screen reader reads the dialog's
   label. It is a container, not a control, and the sheet-wide :focus-visible
   rule was drawing a 2px accent ring around all 404 pixels of it. */
.menu__panel:focus, .menu__panel:focus-visible { outline: none; }
@media (prefers-reduced-motion: reduce) { .menu__scrim, .menu__panel { transition: none; } }

.menu__head {
    flex: 0 0 auto; height: 74px;
    display: flex; align-items: center; gap: .8rem;
    padding: 0 1.15rem 0 1.4rem;
    border-bottom: 1px solid var(--line-soft);
}
.menu__logo { display: block; height: 24px; width: auto; }
.menu__title {
    font-size: .6rem; font-weight: 700; letter-spacing: .3em; text-transform: uppercase;
    color: var(--ink-55); padding-left: .8rem; border-left: 1px solid var(--line);
    margin-right: auto;
}
.menu__x {
    width: 34px; height: 34px; flex: 0 0 auto; padding: 0;
    display: flex; align-items: center; justify-content: center;
    background: transparent; border: 1px solid var(--line); border-radius: 8px;
    color: var(--ink-55); cursor: pointer;
    transition: border-color .24s var(--ease), color .24s var(--ease);
}
.menu__x:hover { border-color: var(--ink); color: var(--ink); }
.menu__x svg { width: 13px; height: 13px; }
@media (max-width: 640px) { .menu__x { width: 44px; height: 44px; } }

.menu__search {
    flex: 0 0 auto; position: relative;
    padding: .9rem 1.4rem;
    border-bottom: 1px solid var(--line-soft);
    background: var(--paper);
}
.menu__search svg {
    position: absolute; left: 2.05rem; top: 50%; transform: translateY(-50%);
    width: 13px; height: 13px; color: var(--ink-55); pointer-events: none;
}
.menu__q {
    font: inherit; font-size: .88rem; font-weight: 500; width: 100%;
    padding: .6rem 2.1rem .6rem 2.05rem;
    color: var(--ink); background: rgba(30, 38, 43, .035);
    /* --line is a rule between two things that are already there; a text input
       is a box that has to announce itself, so it gets a real border. */
    border: 1px solid rgba(30, 38, 43, .52); border-radius: 8px;
    transition: border-color .24s var(--ease), background .24s var(--ease);
    -webkit-appearance: none; appearance: none;
}
.menu__q::placeholder { color: var(--ink-55); font-weight: 400; }
.menu__q::-webkit-search-cancel-button { display: none; }
.menu__q:focus { outline: none; border-color: var(--ink); background: var(--paper); }
.menu__q:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Safari on iOS zooms the page when a focused input is under 16px, and a zoomed
   page carries the 404px panel — and the field that was just tapped — off the
   visual viewport. 16px is the threshold at which it does not, so the field
   takes it wherever the zoom can happen. Matched on the pointer rather than on
   a width: a phone held sideways is 932px wide and still zooms. */
@media (hover: none) and (pointer: coarse) {
    .menu__q { font-size: 16px; }
}
.menu__clear {
    position: absolute; right: 2rem; top: 50%; transform: translateY(-50%);
    width: 22px; height: 22px; padding: 0; display: flex;
    align-items: center; justify-content: center;
    background: transparent; border: 0; border-radius: 6px;
    color: var(--ink-55); cursor: pointer;
}
.menu__clear[hidden] { display: none; }
.menu__clear:hover { color: var(--ink); background: rgba(30, 38, 43, .06); }
.menu__clear svg { width: 10px; height: 10px; }

.menu__count {
    flex: 0 0 auto; margin: 0; padding: .55rem 1.4rem 0;
    font-size: .62rem; font-weight: 700; letter-spacing: .18em;
    text-transform: uppercase; color: var(--ink-55);
}
.menu__count b { color: var(--ink); font-weight: 700; }
.menu__count[data-filtered="1"] b { color: var(--accent); }

.menu__body { flex: 1 1 auto; overflow-y: auto; padding: 1.1rem 1.4rem 1.6rem; }
.menu__sec + .menu__sec { margin-top: 1.7rem; }
.menu__sec[hidden] { display: none; }
.menu__h {
    display: flex; align-items: center; gap: .8rem;
    font-size: .6rem; font-weight: 700; letter-spacing: .22em; text-transform: uppercase;
    color: var(--ink-55); margin: 0 0 .5rem;
}
.menu__h::after { content: ''; height: 1px; background: var(--line); flex: 1 1 auto; }
.menu__row {
    display: flex; align-items: baseline; gap: .7rem;
    padding: .62rem 0; border-bottom: 1px solid var(--line-soft);
    text-decoration: none; color: var(--ink-77);
    transition: padding-left .2s var(--ease), color .2s var(--ease);
}
.menu__row:hover { color: var(--ink); padding-left: .5rem; }
.menu__row[hidden] { display: none; }
.menu__row:last-child { border-bottom: 0; }
.menu__l { flex: 1 1 auto; min-width: 0; }
.menu__l b { display: block; font-size: .9rem; font-weight: 600; letter-spacing: -.015em; color: var(--ink); }
.menu__l span { display: block; font-size: .74rem; font-weight: 400; color: var(--ink-55); line-height: 1.4; }
.menu__go { flex: 0 0 auto; color: var(--ink-55); font-size: .8rem; font-style: normal; }
.menu__empty { margin: 1.4rem 0 0; text-align: center; font-size: .82rem; color: var(--ink-55); }
.menu__empty[hidden] { display: none; }

.menu__foot {
    flex: 0 0 auto; padding: 1rem 1.4rem;
    border-top: 1px solid var(--line-soft); background: var(--paper);
    display: flex; justify-content: center;
}
/* The footer's copy of the pill is the same control, so it is the same size: a
   segmented pill held to its own width and centred, one step larger than the
   header's because a panel footer has the room. Stretched to the full width of
   the panel it reads as a broken button rather than as the pill above. */
.menu__foot .mode-pill { display: inline-flex; width: auto; }
.menu__foot .mode-pill__seg { flex: 0 0 auto; min-width: 3.6rem; padding: .6rem .8rem; }
@media (max-width: 460px) { .menu__panel { max-width: 100%; } }

/* =============================================================================
   BUTTONS
   ========================================================================== */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
    font: inherit; font-size: .82rem; font-weight: 600; letter-spacing: .02em;
    padding: .72rem 1.15rem; border-radius: 8px;
    border: 1px solid transparent; background: var(--ink); color: #fff;
    text-decoration: none; cursor: pointer;
    transition: background .25s var(--ease), color .25s var(--ease),
                border-color .25s var(--ease), transform .25s var(--ease);
}
.btn:hover { background: #000; transform: translateY(-1px); }
.btn:active { transform: none; }
.btn[disabled] { opacity: .45; cursor: not-allowed; transform: none; }
.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn--ghost:hover { background: var(--ink); color: #fff; border-color: var(--ink); }
.btn--sm { font-size: .72rem; padding: .5rem .8rem; }
.btn--outline {
    background: transparent; color: #fff; border-color: rgba(255, 255, 255, .45);
    text-transform: uppercase; font-size: .72rem; letter-spacing: .13em; font-weight: 600;
    padding: .82rem 1.35rem;
}
.btn--outline:hover { background: #fff; color: var(--ink); border-color: #fff; }
.btn i { transition: transform .25s var(--ease); font-style: normal; }
.btn:hover i { transform: translateX(3px); }

/* =============================================================================
   HERO
   ========================================================================== */
.hero { padding-block: clamp(52px, 8vw, 104px) clamp(40px, 6vw, 72px); }
.hero__in { max-width: 46rem; }
.hero h1 { margin-bottom: 1.5rem; }
.hero__act { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: 2rem; }
/* The three counts under the hero. Not a statistic each — a description of
   what the tool has in it, which is the only claim this page can make without
   a server behind it. */
.hero__facts {
    display: flex; flex-wrap: wrap; gap: 0 3rem;
    margin-top: 2.6rem; padding-top: 1.6rem; border-top: 1px solid var(--line);
}
.hero__fact b {
    display: block; font-size: 1.7rem; font-weight: 200; letter-spacing: -.04em;
    color: var(--ink); line-height: 1.1;
}
.hero__fact span {
    display: block; font-size: .625rem; font-weight: 700; letter-spacing: .2em;
    text-transform: uppercase; color: var(--ink-55); margin-top: .3rem;
}

/* =============================================================================
   THE CONSOLE
   -----------------------------------------------------------------------------
   The generator, its controls and its results. Everything here was a literal
   in the old file — #0f0f0f, #1a1a1a, #222, #0c0c0c, five greys inside twenty
   pixels of each other — and the whole of that is now three surfaces:

     --sur-1   the panel               a 2.5% ink wash
     --sur-2   a field inside a panel  a 4% ink wash + a real border
     --sur-3   a result row            paper, held by a hairline

   R2: on the dark ground those become white lifts, and the numbers do not
   transfer — 5% white on black reads far stronger than 5% black on white. The
   answers are in dark.css and they are not the same alphas.
   ========================================================================== */
.console {
    --sur-1: rgba(30, 38, 43, .025);
    --sur-2: rgba(30, 38, 43, .04);
    border: 1px solid var(--line); border-radius: 14px;
    background: var(--sur-1);
    overflow: hidden;
}
/* BOTH CONSOLE HEADS ARE ONE HEIGHT, and it is the status strip's. The stage's
   head was rebuilt to 36px and carry six readings; leaving the deck's at 54 to
   say "nothing loaded" put two different bars on one page claiming to be the
   same piece of furniture. */
.console__head {
    display: flex; align-items: center; gap: .8rem;
    padding: 0 1.2rem; height: 2.25rem;
    border-bottom: 1px solid var(--line);
    background: var(--paper);
}
@media (max-width: 620px) {
    .console__head { height: auto; min-height: 2.25rem; padding: .35rem .9rem; flex-wrap: wrap; }
}
.console__title {
    font-size: .625rem; font-weight: 700; letter-spacing: .2em;
    text-transform: uppercase; color: var(--ink-55); margin: 0 auto 0 0;
    display: flex; align-items: center; gap: .55rem;
}
/* The live dot. The one place the accent is allowed to be a fill, and it is
   6 pixels across. */
.console__dot {
    width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
    flex: 0 0 auto;
}
/* ============================================== THE STATUS STRIP =========== *
   SHORTER, AND IT EARNS THE ROOM. The head was .9rem of padding around a single
   sentence — 54px of console saying "playing your file". It is the one line
   that can report the whole desk at a glance, so it reports it: source, track,
   rate, what is drawing, how hard it is being driven, and what is queued
   behind. Each segment is a micro-label over a value, hairline-separated, and
   HIDES ITSELF when it has nothing to say — a strip padded out with dashes is
   worse than a short one.
   ========================================================================== */
/* .vz-sbar, NOT .vz-bar — .vz-bar is already the meter's 4px level bar in this
   same sheet, and reusing the name handed this strip `display: block` and a
   four-pixel height. The collision is silent: nothing errors, the bar just
   collapses and looks like a padding bug. */
/* SIZED BY ITS CONTENT, with no minimum. The tallest thing in it is a 20px
   segment, so .4rem of padding either side is a 36px strip against the 54px
   the head used to take for one sentence — and it now carries six readings
   instead. A min-height would only put the air back. */
/* A DEFINITE HEIGHT, because the content will not settle on one. The head took
   54px to say one sentence; this says six things in 36 and centres them, and a
   fixed height means adding a seventh reading later cannot quietly grow the
   strip back. On a phone it releases and wraps instead of clipping. */
.vz-sbar {
    padding: 0 1.2rem; height: 2.25rem; gap: 0 1rem;
    align-items: center; flex-wrap: nowrap; overflow: hidden;
}
.vz-sbar .console__title { margin-right: .2rem; }
.vz-sbar__seg {
    display: flex; align-items: baseline; gap: .4rem; min-width: 0;
    padding-left: 1rem; border-left: 1px solid var(--line);
}
.vz-sbar__seg[hidden] { display: none; }
.vz-sbar__seg i {
    font-style: normal; font-size: .54rem; font-weight: 700;
    letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
    flex: 0 0 auto;
}
.vz-sbar__seg b {
    font-size: .76rem; font-weight: 500; color: var(--ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
/* the track name is the one thing that can be long, so it is the one thing
   allowed to take the slack and then ellipsis */
.vz-sbar__seg--grow { flex: 1 1 auto; min-width: 4rem; }
.vz-sbar__seg--next b { color: var(--accent); }
.vz-sbar .cw-stat { margin-left: auto; font-size: .7rem; flex: 0 0 auto; }
/* the dot is the transport: lit while something is actually running */
#vz-dot { background: var(--line); transition: background .2s ease, box-shadow .2s ease; }
#vz-dot[data-live] {
    background: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
@media (max-width: 900px) {
    .vz-sbar__seg--grow { flex-basis: 100%; padding-left: 0; border-left: 0; }
}
@media (max-width: 620px) {
    .vz-sbar { padding: .35rem .9rem; height: auto; flex-wrap: wrap; gap: .25rem .7rem; overflow: visible; }
    .vz-sbar__seg { padding-left: .7rem; }
    .vz-sbar .cw-stat { flex-basis: 100%; margin-left: 0; }
}

.console__body { --cb-pad: 1.2rem; padding: var(--cb-pad); display: grid; gap: 1.15rem; }
@media (max-width: 620px) { .console__body { --cb-pad: .9rem; } }

/* -------------------------------------------------------------- the fields */
.field-row { display: flex; gap: .55rem; align-items: stretch; flex-wrap: wrap; }
.field-row > .field { flex: 1 1 15rem; min-width: 0; }
.field {
    font: inherit; font-size: .95rem; font-weight: 500;
    padding: .74rem .9rem; border-radius: 9px;
    color: var(--ink); background: var(--paper);
    /* A real border, not a hairline: an input is a box that has to announce
       itself, and --line at 12% on a white panel is an input you cannot find. */
    border: 1px solid rgba(30, 38, 43, .5);
    -webkit-appearance: none; appearance: none;
    transition: border-color .22s var(--ease), box-shadow .22s var(--ease);
}
.field::placeholder { color: var(--ink-55); font-weight: 400; }
.field:focus { outline: none; border-color: var(--ink); box-shadow: 0 0 0 3px rgba(30, 38, 43, .07); }
.field:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.field[aria-invalid="true"] { border-color: var(--taken); }

/* The pasted list. A block field rather than a row, because what goes in it is
   several lines and a single-line input that scrolls sideways hides everything
   but the last thing typed. Resizable vertically only: horizontal resize on a
   flex child drags the panel's whole layout with it. */
.field--area {
    display: block; width: 100%; resize: vertical; min-height: 5.5rem;
    line-height: 1.55; font-size: .9rem;
    /* A list of domains is columnar data and reads as such — and a monospace
       face is also what makes a stray character in a pasted line visible. */
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
/* The button under a full-width field, pushed to the end of the row so it sits
   under the field's trailing edge rather than floating at its start. */
.field-row--end { justify-content: flex-end; margin-top: .55rem; }
/* No background fill here on purpose: the only surface tokens are --paper and
   --deep, and a hint sits on either depending on the band it is in, so a filled
   chip would be right in one place and a visible patch in the other. A hairline
   box needs no ground and is correct in both editions. */
.hint code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .92em; padding: .05em .35em; border-radius: 4px;
    border: 1px solid var(--line); color: var(--ink-77);
}

.lbl {
    display: block; font-size: .625rem; font-weight: 700; letter-spacing: .18em;
    text-transform: uppercase; color: var(--ink-55); margin-bottom: .45rem;
}
.hint { font-size: .78rem; color: var(--ink-55); margin: .4rem 0 0; }

/* ------------------------------------------------------------- the segments
   Five TLD sets and two generators. The old build had these as ten buttons
   that turned solid orange when active; here the pressed state is the ink
   itself, which is the same shape the pill and the filters use. */
.seg { display: flex; flex-wrap: wrap; gap: .4rem; }
.seg__b {
    font: inherit; font-size: .78rem; font-weight: 600; letter-spacing: -.005em;
    padding: .5rem .85rem; border-radius: 999px;
    background: transparent; color: var(--ink-77);
    border: 1px solid var(--line); cursor: pointer;
    transition: border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
}
.seg__b:hover { border-color: var(--ink); color: var(--ink); }
.seg__b[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.seg__b small { font-weight: 500; opacity: .62; margin-left: .35rem; }

.seg--wide { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: .4rem; }
.seg--wide .seg__b { text-align: center; border-radius: 9px; }
@media (max-width: 560px) { .seg--wide { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------- the dials */
.dials { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); gap: 1.1rem; }
.dial[hidden] { display: none; }
.dial__top { display: flex; align-items: baseline; justify-content: space-between; gap: .6rem; }
.dial__val {
    font-size: .78rem; font-weight: 700; color: var(--accent);
    font-variant-numeric: tabular-nums;
}
input[type="range"] {
    width: 100%; margin: .35rem 0 0;
    accent-color: var(--ink);
    /* Firefox draws the track at its own height and ignores accent-color for
       the groove on some builds; the height keeps the two engines within a
       pixel of each other. */
    height: 22px;
}
input[type="range"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* -------------------------------------------------------------- the switches
   Real checkboxes with a real label, laid out as a row of chips. The old build
   had four bare checkboxes and four labels floating in a flex row, one of
   which ("Mobile") set a body class that made a desktop render as a phone. */
.switches { display: flex; flex-wrap: wrap; gap: .45rem; }
.sw {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .45rem .8rem .45rem .6rem; border-radius: 999px;
    border: 1px solid var(--line); cursor: pointer;
    font-size: .78rem; font-weight: 600; color: var(--ink-77);
    transition: border-color .2s var(--ease), color .2s var(--ease);
    user-select: none;
}
.sw:hover { border-color: var(--ink); color: var(--ink); }
.sw input { width: 15px; height: 15px; margin: 0; accent-color: var(--ink); flex: 0 0 auto; }
.sw:has(input:checked) { border-color: var(--ink); color: var(--ink); }
.sw:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ------------------------------------------------------------- the run row */
.run { display: flex; gap: .55rem; flex-wrap: wrap; align-items: center; }
.run .btn--go { flex: 1 1 16rem; font-size: .92rem; padding: .95rem 1.4rem; letter-spacing: .06em; text-transform: uppercase; }
.run__kbd { margin-left: auto; font-size: .72rem; color: var(--ink-55); }
kbd {
    font: inherit; font-size: .72rem; font-weight: 600;
    padding: .1rem .35rem; border: 1px solid var(--line); border-radius: 5px;
    background: var(--paper); color: var(--ink-77);
}

/* =============================================================================
   RESULTS
   ========================================================================== */
.out {
    border-top: 1px solid var(--line);
    background: var(--paper);
    padding: 1.2rem;
    min-height: 22rem;
}
@media (max-width: 620px) { .out { padding: .9rem; } }

.out__bar {
    display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
    margin-bottom: .9rem;
}
.out__n {
    font-size: .625rem; font-weight: 700; letter-spacing: .18em;
    text-transform: uppercase; color: var(--ink-55); margin: 0 auto 0 0;
    font-variant-numeric: tabular-nums;
}
.out__n b { color: var(--ink); }

/* The welcome panel: what the tool is, before it has been asked to do
   anything. It is the only long-form copy inside the console. */
.welcome { max-width: 60ch; }
.welcome h3 { font-size: 1.28rem; font-weight: 300; letter-spacing: -.03em; margin: 0 0 .8rem; }
.welcome p { margin: 0 0 .9rem; font-size: .92rem; }
.welcome ul { margin: 0 0 1rem; padding-left: 1.1rem; font-size: .92rem; }
.welcome li { margin-bottom: .35rem; }
.welcome li::marker { color: var(--accent); }
.welcome strong { color: var(--ink); font-weight: 600; }

/* -------------------------------------------------------------- a result row
   The domain is the largest type in the row and everything else is a label
   beside it. It was 1.28rem at 600 on a #1a1a1a slab with a 4px orange bar
   down the left; here the row is paper on paper, held by a hairline, and the
   status is what carries the colour. */
.dom {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center; gap: .5rem 1rem;
    padding: .85rem 1rem;
    border: 1px solid var(--line); border-radius: 10px;
    background: var(--paper);
    transition: border-color .18s var(--ease);
}
.dom + .dom { margin-top: .5rem; }
.dom:hover { border-color: rgba(30, 38, 43, .28); }
.dom[data-state="free"]    { border-left: 3px solid var(--free); }
.dom[data-state="taken"]   { border-left: 3px solid var(--taken); }
.dom[data-state="unknown"] { border-left: 3px solid var(--unsure); }
.dom[data-state="norda"]   { border-left: 3px solid var(--line); }

.dom__name {
    font-size: 1.16rem; font-weight: 600; letter-spacing: -.025em;
    color: var(--ink); overflow-wrap: anywhere; min-width: 0;
    display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
}
.dom__tld { color: var(--ink-55); font-weight: 500; }
.dom__acts { display: flex; gap: .35rem; flex-wrap: wrap; justify-content: flex-end; }

/* On a phone the two-column row fails badly rather than gracefully: the acts
   column resolves to its max-content (~230px of chip and buttons), the 1fr
   name column is left ~20px, and overflow-wrap:anywhere — right for genuinely
   long names — obliges by setting the name vertically, two letters a line.
   One column: the name owns its row and the actions sit under it. */
@media (max-width: 560px) {
    .dom { grid-template-columns: 1fr; }
    .dom__acts { justify-content: flex-start; }
}
.dom__detail { grid-column: 1 / -1; }
.dom__detail:empty { display: none; }

/* The icon buttons on a row: WHOIS, star, copy. Square, hairline, a glyph
   each — at three per row and twenty-five rows, anything with a word in it
   made the column wider than the domains. */
.ib {
    width: 32px; height: 32px; padding: 0; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: 1px solid var(--line); border-radius: 8px;
    color: var(--ink-55); cursor: pointer;
    transition: border-color .2s var(--ease), color .2s var(--ease), background .2s var(--ease);
}
.ib:hover { border-color: var(--ink); color: var(--ink); }
.ib[disabled] { opacity: .4; cursor: default; }
.ib svg { width: 14px; height: 14px; display: block; }
.ib[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); background: rgba(194, 65, 12, .06); }
/* The busy state. A spinner would be a fourth moving thing on a page that is
   already listing twenty-five rows; a pulse on the button that was pressed is
   enough and it stays where the eye already is. */
.ib[data-busy="1"] { animation: pulse 1.1s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .38; } }
@media (prefers-reduced-motion: reduce) { .ib[data-busy="1"] { animation: none; opacity: .55; } }

/* ------------------------------------------------------------- status chips
   Four states, and the fourth is the one the old build did not have. It
   reported AVAILABLE whenever a query failed — a timeout, a CORS refusal, a
   registry with no RDAP at all — which is the single most expensive thing a
   tool like this can get wrong, because the answer looks like the good news. */
.st {
    display: inline-flex; align-items: center; gap: .4rem;
    font-size: .625rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
    padding: .28rem .6rem; border-radius: 999px; white-space: nowrap;
    border: 1px solid currentColor;
}
.st::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.st--free    { color: var(--free);   background: var(--free-wash); }
.st--taken   { color: var(--taken);  background: var(--taken-wash); }
.st--unknown { color: var(--unsure); background: var(--unsure-wash); }
.st--norda   { color: var(--ink-55); background: transparent; }
.st--busy    { color: var(--ink-55); background: transparent; }
/* An ending that publishes no RDAP, where DNS says the name is not delegated.
   Deliberately NOT the green of --free: it is a narrowing, not a yes, and a row
   that looks identical to a confirmed availability is a row that will be read
   as one. The undetermined amber, filled, so it reads as a finding rather than
   as the blank that plain --norda is. */
.st--maybe   { color: var(--unsure); background: var(--unsure-wash); }

/* ---------------------------------------------------------- the detail slab
   What RDAP actually said. A definition list, because that is what it is. */
.rec {
    margin-top: .7rem; padding: .8rem .9rem;
    border: 1px solid var(--line); border-radius: 9px;
    background: rgba(30, 38, 43, .022);
    font-size: .84rem;
}
.rec dl { display: grid; grid-template-columns: auto 1fr; gap: .2rem .9rem; margin: 0; }
.rec dt {
    font-size: .58rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
    color: var(--ink-55); padding-top: .22rem;
}
.rec dd { margin: 0; color: var(--ink-77); overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.rec dd b { color: var(--ink); font-weight: 600; }
.rec__note { margin: .6rem 0 0; font-size: .78rem; color: var(--ink-55); }
@media (max-width: 480px) { .rec dl { grid-template-columns: 1fr; gap: 0 0; } .rec dt { padding-top: .5rem; } }

/* ------------------------------------------------------------- the buy row
   Five registrars. The old build gave each one its brand colour as a solid
   fill: #00cc66 with black type, #0033a0 with white, #f26522 with white at
   2.9:1. Five brand fills in a row is five accents on a page that has one, and
   half of them failed contrast in either edition.

   They are ghost buttons now, all in the ink, each carrying its brand as a
   6px dot. The colour still identifies the registrar; it is no longer trying
   to be a background for text. This is R7's "keep the raw value for fills" —
   the raw value survives, on the only surface small enough to hold it. */
.buys { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .75rem; }
.buy {
    display: inline-flex; align-items: center; gap: .45rem;
    font-size: .74rem; font-weight: 600; letter-spacing: -.005em;
    padding: .45rem .75rem; border-radius: 8px;
    border: 1px solid var(--line); color: var(--ink-77);
    text-decoration: none; white-space: nowrap;
    transition: border-color .2s var(--ease), color .2s var(--ease);
}
.buy:hover { border-color: var(--ink); color: var(--ink); }
.buy__dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto; }
.buy::after {
    content: '↗'; font-size: .68em; color: var(--ink-55); margin-left: .05rem;
}

/* ------------------------------------------------------------ social links */
.socials { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .6rem; }
.social {
    display: inline-flex; align-items: center; gap: .35rem;
    font-size: .74rem; font-weight: 600;
    padding: .38rem .7rem; border-radius: 8px;
    border: 1px solid var(--line); color: var(--info);
    text-decoration: none; white-space: nowrap;
}
.social:hover { border-color: currentColor; }

/* The announcement cards live in their own sheet — assets/css/toast.css, the
   LAB DMT system — because they are a component this page borrows rather than
   one it owns, and keeping them separate is what lets the next page take them
   without taking the console with them. The one rule this sheet has about them
   is where they sit in the stack: above the nav (60) and the menu (200). */

/* The TOAST:ON/OFF switch in the console head. The engine owns its label and
   its state; this is only what it looks like sitting in the bar. */
.tbtn {
    font: inherit; font-size: .58rem; font-weight: 700; letter-spacing: .14em;
    padding: .34rem .6rem; border-radius: 999px;
    background: transparent; color: var(--ink-55);
    border: 1px solid var(--line); cursor: pointer;
    transition: border-color .2s var(--ease), color .2s var(--ease);
}
.tbtn:hover { border-color: var(--ink); color: var(--ink); }
.tbtn.on { color: var(--accent); border-color: var(--accent); }

/* --------------------------------------------------------------- compact
   What replaces the old build's "Mobile" checkbox — which was on by default
   and made a 27-inch display render at phone padding, because it was a
   preference standing in for a media query.

   Width is the media query's job and it is done above. This is the other
   thing that checkbox was half-doing and never said: density. It is a real
   preference — some people want twenty-five rows on one screen — so it is
   off by default, remembered, and it changes nothing but spacing. */
.is-compact .out { padding: .7rem; }
.is-compact .dom { padding: .5rem .7rem; }
.is-compact .dom + .dom { margin-top: .3rem; }
.is-compact .dom__name { font-size: 1rem; }
.is-compact .console__body { gap: .8rem; padding: .9rem; }
.is-compact .rec { padding: .6rem .7rem; font-size: .8rem; }

/* =============================================================================
   THE REGISTER — the TLD sets, as a table of what is in each one
   ========================================================================== */
.reg { border-top: 1px solid var(--line); }
.reg__row {
    display: grid; grid-template-columns: 13rem 1fr auto;
    gap: .4rem 1.6rem; align-items: baseline;
    padding: 1.1rem 0; border-bottom: 1px solid var(--line);
}
@media (max-width: 760px) { .reg__row { grid-template-columns: 1fr; } }
.reg__k { font-size: 1.05rem; font-weight: 600; letter-spacing: -.025em; color: var(--ink); }
.reg__v { font-size: .88rem; color: var(--ink-55); overflow-wrap: anywhere; }
.reg__n {
    font-size: .625rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
    color: var(--ink-55); white-space: nowrap; font-variant-numeric: tabular-nums;
}

/* =============================================================================
   POSITIONS — the three-up grid used by the "what a check tells you" band
   ========================================================================== */
.three { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 2.4rem; }
.pos__n {
    font-size: .625rem; font-weight: 700; letter-spacing: .2em;
    color: var(--accent); margin-bottom: .8rem;
}
.pos h3 { font-size: 1.28rem; font-weight: 300; letter-spacing: -.03em; margin-bottom: .6rem; }
.pos p { margin: 0; font-size: .95rem; }

/* =============================================================================
   FOOTER
   ========================================================================== */
.foot { border-top: 1px solid var(--line-dark); background: var(--deep); color: rgba(255, 255, 255, .5); }
.foot__in { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 2rem; padding-block: 2.2rem; }
.foot__in p { margin: 0; font-size: .78rem; }
.foot__links { display: flex; flex-wrap: wrap; gap: 1.4rem; margin-left: auto; }
.foot__links a { font-size: .78rem; text-decoration: none; color: rgba(255, 255, 255, .5); }
.foot__links a:hover { color: #fff; }

/* =============================================================================
   REVEAL
   -----------------------------------------------------------------------------
   Held to a short rise with no scale and no stagger beyond a beat.

   The hidden state is gated on html.js — the class the inline <head> latch
   sets before anything paints. Only a browser definitely running our script
   gets an element hidden pending that script's reveal; everyone else gets the
   content. Without this, the h1 and the whole console would be opacity:0 until
   the observer ran, so any throw before that point serves an empty page rather
   than an unanimated one.
   ========================================================================== */
html.js .rise { opacity: 0; transform: translateY(14px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
html.js .rise.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
    html.js .rise { opacity: 1; transform: none; transition: none; }
    .btn, .dom, .seg__b, .ib, .buy { transition: none; }
}

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px; }

/* Read, not seen. The pill's lit segment is a span rather than a link, and a
   span carrying only an aria-label is announced by nothing — so it carries its
   words as real text and hides them here instead. */
.vh {
    position: absolute; width: 1px; height: 1px; margin: -1px;
    padding: 0; border: 0; overflow: hidden; white-space: nowrap;
    clip: rect(0 0 0 0); clip-path: inset(50%);
}

/* The skip link. background:var(--ink) + color:#fff, which the dark edition
   has to answer or it is white on white — invisible to every check that only
   looks at what is on screen, and the very first thing a keyboard user
   reaches. */
.skip {
    position: absolute; left: -9999px; top: 0; z-index: 400;
    background: var(--ink); color: #fff; padding: .8rem 1.2rem; text-decoration: none;
}
.skip:focus { left: 0; }

/* Print. The console is a screen tool and its controls mean nothing on paper;
   what a printed page is for is the list of names and their status. */
@media print {
    .nav, .menu, .console__body, .out__bar .btn, .dom__acts, .foot, .hero__act, .toast { display: none !important; }
    body { font-size: 12px; }
    .band { padding-block: 1rem; }
    .dom { break-inside: avoid; border-left-width: 3px; }
}

/* =============================================================================
   NETWORK LABS — the instrument outputs
   -----------------------------------------------------------------------------
   Added for labs.llc/labluxa/. Everything below reads the same tokens the rest
   of the sheet does (--ink, --paper, --line, --free/--taken/--unsure), so the
   dark edition inherits it through dark.css with no second copy. The output
   panels are set in the mono face the rest of the console diagnostics use.
   ========================================================================== */
.probe-out, .short-out, .pw-out {
    margin-top: 1rem;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--paper);
    padding: 1rem 1.15rem;
}
.probe-out {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .82rem;
    line-height: 1.65;
    max-height: 60vh;
    overflow-y: auto;
}
.probe-out > div { margin: .18rem 0; }
.probe-label {
    color: var(--ink-55);
    margin-top: 1rem !important;
    font-size: .74rem;
    letter-spacing: .01em;
}
.probe-note { color: var(--ink-55); font-style: italic; }
.probe-card {
    margin: .5rem 0 !important;
    padding: .7rem .85rem;
    background: var(--tint);
    border-left: 3px solid var(--line);
    border-radius: 8px;
}
.probe-card.good { border-left-color: var(--free); }
.probe-hop {
    padding: .35rem .6rem;
    background: var(--tint);
    border-left: 2px solid var(--line);
    border-radius: 6px;
}
.probe-continent {
    margin-top: 1.1rem !important;
    padding-bottom: .35rem;
    border-bottom: 1px solid var(--line);
    font-weight: 600;
    color: var(--ink);
}
.probe-site {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: baseline;
    padding: .2rem 0;
}
.probe-site b { font-weight: 600; }
.probe-ip { color: var(--ink-55); font-size: .74rem; }
.probe-site .good, .probe-site .bad { margin-left: auto; }
.probe-verdict, .probe-done {
    margin-top: 1.1rem !important;
    padding: .85rem 1rem;
    background: var(--tint);
    border-left: 3px solid var(--accent);
    border-radius: 8px;
}
.probe-rating { font-size: 1.15rem; font-weight: 700; display: inline-block; margin-top: .3rem; }

.good { color: var(--free); font-weight: 600; }
.warn { color: var(--unsure); font-weight: 600; }
.bad  { color: var(--taken); font-weight: 600; }

/* Shortener + password results share one card shape. */
.short-out__link, .pw-out__word {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--ink);
    word-break: break-all;
    margin: 0 0 .8rem 0;
}
.pw-out__word { color: var(--accent); }
.short-out .field-row, .pw-out .field-row { margin-bottom: .6rem; }

@media (max-width: 560px) {
    .probe-out { font-size: .78rem; }
    .short-out__link, .pw-out__word { font-size: 1.05rem; }
}

/* The per-instrument output toolbar (Download PDF). Shown by app.js once a run
   has produced something worth exporting. */
.out-tools {
    display: flex;
    gap: .5rem;
    flex-wrap: wrap;
    margin-top: 1rem;
}

/* =============================================================================
   SEARCH — the box's own furniture
   ========================================================================== */
.sr-quick {
    margin: .4rem 0 .6rem;
    padding: .75rem .95rem;
    background: var(--tint);
    border-left: 3px solid var(--accent);
    border-radius: 8px;
    font-size: .92rem;
}
.sr-snip {
    flex-basis: 100%;
    font-size: .78rem; color: var(--ink-55); line-height: 1.5;
    font-family: var(--font, inherit);
}
.sr-img-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: .5rem;
    margin: .5rem 0;
}
.sr-img {
    display: block; aspect-ratio: 4 / 3; overflow: hidden;
    border: 1px solid var(--line); border-radius: 8px; background: var(--tint);
}
.sr-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sr-img:hover { border-color: var(--accent); }
.sr-launch { display: flex; flex-wrap: wrap; gap: .45rem; }
.sr-history { display: flex; flex-wrap: wrap; gap: .4rem; }
.sr-chip { border-radius: 999px; }
.sr-chip--x { color: var(--ink-55); }
.probe-out .probe-site b a { color: var(--ink); text-decoration: none; }
.probe-out .probe-site b a:hover { text-decoration: underline; }
@media (max-width: 560px) {
    .sr-img-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
}

/* =============================================================================
   STOCKS — the tape's own furniture
   ========================================================================== */
.st-indices {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: .6rem;
}
.st-index {
    display: flex; flex-direction: column; gap: .15rem;
    padding: .7rem .9rem;
    border: 1px solid var(--line); border-radius: 10px;
    background: var(--paper);
}
.st-index__n {
    font-size: .62rem; font-weight: 700; letter-spacing: .16em;
    text-transform: uppercase; color: var(--ink-55);
}
.st-index b {
    font-size: 1.15rem; font-weight: 600; letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
}
.st-row { align-items: center; }
.st-px { margin-left: auto; font-variant-numeric: tabular-nums; text-align: right; }
.st-act { display: inline-flex; gap: .25rem; margin-left: .5rem; }
.st-sugg {
    font: inherit; font-size: .72rem; font-weight: 600;
    padding: .1rem .55rem; margin: 0 .12rem;
    border: 1px solid var(--line); border-radius: 999px;
    background: transparent; color: var(--ink); cursor: pointer;
}
.st-sugg:hover { border-color: var(--accent); }
.st-chart { margin-top: 1rem; }
.st-chart__title { margin: 0 0 .5rem; font-size: .95rem; }
.st-chart canvas {
    width: 100%; height: auto; display: block;
    border: 1px solid var(--line); border-radius: 10px;
    background: var(--paper);
}
@media (max-width: 560px) {
    .st-px { flex-basis: 100%; margin-left: 0; text-align: left; }
}

/* =============================================================================
   THE TAPE — three crawling strips under the hero
   -----------------------------------------------------------------------------
   The homepage's crawl strips, carrying prices instead of properties. Three
   rows rather than two — trending, most active, in the news — because each row
   is a different feed and mixing them would make the tape a list of symbols
   with no answer to "why is this one here".

   Which is also why each row keeps a LABEL at its head, outside the crawl. On
   the homepage every chip is the same kind of thing, so the strip needs no
   caption; here a reader who glances at the tape mid-crawl has to be able to
   tell an unfamiliar ticker that is trending from one that just reported. A
   caption riding inside the crawl would answer that question only while it
   happened to be on screen, so the tag is pinned and the rail scrolls past it.

   Speed is not set here. The script measures the laid-out run and writes
   animation-duration, so a row of six chips and a row of sixteen crawl at the
   same pixels per second — and the three speeds are deliberately unequal, so
   the rows never fall into step and read as one moving block.
   ========================================================================== */
.tape {
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    background: var(--tint);
    overflow: hidden;
    padding: calc(.95rem - 3px) 0 .75rem;
}
.tape__row {
    display: flex; align-items: center; gap: .7rem;
    padding-left: var(--gutter);
}
.tape__row + .tape__row { margin-top: calc(.5rem - 6px); }

/* One column for three tags of different lengths, so the three rails start on
   the same vertical line — the tape reads as a block rather than as three
   independent strips that happen to be stacked. */
.tape__tag {
    flex: 0 0 auto; width: 6.9rem;
    font-size: .58rem; font-weight: 700; letter-spacing: .14em;
    text-transform: uppercase; color: var(--ink-55);
    white-space: nowrap;
}
.tape__tag::before {
    content: '\25C6'; color: var(--accent);
    font-size: .55rem; margin-right: .4rem; letter-spacing: 0;
}

.tape__rail {
    /* min-width: 0 is the whole reason this row cannot widen the document. A
       flex item's min-width is auto, which is its CONTENT width — and the
       content here is a nowrap run of sixteen chips. Without this the rail
       measures a couple of thousand pixels, the row grows to hold it, and the
       page picks up a horizontal scrollbar that drags every fixed element on
       it sideways. */
    flex: 1 1 auto; min-width: 0;
    display: flex; overflow: hidden;
    /* Three pixels of slack inside the clip: a chip works out to a fractional
       height and the clip would otherwise round down and shave its bottom row
       of pixels. */
    padding: 3px 0;
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 92%, transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 92%, transparent 100%);
}
/* The copies past the first, hidden from assistive technology. display:contents
   is what keeps them direct flex children of the track — a plain wrapper would
   collapse every copy into one flex item and the loop would jump. */
.tape__echo { display: contents; }

.tape__track {
    display: flex; align-items: center; flex: 0 0 auto;
    gap: .55rem;
    /* Matches `gap` exactly, or the track is one gap short of twice a run and
       the -50% keyframe lands half a gap off — a visible hitch once per pass. */
    padding-right: .55rem;
    white-space: nowrap;
    animation: tape-run 120s linear infinite;
    backface-visibility: hidden;
    /* Parked until the tape is on screen AND the row has real chips in it. A
       row still saying "reading the tape" has nothing to crawl. */
    animation-play-state: paused;
}
.tape.is-live .tape__row.is-ready .tape__track {
    animation-play-state: running; will-change: transform;
}
@keyframes tape-run {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
}

.t-chip {
    display: inline-flex; align-items: baseline; gap: .5rem;
    flex: 0 0 auto;
    padding: .34rem .8rem;
    border: 1px solid var(--line); border-radius: 999px;
    background: var(--paper);
    font: inherit; text-align: left; cursor: pointer;
    transition: border-color .22s var(--ease);
}
.t-chip:hover { border-color: var(--accent); }
.t-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.t-sym { font-size: .78rem; font-weight: 700; letter-spacing: -.01em; color: var(--ink); }
.t-px  { font-size: .73rem; color: var(--ink-77); font-variant-numeric: tabular-nums; }
.t-mv  { font-size: .69rem; font-weight: 600; font-variant-numeric: tabular-nums; }
/* The two states that are not a quote. Neither is a button — there is nothing
   to press — so neither gets the hover or the pointer. */
.t-chip--wait, .t-chip--none {
    cursor: default; font-size: .73rem; color: var(--ink-55);
    background: transparent; border-style: dashed;
}
.t-chip--none { color: var(--taken); border-color: var(--line); }
.t-chip--wait:hover, .t-chip--none:hover { border-color: var(--line); }

.tape__foot {
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    padding: .55rem var(--gutter) 0;
    font-size: .66rem; color: var(--ink-55);
}
.tape__re {
    font: inherit; font-size: .6rem; font-weight: 700; letter-spacing: .12em;
    text-transform: uppercase;
    padding: .16rem .6rem;
    border: 1px solid var(--line); border-radius: 999px;
    background: transparent; color: var(--ink-55); cursor: pointer;
}
.tape__re:hover { border-color: var(--ink); color: var(--ink); }

/* A visitor who has asked for less motion gets the same chips, parked — and
   the rail becomes scrollable so the ones past the fold are still reachable.
   The mask goes with the crawl: a fade over a strip you scroll by hand reads
   as a rendering fault rather than as an edge. */
@media (prefers-reduced-motion: reduce) {
    .tape__track { animation: none; }
    .tape__rail {
        overflow-x: auto; scrollbar-width: thin;
        -webkit-mask-image: none; mask-image: none;
    }
}

@media (max-width: 640px) {
    .tape { padding: calc(.7rem - 3px) 0 .6rem; }
    .tape__row { padding-left: 14px; gap: .5rem; }
    .tape__tag { width: 5.2rem; font-size: .5rem; letter-spacing: .08em; }
    .tape__tag::before { font-size: .48rem; margin-right: .3rem; }
    .tape__foot { padding-left: 14px; padding-right: 14px; }
    .t-chip { gap: .4rem; padding: .3rem .62rem; }
    .t-sym { font-size: .72rem; } .t-px { font-size: .68rem; } .t-mv { font-size: .64rem; }
}

/* ------------------------------------------------------------- a row that acts
   .menu__row was written for <a>. A <button> wearing the same class arrives
   with the user agent's own 2px outset border, its buttonface background, its
   13px Arial and — because .menu__row is a flex container — a width that
   shrinks to its own text. Eight such rows of differing lengths read as a
   staircase of framed grey boxes beside a clean list of links, which is exactly
   what they were.

   Everything here is a reset, not a style. The point is that a button in this
   menu is indistinguishable from a link in it. The bottom rule is re-declared
   because `border: 0` above would take it with the rest, and the existing
   `.menu__row:last-child` still wins on specificity, so the last row of a
   section stays clean. */
button.menu__row {
    width: 100%;
    box-sizing: border-box;
    -webkit-appearance: none;
    appearance: none;
    background: none;
    border: 0;
    border-bottom: 1px solid var(--line-soft);
    border-radius: 0;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

/* =========================================================== CLOCKWORK ====
   The observatory's own furniture. Everything reads the tokens, so the dark
   edition answers only the sky washes and the four canvas inks — all of them
   tokens of this section, declared here and answered in dark.css. The planet
   colours are identity colours and live in app.js, literal in both editions,
   the way the subway bullets do on Transit. */
:root {
    /* the sky over a city, as a wash on its clock card */
    --cw-day:   rgba(251, 191, 36, .13);
    --cw-civil: rgba(194, 65, 12, .09);
    --cw-night: rgba(29, 78, 216, .08);
    /* canvas inks the JS reads at draw time */
    --cw-sun:      #B45309;   /* the sun's disc, the day arc, the orrery's sun */
    --cw-twi:      #1D4ED8;   /* the twilight wings on the arc                 */
    --cw-moon-lit: #F3EEE2;   /* the moon's lit portion                        */
    --cw-moon-dk:  #232B33;   /* …and its shadowed one                         */
}

.cw-stat { margin-left: auto; font-size: .78rem; letter-spacing: .04em; color: var(--muted);
           font-variant-numeric: tabular-nums; }
.cw-stat--live { color: var(--free); }

/* ------------------------------------------------------- the city finder */
.cw-hits { margin-top: .45rem; border: 1px solid var(--line); border-radius: 10px;
           overflow: hidden; background: var(--paper); }
.cw-hit { display: flex; align-items: center; gap: .7rem; width: 100%; text-align: left;
          padding: .55rem .8rem; border: 0; border-bottom: 1px solid var(--line-soft);
          background: none; color: var(--ink-77); font: inherit; cursor: pointer;
          transition: background .15s var(--ease), color .15s var(--ease); }
.cw-hit:last-child { border-bottom: 0; }
.cw-hit:hover { background: var(--tint); color: var(--ink); }
.cw-hit__name { font-weight: 600; }
.cw-hit__zone { margin-left: auto; font-size: .74rem; color: var(--muted);
                white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }

/* -------------------------------------------------------- the clock board */
.cw-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
           gap: .9rem; }
.cw-card { border: 1px solid var(--line-soft); border-radius: 12px;
           padding: .8rem .7rem .6rem; background: var(--paper);
           display: flex; flex-direction: column; align-items: center; gap: .12rem;
           min-width: 0; }
/* EVERY CARD IS THE EDITION IT IS BEING READ IN. Each one used to be filled
   with the colour of its own sky, so a board of sixteen cities was sixteen
   different grounds — three pale tints fighting each other and the page, and
   on the dark edition a row of pale boxes that had nothing to do with the
   edition at all.

   The sky is still measured and still shown: it is the word under the time,
   and a hairline along the top of the card in that sky's colour. The reading
   is kept; the wallpaper is not. */
.cw-card[data-sky] { background: var(--paper); position: relative; overflow: hidden; }
.cw-card[data-sky]::before {
    content: ''; position: absolute; inset: 0 0 auto 0; height: 2px;
    background: var(--line);
}
.cw-card[data-sky="day"]::before   { background: var(--cw-sun, #B45309); }
.cw-card[data-sky="civil"]::before { background: var(--cw-twi, #1D4ED8); }
.cw-card[data-sky="night"]::before { background: var(--ink); opacity: .38; }

/* One clock, on its own, in its own window. */
/* ALWAYS THERE, NEVER LOUD.
   It was opacity:0 until the card was hovered, which meant that on a phone —
   where there is no hover — every clock's pop-out was invisible and
   unreachable, and on a desktop nobody knew it existed until their mouse
   happened to cross a card. A control that only appears once you have already
   found it is not a control.

   So it sits at a quiet third and comes up to full on hover or focus. On a
   coarse pointer it is full always, because that device will never hover. */
.cw-card__pop {
    position: absolute; top: 6px; right: 6px;
    width: 1.35rem; height: 1.35rem; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
    font: inherit; font-size: .62rem;
    border-radius: 50%; border: 1px solid var(--line);
    background: var(--paper); color: var(--ink-55, var(--muted));
    cursor: pointer; opacity: .38; transition: opacity .16s ease, border-color .16s ease;
}
.cw-card:hover .cw-card__pop,
.cw-card__pop:hover,
.cw-card__pop:focus-visible { opacity: 1; border-color: var(--accent); color: var(--accent); }
@media (pointer: coarse) {
    /* NO HOVER ON THIS DEVICE, EVER — and a bigger target, because 1.35rem is
       under the forty-four pixels a finger needs. */
    .cw-card__pop { opacity: 1; width: 1.9rem; height: 1.9rem; font-size: .72rem; }
}
@media (prefers-reduced-motion: reduce) { .cw-card__pop { transition: none; } }
html[data-cw-pop] .cw-card__pop { display: none; }
.cw-card__cv { width: 110px; height: 110px; }
.cw-card__name { margin: .3rem 0 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
                 white-space: nowrap; font-weight: 600; font-size: .88rem; color: var(--ink); }
.cw-card__time { margin: 0; font-variant-numeric: tabular-nums; font-weight: 700;
                 font-size: 1.04rem; letter-spacing: .02em; color: var(--ink); }
.cw-card__sub { margin: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
                white-space: nowrap; font-size: .68rem; color: var(--muted); }
.cw-card__sky { margin: 0; font-size: .6rem; font-weight: 700; letter-spacing: .16em;
                text-transform: uppercase; color: var(--ink-55); }
.cw-card__ops { display: flex; gap: .3rem; margin-top: .4rem; }
.cw-op { -webkit-appearance: none; appearance: none; border: 1px solid var(--line);
         background: none; color: var(--ink-55); border-radius: 7px;
         width: 27px; height: 22px; padding: 0; font: inherit; font-size: .68rem;
         line-height: 1; cursor: pointer;
         transition: color .15s var(--ease), border-color .15s var(--ease); }
.cw-op:hover { border-color: var(--ink); color: var(--ink); }

/* ------------------------------------------------------------ the sky desk */
select.field { max-width: 24rem; }
.cw-arc { display: block; width: 100%; height: clamp(170px, 32vw, 240px); }
.cw-figs { display: flex; gap: 1rem 1.6rem; flex-wrap: wrap;
           border-top: 1px solid var(--line-soft); padding-top: 1rem; }
.cw-fig { display: flex; flex-direction: column; gap: .12rem; }
.cw-fig__l { font-size: .64rem; font-weight: 700; letter-spacing: .13em;
             text-transform: uppercase; color: var(--muted); }
.cw-fig b { font-variant-numeric: tabular-nums; font-weight: 700; font-size: 1rem;
            color: var(--ink); white-space: nowrap; }
.cw-moonrow { display: flex; gap: 1.3rem; align-items: center; flex-wrap: wrap;
              border-top: 1px solid var(--line-soft); padding-top: 1.1rem; }
.cw-moon { width: 110px; height: 110px; flex: 0 0 auto; }
.cw-moonrow__t { flex: 1 1 16rem; min-width: 0; }
.cw-phase { margin: 0 0 .2rem; font-size: 1.08rem; font-weight: 700;
            letter-spacing: -.01em; color: var(--ink); }

/* -------------------------------------------------------------- the orrery */
.cw-orr { display: block; width: 100%; height: clamp(340px, 58vw, 540px); }
.cw-scrubrow { display: flex; gap: .8rem; align-items: center; flex-wrap: wrap; }
.cw-scrub { flex: 1 1 14rem; min-width: 0; height: 28px; margin: 0;
            accent-color: var(--accent); background: transparent; }
.cw-date { font-variant-numeric: tabular-nums; font-weight: 700; font-size: .95rem;
           color: var(--ink); white-space: nowrap; }

@media (max-width: 640px) {
    .cw-grid { grid-template-columns: repeat(auto-fill, minmax(138px, 1fr)); gap: .7rem; }
    .cw-hit__zone { display: none; }
    .cw-figs { gap: .8rem 1.1rem; }
}

/* Same argument as .menu__q above: under 16px, iOS Safari zooms the focused
   field and carries the layout off-screen. Matched on the pointer, not the
   width, because a narrow desktop window does not zoom. */
@media (hover: none) and (pointer: coarse) {
    .field { font-size: 16px; }
}

/* --------------------------------------------------------------- the dial */
.cw-face { margin-top: .9rem; }
.cwd__say2 b { color: var(--ink); font-weight: 600; }

/* --------------------------------------------------------- the popped board
   Same page, one attribute. Everything that frames the board on the site is
   furniture in a window opened to watch the clocks, so it goes — and the
   board itself is given the room. */
html[data-cw-pop] .nav,
html[data-cw-pop] .foot,
html[data-cw-pop] footer,
html[data-cw-pop] .band:not(:has(.cw-grid)),
html[data-cw-pop] .cw-face,
/* THE WHOLE DESK GOES. It is the controls for a board, and a popped window is
   a VIEW of a board — it has no use for a search field, and the panel rail
   would have written to the SAME localStorage as the board behind it, which is
   the bug popMode() spends a paragraph on. Naming the container rather than
   each control is also why this rule stopped rotting: the last version still
   named .cw-bar three renames later, so the popped window went on carrying a
   search box it had no use for. */
html[data-cw-pop] .cwd,
html[data-cw-pop] .cw-hits,
html[data-cw-pop] .console__head,
html[data-cw-pop] .hint { display: none !important; }
html[data-cw-pop] body { padding: 0; }
/* Nothing in a popped window scrolls: it holds one screenful by construction,
   so a scrollbar there is a rounding error made visible. */
html[data-cw-pop], html[data-cw-pop] body { overflow: hidden; height: 100%; }
/* THE BAND THAT HOLDS THE GRID KEEPS ITS HEADING, and the heading is 241px of
   a 760px window — measured, it pushed the last row of clocks clean off the
   bottom. A popped window was asked for clocks. Everything in that band which
   is not the grid goes, and the band, its wrap and its console stop taking
   room of their own. */
html[data-cw-pop] .band:has(.cw-grid) > .wrap > .eyebrow,
html[data-cw-pop] .band:has(.cw-grid) > .wrap > .d2,
html[data-cw-pop] .band:has(.cw-grid) > .wrap > .lede { display: none !important; }
html[data-cw-pop] .band:has(.cw-grid) { padding: 0; }
html[data-cw-pop] .band:has(.cw-grid) > .wrap { max-width: none; padding: 0; }
html[data-cw-pop] .console { border: 0; box-shadow: none; background: none; }
html[data-cw-pop] .console__body { padding: 0; display: block; }
/* A single popped clock is given the window rather than sitting in the corner
   of a grid built for sixteen. */
/* One clock: the whole window, less a margin, and square. */
html[data-cw-only] .cw-grid { display: flex; align-items: center; justify-content: center; }
html[data-cw-only] .cw-card { margin: 0; width: min(88vmin, 100%); }
html[data-cw-only] .cw-card__cv { width: 100%; height: auto; aspect-ratio: 1 / 1; }
html[data-cw-only] .cw-card[hidden] { display: none !important; }

/* ------------------------------------------------------------- the lights
   The board's small controls — the two arrows that walk a clock along, the
   cross that retires it, and the pop-out — are 20-odd pixels of outline
   each. At that size a flat border-colour swap on hover is almost invisible,
   which is why they read as decoration rather than as controls.

   Three things, and none of them is a colour change on its own:

     LIFT      a pixel of travel, so the control moves under the cursor
     GLOW      a soft ring in the accent, sized in ems so it scales with the
               control rather than being a fixed halo around a shrinking dot
     FILL      the ink ground on press, which is the same language the rest of
               the estate uses for "this is happening now"

   The press state is separate from hover on purpose: a control that only
   lights on hover has nothing to say on a touch screen, where there is no
   hover at all and :active is the whole vocabulary. */
.cw-card__pop,
.cw-op {
    transition: transform .14s var(--ease, ease),
                box-shadow .18s var(--ease, ease),
                background .18s var(--ease, ease),
                border-color .18s var(--ease, ease),
                color .18s var(--ease, ease);
}
.cw-card__pop:hover,
.cw-op:hover {
    transform: translateY(-1px);
    border-color: var(--accent);
    color: var(--accent);
    box-shadow: 0 0 0 .18em color-mix(in srgb, var(--accent) 16%, transparent);
}
.cw-card__pop:active,
.cw-op:active {
    transform: translateY(0);
    background: var(--ink); border-color: var(--ink); color: var(--paper);
    box-shadow: 0 0 0 .1em color-mix(in srgb, var(--ink) 14%, transparent);
}
.cw-card__pop:focus-visible,
.cw-op:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 2px;
}
/* A control that lifts under a cursor should not lift under a finger that is
   only scrolling past it. */
@media (hover: none) {
    .cw-card__pop, .cw-op { opacity: 1; }
    .cw-card__pop:hover, .cw-op:hover {
        transform: none; box-shadow: none;
    }
}
@media (prefers-reduced-motion: reduce) {
    .cw-card__pop, .cw-op { transition: none; }
    .cw-card__pop:hover, .cw-op:hover { transform: none; }
}

/* --------------------------------------------------------------- the bar
   Three controls on one line. The search takes whatever is left after the dial
   and the two buttons have what they need — named minmax(0, 1fr) rather than
   1fr, because a 1fr track grows to its content's min-content width and a long
   placeholder would set the width of the whole row.

   Below 640 the row becomes two: the search across the top, the dial and the
   buttons under it. Not a stack of four — the point of the bar is that the
   controls are together. */
html[data-cw-pop] .cw-card[data-sky]::before { display: none; }
html[data-cw-pop] .cw-card__cv {
    width: var(--cw-dial, 100%); height: auto; aspect-ratio: 1 / 1; max-width: 100%;
}
/* THE TYPE UNDER A POPPED DIAL IS SET IN PIXELS BY popFit().
   It used to be a clamp() here and an unrelated clamp() in the fit, and the
   two disagreed by about six pixels a row — which is nothing on the first row
   and two dozen by the fourth, where the city name ended up under the edge of
   a window with overflow: hidden over it. One set of numbers now: popFit
   writes --cw-name, --cw-time and --cw-lgap, and the line-heights below are
   the ones its arithmetic assumes. Do not put a clamp() back. */
html[data-cw-pop] .cw-card__name {
    margin: var(--cw-lgap, .3rem) 0 0;
    font-size: var(--cw-name, .8rem); line-height: 1.3;
    font-weight: 600; letter-spacing: .01em;
}
html[data-cw-pop] .cw-card__time {
    margin: 0; font-size: var(--cw-time, .92rem); line-height: 1.32;
    font-variant-numeric: tabular-nums; font-weight: 700;
}
html[data-cw-pop] .cw-grid {
    /* Its own 14px top margin, plus a 100vh height, is 100vh + 14 — which is a
       scrollbar on a window that was sized to hold exactly what is in it. */
    margin: 0;
    display: grid;
    gap: var(--cw-gap, 1rem);
    grid-template-columns: repeat(var(--cw-cols, 4), minmax(0, 1fr));
    align-content: center; justify-content: center;
    padding: var(--cw-pad, 1rem);
    height: 100vh; box-sizing: border-box;
    overflow: hidden;
}

/* ------------------------------------------------------------ the crosshair
   One control in the whole window, and it does the one thing worth doing to a
   wall of clocks: deals a different dial.

   Bottom left, because the eye reads a grid from the top left and a control
   parked there would be the first thing it meets. It sits at a quarter
   opacity until the pointer is anywhere in the window, so it is discoverable
   without being present — and it goes to full opacity on its own hover and on
   keyboard focus, which is the only state a keyboard user has.

   The crosshair is drawn, not a glyph: at 18 pixels a font's ✛ lands wherever
   its metrics put it, and this has to sit on its own centre. */
/* THE WHEEL THAT FLOATS. Bottom-left, on the board and in every popped
   window. It was a crosshair, and a crosshair is a picture of aim rather than
   of chance.

   IT IS NOT HIDDEN UNTIL HOVER. The old one sat at opacity .28 and only came
   up under a mouse, which on a touch screen means invisible — there is no
   hover on a phone, so a control that appears on hover is a control that does
   not exist there. It is quiet at rest, full under a pointer, and simply full
   size on a coarse pointer. */
.cw-shuffle {
    position: fixed; left: 1rem; bottom: 1rem; z-index: 40;
    width: 3rem; height: 3rem; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%; cursor: pointer;
    border: 1px solid var(--line);
    background: var(--paper);
    opacity: .62;
    box-shadow: 0 .12rem .5rem rgba(0, 0, 0, .10);
    transition: opacity .2s ease, transform .16s ease,
                box-shadow .2s ease, border-color .2s ease;
}
.cw-shuffle__cv { display: block; width: 2.5rem; height: 2.5rem; }
.cw-shuffle:hover {
    opacity: 1;
    transform: translateY(-1px);
    border-color: var(--accent);
    box-shadow: 0 .2rem .8rem color-mix(in srgb, var(--accent) 20%, transparent);
}
.cw-shuffle:active { transform: translateY(0) scale(.95); }
.cw-shuffle:focus-visible {
    opacity: 1;
    outline: 2px solid var(--accent); outline-offset: 3px;
}
@media (pointer: coarse) {
    .cw-shuffle { width: 3.4rem; height: 3.4rem; opacity: .92; }
    .cw-shuffle__cv { width: 2.9rem; height: 2.9rem; }
}
/* The name of the dial, said once as it changes and then gone. */
.cw-shuffle__say {
    position: fixed; left: 4.2rem; bottom: 1.55rem; z-index: 40;
    font-size: .72rem; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--ink); opacity: 0;
    pointer-events: none; transition: opacity .3s ease;
}
.cw-shuffle__say.is-on { opacity: .68; }
@media (prefers-reduced-motion: reduce) {
    .cw-shuffle, .cw-shuffle:hover, .cw-shuffle:active { transition: none; transform: none; }
}


/* ------------------------------------------------------- the shelf as a door
   Every drawer shelf is a room now, and the room is a page. The heading is the
   link to it: same size, same tracking, same colour as a plain heading, with an
   arrow that only moves on hover — a heading that shouted would compete with
   the tiles under it, which are the thing somebody came to the drawer for. */
.menu__h--door { padding: 0; }
.menu__h--door a {
    display: inline-flex; align-items: center; gap: .4em;
    color: inherit; text-decoration: none;
}
.menu__h--door a i { font-style: normal; opacity: 0; transform: translateX(-3px); }
.menu__h--door a:hover, .menu__h--door a:focus-visible { color: var(--accent); }
.menu__h--door a:hover i, .menu__h--door a:focus-visible i {
    opacity: 1; transform: none;
}
.menu__h--door a i { transition: opacity .16s ease, transform .16s ease; }
.menu__h--door a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
@media (prefers-reduced-motion: reduce) {
    .menu__h--door a i { transition: none; }
}

/* ------------------------------------------------------------- the panels
   Ten slots, a name and two buttons. The row wraps rather than stacking, the
   same way the bar above it does. */



/* ==========================================================================
   THE PANEL WHEEL, THE SINGLE POP-OUT ROW, AND FOCUS
   ==========================================================================
   THE WHEEL'S OWN COLOURS ARE TOKENS, not literals in the canvas code. It is
   drawn from getComputedStyle, so dark.css can re-ink it the way it re-inks
   everything else — and a wheel painted with --ink would come out white on the
   dark edition, because --ink is the TEXT colour there. Mahogany is mahogany
   in both editions; only its brightness moves. */
:root {
    --cw-wheel-rim:   #3B2B1F;
    --cw-wheel-track: #241A12;
    --cw-wheel-cone:  #E9E3D7;
    --cw-wheel-red:   #A82219;
    --cw-wheel-black: #16191D;
    --cw-wheel-green: #1B7442;
    --cw-wheel-brass: #B08D46;
    --cw-wheel-ball:  #FAF7F0;
}

/* The wheel, at a size that can print its own numbers, beside what it does. */
.cw-wheel canvas { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; }
.cw-wheel:hover { transform: scale(1.03); }
.cw-wheel:active { transform: scale(.98); }
.cw-wheel:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* One clock, on its own. */

/* FOCUS — the dial and nothing else.
   The city and the time are drawn ON the canvas in this mode (see drawFace),
   so nothing is lost by taking the type off the card: it has moved onto the
   clock, which is where a clock has always carried it. */
html[data-cw-focus] .cw-card__ops,
html[data-cw-focus] .cw-card__pop,
html[data-cw-focus] .cw-card__name,
html[data-cw-focus] .cw-card__time,
html[data-cw-focus] .cw-card__sub,
html[data-cw-focus] .cw-card__sky { display: none !important; }
html[data-cw-focus] .cw-card {
    border: 0; box-shadow: none; background: none; padding: 0;
}
html[data-cw-focus] .cw-card[data-sky]::before { display: none; }
html[data-cw-focus] .cw-card__cv { width: 100%; height: auto; aspect-ratio: 1 / 1; }
/* THE LATCH IS THE DESK'S, NOT AN ID'S.
   There was a second rule here — #cw-focus[aria-pressed="true"], an 8% accent
   wash with accent text — left over from before the desk existed. At (1,1,0)
   it beat .cwd__b[aria-pressed="true"] at (0,2,0), so the one control that is
   supposed to carry the panel's single orange rendered as a 2%-alpha ghost and
   nothing said so. Two rules for one job, and the id won because it was an id.
   The desk owns its own states; see .cwd__b[aria-pressed="true"] above. */

/* THE POPPED BOARD ON A PHONE. popFit() solves the columns from the real
   width, but 100vh on iOS Safari is TALLER than the visible page — the
   browser's own chrome sits over the bottom of it, which is exactly where the
   last row of city names is. dvh is the visible height, and the visible height
   is what the fit's arithmetic is about. */
@supports (height: 100dvh) {
    html[data-cw-pop], html[data-cw-pop] body { height: 100dvh; }
}


/* ==========================================================================
   THE DESK — the board's controls, as one instrument panel
   ==========================================================================
   A Rams/Braun panel, and what that actually buys is arithmetic rather than
   atmosphere. What was here before:

       five control heights   (input 50, select 50, button 46, slot 30, name 50)
       four radii             (9px field, 8px button, 999px slot, 50% wheel)
       three type sizes for what is one job (.95rem, .82rem, .68rem)
       three column widths in a four-column grid
       a 152px wheel with a paragraph beside it
       seven bands stacked down the page, none aligned with any other

   What is here now:

       ONE height             --cwd-h, everywhere, including the slots
       ONE radius             --cwd-r = 3px, everywhere but the wheel
       ONE label recipe       10px / 600 / .14em / upper / --muted
       ONE control recipe     13px / 500 / --ink
       FOUR equal columns     and four equal keys under them
       an 84px wheel

   NO .field, NO .btn. Both carry html[data-dark] rules in the estate's shared
   dark.css, which is linked AFTER this sheet and therefore wins every tie at
   equal weight — a class this sheet owns and that sheet has never heard of
   needs no #main prefix and no dark twin. The edition difference lives in the
   token VALUES (--ink, --paper, --line, --muted, --accent), which dark.css
   repoints for the whole page at once. That is the laband pattern; the
   alternative is the composer one, and composer needed a paragraph of cascade
   notes to make it hold.

   THE HAIRLINE IS NOT --line. Measured, --line is rgba(30,38,43,.12), which
   resolves to #E4E5E6 on paper — 1.26:1. Faint AND load-bearing is the worst
   pair a border can be: it is what made the old fields need a 50%-ink border
   to be findable at all, which is why they read as heavy. 34% ink mixed with
   TRANSPARENT (never with --paper: a .band--deep section repoints --ink and
   leaves --paper alone, and a two-token mix goes white-on-white there) is
   2.4:1 — a line you can see and not a box you have to look past.
   ========================================================================== */
.cwd {
    --cwd-h:    2.125rem;                       /* 34px: every control, no exceptions */
    --cwd-r:    3px;
    --cwd-gap:  .4rem;
    --cwd-edge: color-mix(in srgb, var(--ink) 34%, transparent);
    --cwd-rule: color-mix(in srgb, var(--ink) 13%, transparent);
    display: grid; gap: .7rem;
    margin: 0 0 1rem;
}

/* THE LABEL. One recipe, used above a field, at the head of a rail and inside
   the readout — the same job in all three places, so the same type. */
.cwd__l {
    display: block;
    font-size: .625rem; font-weight: 600; line-height: 1.2;
    letter-spacing: .14em; text-transform: uppercase;
    color: var(--muted);
    white-space: nowrap;
}

/* FOUR EQUAL COLUMNS. minmax(0, 1fr) rather than 1fr, or a 32-city select
   whose longest option is "Dar es Salaam · Tanzania" sets the width of the
   other three tracks — a 1fr track grows to min-content and the whole desk
   goes asymmetric behind one long word. */
/* FIVE ACROSS, AND FIVE ACROSS UNDER IT. The desk went from four columns to
   five when the board's size became a control rather than a constant; the rule
   that matters is not the number but that BOTH rows carry the same one, so the
   fields and the keys keep sharing a grid. */
.cwd__fields {
    display: grid; gap: var(--cwd-gap);
    grid-template-columns: repeat(5, minmax(0, 1fr));
}
.cwd__f { display: grid; gap: .28rem; min-width: 0; }

.cwd__i {
    font: inherit; font-size: .8125rem; font-weight: 500;
    /* THE FALLBACK IS NOT DECORATION. --cwd-h is declared on .cwd, so a page
       that uses these controls WITHOUT a .cwd container resolves this to
       nothing — and an invalid var() at computed-value time does not fall back
       to the previous declaration, it falls back to `auto`. The two rules
       beside this one already carry the fallback; this one did not, so on such
       a page every input and select shipped 23px tall next to 34px keys and the
       row grew eleven pixels of dead air under them. That gap is what a reader
       sees and reports, and nothing about it points at a missing fallback. */
    height: var(--cwd-h, 2.125rem); line-height: normal;
    padding: 0 .55rem;
    box-sizing: border-box; width: 100%; min-width: 0;
    color: var(--ink); background: var(--paper);
    border: 1px solid var(--cwd-edge, color-mix(in srgb, var(--ink) 34%, transparent));
    border-radius: var(--cwd-r, 3px);
    -webkit-appearance: none; appearance: none;
    transition: border-color .18s ease, box-shadow .18s ease;
}
/* A select needs its own arrow once appearance is none, and it has to be drawn
   rather than fetched — an SVG in a data: URI cannot read a custom property,
   so the caret is a rotated square built from two borders and it inherits the
   ink of the edition it is on. */
select.cwd__i {
    padding-right: 1.5rem;
    background-image:
        linear-gradient(45deg, transparent 50%, currentColor 50%),
        linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: right .78rem center, right .55rem center;
    background-size: .24rem .24rem, .24rem .24rem;
    background-repeat: no-repeat;
    color: var(--ink);
}
/* 42% OF THE INK IS 2.51:1 ON PAPER — a placeholder is the only text in a
   field before anything is typed, and it is the text that tells the reader what
   the field is for. 62% measures 4.62:1 on paper and 4.71:1 on dark. */
.cwd__i::placeholder { color: color-mix(in srgb, var(--ink) 62%, transparent); }
.cwd__i:hover  { border-color: color-mix(in srgb, var(--ink) 52%, transparent); }
.cwd__i:focus  { outline: none; border-color: var(--accent);
                 box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent); }

/* FOUR KEYS, ONE WIDTH, ONE HEIGHT. */
.cwd__keys {
    display: grid; gap: var(--cwd-gap);
    grid-template-columns: repeat(5, minmax(0, 1fr));
}
.cwd__b {
    font: inherit; font-size: .6875rem; font-weight: 600;
    letter-spacing: .1em; text-transform: uppercase;
    height: var(--cwd-h, 2.125rem); padding: 0 .5rem;
    display: inline-flex; align-items: center; justify-content: center;
    box-sizing: border-box; min-width: 0; white-space: nowrap;
    color: var(--ink); background: transparent;
    border: 1px solid var(--cwd-edge, color-mix(in srgb, var(--ink) 34%, transparent));
    border-radius: var(--cwd-r, 3px);
    cursor: pointer;
    transition: background .16s ease, color .16s ease, border-color .16s ease;
}
.cwd__b:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.cwd__b:active { transform: translateY(1px); }
.cwd__b:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* THE ONE LATCHING KEY. Braun's single orange, spent on the one control that
   holds a state rather than performing an action. */
.cwd__b[aria-pressed="true"] {
    background: var(--accent); color: #fff; border-color: var(--accent);
}
/* var(--paper), NOT #fff. On the dark edition --ink IS white, so this mix
   resolves to a PALE orange — rgb(250,140,64) — and white text on it measures
   2.36:1. The rule two above it already knows this and uses --paper; this one
   was written with a literal and, at (0,3,0), outranks the (0,2,0) rule that
   was put there to fix exactly this. Measured after: 5.29:1 on paper against
   rgb(164,60,18), 6.98:1 on dark against rgb(250,140,64). It also governs the
   inline SVG glyphs on the icon keys, which are stroked in currentColor. */
.cwd__b[aria-pressed="true"]:hover {
    background: color-mix(in srgb, var(--accent) 82%, var(--ink));
    border-color: color-mix(in srgb, var(--accent) 82%, var(--ink));
    color: var(--paper);
}

/* AN AUTHOR `display` BEATS THE UA SHEET'S [hidden] RULE, WHICH IS NOT WHAT
   ANYBODY MEANS BY IT. `.cwd__b { display: inline-flex }` is an author
   declaration; `[hidden] { display: none }` in the browser's own sheet is not,
   and origin beats specificity — so every .cwd__b the page tried to hide with
   the attribute stayed on screen. The queue's own NEXT key (#dk-next) ships
   hidden and is revealed only when something is queued: it was permanently
   visible, and pressing it over an empty queue did nothing at all. The sheet
   already carries this companion for four other classes; these are the two it
   was missing. */
.cwd__b[hidden] { display: none !important; }

/* THE RAIL. Label, ten slots, a name and two keys, all on the one height. */
.cwd__rail {
    display: flex; align-items: center; gap: var(--cwd-gap); flex-wrap: wrap;
    padding-top: .7rem; border-top: 1px solid var(--cwd-rule);
}
.cwd__l--rail { align-self: center; }
.cwd__i--name { flex: 1 1 9rem; min-width: 7rem; max-width: 18rem; width: auto; }
.cwd__rail .cwd__b { flex: 0 0 auto; padding: 0 .85rem; }

/* THE SLOTS. Square, on the desk's height, and numbered — the whole value of
   a rail of ten over a dropdown of ten is seeing which are taken without
   opening anything. */
.cw-slots { display: flex; flex-wrap: wrap; gap: .25rem; }
.cw-slot {
    font: inherit; font-size: .6875rem; font-weight: 600;
    font-variant-numeric: tabular-nums;
    height: var(--cwd-h, 2.125rem); min-width: var(--cwd-h, 2.125rem); padding: 0 .3rem;
    display: inline-flex; align-items: center; justify-content: center;
    box-sizing: border-box;
    color: var(--muted); background: transparent;
    border: 1px solid var(--cwd-rule); border-radius: var(--cwd-r);
    cursor: pointer;
    transition: border-color .16s ease, color .16s ease, background .16s ease;
}
.cw-slot b { font-size: inherit; font-weight: 600; }
/* The name a full slot carries is a title, not a second line — the rail is one
   row high and stays one row high. */
.cw-slot span { display: none; }
.cw-slot.is-full { color: var(--ink); border-color: var(--cwd-edge); }
.cw-slot.is-on {
    color: #fff; background: var(--accent); border-color: var(--accent);
}
.cw-slot:hover { border-color: var(--ink); color: var(--ink); }
.cw-slot.is-on:hover { color: #fff; background: var(--accent); }
.cw-slot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* THE READOUT. The wheel and everything the desk has to say. */
.cwd__read {
    display: flex; align-items: flex-start; gap: .85rem;
    padding-top: .7rem; border-top: 1px solid var(--cwd-rule);
}
.cw-wheel {
    flex: 0 0 auto; padding: 0; border: 0; background: none; cursor: pointer;
    width: 5.25rem;                              /* 84px, from 152 */
    border-radius: 50%; line-height: 0;
    transition: transform .16s ease;
}
.cw-wheel canvas { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; }
.cw-wheel:hover { transform: scale(1.04); }
.cw-wheel:active { transform: scale(.97); }
.cw-wheel:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.cwd__say { flex: 1 1 0; min-width: 0; display: grid; gap: .18rem; align-content: start; }
.cwd__say1 { margin: 0; font-size: .78rem; line-height: 1.45; color: var(--muted); }
.cwd__say2 {
    margin: 0; font-size: .72rem; line-height: 1.4; color: var(--muted);
    display: flex; gap: .5rem; align-items: baseline;
}
.cwd__say2 .cwd__l { flex: 0 0 2.6rem; }
.cwd__say2 b { color: var(--ink); font-weight: 600; }
.cwd__say3 { margin: .1rem 0 0; font-size: .72rem; line-height: 1.4; color: var(--accent); }

/* --------------------------------------------------------------- MOBILE
   TWO columns, not one. Four fields stacked is four rows of a phone screen
   spent before you reach a clock; two by two is one screenful of desk and the
   grid stays symmetric, which is the whole point of it.

   16px ON THE INPUTS, and the test is the POINTER, not the width: iOS Safari
   zooms the page on focus for anything under 16px and then leaves the fixed
   furniture off-screen, and a narrow desktop window does not zoom. */
/* Five columns of select need about a thousand pixels to stay readable; below
   that the desk folds to three and then to two rather than squeezing five
   labels into a hundred pixels each. */
@media (max-width: 1000px) {
    .cwd__fields,
    .cwd__keys { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
    .cwd__fields,
    .cwd__keys { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .cwd__i--name { flex: 1 1 100%; max-width: none; }
    .cwd__read { gap: .7rem; }
    .cw-wheel { width: 4.25rem; }
}
@media (hover: none) and (pointer: coarse) {
    .cwd { --cwd-h: 2.75rem; }
    .cwd__i { font-size: 16px; }
    .cw-slot { min-width: 2.4rem; }
}


/* ==========================================================================
   THE PLACARD — the same desk type, floating
   ==========================================================================
   Set in the desk's own recipe so it reads as part of the instrument rather
   than as a browser tooltip: the 10px/600/.14em label on the left, tabular
   values on the right, one hairline between the head and the table.

   IT IS NOT A .cw-card CHILD. One element for the whole board, moved and
   refilled — see geoShow(). position: fixed rather than absolute, because it
   is placed from getBoundingClientRect(), which is already viewport-relative;
   an absolute placard inside a scrolled page lands a scroll-offset away. */
.cw-geo {
    position: fixed; z-index: 60;
    min-width: 15rem; max-width: min(21rem, calc(100vw - 1rem));
    padding: .6rem .7rem;
    background: var(--paper);
    border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent);
    border-radius: 3px;
    box-shadow: 0 .35rem 1.1rem rgba(0, 0, 0, .13);
    pointer-events: none;                 /* never steal the hover it describes */
}
.cw-geo[hidden] { display: none; }
.cw-geo__h {
    margin: 0; font-size: .82rem; font-weight: 600; line-height: 1.25;
    color: var(--ink);
}
.cw-geo__s {
    margin: .05rem 0 .45rem;
    font-size: .625rem; font-weight: 600; letter-spacing: .12em;
    text-transform: uppercase; color: var(--accent);
}
.cw-geo__dl {
    margin: 0; padding-top: .45rem;
    border-top: 1px solid color-mix(in srgb, var(--ink) 13%, transparent);
    display: grid; grid-template-columns: 4.4rem minmax(0, 1fr);
    gap: .2rem .5rem; align-items: baseline;
}
.cw-geo__dl dt {
    margin: 0; font-size: .5625rem; font-weight: 600; letter-spacing: .13em;
    text-transform: uppercase; color: var(--muted); line-height: 1.5;
}
.cw-geo__dl dd {
    margin: 0; font-size: .7rem; line-height: 1.45; color: var(--ink);
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}
/* NO PLACARD ON A TOUCH SCREEN. There is no hover to open it with, and the
   focusin path would fire it under the finger on a card's own arrows. The
   title= attribute is what answers there, and a long press is how. */
@media (hover: none) { .cw-geo { display: none !important; } }

/* ==========================================================================
   THE BENCH — stopwatches and countdowns
   --------------------------------------------------------------------------
   The face is Orbitron, the same squared instrument type the flight-deck dials
   are set in, so a stopwatch on this page reads as the same machine as the
   clocks above it rather than as a widget bolted on.
   ========================================================================== */
.tm-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .8rem;
    margin: 1.4rem 0 1.2rem; padding-bottom: 1rem;
    border-bottom: 1px solid var(--line);
}
.tm-bar__n {
    margin-left: auto; font-size: .66rem; font-weight: 700; letter-spacing: .12em;
    text-transform: uppercase; color: var(--ink-55); font-variant-numeric: tabular-nums;
}
/* Each way of aiming a countdown is one unbroken group, so a wrap never leaves
   a stray number above the field it belongs to. */
.tm-set { display: inline-flex; align-items: center; gap: .3rem; }
.tm-set__l {
    font-size: .6rem; font-weight: 700; letter-spacing: .12em;
    text-transform: uppercase; color: var(--ink-55);
}
/* SIXTEEN PIXELS on every field: under it, iOS Safari zooms the page on focus.
   The estate's rule; see the note in the shared site.css. */
.tm-set__f, .tm-set__n, .tm-set__u, .tm-set__o {
    font-size: 16px; padding: .34rem .45rem; border-radius: 4px;
    border: 1px solid var(--line); background: var(--paper); color: var(--ink);
}
.tm-set__n { width: 4.4rem; font-variant-numeric: tabular-nums; }
.tm-set__o { max-width: 15rem; }

.tm-grid {
    display: grid; gap: .7rem;
    grid-template-columns: repeat(auto-fill, minmax(min(248px, 100%), 1fr));
}
.tm__none { grid-column: 1 / -1; margin: 0; font-size: .84rem; color: var(--ink-55); max-width: 60ch; }

.tm {
    border: 1px solid var(--line); border-radius: 8px;
    padding: .6rem .65rem; background: var(--paper);
    border-left: 2px solid var(--line);
}
/* A running instrument is marked at its edge and nowhere else — a card that
   changed colour while it ran would be a card that is hard to read while it is
   doing the one thing it is for. */
.tm[data-run="1"] { border-left-color: var(--accent); }
.tm--down[data-over="1"] { border-left-color: var(--taken); }

.tm__h { display: flex; align-items: center; gap: .3rem; }
.tm__lab {
    flex: 1 1 auto; min-width: 0;
    font-family: inherit; font-weight: 600; font-size: .68rem; line-height: 1.2; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ink-55); background: none; border: 0; padding: .1rem 0;
}
.tm__lab:focus { outline: none; color: var(--ink); }
.tm__x {
    flex: 0 0 auto; width: 20px; height: 20px; line-height: 1; font-size: .9rem;
    background: none; border: 0; cursor: pointer; color: var(--ink-55); border-radius: 4px;
}
.tm__x:hover { color: var(--taken); }

/* THE FACE. Tabular figures so the digits do not jitter sideways as they change
   — at a hundredth of a second a proportional face is unreadable. The hundredths
   are smaller and quieter: at the same weight as the seconds, two digits
   changing a hundred times a second drag the eye off the number that matters. */
.tm__face {
    margin: .15rem 0 .1rem; display: flex; align-items: baseline; gap: .12rem;
    font-family: 'Orbitron', ui-monospace, SFMono-Regular, Menlo, monospace;
    font-variant-numeric: tabular-nums; color: var(--ink);
}
.tm__face b { font-size: 1.5rem; font-weight: 700; letter-spacing: -.01em; }
.tm__face i {
    font-style: normal; font-size: .84rem; font-weight: 600;
    color: var(--ink-55); padding-bottom: .08rem;
}
.tm__face i::before { content: '.'; }
.tm--down[data-over="1"] .tm__face b { color: var(--taken); }

.tm__sub {
    margin: 0 0 .45rem; font-size: .64rem; line-height: 1.3; color: var(--ink-55);
    min-height: .9rem;
}
.tm__btns { display: flex; gap: .3rem; }
.tm__b {
    flex: 1 1 auto;
    font-family: inherit; font-weight: 700; font-size: .62rem; line-height: 1; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ink); background: none;
    border: 1px solid var(--line); border-radius: 4px;
    padding: .38rem .3rem; cursor: pointer;
}
.tm__b:hover { border-color: var(--accent); color: var(--accent); }
.tm__b--go { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.tm__b--go:hover { background: var(--accent); border-color: var(--accent); color: var(--paper); }
.tm[data-run="1"] .tm__b--go { background: var(--accent); border-color: var(--accent); }

.tm__laps { list-style: none; margin: .45rem 0 0; padding: 0; max-height: 7.2rem; overflow-y: auto; }
.tm__laps li {
    display: grid; grid-template-columns: 1.3rem 1fr auto; gap: .3rem;
    align-items: baseline; padding: .16rem 0;
    border-top: 1px solid var(--line-soft);
    font-family: 'Orbitron', ui-monospace, Menlo, monospace;
    font-variant-numeric: tabular-nums; font-size: .68rem;
}
.tm__laps i { font-style: normal; font-size: .56rem; color: var(--ink-55); }
.tm__laps span { color: var(--ink-55); font-size: .6rem; }

/* ---------------------------------------------------------- popped out
   The bench alone: no navbar, no other bands, no footer. app.js already sets
   data-cw-pop for the clocks; this adds the one case it did not know about. */
html[data-cw-pop="1"][data-cw-only="bench"] .band:not(#bench),
html[data-cw-pop="1"][data-cw-only="bench"] .nav,
html[data-cw-pop="1"][data-cw-only="bench"] .foot { display: none !important; }

/* THE BENCH HAS TO BE PUT BACK, not merely left alone. The clock pop hides
   every band that does not hold a clock grid — html[data-cw-pop] .band:not(
   :has(.cw-grid)) — and it hides it with !important, so the bench, which has
   no grid, was hidden by the very rule that makes a popped clock work. Setting
   its padding was not enough: a popped bench opened on an empty page. */
html[data-cw-pop="1"][data-cw-only="bench"] #bench { display: block !important; padding: .9rem 0; }
html[data-cw-pop="1"][data-cw-only="bench"] #bench > .wrap { max-width: none; padding: 0 .9rem; }

/* A POPPED BOARD OF CLOCKS IS SIZED TO FIT AND MUST NOT SCROLL; ten timers are
   a list and must. The pop rules lock the document at 100% with overflow
   hidden, which would cut the tenth instrument off the bottom of the window. */
html[data-cw-pop="1"][data-cw-only="bench"],
html[data-cw-pop="1"][data-cw-only="bench"] body { overflow: auto; height: auto; }

/* ---------------------------------------------------------------------------
   THE POPPED BENCH IS THE INSTRUMENTS AND NOTHING ELSE.
   It used to carry the whole desk into the window — add-a-stopwatch, three
   ways to aim a countdown, the pop button that had just been pressed, and the
   tally — above one small card in a 760x680 sheet of empty white. A popped
   window is a thing you park in the corner of a screen and glance at; every
   control in it is a control you already used to make it. So the bar goes, and
   what is left is the size and shape of the stopwatches themselves. The window
   then measures its own content and shrinks to it — see fitPopWindow(). */
html[data-cw-pop="1"][data-cw-only="bench"] .tm-bar { display: none !important; }
html[data-cw-pop="1"][data-cw-only="bench"] #bench { padding: 0 !important; }
html[data-cw-pop="1"][data-cw-only="bench"] #bench > .wrap { max-width: none; padding: 0; }
html[data-cw-pop="1"][data-cw-only="bench"] .tm-grid {
    gap: 8px; padding: 8px; align-content: start;
    grid-template-columns: repeat(auto-fill, minmax(min(228px, 100%), 1fr));
}
/* The card loses the framing it needs on a page and keeps the edge that says
   whether it is running. A popped window has its own frame already. */
html[data-cw-pop="1"][data-cw-only="bench"] .tm { border-radius: 5px; padding: .5rem .55rem; }
/* × removes an instrument from the bench behind this window, which is not what
   a close control in a popped window looks like it does. Closing the window is
   the close control. */
html[data-cw-pop="1"][data-cw-only="bench"] .tm__x { display: none; }
html[data-cw-pop="1"][data-cw-only="bench"] .tm__lab { pointer-events: none; }
html[data-cw-pop="1"][data-cw-only="bench"] #bench .eyebrow,
html[data-cw-pop="1"][data-cw-only="bench"] #bench .d2,
html[data-cw-pop="1"][data-cw-only="bench"] #bench .lede { display: none; }


/* =============================================================================
   THE MOON
   -----------------------------------------------------------------------------
   ONE INK, AND THE DARK EDITION BRINGS ITS OWN PAPER. The disc is engraved, not
   painted: every tone on it is the density of a single ink laid in dots and
   lines. So there is no light-moon colour and no dark-moon colour to keep in
   step — the dark edition fills the lit limb with its own sheet of ivory first
   and then takes the identical plate on top of it. Four tokens, and two of them
   exist only for the dark ground.
   ========================================================================== */
:root {
    --mn-ink:   #141210;                  /* the one ink */
    --mn-sheet: rgba(20, 18, 16, .20);    /* paper: no sheet — this is for the phase marks */
    --mn-night: transparent;              /* paper: the night IS the page */
    --mn-glow:  rgb(150, 162, 192);
    --mn-ashen: rgb(126, 134, 158);
}
html[data-dark] {
    --mn-ink:   #262933;
    --mn-sheet: #EEEADE;                  /* the ivory the dark edition prints on */
    --mn-night: #0A0A0D;
}

.mn { display: grid; gap: 1.6rem; grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
      align-items: start; }
.mn__disc { margin: 0; display: flex; flex-direction: column; gap: .55rem; align-items: center; }
.mn__cv { display: block; width: 100%; max-width: 400px; height: auto; }
.mn__cap { margin: 0; font-size: .72rem; line-height: 1.5; color: var(--muted); text-align: center;
           max-width: 34ch; }
.mn__side { min-width: 0; display: flex; flex-direction: column; gap: .55rem; }
.mn__name { margin: 0; font-size: 1.5rem; font-weight: 700; letter-spacing: -.015em;
            color: var(--ink); line-height: 1.1; }
.mn__illum { margin: 0 0 .3rem; font-size: .88rem; color: var(--muted); }
.mn__illum b { color: var(--ink); font-variant-numeric: tabular-nums; }
.mn__figs { margin-top: .2rem; }
.mn__lbl { margin: .5rem 0 0; font-size: .64rem; font-weight: 700; letter-spacing: .13em;
           text-transform: uppercase; color: var(--muted); }

/* WHAT COMES NEXT — four rows on one grid so the dates line up under each
   other. A ragged column of dates is a column nobody scans. */
.mn__next { list-style: none; margin: .1rem 0 0; padding: 0; display: grid; gap: .3rem; }
.mn__n { display: grid; grid-template-columns: 1.1rem minmax(6.5rem, auto) 1fr auto;
         align-items: baseline; gap: .5rem; font-size: .84rem; }
.mn__nt { color: var(--ink); font-weight: 600; }
.mn__nd { color: var(--muted); font-variant-numeric: tabular-nums; }
.mn__na { color: var(--muted); font-variant-numeric: tabular-nums; font-size: .78rem; }

/* The little disc beside each phase is drawn in CSS: a circle, and a second
   one over it in the page's own ground, offset to cut the crescent. */
.mn__ni { width: .78rem; height: .78rem; border-radius: 50%; display: inline-block;
          align-self: center; border: 1px solid var(--mn-ink); background: var(--mn-sheet);
          opacity: .85; }
.mn__ni[data-ph="new"]   { background: var(--mn-night); }
.mn__ni[data-ph="full"]  { background: var(--mn-sheet); }
.mn__ni[data-ph="first"] { background: linear-gradient(90deg, var(--mn-night) 50%, var(--mn-sheet) 50%); }
.mn__ni[data-ph="last"]  { background: linear-gradient(90deg, var(--mn-sheet) 50%, var(--mn-night) 50%); }

.mn__scrub { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap;
             border-top: 1px solid var(--line-soft); padding-top: 1rem; margin-top: .3rem; }
.mn__scrub .mn__lbl { margin: 0; flex: 0 0 auto; }

/* THE PLAY KEY. A disc with a triangle in it, drawn in CSS rather than shipped
   as a glyph — and it becomes two bars while it is running, because a control
   that looks identical whether or not it is doing something is a control you
   press twice. */
.mn__play {
    flex: 0 0 auto; width: 1.7rem; height: 1.7rem; padding: 0; cursor: pointer;
    border: 1px solid var(--line); border-radius: 50%;
    background: none; color: var(--ink);
    display: inline-flex; align-items: center; justify-content: center;
}
.mn__play:hover { border-color: var(--accent); color: var(--accent); }
.mn__play span {
    width: 0; height: 0; margin-left: .13em;
    border-left: .48em solid currentColor;
    border-top: .30em solid transparent;
    border-bottom: .30em solid transparent;
}
.mn__play[aria-pressed="true"] span {
    margin-left: 0; width: .5em; height: .56em; border: 0;
    background: linear-gradient(90deg, currentColor 0 34%, transparent 34% 66%, currentColor 66% 100%);
}
.mn__range { flex: 1 1 12rem; min-width: 8rem; }
.mn__now { flex: 0 0 auto; }

@media (max-width: 760px) {
    .mn { grid-template-columns: minmax(0, 1fr); }
    .mn__disc { max-width: 100%; }
    .mn__n { grid-template-columns: 1.1rem minmax(5.5rem, auto) 1fr; }
    .mn__na { grid-column: 2 / -1; }
}

/* =============================================================================
   VIZZI
   -----------------------------------------------------------------------------
   THE STAGE IS THE PAGE'S ONE PIECE OF FURNITURE. Everything else is a control
   sitting under it, so the layout rule is simple: the stage keeps a fixed
   aspect until it is asked to fill the screen, and the tiles inside it are a
   square grid that never reflows mid-performance.
   ========================================================================== */
/* THE STAGE HEADING NEEDS AIR UNDER IT. .d2 carries no bottom margin — it is
   used in sixty places and most of them follow it with a lede that has its own
   — but here the next thing is the console's own border, which landed directly
   under the heading's descenders. Scoped, so the other fifty-nine are untouched. */
.vz-hed { margin: 0 0 1.6rem; }

/* TWENTY-FOUR TRACKS, AND THE ROW IS FLUSH BECAUSE THE SPANS SAY SO.

   The desk was four equal columns holding FIVE cells, one of which spanned the
   full width. Five cells cannot tile four tracks: auto-placement put SOURCE
   alone in row one and pushed MODE to row two, so columns two to four of the
   first row — 844 x 54 px — were empty, and column four of the last row another
   269 x 54. Measured at 1280 that is 60,102 px2 of a 396,228 px2 desk: FIFTEEN
   PER CENT of the panel, in two rectangles, and no amount of padding, gap or
   align-content moves it. That is the hole in the screenshot.

   Three runs whose spans sum to the module close the row by construction:
   6 + 8 + 10 = 24. Nothing is left over because nothing can be. */
.vz-desk {
    display: grid; gap: 1rem var(--cwd-gap, .4rem);
    grid-template-columns: repeat(24, minmax(0, 1fr));
    margin-bottom: 1rem;
}
.vz-desk__f--src   { grid-column: span 6; }
.vz-desk__f--stage { grid-column: span 8; }
.vz-desk__f--word  { grid-column: span 10; }
.vz-desk__f--wide  { grid-column: 1 / -1; }
/* the mode band's heading carries Shuffle, because shuffling is something you
   do to MODES — and it keeps the key inside the panel slots.js already watches */
/* IT HAS TO WRAP. Six transport keys at 34px, the word MODE, the mode's name
   and Shuffle come to more than a phone is wide, and the phone rule below —
   `.vz-transport { flex-basis: 100% }`, the estate's give-it-its-own-line
   idiom — only does anything in a WRAPPING container. Without this the row was
   laid out at its max-content width and the last two controls, Repeat and
   Shuffle, sat outside the console's clip: not small, not squeezed, GONE. */
.vz-desk__l--head { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
/* and the track it sits in must be allowed to be narrower than its contents,
   or the wrap never gets the chance to happen */
.vz-desk__f { min-width: 0; grid-template-columns: minmax(0, 1fr); }
/* ONE KEY HEIGHT, NO EXCEPTIONS — this was the last 26px key on a console of
   eighty-two at 34, and one odd key is exactly the kind of thing you cannot
   name but can see. */
.vz-shuf { margin-left: auto; font-size: .58rem; height: var(--cwd-h, 2.125rem); padding: 0 .7rem; }
@media (max-width: 900px) {
    .vz-desk__f--src   { grid-column: span 10; }
    .vz-desk__f--stage { grid-column: span 14; }
    .vz-desk__f--word  { grid-column: 1 / -1; }
}
@media (max-width: 620px) {
    .vz-desk__f--src, .vz-desk__f--stage, .vz-desk__f--word { grid-column: 1 / -1; }
}
.vz-desk__f { display: grid; gap: .28rem; min-width: 0; align-content: start; }
.vz-desk__l {
    font-size: .6rem; font-weight: 700; letter-spacing: .14em;
    text-transform: uppercase; color: var(--muted);
}
.vz-keys { display: flex; gap: .3rem; flex-wrap: wrap; }
.vz-keys .cwd__b { flex: 1 1 auto; }

/* THE DROP ZONE IS THE LABEL, so a tap opens the picker and a drag does the
   same job without one. */
.vz-drop {
    display: block; text-align: center; cursor: pointer;
    border: 1px dashed var(--line); border-radius: 8px;
    padding: .7rem .9rem; margin: 0 0 1rem;
    transition: border-color .16s ease, background .16s ease;
}
.vz-drop b { display: block; font-size: .92rem; color: var(--ink); }
.vz-drop span { display: block; font-size: .76rem; color: var(--muted); margin-top: .12rem; }
.vz-drop:hover, .vz-drop[data-over] { border-color: var(--accent); }
.vz-drop[data-over] { background: color-mix(in srgb, var(--accent) 6%, transparent); }

/* THE ROWS MUST BE NAMED, and this is not tidiness — it was a live defect.

   The stage named its COLUMNS and left the rows to `grid-auto-rows: auto`, so
   each row was sized by its content. The content is a canvas whose intrinsic
   height app.js sets from the PREVIOUS measurement, which is a feedback loop
   with no fixed point: measured on the shipping page, four tiles gave rows of
   145.672 and 99.531 — the top row 46% taller than the bottom — and nine gave
   108.641 / 70.766 / 59.797, an 82% spread, with nine different canvas backing
   heights (80, 97, 108, 62, 67, 70, 56, 58, 59) NOT ONE of which matched its
   own tile. Every tile on the stage was drawing at the wrong resolution and
   being stretched to fit.

   Naming the rows makes the tracks independent of what is in them, and the loop
   has nowhere to run: measured after, 204.609 three times over, every canvas
   274x205. */
.vz-stage {
    /* THE GUTTERS ARE THE ONLY LINES ON THE STAGE, so they are drawn as lines
       and not as margins: one hairline of ground between pictures, not six
       pixels of it. At 6px the grid read as a set of framed thumbnails; at 2px
       it reads as one instrument divided. */
    display: grid; gap: 2px;
    aspect-ratio: 16 / 9;
    /* EDGE TO EDGE. The radius and the console's own padding put a white margin
       and four rounded corners around the picture, which is furniture drawn on
       top of the one thing on the page that is supposed to be looked at. The
       stage runs to the console's edges and the gutters between tiles are the
       only lines in it. */
    background: var(--ink); border-radius: 0; overflow: hidden;
    margin-left: calc(var(--cb-pad, 1.2rem) * -1);
    margin-right: calc(var(--cb-pad, 1.2rem) * -1);
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
}
.vz-stage[data-n="4"]  { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr)); }
.vz-stage[data-n="9"]  { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(3, minmax(0, 1fr)); }
/* TWELVE IS FOUR BY THREE, and that is arithmetic rather than taste. Split a
   stage of aspect A into c columns and r rows and every tile is A*r/c: on the
   16:9 stage 4x3 gives 1.337, 3x4 gives 2.411, 6x2 gives 1.715 and 2x6 gives
   5.554, measured as distance from square. 4x3 wins by 1.28x over its nearest
   rival — and on the 4:3 phone stage the same grid comes out at 0.993, which is
   square. One rule, both stages. */
.vz-stage[data-n="12"] { grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-rows: repeat(3, minmax(0, 1fr)); }
/* FULL SCREEN IS EDGE TO EDGE. The 6px gutter that separates the tiles on the
   page is furniture — it belongs to a stage sitting inside a console with a
   border and a radius. On a whole display it becomes a bright frame drawn
   across the picture, and the UA's own fullscreen backdrop shows through it in
   whatever colour it likes. Nothing between the tiles, and the backdrop
   explicitly painted so no edition can leak white into the gaps. */
.vz-stage:fullscreen { aspect-ratio: auto; width: 100%; height: 100%; border-radius: 0; gap: 0; padding: 0; }
.vz-stage:fullscreen::backdrop { background: #07090C; }
.vz-stage:-webkit-full-screen { aspect-ratio: auto; width: 100%; height: 100%; border-radius: 0; gap: 0; }
/* and a single tile popped out from the right-click menu, same rule */
.vz-tile:fullscreen { width: 100%; height: 100%; }
.vz-tile:fullscreen::backdrop { background: #07090C; }
.vz-tile:fullscreen::after { border-color: transparent; box-shadow: none; }
.vz-tile { position: relative; min-width: 0; min-height: 0; overflow: hidden; cursor: pointer; }
.vz-cv { display: block; width: 100%; height: 100%; }
/* THE TILE'S NAME MOVES TO WHATEVER CORNER IS QUIET — app.js measures all four
   and writes data-corner and data-ink. No blend mode: mix-blend-mode: difference
   is a good trick on a dark ground and a bad one on saturated colour, where it
   returns another mid colour and the word vanishes. A chosen ink with a
   matching halo behind it reads on anything. */
.vz-tile__n {
    position: absolute; margin: 0; pointer-events: none;
    font-size: .56rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
    padding: 2px 5px; border-radius: 3px;
    color: rgba(255, 255, 255, .92);
    background: rgba(0, 0, 0, .34);
    -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
    transition: opacity .18s ease;
}
.vz-tile__n[data-ink="1"] {
    color: rgba(12, 14, 18, .92);
    background: rgba(255, 255, 255, .40);
}
.vz-tile__n[data-corner="bl"], .vz-tile__n:not([data-corner]) { left: 5px; bottom: 5px; top: auto; right: auto; }
.vz-tile__n[data-corner="br"] { right: 5px; bottom: 5px; top: auto; left: auto; }
.vz-tile__n[data-corner="tl"] { left: 5px; top: 5px; bottom: auto; right: auto; }
.vz-tile__n[data-corner="tr"] { right: 5px; top: 5px; bottom: auto; left: auto; }
.vz-stage:fullscreen .vz-tile__n { font-size: .7rem; }

/* THE AIM RING, AND IT IS A PSEUDO-ELEMENT RATHER THAN A BOX-SHADOW.

   An inset box-shadow paints between an element's own background and its CHILD
   CONTENT — and the child here is an opaque canvas filling the tile, so an
   inset ring is drawn and then immediately covered. It is invisible, and it
   looks like the aim is not working rather than like the ring is behind
   something. ::after is a child too, and it comes last, so it paints on top.

   Two rings, not one: the accent has to separate from an arbitrary picture, and
   whichever of the two the tile happens to match, it cannot match both. */
.vz-tile::after {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    border: 1px solid transparent; border-radius: 0;
    transition: border-color .12s ease, box-shadow .12s ease;
}
/* A TRUE HAIRLINE WHERE THE SCREEN CAN DRAW ONE. 1px on a 2× display is two
   device pixels — twice as heavy as the thinnest line that panel can make. */
@media (min-resolution: 2dppx) {
    .vz-tile::after { border-width: .5px; }
}
.vz-tile[data-aim]::after {
    border-color: var(--accent);
    /* the second ring is a shadow now rather than a second border, so the aim
       costs the picture no width at all */
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .34), 0 0 0 0 transparent;
}
/* var(--paper), not #fff: white on the accent measures 2.80:1 on the dark
   edition and fails. --paper inverts with the edition — 5.18:1 on paper,
   6.77:1 on dark. Both measured. */
.vz-tile[data-aim] .vz-tile__n { background: var(--accent); color: var(--paper); }

/* THE METER — what the modes are actually being handed. Without it a still
   picture and a dead microphone look exactly the same, which is the whole
   reason it exists — and it used to say so in a thin line with two thirds of
   the measure empty beside it. Six cells on the panel's own grid, one figure
   each, big enough to read from where you are actually sitting.
   ========================================================================== */
/* NINE TRACKS, BECAUSE THE CELLS ASK FOR NINE. Seven cells, two of which span
   two — bands and sensitivity — is nine track-units, and in a seven-track grid
   that is six cells and one orphan on a second row. Counting them is the whole
   fix. */
.vz-meter {
    display: grid; grid-template-columns: repeat(9, minmax(0, 1fr));
    gap: var(--cwd-gap, .4rem);
    margin: .9rem 0 .2rem; padding-top: .9rem;
    border-top: 1px solid var(--line-soft);
}
.vz-mcell {
    display: grid; gap: .22rem; align-content: start; min-width: 0;
    padding: .45rem .55rem;
    border: 1px solid var(--line); border-radius: var(--cwd-r, 3px);
}
/* the bands cell carries eight bars where the others carry one figure, so it
   takes two tracks — 1+1+2+1+1+1 is seven, and seven is what the mode grid
   above it uses, so the two bands line up */
.vz-mcell--wide { grid-column: span 2; }
.vz-mcell__l {
    display: flex; align-items: center; gap: .35rem;
    font-size: .55rem; font-weight: 700; letter-spacing: .13em;
    text-transform: uppercase; color: var(--muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.vz-mcell__n {
    font-variant-numeric: tabular-nums; font-size: 1.35rem; font-weight: 600;
    line-height: 1; letter-spacing: -.02em; color: var(--ink);
}
.vz-mcell__u { font-size: .55rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.vz-bar { display: block; width: 100%; height: 4px; background: var(--line); border-radius: 2px; overflow: hidden; }
.vz-bar i { display: block; height: 100%; width: 0; background: var(--accent); transition: width .08s linear; }
.vz-bands { display: flex; align-items: flex-end; gap: 3px; height: 34px; }
.vz-bands i { display: block; flex: 1 1 0; min-width: 0; height: 2px; background: var(--accent); border-radius: 1px; transition: height .07s linear; }
.vz-beat {
    width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex: 0 0 auto;
    border: 1px solid var(--line); background: none; transition: background .09s ease;
}
.vz-beat[data-hit] { background: var(--accent); border-color: var(--accent); }
/* THE CELLS ARE SEVEN BUT THE UNITS ARE NINE — Bands and Sensitivity are two
   each — so the count that has to divide the tracks is nine, not seven. At nine
   tracks it is one clean row. At five it was 5 + 4, an orphan unit on the second
   row; at two it was four full rows and a fifth holding Crest alone with a hole
   beside it. Widening one cell at each breakpoint is the whole fix, and it is
   arithmetic rather than taste: 5 + 5 and 2 + 2 + 2 + 2 + 2. */
@media (max-width: 1000px) {
    .vz-meter { grid-template-columns: repeat(5, minmax(0, 1fr)); }
    .vz-mcell--sens { grid-column: span 3; }
}
@media (max-width: 620px)  { .vz-meter { grid-template-columns: repeat(2, minmax(0, 1fr)); }
                             .vz-mcell--wide { grid-column: span 2; } }

.vz-fxl { margin: .9rem 0 .35rem; font-size: .6rem; font-weight: 700;
          letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
/* EIGHT ACROSS, WHICH IS EXACTLY TWO ROWS FOR SIXTEEN EFFECTS. A wrapping
   flex line put fifteen keys on the first row and two on the second — a widow,
   and a different one at every width, which is the ragged edge the panel was
   asked to lose. A grid cannot wrap unevenly: it either fits the track count or
   it does not, and sixteen fits eight twice. NONE moved to the heading to make
   the count come out; it was the seventeenth key and it is a master control
   over the other sixteen, so it did not belong in the rank anyway. */
.vz-fx { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: var(--cwd-gap, .4rem); }
.vz-fx__b { justify-content: center; text-align: center; padding: 0 .3rem; overflow: hidden; }
.vz-fxhead { display: flex; align-items: center; justify-content: space-between; gap: .6rem; margin: .9rem 0 .35rem; }
.vz-fxhead .vz-fxl { margin: 0; }
@media (max-width: 1000px) { .vz-fx { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .vz-fx { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.vz-fx__b {
    font: inherit; font-size: .68rem; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; cursor: pointer;
    /* ONE KEY HEIGHT ON THE WHOLE CONSOLE. Measured at 1280 before this: the
       desk keys and the mode keys were 34.0, the FX keys 29.2 and the slot keys
       31.2 — three heights on a panel whose whole brief was to be one. */
    height: var(--cwd-h, 2.125rem);
    display: inline-flex; align-items: center;
    padding: 0 .6rem; border-radius: var(--cwd-r, 3px);
    border: 1px solid var(--line); background: none; color: var(--muted);
}
.vz-fx__b:hover { border-color: var(--accent); color: var(--accent); }
.vz-fx__b[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/* The catalogue under the stage. */
/* THE CATALOGUE — ONE GRID OF TWENTY-EIGHT, four across and seven down, which
   comes out exactly. Seven per-family grids left five ragged rows between them;
   the family rides on each card instead. */
.vz-cat--flat {
    display: grid; gap: var(--cwd-gap, .4rem);
    grid-template-columns: repeat(4, minmax(0, 1fr));
    margin-top: 1.1rem;
}
.vz-card {
    padding: .6rem .7rem; min-width: 0;
    border: 1px solid var(--line); border-radius: var(--cwd-r, 3px);
}
.vz-card__g {
    margin: 0 0 .3rem; font-size: .52rem; font-weight: 700;
    letter-spacing: .14em; text-transform: uppercase; color: var(--accent);
}
.vz-card__n { margin: 0; font-size: .86rem; font-weight: 600; color: var(--ink); }
.vz-card__l { margin: .15rem 0 0; font-size: .74rem; line-height: 1.42; color: var(--muted); }
@media (max-width: 1000px) { .vz-cat--flat { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .vz-cat--flat { grid-template-columns: minmax(0, 1fr); } }

.vz-cols { display: grid; gap: 2rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.vz-cols p { font-size: .89rem; line-height: 1.62; color: var(--ink-70, var(--muted)); max-width: 62ch; }
.vz-h3 { margin: 1.1rem 0 .3rem; font-size: .95rem; letter-spacing: -.01em; color: var(--ink); }
.vz-h3:first-child { margin-top: 0; }

/* ONE COLUMN ON A PHONE, and it is the four-key rows that force it. Source and
   Stage each hold four keys; in a half-width cell at 320px that is 25px a key,
   which is a quarter of the 44px floor and unusable however tidy it looks. Full
   width gives each of them 57.5px. Nothing else on the desk is harmed by having
   the whole measure — the fields were only ever side by side to save vertical
   room on a desktop. */

@media (max-width: 760px) {
    .vz-cols { grid-template-columns: minmax(0, 1fr); }
    .vz-stage { aspect-ratio: 4 / 3; }
}
/* THE PHONE FORK IS ABOUT SIZE, NOT SHAPE. 4x3 on a 4:3 phone stage is a
   perfect square and only 75px across; a 3:4 portrait stage three tiles wide
   gives the SAME square at 102px — 83% more tile area — measured 102.4 x 101.9
   at 390 wide and 95.6 x 95.1 at 320. Gated on portrait as well as width,
   because at a tablet's 505px of stage the portrait rule would build a 673px
   tower. */
@media (max-width: 560px) and (orientation: portrait) {
    .vz-stage { aspect-ratio: 3 / 4; }
    .vz-stage[data-n="4"]  { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr)); }
    .vz-stage[data-n="9"]  { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(3, minmax(0, 1fr)); }
    .vz-stage[data-n="12"] { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(4, minmax(0, 1fr)); }
}

/* =============================================================================
   THE DECK
   -----------------------------------------------------------------------------
   Laband's desk, in VIZZI's clothes: the same controls, the same honesty about
   what each one does, but sitting under a stage rather than beside a turntable.
   Every row is LABEL / SLIDER / READOUT on one grid, so the eye can run down
   the readouts alone and take in the whole state.
   ========================================================================== */
.dk-top { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 1.2rem;
          align-items: center; margin-bottom: 1.2rem; }

/* The platter is drawn in CSS: a disc, a label, and a spindle. It turns only
   while something is playing, which is the one piece of state on this page you
   can read from across a room. */
/* IT IS A BUTTON NOW, because it does something. A disc that turns while the
   music plays and cannot be touched is a picture of a turntable; one you can
   stop with a finger and drag to find a bar is a turntable. */
.dk-platter {
    -webkit-appearance: none; appearance: none;
    position: relative; width: 92px; height: 92px; flex: 0 0 auto;
    padding: 0; border: 0; background: none; cursor: grab;
    touch-action: none;
}
.dk-platter:active, .dk-platter[data-grab] { cursor: grabbing; }
.dk-platter:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* A RECORD, NOT A STRIPED DISC. The old face was one repeating ring at a
   uniform 3px pitch, which reads as a target rather than as vinyl. A pressing
   has four distinct zones and they are what the eye recognises:

     the LEAD-IN     a smooth band at the rim, no groove in it at all
     the PROGRAMME   the music, grooves packed tight and unevenly — loud
                     passages cut wider, so real vinyl is visibly banded
     the RUN-OUT     smooth again, just outside the label
     the LABEL       paper, and the only colour on the whole thing

   Three stacked repeating gradients at 2px, 3px and 5px give the groove pitch a
   beat rather than a single frequency, which is what stops it reading as a
   pattern. Everything is layered on one element so the whole face turns
   together — except the sheen, which does not (see below). */
.dk-platter__d {
    display: block; width: 92px; height: 92px; border-radius: 50%;
    background:
        /* the label, and the paper ring around the run-out groove */
        radial-gradient(circle at 50% 50%,
            var(--accent) 0 30%, rgba(255,255,255,.10) 30% 31%, transparent 31.5%),
        /* run-out: smooth */
        radial-gradient(circle at 50% 50%, transparent 0 31%,
            rgba(255,255,255,.045) 31% 34%, transparent 34.5%),
        /* the programme, three pitches beating against each other */
        repeating-radial-gradient(circle at 50% 50%,
            rgba(255,255,255,.13) 0 .5px, transparent .5px 2px),
        repeating-radial-gradient(circle at 50% 50%,
            rgba(255,255,255,.07) 0 .5px, transparent .5px 3px),
        repeating-radial-gradient(circle at 50% 50%,
            rgba(0,0,0,.55) 0 1px, transparent 1px 5px),
        /* lead-in: smooth, and a touch lighter where the rim catches the light */
        radial-gradient(circle at 50% 50%, transparent 0 93%,
            rgba(255,255,255,.16) 93% 100%),
        /* the body: vinyl is not flat black, it is black with a sheen in it */
        radial-gradient(circle at 34% 28%, #3A3F45 0%, #16191D 46%, #090B0D 100%);
    box-shadow: 0 1px 5px rgba(0,0,0,.28), inset 0 0 0 1px rgba(0,0,0,.5);
}
/* the spindle, and the small shadow the hole casts into the paper */
.dk-platter__d i {
    position: absolute; left: 50%; top: 50%; width: 6px; height: 6px;
    margin: -3px 0 0 -3px; border-radius: 50%;
    background: var(--paper);
    box-shadow: inset 0 1px 1.5px rgba(0,0,0,.55), 0 0 0 1px rgba(0,0,0,.28);
}
/* THE LIGHT DOES NOT TURN WITH THE RECORD. A highlight drawn onto the disc
   rotates with it, which is the one thing that instantly says "this is a
   picture of a record" rather than a record — the sheen on a real one stays
   where the lamp is while the vinyl moves underneath it. So it lives on the
   BUTTON, over the disc, and never rotates. */
.dk-platter::after {
    content: ''; position: absolute; inset: 0; border-radius: 50%;
    pointer-events: none;
    background:
        linear-gradient(118deg,
            rgba(255,255,255,0) 30%,
            rgba(255,255,255,.11) 44%,
            rgba(255,255,255,.02) 52%,
            rgba(255,255,255,0) 62%);
}
/* and it brightens under the hand, the way a record does when you lean over it */
.dk-platter[data-grab]::after { background:
        linear-gradient(118deg,
            rgba(255,255,255,0) 28%,
            rgba(255,255,255,.17) 44%,
            rgba(255,255,255,.04) 54%,
            rgba(255,255,255,0) 64%); }
@media (prefers-reduced-motion: reduce) { .dk-platter::after { transition: none; } }
/* NO CSS ANIMATION. A keyframe runs on its own clock and cannot be scrubbed,
   paused mid-turn or dragged; the angle is written from the playhead instead,
   which also means the disc is in step with the audio rather than merely near
   it, and a change of speed shows on the label. */
.dk-platter__d { will-change: transform; }
.dk-platter__logo {
    position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    font-size: .42rem; font-weight: 700; letter-spacing: .18em;
    color: var(--paper); pointer-events: none; user-select: none;
    /* it sits on the accent centre label, and turns with the disc because it
       is a child of the thing being rotated */
    text-shadow: 0 0 2px rgba(0,0,0,.35);
}
.dk-platter b {
    position: absolute; left: 50%; bottom: -2px; transform: translateX(-50%);
    font-size: .6rem; font-weight: 700; font-variant-numeric: tabular-nums;
    background: var(--paper); padding: 0 .25rem; border-radius: 2px; color: var(--ink);
}

.dk-now { min-width: 0; }
.dk-now__t { margin: 0; font-size: 1.05rem; font-weight: 700; letter-spacing: -.015em; color: var(--ink); }
.dk-now__s { margin: .1rem 0 .5rem; font-size: .78rem; color: var(--muted); }
.dk-scrub { display: grid; gap: .2rem; margin-bottom: .55rem; }
.dk-times { display: flex; justify-content: space-between; font-size: .7rem;
            font-variant-numeric: tabular-nums; color: var(--muted); }
.dk-keys { display: flex; gap: .3rem; flex-wrap: wrap; }
.dk-play { min-width: 6rem; background: var(--ink); color: var(--paper); border-color: var(--ink); }
.dk-play:hover { background: var(--accent); border-color: var(--accent); }

.dk-grid { display: grid; gap: 1.6rem; grid-template-columns: repeat(3, minmax(0, 1fr));
           border-top: 1px solid var(--line-soft); padding-top: 1.1rem; }
.dk-col { min-width: 0; }
.dk-h { margin: 0 0 .5rem; font-size: .6rem; font-weight: 700; letter-spacing: .14em;
        text-transform: uppercase; color: var(--accent); }
.dk-row { display: grid; grid-template-columns: 3.4rem minmax(0, 1fr) 4.4rem;
          align-items: center; gap: .5rem; margin-bottom: .4rem; font-size: .74rem; }
.dk-row > span { color: var(--muted); font-weight: 700; letter-spacing: .06em;
                 text-transform: uppercase; font-size: .58rem; }
.dk-row > b { text-align: right; font-variant-numeric: tabular-nums; color: var(--ink);
              font-size: .72rem; font-weight: 600; }
.dk-row--sub { opacity: .78; }
.dk-row--sub > span { font-size: .54rem; }
.dk-range { width: 100%; }
.dk-note { margin: .1rem 0 .6rem; font-size: .68rem; line-height: 1.45; color: var(--muted); }
.dk-curve { display: block; width: 100%; height: auto; margin: .5rem 0 .1rem; }
.dk-chips { display: flex; gap: .25rem; flex-wrap: wrap; margin-top: .5rem; }
.dk-chip {
    font: inherit; font-size: .64rem; font-weight: 700; letter-spacing: .06em;
    cursor: pointer; padding: .24rem .5rem; border-radius: 3px;
    border: 1px solid var(--line); background: none; color: var(--muted);
}
.dk-chip:hover { border-color: var(--accent); color: var(--accent); }
.dk-meters { display: flex; gap: .9rem; margin: .7rem 0 .6rem; }
.dk-m { display: flex; flex-direction: column; gap: .05rem; }
.dk-m i { font-style: normal; font-size: .54rem; font-weight: 700; letter-spacing: .13em;
          text-transform: uppercase; color: var(--muted); }
.dk-m b { font-size: .8rem; font-variant-numeric: tabular-nums; color: var(--ink); }
.dk-reset { width: 100%; }

@media (max-width: 960px) { .dk-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) {
    .dk-grid { grid-template-columns: minmax(0, 1fr); }
    .dk-top { grid-template-columns: minmax(0, 1fr); }
    .dk-platter { margin: 0 auto; }
}


/* =============================================================================
   THE CONTROLS — laband's Braun sliders, brought over whole
   -----------------------------------------------------------------------------
   Not re-drawn from scratch: this is the same restyle laband already carries,
   copied with its reasoning intact, because a second implementation of a
   slider is a second set of engine quirks to rediscover. It needs
   assets/js/ranges.js, which writes --lb-p (the value, 0..1) and --lb-o (the
   origin — 0.5 on a bipolar control, 0 otherwise) onto every range, and the
   rules below paint the fill BETWEEN those two. That is what makes a control
   centred on zero fill outward from the middle rather than from the left.
   ========================================================================== */
/* ========================================================== THE SLIDERS
   Ten range inputs on this page — a trim and three EQ bands on every card, and
   pitch, speed, level and three more bands on the deck — and until now not one
   of them was drawn. Every one arrived as the browser's default: a fat thumb
   on a pale groove, on a page that measures its own hairlines. The furthest
   the estate had gone was `accent-color: var(--ink)` at site.css:743, which is
   a colour, not a design; nothing more can be said about a native slider until
   it is taken apart with appearance: none.

   Taken apart, it is drawn the way everything else here is: a 2px rail, a
   small square-shouldered thumb, and the accent spent ONCE, on the filled part
   of the rail — the only thing on the control that is a value.

   THE FILL IS NOT GUESSED. js/ranges.js writes --lb-p (0..1) on each input
   from that input's own min/max/value, and the geometry below turns it into
   the exact pixel the thumb centre stands on. Where the control is BIPOLAR —
   pitch, and all six EQ bands, whose min is the negative of their max — the
   fill grows from the CENTRE outward, because a bar anchored at the left edge
   under a band that is cutting 6 dB states the opposite of what is true. */
input[type="range"] {
    /* The geometry lives in one place because the arithmetic below is DERIVED
       from it. Typed out twice, the thumb stops being centred the first time
       somebody changes a size and edits one of the two copies. */
    --lb-trk: 2px;     /* the rail                    */
    --lb-tw:  10px;    /* the thumb, ALONG the travel */
    --lb-th:  14px;    /* the thumb, ACROSS the rail  */
    --lb-p:   0;       /* where the value is,   0..1 — written by ranges.js */
    --lb-o:   0;       /* where the fill starts, 0..1 — 0 left, .5 centre   */

    --lb-rail: var(--line, rgba(30, 38, 43, .12));
    --lb-fill: var(--accent, #C2410C);
    --lb-tick: transparent;   /* only a bipolar rail gets a centre pip */

    /* THE THUMB TRAVELS INSET BY HALF ITS OWN WIDTH: its centre runs from
       tw/2 to (100% - tw/2), never 0 to 100%. A fill painted at a plain
       `--lb-p * 100%` therefore runs ahead of the thumb at one end and lags it
       at the other by tw/2 — five pixels, which on a 54px per-card slider is a
       fifth of the travel and reads as a control that cannot agree with
       itself. Both stops are computed in the thumb's own space:
              x = tw/2 + p x (100% - tw)                                    */
    --lb-x1: calc(var(--lb-tw) / 2 + min(var(--lb-p), var(--lb-o)) * (100% - var(--lb-tw)));
    --lb-x2: calc(var(--lb-tw) / 2 + max(var(--lb-p), var(--lb-o)) * (100% - var(--lb-tw)));

    -webkit-appearance: none; appearance: none;
    /* 18px = the 14px thumb plus its 2px shoulder above and below, exactly. At
       16px Chrome clips the shoulder against the input box; more reserves
       height the control does not use. */
    height: 18px;
    margin: 0; padding: 0; border: 0;
    background: transparent;
    vertical-align: middle;
    cursor: pointer;
    /* pan-y, so dragging the RAIL moves the value and dragging the page still
       scrolls it. Without this a horizontal drag on a phone does both. */
    touch-action: pan-y;
    -webkit-tap-highlight-color: transparent;
}
/* site.css:742 gives every range `margin: .35rem 0 0`, which is right under a
   stacked dial label and wrong inside a flex row: in .lb-ops__band it pushed
   the slider 5.6px below the LO/MID/HI label it belongs to. The base rule
   zeroes it for everybody; the deck's dials, which do want the air, ask for it
   back. (Note that site.css's own rule is 0,1,1 — the same specificity as the
   base rule above — so this sheet wins only because index.html loads it second
   at line 54. That is already true of every other range declaration here.) */
.dial input[type="range"] { margin-top: .35rem; }

/* ranges.js marks the sliders whose min is the negative of their max. The
   attribute only turns a colour on: the centre pip that says where zero is. */
input[type="range"][data-lb-bi="1"] { --lb-tick: var(--ink-55, rgba(30, 38, 43, .64)); }

/* THE TWO ENGINES CANNOT SHARE A RULE. A selector list containing a pseudo the
   engine does not know is INVALID AND THE WHOLE RULE IS DROPPED, so grouping
   ::-webkit-slider-runnable-track with ::-moz-range-track loses the track in
   both browsers rather than half of it in one. They are written twice on
   purpose; the values come from the same custom properties, so there is still
   only one place to change a number. Counted, rather than asserted: of the 77
   blocks in this restyle Blink parses 67 and drops exactly the 10 that are
   ::-moz- only, and none of the survivors lost a declaration. */
input[type="range"]::-webkit-slider-runnable-track {
    height: var(--lb-trk);
    border: 0; border-radius: 0;   /* a rail is a ruled line, not a pill */
    background-color: var(--lb-rail);
    background-image:
        /* the fill, painted between the origin and the value */
        linear-gradient(to right,
            transparent var(--lb-x1), var(--lb-fill) var(--lb-x1),
            var(--lb-fill) var(--lb-x2), transparent var(--lb-x2)),
        /* and, underneath it, the 1px centre pip — hidden by the thumb while
           the value IS zero, which is the one moment it has nothing to say */
        linear-gradient(to right,
            transparent calc(50% - .5px), var(--lb-tick) calc(50% - .5px),
            var(--lb-tick) calc(50% + .5px), transparent calc(50% + .5px));
}
input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    box-sizing: border-box;   /* so the forced-colors border cannot resize it */
    width: var(--lb-tw); height: var(--lb-th);
    border: 0; border-radius: 1px;
    background: var(--ink, #1E262B);
    /* THE SHOULDER IS STRUCTURAL. Ink on accent measures 2.97:1 in paper and
       2.80:1 in dark — both under the 3:1 a control needs against what is next
       to it — so where the thumb sits on top of its own fill the two would
       smear into one shape. 2px of page ground between them means neither has
       to carry contrast against the other. */
    box-shadow: 0 0 0 2px var(--paper, #ffffff);
    /* CHROME TOP-ALIGNS THE THUMB TO THE TRACK, so against a 2px rail it hangs
       6px low unless it is pulled up by half the difference:
              margin-top = (track - thumb) / 2 = (2 - 14) / 2 = -6px
       At pointer: coarse the thumb is 18px, so the same expression gives -8px,
       and under prefers-contrast: more the rail is 3px, giving -5.5px. That is
       why it is a calc and not a number: three sizes, one line, none of them
       able to drift. WebKit needs it too — at margin-top: 0 the thumb hangs
       below the rail there exactly as it does in Blink. */
    margin-top: calc((var(--lb-trk) - var(--lb-th)) / 2);
    transition: background-color .16s var(--ease), box-shadow .16s var(--ease);
}
/* Firefox centres its own thumb and needs no margin — and must not be given
   one, or it lands 6px high. */
input[type="range"]::-moz-range-track {
    height: var(--lb-trk);
    border: 0; border-radius: 0;
    background-color: var(--lb-rail);
    background-image:
        linear-gradient(to right,
            transparent var(--lb-x1), var(--lb-fill) var(--lb-x1),
            var(--lb-fill) var(--lb-x2), transparent var(--lb-x2)),
        linear-gradient(to right,
            transparent calc(50% - .5px), var(--lb-tick) calc(50% - .5px),
            var(--lb-tick) calc(50% + .5px), transparent calc(50% + .5px));
}
input[type="range"]::-moz-range-thumb {
    box-sizing: border-box;
    width: var(--lb-tw); height: var(--lb-th);
    border: 0; border-radius: 1px;
    background: var(--ink, #1E262B);
    box-shadow: 0 0 0 2px var(--paper, #ffffff);
    transition: background-color .16s var(--ease), box-shadow .16s var(--ease);
}
/* Firefox is the one engine that fills the rail itself, and it is worth
   having: it is the only fill on the page that survives with JavaScript off.
   But it can only ever fill FROM THE LEFT, so on the seven bipolar controls it
   would draw a half-full bar under a band that is doing nothing. There it is
   turned off and the gradient above — which knows where zero is — does the
   work in every engine alike. */
input[type="range"]::-moz-range-progress {
    height: var(--lb-trk);
    border: 0; border-radius: 0;
    background: var(--lb-fill);
}
input[type="range"][data-lb-bi="1"]::-moz-range-progress { background: transparent; }
input[type="range"]::-moz-focus-outer { border: 0; }

/* THE STATES. There is no hover: a colour flicker on a 10px object is not
   information, and the cursor has already said the thing is movable. :active
   is the one moment worth marking — the shoulder takes the accent, so the
   control lights where the hand is touching it and nothing else moves. */
input[type="range"]:active::-webkit-slider-thumb { box-shadow: 0 0 0 2px var(--accent, #C2410C); }
input[type="range"]:active::-moz-range-thumb     { box-shadow: 0 0 0 2px var(--accent, #C2410C); }

/* The house ring at a smaller offset than site.css:749's 4px. The ring sits
   2px + 2px = 4px outside the box; the gap from a slider to the next band's
   label is .34rem = 5.44px, so it clears by 1.44px. At the inherited 4px
   offset it stood 6px proud and struck the label, and a focus ring that
   damages the thing beside it reads as a bug rather than as focus. */
input[type="range"]:focus { outline: none; }
input[type="range"]:focus-visible { outline: 2px solid var(--accent, #C2410C); outline-offset: 2px; }

/* A KEY THAT IS DOING SOMETHING STAYS LIT. Source and FX are STATE, not
   actions: which input is feeding the page, and which effects are on. A control
   that looks the same engaged as idle is one people press twice — so the
   engaged state is a full inversion, not a tint, and it carries a lamp so it
   reads at a glance and in a photograph. */
.cwd__b, .vz-fx__b {
    /* A <button> KEEPS ITS NATIVE CHROME until appearance is taken off it, and
       on the engines that still honour it the platform control paints over any
       background the sheet asks for. Everything else about these keys was
       styled; this was the one property nobody had needed to set until a key
       had to fill. */
    -webkit-appearance: none; appearance: none;
}
.cwd__b[aria-pressed="true"],
.vz-fx__b[aria-pressed="true"] {
    background: var(--ink); border-color: var(--ink); color: var(--paper);
}
.cwd__b[data-src] { position: relative; padding-left: 1.5rem; }
.cwd__b[data-src]::before {
    content: ''; position: absolute; left: .62rem; top: 50%; margin-top: -3px;
    width: 6px; height: 6px; border-radius: 50%;
    border: 1px solid var(--line); background: none;
}
.cwd__b[data-src][aria-pressed="true"]::before {
    background: var(--accent); border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 28%, transparent);
}
.vz-fx__b[aria-pressed="true"]:hover { background: var(--accent); border-color: var(--accent); }
.vz-fx__b--none { border-style: dashed; }

/* ================================================ THE CURTAIN ============== *
   THE ELEMENT AND THE HANDLERS SHIPPED WITHOUT ANY CSS, which is the quietest
   way a feature can be missing: the markup is in the page, the drag handlers
   set data-on exactly as they should, and nothing appears — so it reads as a
   drag that the page ignored rather than as a stylesheet with a hole in it.

   It is pointer-events: none, and that is load-bearing. A full-screen overlay
   that accepts pointer events becomes the drop target itself and swallows the
   file; it also fires dragleave on the document the instant it appears, which
   makes it strobe as the cursor moves.
   ========================================================================== */
.vz-curtain {
    position: fixed; inset: 0; z-index: 1400;
    display: flex; align-items: center; justify-content: center;
    pointer-events: none; opacity: 0;
    background: color-mix(in srgb, var(--paper) 82%, transparent);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    transition: opacity .14s ease;
}
.vz-curtain[data-on] { opacity: 1; }
.vz-curtain__in {
    text-align: center; padding: 2rem 2.6rem;
    border: 2px dashed var(--accent); border-radius: 10px;
    background: var(--paper);
    box-shadow: 0 18px 60px rgba(0, 0, 0, .18);
    transform: scale(.97);
    transition: transform .14s ease;
}
.vz-curtain[data-on] .vz-curtain__in { transform: scale(1); }
.vz-curtain__in b {
    display: block; font-size: 1.35rem; font-weight: 600;
    letter-spacing: -.02em; color: var(--ink);
}
.vz-curtain__in span {
    display: block; margin-top: .3rem; font-size: .84rem; color: var(--muted);
}
@media (prefers-reduced-motion: reduce) {
    .vz-curtain, .vz-curtain__in { transition: none; }
}

/* ============================================== THE TRANSPORT ============== *
   Six glyph keys beside the stage, mirroring the deck's own. They are square
   because a symbol has no natural width, and they sit in the mode heading
   because that is the row you are already looking at while the picture runs.
   ========================================================================== */
.vz-transport { display: inline-flex; gap: 3px; margin-left: auto; }
.vz-tk {
    width: var(--cwd-h, 2.125rem); min-width: var(--cwd-h, 2.125rem);
    padding: 0; flex: 0 0 auto;
}
.vz-tk svg { width: 1rem; height: 1rem; }
/* THE WIDTH HAS TO GROW WITH THE HEIGHT. The house raises --cwd-h to 2.75rem on
   a touch screen, and these keys take their HEIGHT from that token and their
   WIDTH from a second reference to it that the coarse block never re-evaluates
   — so six of the most-used keys on the page ended up 34px wide and 44px tall:
   a target you can hit vertically and miss horizontally, which is the harder of
   the two to notice and the easier of the two to blame on yourself. */
@media (pointer: coarse) {
    .vz-tk { width: 2.75rem; min-width: 2.75rem; }
}
.vz-tk[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--paper); }
/* Shuffle already carried the auto margin that pushed it right; with the
   transport in front of it, the transport takes that job. */
.vz-shuf { margin-left: .4rem; font-size: .58rem; height: var(--cwd-h, 2.125rem); padding: 0 .7rem; }
@media (max-width: 620px) {
    .vz-transport { margin-left: 0; flex-basis: 100%; justify-content: space-between; }
    .vz-shuf { margin-left: 0; }
}

/* =============================================== THE MODE PANEL ============ *
   A FAMILY RAIL OVER A FILTERED GRID. Twenty-six keys laid flat is most of a
   phone screen; behind a <select> they are invisible and ungrouped. One family
   at a time is the only arrangement that is neither, and the grid's own height
   does the work of keeping the panel a fixed object rather than something that
   grows and shrinks under a thumb already reaching for it.

   COLUMN COUNT GATES ON WIDTH, KEY HEIGHT GATES ON POINTER, and keeping those
   two apart is the whole trick. A phone held sideways is 844px wide and still
   coarse: put the column count in the pointer query and that phone gets two
   420px keys. Height in the pointer query can never cause overflow, so it is
   safe there and nowhere else.
   ========================================================================== */
.vz-desk__f--wide { grid-column: 1 / -1; }
.vz-modep { display: grid; gap: var(--cwd-gap, .4rem); min-width: 0; }
/* NINE CHIPS IN A ROW OF EIGHT LEAVES ONE ON ITS OWN, and a whole row of
   whitespace beside it. The rail was fixed at eight because eight divided
   cleanly; adding Reactors made nine and orphaned it. It flows now — the chips
   are only as wide as their words and wrap when they run out of room, so the
   count can change again without redesigning the rail. */
.vz-modeg {
    display: flex; flex-wrap: wrap;
    gap: var(--cwd-gap, .4rem);
}
.vz-modeg .vz-gchip { flex: 0 1 auto; padding: 0 .55rem; }
/* Nine columns rather than seven: the keys are short words and this is the
   difference between four rows of them and three. */
.vz-modek {
    display: grid; grid-template-columns: repeat(9, minmax(0, 1fr));
    gap: var(--cwd-gap, .4rem);
}
/* -------------------------------------------------------- THE INK SWATCHES
   FOURTEEN SQUARES IN TWO ROWS OF SEVEN, in a menu that is otherwise a column
   of words — which is the point: a colour is the one thing on this desk that a
   name cannot tell you. Each swatch carries its OWN look, gradients included,
   so the four that are not flat colours declare themselves before they are
   pressed rather than after.

   The chosen one is ringed rather than filled, because filling it would cover
   the colour it is there to show. */
.vz-sw {
    display: grid; grid-template-columns: repeat(7, 1fr);
    gap: 4px; padding: .3rem .6rem .15rem;
}
.vz-sw__k {
    -webkit-appearance: none; appearance: none;
    width: 100%; aspect-ratio: 1 / 1; min-height: 20px;
    padding: 0; border-radius: 4px; cursor: pointer;
    /* a hairline in the ink's own colour at low alpha, so a white swatch on a
       white menu still has an edge */
    border: 1px solid rgba(128, 128, 128, .38);
    box-shadow: none;
    transition: transform .1s ease, box-shadow .12s ease;
}
.vz-sw__k:hover { transform: scale(1.12); }
.vz-sw__k:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.vz-sw__k[aria-checked="true"] {
    box-shadow: 0 0 0 2px var(--paper), 0 0 0 3.5px var(--accent);
}
.vz-sw__n {
    margin: .3rem .6rem .1rem; text-align: center;
    font-size: .58rem; font-weight: 700; letter-spacing: .12em;
    text-transform: uppercase; color: var(--muted);
}
@media (pointer: coarse) {
    .vz-sw { gap: 6px; }
    .vz-sw__k { min-height: 30px; }
}

/* ------------------------------------------------------ THE TILE STRIP
   Braun keys: square, flat, unlit until they are the answer. The lit key is
   filled with the accent and given a soft halo of the same colour, because on
   a panel where six other things are also outlined a border alone is not a
   difference you see without looking for it. The halo is drawn OUTSIDE the key
   (box-shadow with a negative spread), so it costs no layout and cannot move
   the row when it appears. */
/* SQUARES, NOT TRACKS. Laid on the mode grid the keys stretched to eighty
   pixels wide and twenty-six tall to hold a single digit — thirteen wide flat
   slabs, which is the opposite of the thing being asked for. A fixed square
   that does not care how many of it there are is the Braun answer: the row is
   as long as the stage is big, it wraps on a phone without a rule about it, and
   every key is the same object whether there are four or twelve. */
.vz-tsel[hidden] { display: none !important; }
/* IT RIDES THE HEADING ROW, not a row of its own. Five keys did not justify a
   whole line of panel height, and the strip belongs to the sentence beside it —
   "Mode · Waterfall", and these are the tiles that sentence is about. */
.vz-tsel {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: .3rem;
    margin: 0 .35rem;
}
.vz-tsel__k {
    -webkit-appearance: none; appearance: none;
    font: inherit; font-size: .65rem; font-weight: 700;
    letter-spacing: .02em; text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    /* ONE KEY HEIGHT ACROSS THE ROW. Sharing a line with the six transport keys
       means sharing their height, or the row reads as two rows that failed to
       line up. */
    width: var(--cwd-h, 2.125rem); height: var(--cwd-h, 2.125rem);
    flex: 0 0 auto; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    line-height: 1;
    color: var(--muted); background: none;
    border: 1px solid var(--line); border-radius: var(--cwd-r, 3px);
    cursor: pointer;
    transition: color .12s ease, border-color .12s ease,
                background-color .12s ease, box-shadow .16s ease;
}
.vz-tsel__k:hover { color: var(--accent); border-color: var(--accent); }
.vz-tsel__k[aria-checked="true"] {
    color: var(--paper);
    background: var(--accent);
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent), 0 0 9px -1px var(--accent);
}
/* ALL IS NOT A TILE, so it is not a numbered square. Same height and the same
   key, wider by the three letters it holds, and set off by a gap of its own —
   grouping by space rather than by a rule, which is the only separator that
   costs the panel nothing. */
.vz-tsel__k[data-all] {
    width: auto; min-width: 2.9rem; padding: 0 .5rem;
    letter-spacing: .06em; white-space: nowrap;
    margin-right: .35rem;
}
/* A FINGER IS 44 PIXELS WIDE. A 27px square is a mouse target and a miss on a
   phone, and the fix is not a smaller gap — it is a bigger key and one more
   row. Twelve numbers plus ALL wrap to three rows on a 375px screen, which is
   the honest cost of twelve tiles; at one or four tiles, which is what a phone
   actually runs, it is a single row either way.

   GATED ON THE POINTER, NOT THE WIDTH — a narrow desktop window still has a
   mouse, and every screen ever made is at some point 560px wide. */
@media (pointer: coarse) {
    .vz-tsel { gap: .34rem; }
    .vz-tsel__k { width: 2.75rem; height: 2.75rem; font-size: .72rem; }
    .vz-tsel__k[data-all] { width: auto; min-width: 3.4rem; padding: 0 .6rem; }
}
/* on a phone the strip takes the whole line under the heading, the same way the
   transport does — three things on one line is a desk, not a phone */
@media (max-width: 700px) {
    .vz-tsel { flex-basis: 100%; margin: .15rem 0 0; }
}
.vz-gchip, .vz-mkey {
    -webkit-appearance: none; appearance: none;
    font: inherit; font-size: .625rem; font-weight: 700;
    letter-spacing: .04em; text-transform: uppercase;
    height: var(--cwd-h, 2.125rem); min-width: 0; padding: 0 .3rem;
    display: inline-flex; align-items: center; justify-content: center;
    text-align: center; line-height: 1.1;
    color: var(--muted); background: none;
    border: 1px solid var(--line); border-radius: var(--cwd-r, 3px);
    cursor: pointer; overflow: hidden;
    transition: background .12s, border-color .12s, color .12s;
}
.vz-gchip:hover, .vz-mkey:hover { border-color: var(--accent); color: var(--accent); }
.vz-gchip:focus-visible, .vz-mkey:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A BRAUN LIT STATE IS A FULL INVERSION, never a tint: the shipped accent-on-ink
   lamp measured 2.2:1 on the paper edition, which is a rumour and not an
   indicator. */
/* WHAT IS CHOSEN IS GREEN. The rail was a row of identical outlines whose lit
   one differed only by being filled, and the mode keys used the accent — which
   on this page is also the colour of the aim ring, the live dot and half the
   drawn output. Green says "this one" and is not competing with anything the
   modes themselves draw. */
.vz-gchip[aria-selected="true"] {
    background: transparent; border-color: #15803D; color: #15803D;
    box-shadow: inset 0 0 0 1px #15803D;
}
.vz-mkey[aria-checked="true"] {
    background: transparent; border-color: #15803D; color: #15803D;
    box-shadow: inset 0 0 0 1px #15803D;
}
html[data-dark] .vz-gchip[aria-selected="true"],
html[data-dark] .vz-mkey[aria-checked="true"] {
    border-color: #4ADE80; color: #4ADE80; box-shadow: inset 0 0 0 1px #4ADE80;
}
.vz-mkey[hidden] { display: none; }

@media (max-width: 1180px) { .vz-modek { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .vz-modek { grid-template-columns: repeat(4, minmax(0, 1fr)); }
                             .vz-modeg { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .vz-modek { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* 460 is set by TYPE, not by taste: 'Tuned Between' is the longest label and
   needs about 108px of key, which three columns stop delivering below here. */
@media (max-width: 460px)  { .vz-modek { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ============================================== THE SOURCE KEYS ============ *
   FOUR SQUARE KEYS CARRYING FOUR SYMBOLS. Square because a symbol has no
   natural width and four equal squares are the only arrangement that cannot
   look accidental; and because a square key is the same target in both axes,
   which is what makes the 44px floor easy rather than a special case.

   The lamp is the key itself. A tinted dot on an untinted key measured 2.2:1 on
   the paper edition — not an indicator, a rumour — so the engaged state is the
   full inversion the rest of this panel uses, and the symbol inverts with it
   because it is drawn in currentColor.
   ========================================================================== */
/* FIVE, NOT FOUR. The source row gained the shelf key; a grid pinned at four
   columns would have wrapped it onto a line of its own under the other three.
   Named .vz-quad still because every other rule in this sheet refers to it. */
.vz-quad { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--cwd-gap, .4rem); }
@media (max-width: 420px) { .vz-quad { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.vz-desk__f--stage .vz-keys { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--cwd-gap, .4rem); }
.vz-desk__f--stage .cwd__b { flex: none; min-width: 0; }
.vz-ik {
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0; min-width: 0;
}
.vz-ik svg { width: 1.15rem; height: 1.15rem; display: block; }
/* THE SYMBOL IS THE ONLY THING ON THE KEY, so the key must not also reserve
   room for the label text that is no longer there. */
.vz-ik[data-src]::before { content: none; }
.vz-ik[data-src] { padding-left: 0; }
/* the word that says what the symbol means, live, in the band heading — so the
   glyph is never the only thing telling you what is playing */
.vz-srcw {
    font-weight: 700; letter-spacing: .1em; color: var(--accent);
    margin-left: .45rem;
}

/* ============================================== SENSITIVITY ================ *
   Four keys and a lamp. Not every mode moves the same amount for the same
   music — a band that idles at 0.3 and peaks at 0.5 draws a picture that barely
   breathes, and the reader concludes the MODE is broken rather than that the
   track is quiet there. One gain in front of every mode fixes it in the one
   place it can be fixed without every mode implementing it separately.
   ========================================================================== */
.vz-mcell--sens { grid-column: span 2; }
.vz-sens { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 3px; }
.vzs {
    -webkit-appearance: none; appearance: none;
    font: inherit; font-size: .55rem; font-weight: 700;
    letter-spacing: .07em; text-transform: uppercase;
    padding: .3rem .1rem; min-width: 0;
    border: 1px solid var(--line); border-radius: var(--cwd-r, 3px);
    background: none; color: var(--muted); cursor: pointer;
}
.vzs:hover { border-color: var(--accent); color: var(--accent); }
.vzs[aria-checked="true"] { background: var(--ink); border-color: var(--ink); color: var(--paper); }
/* THE LAMP IS GREEN AND IT IS THE ONLY GREEN ON THE PAGE, which is what makes
   it read as an indicator rather than as decoration. It is lit only while AUTO
   is actually holding the gain — not merely selected. */
.vz-lamp {
    width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex: 0 0 auto;
    border: 1px solid var(--line); background: none;
    transition: background .2s ease, box-shadow .2s ease;
}
.vz-lamp[data-on] {
    background: #34d17a; border-color: #34d17a;
    box-shadow: 0 0 0 2px color-mix(in srgb, #34d17a 26%, transparent);
}
.vz-menu__h--sub { border-top: 1px solid var(--line-soft); border-bottom: 0; margin-top: .1rem; }
.vz-menu__sens { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 3px; padding: 0 .6rem .5rem; }
@media (pointer: coarse) {
    .vzs { min-height: 2.75rem; display: inline-flex; align-items: center; justify-content: center; }
}
@media (max-width: 620px) {
    .vz-mcell--sens { grid-column: span 2; }
    .vz-mcell--crest { grid-column: span 2; }
}

/* ================================================= THE QUEUE =============== *
   It only takes up room once there is something in it. An empty queue is one
   line of type; a full one is a list you can reorder by playing out of it.
   ========================================================================== */
.dk-queue { margin: .9rem 0 .2rem; padding-top: .85rem; border-top: 1px solid var(--line-soft); }
.dk-queue__top { display: flex; align-items: center; justify-content: space-between; gap: .6rem; flex-wrap: wrap; }
.dk-queue__top p { margin: 0; }
.dk-qlist { list-style: none; margin: .6rem 0 0; padding: 0; display: grid; gap: .3rem; }
.dk-qitem {
    display: flex; align-items: center; gap: .5rem;
    padding: .32rem .3rem .32rem .6rem;
    border: 1px solid var(--line); border-radius: var(--cwd-r, 3px);
    counter-increment: q;
}
.dk-qitem::before {
    content: counter(q); flex: 0 0 auto;
    font-size: .6rem; font-weight: 700; color: var(--muted);
    font-variant-numeric: tabular-nums; min-width: 1.1em;
}
.dk-qitem__n {
    flex: 1 1 auto; min-width: 0; font-size: .8rem; color: var(--ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dk-qitem__b {
    -webkit-appearance: none; appearance: none; flex: 0 0 auto;
    font: inherit; font-size: .62rem; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase;
    padding: .3rem .55rem; border-radius: var(--cwd-r, 3px);
    border: 1px solid var(--line); background: none; color: var(--muted);
    cursor: pointer;
}
.dk-qitem__b:hover { border-color: var(--accent); color: var(--accent); }
.dk-qitem__b--x { font-size: .95rem; line-height: 1; padding: .2rem .5rem; }
/* the one key on the queue row that DELETES something was also the smallest —
   26px wide, beside a 44px-tall neighbour */
@media (pointer: coarse) {
    .dk-qitem__b--x { min-width: 2.75rem; padding: .2rem .6rem; }
}
.dk-qhint { margin: .55rem 0 0; font-size: .72rem; color: var(--muted); }
/* THE QUEUE'S OWN DROP TARGET. The page-wide drop has to guess play-or-queue
   from whether something is already running; dropping on this cannot be
   ambiguous, which is the whole reason it is worth the room. */
.dk-qdrop {
    display: block; margin-top: .6rem; padding: .8rem .9rem;
    text-align: center; cursor: pointer;
    border: 1px dashed var(--line); border-radius: var(--cwd-r, 3px);
    transition: border-color .14s ease, background .14s ease;
}
.dk-qdrop b { display: block; font-size: .82rem; color: var(--ink); }
.dk-qdrop span { display: block; font-size: .7rem; color: var(--muted); margin-top: .15rem; }
.dk-qdrop:hover, .dk-qdrop[data-over] { border-color: var(--accent); }
.dk-qdrop[data-over] { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.dk-qdrop[data-over] b { color: var(--accent); }
.dk-qlist { counter-reset: q; }
@media (pointer: coarse) {
    .dk-qitem__b { min-height: 2.75rem; display: inline-flex; align-items: center; }
}

/* ============================================ THE WORD CONTROLS ============ *
   A checkbox and a right-click. The word belongs to one mode out of
   twenty-eight and the reader who typed it usually wants to see it over
   whatever is drawing — so the checkbox promotes it, and the face is a
   right-click on the field itself, which is where you are when you decide it
   looks wrong.
   ========================================================================== */
.vz-chk {
    display: inline-flex; align-items: center; gap: .3rem;
    margin-left: auto; cursor: pointer;
    font-size: .55rem; font-weight: 700; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted);
}
.vz-chk input {
    -webkit-appearance: none; appearance: none;
    width: 13px; height: 13px; margin: 0; flex: 0 0 auto;
    border: 1px solid var(--line); border-radius: 2px;
    background: none; cursor: pointer; position: relative;
}
.vz-chk input:checked { background: var(--accent); border-color: var(--accent); }
/* A 13px BOX IS A MOUSE TARGET. On a touch screen the whole label is made the
   target instead of the box: the box stays 13px so the row still looks like a
   line of type, and the tappable area becomes the label around it, which is
   what a <label> is for and what everybody aims at anyway. */
@media (pointer: coarse) {
    .vz-chk { min-height: 2.75rem; padding: .35rem 0; }
    .vz-chk input { width: 18px; height: 18px; }
    .vz-chk input:checked::after { left: 5.5px; top: 2px; width: 5px; height: 10px; }
}
/* the tick is drawn, not a glyph — a character would sit differently in every
   face the reader might have installed */
.vz-chk input:checked::after {
    content: ''; position: absolute; left: 3.5px; top: 0.5px;
    width: 4px; height: 8px; border: solid var(--paper);
    border-width: 0 2px 2px 0; transform: rotate(42deg);
}
.vz-chk input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.vz-chk:hover { color: var(--accent); }
/* each face previews itself in the picker, which is the only way to choose one */
.vz-menu__i--font b { font-size: .95rem; font-weight: 600; }

/* ============================================== THE TILE MENU ============== *
   Right-click on a tile. It names the tile it is about before any verb, because
   a menu that opens over twelve near-identical pictures and does not say which
   one it belongs to is a menu you have to guess at.
   ========================================================================== */
/* A FIXED ELEMENT DOES NOT SCROLL WITH THE PAGE, so anything below the fold of
   a fixed menu can never be reached — and the tile menu is ~760px tall at
   pointer:coarse, on a phone whose viewport is ~745px. Everything from the
   sensitivity keys down was unreachable at the SHIPPED DEFAULT of one tile.
   It scrolls inside itself now, which is the only thing a fixed panel can do. */
.vz-menu {
    position: fixed; z-index: 1200; min-width: 15rem; max-width: 19rem;
    max-height: calc(100vh - 16px);
    max-height: calc(100dvh - 16px);
    overflow-y: auto; -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: .3rem; margin: 0;
    background: var(--paper); color: var(--ink);
    border: 1px solid var(--line);
    border-radius: 6px;
    box-shadow: 0 10px 34px rgba(0, 0, 0, .30), 0 2px 6px rgba(0, 0, 0, .18);
}
.vz-menu__h {
    margin: 0; padding: .5rem .6rem .45rem;
    font-size: .58rem; font-weight: 700; letter-spacing: .13em;
    text-transform: uppercase; color: var(--accent);
    border-bottom: 1px solid var(--line-soft);
}
.vz-menu__s { height: 1px; background: var(--line-soft); margin: .25rem .3rem; }
.vz-menu__i {
    -webkit-appearance: none; appearance: none;
    display: block; width: 100%; text-align: left;
    font: inherit; padding: .42rem .6rem;
    background: none; border: 0; border-radius: 3px;
    color: var(--ink); cursor: pointer;
}
.vz-menu__i b { display: block; font-size: .82rem; font-weight: 500; }
.vz-menu__i span { display: block; font-size: .68rem; color: var(--muted); margin-top: .05rem; }
.vz-menu__i:hover, .vz-menu__i:focus-visible {
    background: var(--accent); outline: none;
}
/* the only literal colour in this block was #fff on the accent, which is
   2.80:1 on dark. --paper is the token that inverts with the edition. */
.vz-menu__i:hover b, .vz-menu__i:focus-visible b,
.vz-menu__i:hover span, .vz-menu__i:focus-visible span { color: var(--paper); }
@media (pointer: coarse) {
    .vz-menu { min-width: 17rem; }
    .vz-menu__i { padding: .7rem .7rem; }
}

/* ================================================ TOUCH TARGETS ============ *
   FORTY-FOUR PIXELS, GATED ON THE POINTER AND NOT ON THE WIDTH.

   The .cwd recipe already carries a coarse bump — `.cwd { --cwd-h: 2.75rem }` —
   and it has never once fired on this page, because the bump is declared on
   .cwd and this page has no element with that class. Every key therefore
   shipped at its 34px desktop height on every phone, against Apple's 44px
   floor. .cwd__b sets `height` outright, so a `min-height` cannot beat it: the
   height itself has to be raised, and it has to be raised by a rule that wins
   on specificity rather than by repointing a token nothing here reads.

   THE GATE IS (pointer: coarse), NEVER A WIDTH. A phone held sideways is 844px
   and still needs 44px keys; a 500px browser window on a desktop does not. And
   the input's font-size rides the same gate for the same reason — under 16px
   iOS Safari zooms on focus and carries the panel off the screen.
   ========================================================================== */
@media (pointer: coarse) {
    .console .cwd__b,
    .console .cwd__i,
    .console .vz-fx__b,
    .console .vz-gchip,
    .console .vz-mkey { height: 2.75rem; min-height: 2.75rem; }
    .console .cwd__i { font-size: 16px; }
    .console .vz-fx__b { padding-left: .7rem; padding-right: .7rem; }
    /* THE DECK IS PART OF THE PAGE TOO. Its sliders measured 18px tall and its
       preset chips 26px — a range input is the hardest control on any page to
       hit with a thumb, because the target is the THUMB and not the track, and
       an 18px track puts that thumb inside a 12px circle. The track grows and
       the chips come up to the floor with everything else. */
    .console .dk-range { height: 2.75rem; }
    /* height AND width — the coarse block raised one and left the other, so the
       preset chips came out 38-40px wide against a 44px height */
    .console .dk-chip {
        min-height: 2.75rem; min-width: 2.75rem;
        display: inline-flex; align-items: center; justify-content: center;
    }
}

/* ================================================== THE TEN SLOTS ========== *
   A rack of ten numbered keys, each holding an entire desk. They are RADIOS:
   at most one is lit, and it goes dark the moment any control moves, because
   at that point you are no longer in that slot — you are somewhere near it.
   A key that stayed lit while the settings drifted underneath it would be
   telling you something untrue.
   ========================================================================== */
.vz-slotbar {
    display: flex; align-items: center; gap: .5rem;
    flex-wrap: wrap; margin: .9rem 0 .2rem;
}
.vz-fxl--in { margin: 0; }
/* THE RACK IS A GRID, AND THAT IS A BUG FIX, NOT A TIDY-UP.

   It was a flex row of ten RIGID 27.19px keys with 3px gaps — 298.88px that
   cannot shrink — living in a 249.2px panel at 320px. Flex happily overflows
   its container, the console carries overflow: hidden, and the surplus was
   silently eaten: measured at 320px the console's right edge is at x=300 while
   slot 9 sits at 276.9..304.1 and slot 10 at 307.1..334.3. TWO OF THE TEN
   SLOTS, and an FX key beside them, were off the page and untappable on every
   phone under about 370px — and the document reported no horizontal overflow
   at all, so nothing pointed at it.

   minmax(0, 1fr) tracks divide what is actually there and cannot do this by
   construction. Ten 44px columns need 10*44 + 9*6.4 = 497.6px of panel, so
   below that the rack folds to five and takes two rows. */
.vz-slots {
    display: grid; grid-template-columns: repeat(10, minmax(0, 1fr));
    gap: .4rem; flex: 1 1 auto; min-width: 0;
}
/* AND IT TAKES ITS OWN LINE. Sharing the bar with the SAVE label, the two
   keys and the status word left the rack 149.5px of a 249.2px panel — five
   columns of 24.8px, legible but not tappable. On its own line the same five
   columns measure 44.7px, which clears the 44px floor. */
@media (max-width: 620px) {
    .vz-slots { grid-template-columns: repeat(5, minmax(0, 1fr)); flex-basis: 100%; order: 2; }
    .vz-slotbar .vz-fxl--in { order: 1; }
    .vz-slot__k { order: 3; flex: 1 1 auto; }
    .vz-slot__w { order: 4; }
}
.vz-slot {
    -webkit-appearance: none; appearance: none;
    width: auto; min-width: 0; height: var(--cwd-h, 2.125rem); padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    font: 700 .72rem/1 var(--mono, ui-monospace, Menlo, monospace);
    letter-spacing: 0;
    color: var(--muted); background: none;
    border: 1px solid var(--line); border-radius: 3px;
    cursor: pointer; position: relative;
    transition: background .12s, border-color .12s, color .12s;
}
.vz-slot:hover { border-color: var(--accent); color: var(--accent); }
.vz-slot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A FILLED SLOT LOOKS DIFFERENT FROM AN EMPTY ONE even when it is not
   selected, so the rack tells you what you have without being pressed. */
.vz-slot[data-full="1"] { color: var(--ink); border-color: var(--ink); }
.vz-slot[data-full="1"]::after {
    content: ''; position: absolute; left: 50%; bottom: 3px; margin-left: -3px;
    width: 6px; height: 2px; border-radius: 1px; background: var(--accent);
}
.vz-slot[aria-checked="true"] {
    background: var(--ink); border-color: var(--ink); color: var(--paper);
}
.vz-slot[aria-checked="true"]::after { background: var(--paper); }
.vz-slot__k { font-size: .62rem; }
.vz-slot__w {
    margin: 0; font-size: .66rem; color: var(--muted);
    font-family: var(--mono, ui-monospace, Menlo, monospace);
}
/* KEY SIZE GATES ON POINTER, NEVER ON WIDTH. A phone held sideways is 844px
   wide and still reports a coarse pointer; a 640px browser window on a desktop
   has a mouse. Shrinking on width gets both backwards — it makes the phone's
   keys small exactly where there is room, and leaves the desktop's alone. */
@media (pointer: coarse) {
    .vz-slot { height: 2.75rem; font-size: .72rem; }
    .vz-slot__w { flex-basis: 100%; }
}
@media (max-width: 640px) { .vz-slot__w { flex-basis: 100%; } }

/* THE CHIP THAT HOLDS THE LIT MODE, when you are looking at another family.
   The rail used to jump to it; now it just says where the mode went, so the
   filter you chose is the filter you keep. */
.vz-gchip.is-home::after {
    content: ''; display: inline-block; width: 4px; height: 4px; margin-left: .3rem;
    border-radius: 50%; background: var(--accent); vertical-align: middle;
}

/* THE PLUS AND MINUS KEYS on the platter rows, and the keep-pitch switch. */
/* The key, the slider and the key as ONE cell of the row. */
.dk-set { display: flex; align-items: center; gap: .3rem; min-width: 0; }
.dk-set .dk-range { flex: 1 1 auto; min-width: 0; }
.dk-note--tip { cursor: help; border-bottom: 1px dotted var(--line); display: inline-block; }
.dk-pm__b {
    appearance: none; -webkit-appearance: none;
    width: 18px; height: 18px; padding: 0; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: 1px solid var(--line); border-radius: 5px;
    font: inherit; font-size: .72rem; font-weight: 700; line-height: 1;
    color: var(--ink-55); cursor: pointer;
    touch-action: manipulation; -webkit-tap-highlight-color: transparent;
    transition: color .15s var(--ease), border-color .15s var(--ease);
}
.dk-pm__b:hover { color: var(--accent); border-color: var(--accent); }
.dk-pm__b:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
@media (pointer: coarse) { .dk-pm__b { width: 30px; height: 30px; } }

/* ----------------------------------------------------------------- SHELF ---
   The music folder, as a compact panel. It sits over the stage rather than
   pushing it: a browser that reflows the instrument every time you open it is
   a browser you stop opening.
   -------------------------------------------------------------------------- */
.vz-shelf {
    position: absolute; z-index: 40; right: .6rem; top: .6rem;
    width: min(22rem, calc(100% - 1.2rem));
    background: var(--paper); border: 1px solid var(--line); border-radius: 10px;
    box-shadow: 0 18px 44px rgba(20, 24, 30, .18), 0 2px 6px rgba(20, 24, 30, .08);
    overflow: hidden;
}
html[data-dark] .vz-shelf { box-shadow: 0 18px 44px rgba(0, 0, 0, .6), 0 2px 6px rgba(0, 0, 0, .45); }
.vz-shelf[hidden] { display: none; }
.vz-shelf__bar {
    display: flex; align-items: center; gap: .4rem;
    padding: .4rem .5rem; border-bottom: 1px solid var(--line-soft, rgba(30,38,43,.07));
}
.vz-shelf__t { font-size: .62rem; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; }
.vz-shelf__q {
    flex: 1 1 auto; min-width: 0;
    /* 16px on a touch screen or iOS Safari zooms the page and carries the
       panel off with it — the estate has been here before. */
    font: inherit; font-size: 16px; padding: .22rem .4rem;
    background: transparent; border: 1px solid var(--line); border-radius: 6px; color: var(--ink);
}
@media (pointer: fine) { .vz-shelf__q { font-size: .72rem; } }
.vz-shelf__n { font-size: .58rem; color: var(--ink-55); white-space: nowrap; font-variant-numeric: tabular-nums; }
.vz-shelf__x {
    appearance: none; -webkit-appearance: none; background: transparent; border: 0;
    font: inherit; font-size: 1rem; line-height: 1; color: var(--ink-55); cursor: pointer; padding: 0 .2rem;
}
.vz-shelf__x:hover { color: var(--accent); }
.vz-shelf__gate { display: flex; align-items: center; gap: .35rem; padding: .5rem; flex-wrap: wrap; }
.vz-shelf__gate[hidden] { display: none; }
.vz-shelf__pw {
    flex: 1 1 6rem; min-width: 0; font: inherit; font-size: 16px;
    padding: .3rem .45rem; background: transparent;
    border: 1px solid var(--line); border-radius: 6px; color: var(--ink);
}
@media (pointer: fine) { .vz-shelf__pw { font-size: .78rem; } }
.vz-shelf__go {
    appearance: none; -webkit-appearance: none; cursor: pointer;
    font: inherit; font-size: .7rem; font-weight: 700; padding: .32rem .6rem;
    background: var(--ink); color: var(--paper); border: 0; border-radius: 6px;
}
.vz-shelf__err { flex: 1 1 100%; font-size: .64rem; color: var(--accent); min-height: .8rem; }
.vz-shelf__list { max-height: 15rem; overflow-y: auto; overscroll-behavior: contain; padding: .25rem; }
.vz-shelf__list[hidden] { display: none; }
.vz-shelf__r {
    appearance: none; -webkit-appearance: none; cursor: pointer;
    display: flex; align-items: baseline; justify-content: space-between; gap: .5rem;
    width: 100%; text-align: left; padding: .3rem .4rem; border: 0; border-radius: 5px;
    background: transparent; font: inherit; color: var(--ink);
}
.vz-shelf__r:hover, .vz-shelf__r:focus-visible { background: var(--line-soft, rgba(30,38,43,.07)); outline: 0; }
.vz-shelf__r b {
    font-size: .72rem; font-weight: 600; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vz-shelf__r span { font-size: .58rem; color: var(--ink-55); white-space: nowrap; font-variant-numeric: tabular-nums; }
.vz-shelf__none { padding: .6rem .4rem; font-size: .68rem; color: var(--ink-55); }

/* THE MODE PAGER. Same key shape as the family chips, one row under the grid. */
.vz-modepg { display: flex; gap: 3px; margin-top: .3rem; }
.vz-modepg[hidden] { display: none; }
.vz-pg {
    appearance: none; -webkit-appearance: none; cursor: pointer;
    min-width: 22px; height: 20px; padding: 0 .3rem;
    background: transparent; border: 1px solid var(--line); border-radius: 5px;
    font: inherit; font-size: .58rem; font-weight: 800;
    font-variant-numeric: tabular-nums; color: var(--ink-55);
}
.vz-pg[aria-selected="true"] { color: var(--accent); border-color: var(--accent); }
.vz-pg:hover { color: var(--ink); }
@media (pointer: coarse) { .vz-pg { min-width: 34px; height: 30px; } }

/* -------------------------------------------------- THE PLATTER, UP TOP ----
   Pitch and speed beside the transport: a reading with a key either side. Same
   height as the transport keys so the bar stays one line.
   -------------------------------------------------------------------------- */
.vz-plat { display: inline-flex; align-items: center; gap: .45rem; }
.vz-plat__c {
    display: inline-flex; align-items: center; gap: .15rem;
    border: 1px solid var(--line); border-radius: var(--cwd-r, 3px);
    padding: 0 .2rem; height: var(--cwd-h, 2.125rem);
}
.vz-plat__b {
    -webkit-appearance: none; appearance: none; cursor: pointer;
    width: 17px; height: 100%; padding: 0; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    background: none; border: 0; color: var(--muted);
    font: inherit; font-size: .8rem; font-weight: 700; line-height: 1;
}
.vz-plat__b:hover { color: var(--accent); }
.vz-plat__b:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* THE READING IS THE POINT OF THE CLUSTER, so it is the only part set in the
   ink colour and in tabular figures — it changes on every press and must not
   jitter the two keys either side of it. */
.vz-plat__v {
    min-width: 2.9rem; text-align: center;
    font-variant-numeric: tabular-nums; font-size: .7rem; font-weight: 700;
    color: var(--ink); cursor: help;
}
.vz-plat__l { font-style: normal; font-size: .55rem; font-weight: 700;
              color: var(--muted); padding-right: .15rem; }
@media (max-width: 900px) { .vz-plat { display: none; } }

/* ------------------------------------------------------- THE LIVE BAR ------
   One line, and deliberately thin: it sits between the desk and the stage and
   must not take height from either.
   -------------------------------------------------------------------------- */
/* .vz-live, NOT .vz-bar — that name is already the meter's 4px level bar in
   this same sheet, and taking it handed this strip `display: block`, a
   four-pixel height and `overflow: hidden`. The collision is silent: nothing
   errors, the strip just collapses to a grey capsule with its own text cut off
   inside it, which reads as a padding bug and is not one. The sheet had a
   comment warning about exactly this, two thousand lines up.

   NO GROUND OF ITS OWN. It sits between the desk and the stage and is a line of
   type, not a panel — a filled pill there is a third box competing with the two
   real ones either side of it. Transparent, one hairline under it, and the grid
   gap taken out so it belongs to the stage it describes rather than floating
   between them. */
.vz-live {
    display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
    margin: -.95rem 0 -.6rem; padding: .26rem .1rem;
    background: transparent;
    border: 0; border-bottom: 1px solid var(--line-soft, rgba(30,38,43,.07));
    border-radius: 0;
    font-size: .66rem; line-height: 1.3; color: var(--muted);
}
.vz-live__dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto; background: var(--muted); }
.vz-live.is-live .vz-live__dot { background: #15803D; animation: vz-pulse 1.8s ease-in-out infinite; }
html[data-dark] .vz-live.is-live .vz-live__dot { background: #4ADE80; }
@keyframes vz-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .vz-live.is-live .vz-live__dot { animation: none; } }
.vz-live__m { font-size: .7rem; font-weight: 800; letter-spacing: -.01em; color: var(--ink); }
.vz-live__d { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 8rem; }
.vz-live__d[hidden] { display: none; }
.vz-live__n { font-variant-numeric: tabular-nums; white-space: nowrap; }
.vz-live__src { margin-left: auto; opacity: .8; white-space: nowrap; }
@media (max-width: 620px) { .vz-live__d { display: none; } .vz-live__src { margin-left: 0; } }

/* THE MIDDLE OF THE HEAD ROW. Mode at one end, transport at the other, and
   what is playing plus what it is playing at between them — the two readings
   you want while actually watching the stage. */
.vz-mid { display: flex; align-items: center; gap: .5rem; margin: 0 auto; min-width: 0; }
.vz-mid__t {
    max-width: 15rem; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: .68rem; font-weight: 700; letter-spacing: -.01em; color: var(--ink);
}
@media (max-width: 1100px) { .vz-mid__t { max-width: 9rem; } }
@media (max-width: 900px)  { .vz-mid { margin: 0; } .vz-mid__t { display: none; } }

/* ------------------------------------------------- THE DESK, TIGHTENED -----
   The console body is a grid with a 1.15rem gap, which is right between the
   big blocks and far too much between the parts of one control. The mode
   panel, its pager and the strip under it are ONE thing and now read as one.
   -------------------------------------------------------------------------- */
.vz-modepg { margin-top: .25rem; }
/* the pager was the last row of the mode panel and the grid gap put a band of
   nothing under it before the stage */
.vz-modep { margin-bottom: -.35rem; }

/* THE TRANSPORT AS ONE OBJECT. Six keys, a repeat, a dice and Shuffle were
   nine separate outlines with air between them, which reads as nine unrelated
   controls rather than one transport. They are grouped: the moving keys in a
   single bordered cluster with hairlines between them, the two toggles beside
   it, Shuffle last. */
.vz-transport {
    display: inline-flex; align-items: center; gap: 0;
    border: 1px solid var(--line); border-radius: var(--cwd-r, 3px);
    overflow: hidden;
}
.vz-transport .vz-tk {
    border: 0; border-right: 1px solid var(--line); border-radius: 0;
    margin: 0;
}
.vz-transport .vz-tk:last-child { border-right: 0; }
.vz-transport .vz-tk:hover { background: var(--line-soft, rgba(30,38,43,.05)); }

/* THE SMALL EQ. Eight bars, the height of the controls beside it. */
.vz-eq {
    display: block; width: 56px; height: var(--cwd-h, 2.125rem);
    border: 1px solid var(--line); border-radius: var(--cwd-r, 3px);
}
@media (max-width: 1000px) { .vz-eq { display: none; } }
