/* =============================================================================
   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. */
@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(64px, 9vw, 128px);
    --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 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;
}
.console__head {
    display: flex; align-items: center; flex-wrap: wrap; gap: .8rem;
    padding: .9rem 1.2rem; border-bottom: 1px solid var(--line);
    background: var(--paper);
}
.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;
}
.console__body { padding: 1.2rem; display: grid; gap: 1.15rem; }
@media (max-width: 620px) { .console__body { padding: .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. */
.cwd__fields {
    display: grid; gap: var(--cwd-gap);
    grid-template-columns: repeat(4, minmax(0, 1fr));
}
.cwd__f { display: grid; gap: .28rem; min-width: 0; }

.cwd__i {
    font: inherit; font-size: .8125rem; font-weight: 500;
    height: var(--cwd-h); 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);
}
.cwd__i::placeholder { color: color-mix(in srgb, var(--ink) 42%, 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(4, 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);
}
.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: #fff;
}

/* 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. */
@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; } }


/* A NOTE ON --cwd-h AND WHY IT CARRIES A FALLBACK.
   The desk's tokens are declared on .cwd, which is right — they describe that
   panel. But .cwd__b is a BUTTON SPEC, and this property uses one outside the
   panel: the "Load more" key under the table. Out there the custom property is
   unset, height resolves to nothing, and the key came out 18px tall next to a
   row of 34s. Every declaration that a .cwd__b might need beyond the panel now
   names its own default, so the class travels. */

/* ==========================================================================
   THE REGISTER TABLE AND THE LOT SHEET
   ==========================================================================
   The desk above this (.cwd) is the estate's shared Braun panel — one control
   height, one radius, one label recipe. Everything below is the two things
   this property adds: a table of forty-nine thousand lots and a sheet for one.

   THE TABLE SCROLLS INSIDE ITSELF, NOT THE PAGE. Eight columns of address,
   money and floor area will not fit a phone, and the alternative to a
   horizontally scrolling table is either a table with an unreadable address
   column or a document that scrolls sideways — which drags every fixed
   element on the page with it. */
.cre-tablewrap {
    margin: .9rem 0 0;
    overflow-x: auto;
    border: 1px solid color-mix(in srgb, var(--ink) 13%, transparent);
    border-radius: 3px;
    -webkit-overflow-scrolling: touch;
}
.cre-t {
    width: 100%; border-collapse: collapse;
    font-size: .8125rem;
    /* A minimum, so the columns do not crush before the wrapper starts to
       scroll. Below this width the address becomes one letter per line. */
    min-width: 46rem;
}
.cre-t thead th {
    position: sticky; top: 0; z-index: 1;
    background: var(--paper);
    text-align: left; padding: .5rem .6rem;
    font-size: .625rem; font-weight: 600; letter-spacing: .13em;
    text-transform: uppercase; color: var(--muted);
    border-bottom: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
    white-space: nowrap;
}
.cre-t__n { text-align: right; font-variant-numeric: tabular-nums; }
.cre-t th.cre-t__n { text-align: right; }
.cre-t tbody td {
    padding: .5rem .6rem; vertical-align: top;
    border-bottom: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
}
.cre-t__a { min-width: 15rem; }
.cre-r { cursor: pointer; }
.cre-r:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.cre-r:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.cre-r__o {
    display: block; margin-top: .1rem;
    font-size: .6875rem; color: var(--muted);
    /* An owner name can be 60 characters of holding company. */
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 22rem;
}
.cre-r__c {
    display: block; margin-top: .1rem;
    font-size: .625rem; letter-spacing: .08em; color: var(--muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.cre-more {
    display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
    margin: .7rem 0 0;
}
.cre-more__n { margin: 0; font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }

/* A code, a class, a raw value from the file — set apart so a reader can see
   it is the city's word and not ours. */
.cre-k {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .8em; letter-spacing: .04em;
    padding: .05em .35em; border-radius: 2px;
    background: color-mix(in srgb, var(--ink) 8%, transparent);
    color: var(--ink);
}

/* ---------------------------------------------------------------- the sheet */
.cre-blk { margin: 0 0 1.4rem; }
.cre-blk:last-child { margin-bottom: 0; }
.cre-blk__h {
    margin: 0 0 .6rem; font-size: .68rem; font-weight: 600;
    letter-spacing: .13em; text-transform: uppercase; color: var(--muted);
    padding-bottom: .4rem;
    border-bottom: 1px solid color-mix(in srgb, var(--ink) 13%, transparent);
}
.cre-blk__note { margin: .5rem 0 0; font-size: .75rem; line-height: 1.5; color: var(--muted); }
.cre-dl { margin: 0; display: grid; gap: .5rem; }
.cre-row { display: grid; grid-template-columns: 11rem minmax(0, 1fr); gap: .3rem 1rem; align-items: baseline; }
.cre-row dt {
    margin: 0; font-size: .625rem; font-weight: 600; letter-spacing: .11em;
    text-transform: uppercase; color: var(--muted); line-height: 1.6;
}
.cre-row dd { margin: 0; font-size: .84rem; line-height: 1.5; color: var(--ink); }
/* THE NOTE IS THE POINT OF THIS PAGE. Every figure that is not what a reader
   would assume it is carries a line saying what it actually is, and those
   lines are set to be read, not to be skipped. */
.cre-row__n {
    display: block; margin-top: .2rem;
    font-size: .72rem; line-height: 1.5; color: var(--muted);
}

/* The deeds. One card each, because a deed is a document and not a row. */
.cre-deeds { display: grid; gap: .6rem; }
.cre-deed {
    border: 1px solid color-mix(in srgb, var(--ink) 13%, transparent);
    border-radius: 3px; padding: .6rem .7rem;
}
.cre-deed__t {
    margin: 0 0 .25rem; display: flex; gap: .6rem; align-items: baseline;
    font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.cre-deed__t b { color: var(--ink); font-weight: 600; }
.cre-deed__t span { font-variant-numeric: tabular-nums; }
.cre-deed__a {
    margin: 0 0 .45rem; font-size: 1.05rem; font-weight: 600;
    font-variant-numeric: tabular-nums; color: var(--ink);
}
.cre-deed__a--none { font-size: .84rem; font-weight: 500; color: var(--muted); }
.cre-deed__w {
    display: block; margin-top: .12rem;
    font-size: .7rem; font-weight: 500; letter-spacing: 0;
    text-transform: none; color: var(--accent);
}
.cre-pty { margin: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem 1rem; }
.cre-pty dt {
    margin: 0 0 .12rem; font-size: .6rem; font-weight: 600; letter-spacing: .12em;
    text-transform: uppercase; color: var(--muted);
}
.cre-pty dd { margin: 0; font-size: .8rem; line-height: 1.4; overflow-wrap: anywhere; }

/* The sources panel. */
.cre-src { display: grid; gap: .9rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }
.cre-s {
    border: 1px solid color-mix(in srgb, var(--ink) 13%, transparent);
    border-radius: 3px; padding: .7rem .8rem;
}
.cre-s__h { margin: 0 0 .3rem; font-size: .8rem; font-weight: 600; line-height: 1.3; color: var(--ink); }
.cre-s__w { margin: 0 0 .35rem; font-size: .76rem; line-height: 1.5; color: var(--muted); }
.cre-s__c { margin: 0; font-size: .7rem; line-height: 1.45; color: var(--ink); opacity: .8; }
.cre-s__d {
    margin: .35rem 0 0; font-size: .66rem; letter-spacing: .04em;
    color: var(--accent); font-variant-numeric: tabular-nums;
}

@media (max-width: 640px) {
    .cre-row { grid-template-columns: minmax(0, 1fr); gap: .1rem; }
    .cre-pty { grid-template-columns: minmax(0, 1fr); }
    .cre-r__o { max-width: 14rem; }
}
