/* =============================================================================
   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; }
/* The list under a lede: what the page keeps, one line each. Not .welcome ul —
   that one lives inside the console at a smaller measure. */
.keeps {
    max-width: 62ch; margin: 1rem 0 0; padding-left: 1.15rem;
    font-size: .95rem; line-height: 1.6; color: var(--ink-77);
}
.keeps li { margin-bottom: .5rem; }
.keeps li::marker { color: var(--accent); }
.keeps b { color: var(--ink); font-weight: 600; }
.keeps + .lede { margin-top: 1.2rem; }

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; }
}
/* THE SAME ARGUMENT THE FIELD BELOW MAKES, APPLIED TO THE BAR. A phone held
   sideways is 844px (iPhone 14/15) or 932px (Pro Max) wide, so the 640px query
   above never matches it — measured in landscape, the burger was 40x40 and the
   pill segment 30x32, and those are the only two controls left in the bar on a
   phone. The pill's glyph stays at 13px, per the house note on its geometry:
   what grows is the box. flex: 0 0 auto because the burger is an unflexed item
   in .nav__in and shrank two pixels below its own declared 44 at 390px. */
@media (hover: none) and (pointer: coarse) {
    .nav__burger { width: 44px; height: 44px; flex: 0 0 auto; }
    .mode-pill__seg { min-height: 44px; }
}
@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; }
    /* The principle was applied to the input and not to the two buttons around
       it: the drawer's close measured 34x34 in landscape and the search clear
       22x22 at EVERY width, portrait included. .menu__clear is absolutely
       positioned, so growing it moves no other box. */
    .menu__x, .menu__clear { width: 44px; height: 44px; }
}
.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; }
/* A hint that is a REFUSAL rather than a note: the regex that will not compile,
   the browser that cannot do this at all. Everything in it is a token, so the
   dark edition needs no second line. */
.hint.bad {
    color: var(--taken); font-weight: 500;
    border: 1px solid var(--taken); border-left-width: 3px; border-radius: 8px;
    padding: .62rem .78rem; background: var(--taken-wash);
}
.hint.bad strong, .hint.bad b { color: var(--taken); font-weight: 700; }
.hint.bad code { color: inherit; }

/* ------------------------------------------------------------- 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); }
/* A BADGE THAT IS ALSO A CONTROL. A <button> does not inherit the page's font
   and paints a UA background of its own, so wearing .st it renders as a framed
   grey box in the wrong face — the same trap the house already documents for
   .menu__row. Nothing else about the badge changes. */
button.st {
    font-family: inherit; cursor: pointer;
    -webkit-appearance: none; appearance: none;
}
@media (hover: none) and (pointer: coarse) { button.st { min-height: 44px; } }

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

/* =============================================================================
   CONCERTS — the bill's own furniture
   -----------------------------------------------------------------------------
   Added for labs.llc/concerts/. Everything below reads the tokens the rest of
   the sheet reads (--ink, --paper, --tint, --line, the semantic hues), so the
   dark edition inherits it through dark.css; the handful of literals it needs
   are answered at the end of that sheet. The photographs are the one thing on
   this site that arrives with its own colours — the chips drawn over them sit
   on a fixed dark scrim, which is correct over a photo in either edition.
   ========================================================================== */
.ct-controls {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 1.1rem;
}
.ct-select { width: 100%; }
.ct-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .85rem; }

/* The saved-count badge riding the view segment. */
.ct-favn {
    font-size: .66rem; font-weight: 700; font-variant-numeric: tabular-nums;
    padding: .05rem .42rem; margin-left: .3rem; border-radius: 999px;
    border: 1px solid currentColor; opacity: .75;
}

/* ------------------------------------------------------------------ the grid */
.ct-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(15.5rem, 1fr));
    gap: .8rem;
}
.ct-card {
    display: flex; flex-direction: column; min-width: 0; text-align: left;
    font: inherit; color: inherit; padding: 0; cursor: pointer;
    border: 1px solid var(--line); border-radius: 12px;
    background: var(--paper); overflow: hidden;
    transition: border-color .18s var(--ease), transform .18s var(--ease);
}
.ct-card:hover { border-color: rgba(30, 38, 43, .3); transform: translateY(-2px); }
.ct-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .ct-card:hover { transform: none; } }

.ct-card__img {
    position: relative; aspect-ratio: 16 / 9; background: var(--tint);
    overflow: hidden;
}
.ct-card__img img {
    width: 100%; height: 100%; object-fit: cover; display: block;
}
/* The chips over the photograph. A photo is its own ground, so these do not
   read the edition's tokens: a fixed dark scrim and white type hold on any
   picture, in either edition. */
.ct-chip {
    position: absolute; z-index: 1;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .64rem; font-weight: 600; letter-spacing: .04em;
    padding: .2rem .5rem; border-radius: 999px;
    background: rgba(10, 13, 16, .72); color: #fff;
    border: 1px solid rgba(255, 255, 255, .22);
}
.ct-chip--date  { top: .55rem; left: .55rem; }
.ct-chip--price { bottom: .55rem; right: .55rem; }
.ct-chip--feat  {
    bottom: .55rem; left: .55rem;
    background: var(--accent-fill); color: var(--on-accent);
    border-color: transparent; letter-spacing: .12em;
}

.ct-card__body { padding: .85rem .95rem 1rem; display: flex; flex-direction: column; gap: .3rem; flex: 1; }
.ct-card__t {
    font-size: .98rem; font-weight: 600; letter-spacing: -.02em; color: var(--ink);
    line-height: 1.25; overflow-wrap: anywhere;
}
.ct-card__m { font-size: .78rem; color: var(--ink-55); overflow-wrap: anywhere; }
.ct-card__foot {
    margin-top: auto; padding-top: .6rem; border-top: 1px solid var(--line-soft);
    display: flex; align-items: center; gap: .45rem; flex-wrap: wrap;
}
.ct-card__foot .st { letter-spacing: .1em; }
.ct-hot {
    margin-left: auto; font-size: .68rem; font-weight: 700;
    color: var(--accent); font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* --------------------------------------------------------- the detail slab
   A card opens into the full record: a slab spanning the grid, in the same
   shape the registry answer uses on the domains lab. */
.ct-detail {
    grid-column: 1 / -1;
    border: 1px solid var(--line); border-left: 3px solid var(--accent);
    border-radius: 10px; background: var(--tint);
    padding: 1rem 1.1rem;
}
.ct-detail__t { font-size: 1.22rem; font-weight: 600; letter-spacing: -.025em; color: var(--ink); margin: 0 0 .2rem; }
.ct-detail__perf { font-size: .85rem; color: var(--accent); font-weight: 600; margin: 0 0 .7rem; }
.ct-detail dl { display: grid; grid-template-columns: auto 1fr; gap: .22rem .9rem; margin: 0 0 .8rem; font-size: .86rem; }
.ct-detail dt {
    font-size: .58rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
    color: var(--ink-55); padding-top: .22rem;
}
.ct-detail dd { margin: 0; color: var(--ink-77); overflow-wrap: anywhere; }
.ct-detail dd b { color: var(--ink); font-weight: 600; }
.ct-detail__desc { font-size: .88rem; margin: 0 0 .9rem; max-width: 68ch; }
.ct-detail__acts { display: flex; gap: .45rem; flex-wrap: wrap; }
.ct-tags { display: flex; gap: .35rem; flex-wrap: wrap; margin: 0 0 .9rem; }
.ct-tag {
    font-size: .66rem; font-weight: 600; letter-spacing: .04em;
    padding: .18rem .55rem; border-radius: 999px;
    border: 1px solid var(--line); color: var(--ink-55);
}
@media (max-width: 480px) { .ct-detail dl { grid-template-columns: 1fr; } .ct-detail dt { padding-top: .5rem; } }

/* ------------------------------------------------------------ the calendar */
.ct-cal { display: grid; gap: .45rem; }
.ct-cal__day {
    margin-top: .8rem; padding-bottom: .35rem; border-bottom: 1px solid var(--line);
    font-size: .8rem; font-weight: 700; letter-spacing: .06em; color: var(--ink);
    display: flex; align-items: baseline; gap: .6rem;
}
.ct-cal__day:first-child { margin-top: 0; }
.ct-cal__day span { font-size: .68rem; font-weight: 600; color: var(--ink-55); letter-spacing: .02em; }
.ct-cal__row {
    display: flex; align-items: center; gap: .6rem 1rem; flex-wrap: wrap;
    width: 100%; text-align: left; font: inherit; color: inherit; cursor: pointer;
    padding: .55rem .8rem; border: 1px solid var(--line); border-radius: 9px;
    background: var(--paper);
    transition: border-color .18s var(--ease);
}
.ct-cal__row:hover { border-color: rgba(30, 38, 43, .3); }
.ct-cal__row b { font-weight: 600; color: var(--ink); font-size: .9rem; overflow-wrap: anywhere; min-width: 0; }
.ct-cal__row small { font-size: .76rem; color: var(--ink-55); overflow-wrap: anywhere; }
.ct-cal__px {
    margin-left: auto; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .74rem; color: var(--ink-77); white-space: nowrap;
}
@media (max-width: 560px) { .ct-cal__px { margin-left: 0; flex-basis: 100%; } }

/* -------------------------------------------------------------- the venues */
.ct-venues { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: .6rem; }
.ct-venue {
    text-align: left; font: inherit; color: inherit; cursor: pointer;
    padding: .85rem 1rem; border: 1px solid var(--line); border-radius: 10px;
    background: var(--paper);
    transition: border-color .18s var(--ease);
}
.ct-venue:hover { border-color: rgba(30, 38, 43, .3); }
.ct-venue b { display: block; font-weight: 600; color: var(--ink); font-size: .95rem; letter-spacing: -.015em; overflow-wrap: anywhere; }
.ct-venue span { display: block; font-size: .76rem; color: var(--ink-55); margin-top: .15rem; }
.ct-venue em {
    display: inline-block; font-style: normal; margin-top: .5rem;
    font-size: .66rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
    color: var(--accent);
}

/* ----------------------------------------------------------------- the map */
.ct-map canvas {
    width: 100%; height: auto; display: block; margin-top: .8rem;
    border: 1px solid var(--line); border-radius: 10px;
    background: var(--paper); cursor: crosshair;
    touch-action: manipulation;
}
#ct-mapout { margin-top: .8rem; display: grid; gap: .45rem; }
#ct-mapout:empty { display: none; }

/* --------------------------------------------------------- the empty state */
.ct-empty { padding: 2.2rem 0; text-align: center; }
.ct-empty h3 { font-size: 1.15rem; font-weight: 300; letter-spacing: -.02em; margin: 0 0 .4rem; }
.ct-empty p { margin: 0; font-size: .88rem; color: var(--ink-55); }

/* --------------------------------------------------------- the collections */
.ct-colls { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: .8rem; }
.ct-coll {
    display: flex; flex-direction: column; align-items: flex-start; gap: .3rem;
    text-align: left; font: inherit; color: inherit; cursor: pointer;
    padding: 1.15rem 1.2rem 1.05rem; border: 1px solid var(--line); border-radius: 12px;
    background: var(--paper); position: relative;
    transition: border-color .18s var(--ease), transform .18s var(--ease);
}
.ct-coll:hover { border-color: var(--accent); transform: translateY(-2px); }
.ct-coll:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .ct-coll:hover { transform: none; } }
.ct-coll__n { font-size: .625rem; font-weight: 700; letter-spacing: .2em; color: var(--accent); }
.ct-coll b { font-size: 1.12rem; font-weight: 600; letter-spacing: -.025em; color: var(--ink); }
.ct-coll__d { font-size: .82rem; color: var(--ink-55); line-height: 1.5; }
.ct-coll__c {
    position: absolute; top: 1rem; right: 1.1rem;
    font-size: .72rem; font-weight: 700; font-variant-numeric: tabular-nums;
    color: var(--ink-55); border: 1px solid var(--line); border-radius: 999px;
    padding: .12rem .55rem;
}
/* A collection that is currently applied to the bill. */
.ct-coll.is-on { border-color: var(--accent); }
.ct-coll.is-on .ct-coll__c { color: var(--accent); border-color: var(--accent); }
/* The paging control under the grid. */
.ct-more { grid-column: 1 / -1; justify-self: center; margin-top: .4rem; }

/* =============================================================================
   FILE SEARCH — the searcher's own furniture
   -----------------------------------------------------------------------------
   Added for labs.llc/filesearch/. Everything below reads the tokens the rest
   of the sheet reads; the literals it could not avoid are answered at the end
   of dark.css. The results table is the one real <table> on the site, and the
   file names in it are set in the mono face for the same reason the pasted
   list on the domains lab is: a stray character in a filename should be
   visible, and columns of names read as the data they are.
   ========================================================================== */
.fs-drop {
    border: 2px dashed rgba(30, 38, 43, .3);
    border-radius: 12px;
    padding: 1.3rem 1rem;
    text-align: center;
    cursor: pointer;
    transition: border-color .2s var(--ease), background .2s var(--ease);
}
.fs-drop b { display: block; color: var(--ink); font-weight: 600; font-size: .95rem; }
.fs-drop span { display: block; margin-top: .3rem; font-size: .78rem; color: var(--ink-55); }
.fs-drop em { font-style: normal; font-weight: 600; }
.fs-drop:hover, .fs-drop.dragover { border-color: var(--accent); background: rgba(194, 65, 12, .04); }
.fs-drop:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.fs-progress {
    height: 3px; border-radius: 2px; overflow: hidden;
    background: var(--line);
}
.fs-progress > div {
    height: 100%; width: 0%;
    background: var(--accent);
    transition: width .2s var(--ease);
}

/* ------------------------------------------------------------ the controls */
.fs-controls {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    gap: 1.1rem 1.4rem;
    align-items: start;
}
.fs-sec { display: grid; gap: .55rem; align-content: start; }
.fs-sec .lbl { margin-bottom: 0; display: flex; align-items: baseline; gap: .5rem; }
.fs-seg { gap: .3rem; }
.fs-seg .seg__b { font-size: .72rem; padding: .38rem .7rem; }
.fs-2col { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; align-items: center; }
.fs-3col { display: grid; grid-template-columns: 1fr 1fr auto; gap: .5rem; }
/* A number or date input's intrinsic width is wider than a phone column, and
   a grid item will not shrink below it unless told to — without this the Size
   row holds the whole section at 465px and the console clips its right edge
   on a 375px screen. */
.fs-2col > *, .fs-3col > * { min-width: 0; }
.fs-2col .hint { margin: 0; }
.fs-mini {
    font: inherit; font-size: .6rem; font-weight: 700; letter-spacing: .1em;
    text-transform: uppercase; padding: .1rem .45rem; border-radius: 999px;
    background: transparent; border: 1px solid var(--line); color: var(--ink-55);
    cursor: pointer;
}
.fs-mini:hover { border-color: var(--ink); color: var(--ink); }

.fs-presets { display: grid; gap: .35rem; }
.fs-preset {
    display: flex; align-items: center; gap: .5rem;
    padding: .4rem .6rem; border: 1px solid var(--line); border-radius: 8px;
    background: var(--paper); font-size: .8rem;
}
.fs-preset b { font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0; }
.fs-preset .ib { width: 26px; height: 26px; }

/* -------------------------------------------------------------- the results */
.out__bar .fs-sort { width: auto; font-size: .78rem; padding: .45rem .6rem; }

/* THE HEADER COULD NEVER STICK. `overflow-x: auto` alone computes the other
   axis to `auto` as well, which makes this box the sticky containing block for
   the <th> below — and the box had no height constraint, so scrollHeight and
   clientHeight were the same number and there was zero travel to stick
   through. With PAGE_SIZE at 200 a reader scrolled past two hundred rows with
   no column headings. Bounding it gives the header somewhere to stay, and the
   bound subtracts the sticky bar, which is what --nav-h is published for. */
.fs-table-wrap { overflow: auto; max-height: calc(100dvh - var(--nav-h) - 2rem); overscroll-behavior: contain; }
.fs-table { width: 100%; border-collapse: collapse; font-size: .82rem; }
.fs-table th {
    text-align: left; padding: .5rem .6rem; white-space: nowrap;
    font-size: .6rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
    color: var(--ink-55); border-bottom: 1px solid var(--line);
    cursor: pointer; user-select: none;
    position: sticky; top: 0; background: var(--paper); z-index: 2;
}
.fs-table th:hover { color: var(--ink); }
.fs-table th.sorted { color: var(--accent); }
.fs-table td { padding: .5rem .6rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.fs-table tr:hover td { background: rgba(30, 38, 43, .025); }
.fs-table tr.selected td { background: rgba(194, 65, 12, .06); }
.fs-table tr.is-dup td { box-shadow: inset 3px 0 0 var(--unsure); }
.fs-name {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .78rem; color: var(--ink); overflow-wrap: anywhere;
}
.fs-path {
    font-size: .72rem; color: var(--ink-55); overflow-wrap: anywhere;
    max-width: 30ch;
}
.fs-size { font-variant-numeric: tabular-nums; white-space: nowrap; }
.fs-date { white-space: nowrap; color: var(--ink-55); font-size: .76rem; }
.fs-snip {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .7rem; color: var(--free); margin-top: .2rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 42ch;
}
/* THE GREP BADGE. It was called .fs-hit, and so is the checkbox hit target
   forty lines below — one class name, two unrelated components. The wrapper's
   rules (display:inline-flex, cursor:pointer) and the coarse-pointer floor
   (min-width/min-height: 44px) both landed on this decorative span, so on any
   touch device every row the grep matched grew a 44x44 finger-sized box around
   a "content" marker that clicks nothing. Renamed here, not there: the wrapper
   is emitted four times and styled six, the badge twice and once. */
.fs-hit-badge { color: var(--free); font-size: .66rem; font-weight: 700; letter-spacing: .08em; }
.fs-check { width: 16px; height: 16px; accent-color: var(--ink); cursor: pointer; margin: 0; }
/* THE HIT AREA AROUND A CHECKBOX.
   A 16px box is the right SIZE for a checkbox and the wrong size for a finger,
   and the two do not have to be the same thing: the label around it carries the
   target, the box carries the look. Under a coarse pointer the label grows to
   44 and the checkbox does not move. */
.fs-hit {
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer; margin: 0; padding: 0;
}
.fs-hit:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }

/* The category chips: the semantic hues, hairline, never a fill. */
.fs-badge {
    display: inline-flex; align-items: center;
    font-size: .62rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
    padding: .16rem .5rem; border-radius: 999px;
    border: 1px solid currentColor; white-space: nowrap;
}
.fs-badge--image    { color: var(--free); }
.fs-badge--video    { color: var(--unsure); }
.fs-badge--audio    { color: var(--info); }
.fs-badge--document { color: var(--accent); }
.fs-badge--code     { color: var(--taken); }
.fs-badge--archive,
.fs-badge--other    { color: var(--ink-55); }

/* the cards view */
.fs-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(15.5rem, 1fr)); gap: .6rem; }
.fs-card {
    border: 1px solid var(--line); border-radius: 10px; background: var(--paper);
    padding: .8rem .9rem; display: flex; gap: .6rem; align-items: flex-start;
    transition: border-color .18s var(--ease);
}
.fs-card:hover { border-color: rgba(30, 38, 43, .3); }
.fs-card.selected { border-color: var(--accent); }
.fs-card > div { flex: 1; min-width: 0; }
.fs-card .fs-meta {
    display: flex; flex-wrap: wrap; gap: .3rem .7rem; align-items: center;
    margin-top: .4rem; font-size: .74rem; color: var(--ink-55);
}
.fs-card .btn { margin-top: .6rem; }

/* the thumbnail wall */
.fs-thumbs { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: .6rem; }
.fs-thumb {
    position: relative; border: 1px solid var(--line); border-radius: 10px;
    overflow: hidden; cursor: pointer; background: var(--tint);
    transition: border-color .18s var(--ease);
}
.fs-thumb:hover { border-color: rgba(30, 38, 43, .3); }
.fs-thumb.selected { border-color: var(--accent); }
.fs-thumb img { width: 100%; height: 110px; object-fit: cover; display: block; }
.fs-thumb .fs-hit {
    position: absolute; top: 7px; left: 7px; z-index: 2;
    background: rgba(255, 255, 255, .82); border-radius: 6px;
}
.fs-thumb figcaption {
    padding: .35rem .5rem; font-size: .7rem; color: var(--ink-77);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* --------------------------------------------------------------- the pages */
.fs-pages {
    display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
    gap: .6rem; padding-top: 1rem; font-size: .82rem;
}
.fs-pages .field { width: auto; padding: .4rem .6rem; font-size: .78rem; }
.fs-pages b { font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------ the insights */
.fs-insights {
    border: 1px solid var(--line); border-left: 3px solid var(--accent);
    border-radius: 10px; background: var(--tint);
    padding: .9rem 1rem; margin-bottom: 1rem;
}
.fs-insights h3 {
    font-size: .625rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
    color: var(--ink-55); margin: 0 0 .6rem;
}
.fs-insights__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: .5rem; }
.fs-stat { border: 1px solid var(--line); border-radius: 8px; background: var(--paper); padding: .45rem .6rem; }
.fs-stat b { display: block; font-size: 1.05rem; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.fs-stat span { font-size: .66rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-55); overflow-wrap: anywhere; }
.fs-stat--wide { grid-column: span 2; }

/* ------------------------------------------------------------ the bulk bar */
.fs-bulk {
    position: sticky; bottom: 0; z-index: 5;
    display: flex; align-items: center; flex-wrap: wrap; gap: .45rem;
    margin: 1rem -1.2rem -1.2rem; padding: .7rem 1.2rem;
    background: var(--paper); border-top: 1px solid var(--line);
    box-shadow: 0 -10px 24px rgba(30, 38, 43, .06);
}
.fs-bulk b { font-size: .78rem; color: var(--accent); font-variant-numeric: tabular-nums; margin-right: .3rem; }
@media (max-width: 620px) { .fs-bulk { margin: 1rem -.9rem -.9rem; padding: .7rem .9rem; } }

/* -------------------------------------------------------------- the modal
   The preview dialog. The scrim is the menu's scrim; the panel is a paper
   surface with the console's hairline grammar. */
.fsm { position: fixed; inset: 0; z-index: 320; }
.fsm[hidden] { display: none; }
.fsm__scrim { position: absolute; inset: 0; background: rgba(17, 22, 28, .46); }
.fsm__panel {
    position: relative; z-index: 1;
    width: min(960px, calc(100vw - 2rem));
    max-height: min(86vh, 100dvh - 2rem);
    margin: 4.5vh auto 0;
    display: flex; flex-direction: column;
    background: var(--paper);
    border: 1px solid var(--line); border-radius: 14px;
    box-shadow: 0 30px 80px rgba(17, 22, 28, .25);
    overflow: hidden;
}
.fsm__head {
    display: flex; align-items: center; gap: .8rem;
    padding: .8rem 1rem; border-bottom: 1px solid var(--line);
}
.fsm__head b {
    font-weight: 600; color: var(--ink); font-size: .92rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    flex: 1; min-width: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.fsm__acts { display: flex; gap: .35rem; flex: 0 0 auto; }
.fsm__body {
    padding: 1rem; overflow: auto; flex: 1; min-height: 8rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .78rem; line-height: 1.55;
    white-space: pre-wrap; overflow-wrap: anywhere;
    background: var(--tint);
}
.fsm__body img { max-width: 100%; height: auto; display: block; margin: 0 auto; transition: transform .2s var(--ease); transform-origin: top left; }
.fsm__body img.zoomed { max-width: none; cursor: zoom-out; }
.fsm__body video { max-width: 100%; max-height: 68vh; display: block; margin: 0 auto; }
.fsm__body audio { width: 100%; }
/* THE PDF FRAME.
   An <object> cannot load here at all: the yard's own header sets
   object-src 'none', so the element this used to be resolved to the fallback
   paragraph on every reader's screen while three places on the page promised a
   preview. An <iframe> is the element frame-src allows — and because a blob:
   frame is not something every policy has to permit either, the buttons above
   it are always drawn and always work. */
.fs-pdf { display: grid; gap: .6rem; }
.fs-pdf__acts { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; }
.fs-pdf__acts .hint { margin: 0; flex: 1 1 18rem; min-width: 0; }
.fs-pdf__frame {
    width: 100%; height: 62vh; min-height: 18rem;
    border: 1px solid var(--line); border-radius: 8px;
    background: var(--paper); display: block;
}
@media (max-width: 560px) { .fsm__panel { margin-top: 1rem; width: calc(100vw - 1.2rem); } }

/* The hidden ATTRIBUTE must always win. The UA sheet's [hidden] rule is
   display:none at zero importance, so any author display — .st's inline-flex,
   a grid on a results panel — silently overrides it and a panel the script
   hid stays painted. One guard ends the whole class of bug. */
[hidden] { display: none !important; }

/* ===== v2: the cursor, the snapshot ribbon, marks in the preview ===== */
.is-cursor { outline: 2px solid var(--accent, #C2410C); outline-offset: -2px; }
.fs-snapshot {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
    border: 1px dashed var(--line, rgba(30,38,43,.25)); border-radius: 10px;
    padding: 9px 13px; margin: 0 0 12px; font-size: .78rem; opacity: .9;
}
.fs-snapshot span { flex: 1 1 16em; }
#modalBody mark { background: var(--accent, #C2410C); color: #fff; border-radius: 2px; padding: 0 1px; }
.fs-pre { margin: 0; white-space: pre-wrap; word-break: break-word; font: inherit; }
#btnCancelScan { margin-top: 6px; }
#pinnedBadge { cursor: pointer; }

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

/* =============================================================================
   TOUCH — the console measured against a finger
   -----------------------------------------------------------------------------
   Gated on the POINTER, never on a width: a phone held sideways is 932px wide
   and still has a finger on it.

   1. EVERY FIELD IS 16px. iOS Safari zooms the page when a focused input is
      under 16px, and a zoomed page carries the fixed header — and the field
      that was just tapped — off the visual viewport. The hamburger's own
      search field already carried this rule (see .menu__q above). The
      console's fifteen inputs did not: name, path, extensions, both size
      boxes, both dates, the grep, the exclude area, the preset name and four
      <select>s, all between 14.4 and 15.2px.

   2. NOTHING A FINGER MUST HIT IS UNDER 44px. At a 16px root the six name
      modes, six size presets, five date presets, six type chips and two grep
      modes measured 26px; .btn--sm 30; the preset delete 26; the "clear"
      beside Type about 15 tall; and .fs-check — the row-selection control, in
      all three views — 15 square. The house already knew the number: .menu__x
      takes 44 under 640px. It was never carried past the chrome.
   ========================================================================== */
@media (hover: none) and (pointer: coarse) {
    .field, .field--area, .fs-pages .field, .out__bar .fs-sort { font-size: 16px; }

    .seg__b, .fs-seg .seg__b { min-height: 44px; padding: .7rem .95rem; font-size: .82rem; }
    .btn--sm { min-height: 44px; padding: .7rem .95rem; }
    .sw { min-height: 44px; }
    .sw input { width: 20px; height: 20px; }
    .ib, .fs-preset .ib { width: 44px; height: 44px; }
    /* The "clear" beside Type is a tertiary control on a LABEL row, so it
       takes its 44 as a hit box and gives the block back: the negative block
       margin keeps the row the height the label sets, and the finger still
       gets a full target. */
    .fs-mini { min-height: 44px; padding: .45rem .85rem; font-size: .66rem; margin-block: -7px; }
    .fs-check { width: 20px; height: 20px; }
    .fs-hit { min-width: 44px; min-height: 44px; }
    .fs-table td, .fs-table th { padding: .6rem; }
    .fs-table td:first-child, .fs-table th:first-child { padding: 0 .2rem; width: 1%; }
    .fs-thumb .fs-hit { top: 5px; left: 5px; }
    .out__bar .fs-sort { min-height: 44px; }
    .fs-pages .field { min-height: 44px; }
    .fs-drop { padding-block: 1.7rem; }
}
