/* =============================================================================
   labs.llc — institutional rebuild
   -----------------------------------------------------------------------------
   The previous build was a dark neon console: forty-odd accent colours, glass,
   halos, a live control panel. This one is its opposite and deliberately so.
   The register here is a financial firm's: white paper, one ink, a great deal
   of air, and type doing the work that colour 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 10% ink hairline; there are no heavy borders anywhere
     measure  1265px

   Type is DM Sans at four weights. Display sizes run LIGHT (200-300) with
   tight negative tracking, which is what makes a large heading read as calm
   rather than loud; small text runs 500-700 and positive-tracked. That
   inversion -- big text light, small text heavy -- is the single most load-
   bearing rule in the sheet.
   ========================================================================== */

/* ------------------------------------------------------------------ webfont
   DM Sans, self-hosted. It used to come from fonts.googleapis.com, which cost
   a render-blocking stylesheet on a third-party origin, two preconnects, and
   a DNS+TLS round trip before a single glyph could be requested — in a build
   that otherwise has no CDN in it at all.

   These are the same two files Google serves, and they are variable across
   both axes the sheet uses (opsz 9-40, wght 200-700), so the whole four-weight
   inversion the design rests on is 94 KB of woff2 rather than twelve separate
   faces. `font-weight: 200 700` is the range declaration a variable face needs;
   `font-optical-sizing: auto` is the default and is what drives opsz.

   Split on the same unicode ranges Google splits on, so a page of English
   never downloads the accented block. Both are preloaded from the head — a
   font discovered only when the CSS parser reaches this rule is a font
   requested one round trip late, which is exactly the flash of fallback text
   the two ResizeObserver re-measures in site.js exist to clean up after. */
@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);
    --ink-55:     rgba(30, 38, 43, .55);
    --paper:      #ffffff;
    --deep:       #11161C;
    --deep-soft:  #171E26;
    --line:       rgba(30, 38, 43, .12);
    --line-soft:  rgba(30, 38, 43, .07);
    --line-dark:  rgba(255, 255, 255, .13);
    --muted:      #878787;

    /* One restrained accent, used for rules and live indicators only -- never
       for large fills. The old build's orange survives here as a hairline. */
    --accent:     #C2410C;

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

    /* The height of the sticky header, published as a token because two other
       things have to know it: the header itself, 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, and the value that
       does the subtracting has to be the same one the bar is drawn at. The
       satellite pages override it — their header carries a second row. */
    --nav-h:      74px;

    /* A section's vertical rhythm. Everything is a multiple of this so bands
       stack without anyone having to think about it. */
    --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; } }

/* Every in-page link on this site points at a section, and the header that sits
   above it never scrolls away. Without this the band a visitor asked for
   arrives with its eyebrow and the first line of its heading behind the bar —
   which on a phone, where the header is a larger share of the screen, is most
   of what they came for. Applied to anything with an id rather than to a
   hand-kept list of sections, because 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;
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

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

/* Display sizes. Light weight, tight tracking, short leading -- the three
   together are the house voice. */
.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. */
.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;
}

a { color: inherit; }

.wrap { max-width: var(--measure); margin: 0 auto; padding-inline: var(--gutter); }
.band { padding-block: var(--band); }
.band--deep { background: var(--deep); color: rgba(255,255,255,.72); }
.band--deep h1, .band--deep h2, .band--deep h3, .band--deep h4 { color: #fff; }
.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: 1.5rem;
}
.nav__mark { display: flex; align-items: center; gap: .8rem; text-decoration: none; margin-right: auto; }
.nav__mark img { display: block; width: 78px; height: 33px; }
.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: 900px) { .nav__mark span { display: none; } }

.nav__links { display: flex; align-items: center; gap: 1.9rem; }
@media (max-width: 1040px) { .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

   Both editions of labs.llc run out of one root — this paper-white site and
   the dark console at /site2/ — and this is the switch between them. It
   replaces the old LIVE CONSOLE cross-link: same destination, but a segmented
   control states the relationship the link could only imply, which is that the
   two are the same firm in two registers rather than one site linking out to
   another.

   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, and
   the two colour rules below travel with it so the label it lands on turns
   paper and the one it leaves turns back to ink.

   Everything is a hairline and a fill of the ink already in use; no second
   accent enters the header for this.
   ------------------------------------------------------------------------ */
.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 —
       at anything heavier the pill starts competing with the Contact button. */
    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. :has is what makes the container answer for its children;
   where it is unsupported the pill simply does not preview, which costs
   nothing — the segments are still links and still say which is which. */
/* Hovering the unlit segment slides the thumb across and previews the switch.
   There are exactly two, so "the unlit one is hovered" already says which side
   the thumb belongs on and the rule needs no position selector — which matters
   now that the lit segment is a <span> and the unlit one an <a>: the old
   :first-of-type / :last-of-type pair counted types, and with one of each
   element in the track the same anchor answered to both. */
.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. Nothing here sets
       type: the segments are a glyph each, and their words live in aria-label
       and title. */
    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. It keeps the class, so the track still holds two equal halves. */
.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 has to
   follow 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 300×150 when no stylesheet reaches it, which is how a sun
   and a moon the size of a fist ended up sitting in the middle of the hero. The
   attributes make the glyph 13px with no CSS at all; this rule keeps it 13px
   when the CSS does arrive and something else has set a width on svg. */
.mode-pill__i { width: 13px; height: 13px; flex: 0 0 auto; display: block; }

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

/* No fold. The pill used to be hidden below 1040 and left to the menu, which
   meant the one control that says "there are two of these sites" was invisible
   on every phone — while the console, which this pair is supposed to mirror,
   has always carried it in the bar at every width. Two glyphs are about 110
   pixels all in and fit a 320px navbar; what gives way instead, further down,
   is the Contact button, which the menu already carries twice over. */

/* The menu's copy of the pill sits in the panel's footer; its rules live with
   the rest of the menu further down. */

/* The hamburger. It used to appear only below 1040, where it opened a stacked
   list of the same five links the navbar was hiding. It is at every width now,
   the way the console's is, and it opens the whole menu rather than a fold of
   the header — so on a wide screen the navbar's five section links and the
   menu's forty-six properties 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; }
/* The two bars morph into a close cross. 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 in the list — CSS applies transform
   functions right to left, so this reads "slide the bar to the centre line,
   then turn it about the centre". */
.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 rather than eyeballed, because the
   header is the one component that cannot be allowed to wrap on the narrow one.

   The mark loses its rule-and-label at 900 already; below 640 the logo itself
   comes down to 64px, the gaps close to a third of what they are on a desktop,
   and the bar loses twelve pixels of height. That buys about 90 pixels, which
   is what the pill costs.
   -------------------------------------------------------------------------- */
@media (max-width: 1040px) { .nav__in { gap: .9rem; } }

@media (max-width: 640px) {
    :root { --nav-h: 62px; }
    .nav__in { gap: .55rem; }
    .nav__mark img { width: 64px; height: 27px; }

    /* 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 on a phone is the
       box around it, so the control is thumbable without becoming loud. */
    .mode-pill__seg { min-width: 1.9rem; padding: .58rem .4rem; }

    /* No ring on a phone. On a desktop the hairline box tells you the hamburger
       is a control among other controls; on a phone it is the only thing in that
       corner, the mark and the pill beside it are unboxed, and the outline was
       the heaviest object in the bar for no work it was doing. */
    .nav__burger { width: 42px; height: 42px; border-color: transparent; }
    .nav__burger:hover { border-color: transparent; }
}

/* Below 640 the Contact button leaves the bar on the pages that have the menu.
   It is not lost: the menu lists it under This page, the panel's Reach the desk
   section carries the address and the number, and the contact band is the last
   thing on the page. On the satellite pages, which have no menu, the button is
   the only action there is and it stays — see .nav--sat below. */
@media (max-width: 640px) { .nav--menu .nav__in > .btn { display: none; } }

/* -----------------------------------------------------------------------------
   THE SATELLITE HEADER

   network-paper, tech-paper, newsy-paper and windy-paper carry the same header
   as the homepage without the homepage's menu behind it — so when .nav__links
   went out at 1040 those four pages had, on a phone, no navigation at all: a
   mark and one button. The links come back as a second row, scrolled sideways
   rather than wrapped, which is the one shape that holds four items at any
   width without the header growing a third line.

   The row bleeds to both edges and re-pads itself, so the first and last link
   sit on the gutter when the row is at rest and the run passes under the edge
   rather than stopping short of it — which is what tells a reader there is more
   of it to the right.
   -------------------------------------------------------------------------- */
@media (max-width: 1040px) {
    .nav--sat .nav__in {
        flex-wrap: wrap; height: auto;
        padding-top: .6rem; padding-bottom: 0;
    }
    .nav--sat .nav__links {
        display: flex; order: 5; flex: 1 0 100%;
        gap: 1.35rem;
        margin: .4rem calc(var(--gutter) * -1) 0;
        padding: 0 var(--gutter) .6rem;
        overflow-x: auto; overscroll-behavior-x: contain;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none; -ms-overflow-style: none;
    }
    .nav--sat .nav__links::-webkit-scrollbar { display: none; }
    .nav--sat .nav__links a { font-size: .8rem; white-space: nowrap; padding-block: .35rem; }
    .nav--sat .nav__in > .btn { padding: .6rem .9rem; font-size: .76rem; }
}
/* The two-row header is taller than the token says, and the token is what every
   anchor on the page subtracts. The four satellite pages re-declare it on <body>
   in their own sheets — a custom property inherits, so one line there moves the
   header height and every scroll margin under it together. */
@media (max-width: 640px) {
    .nav--sat .nav__in > .btn { padding: .55rem .8rem; font-size: .72rem; }
    .nav--sat .nav__links { gap: 1.15rem; }
    .nav--sat .nav__links a { font-size: .78rem; }
}
/* 360 and below. The satellite bar is mark, pill and button with no menu to
   fall back on, and at 320 there are 280 usable pixels for all three. Measured,
   with the longest label the four pages carry:

       mark 56 + pill 88 + "Change location" 108 + two 6.4 gaps = 265

   which leaves fifteen. The mark is what gives, because it is the one of the
   three that is still legible a size down and the only one that is not a
   control.

   360 rather than 380, so that a 375 or 390 phone — which is most of them —
   keeps the roomier pill and the full-size mark. At 375 the ≤640 sizes come to
   296 against 335 available, so there is nothing to buy there. */
@media (max-width: 360px) {
    .nav__in { gap: .4rem; }
    .nav__mark img { width: 56px; height: 24px; }
    .mode-pill__seg { min-width: 1.7rem; }
    .nav--sat .nav__in > .btn { padding: .55rem .62rem; letter-spacing: 0; }
}

/* =============================================================================
   THE MENU

   The console's hamburger opens a full-height panel off the right edge with a
   search field at the top and every property in the book below it. This is the
   same instrument in the paper register.

   What did not come across is the colour. The console codes forty-seven tiles
   in forty-seven hues, which is legible there because the whole page is built
   that way; dropped into a site with one ink and one accent it would be the
   loudest thing by an order of magnitude. The tiles carry their practice's
   glyph instead — the same four marks the Practices band uses — and the
   grouping does the work the colour was doing.
   ========================================================================== */
.menu[hidden] { display: none; }
.menu {
    position: fixed; inset: 0; z-index: 200;
    display: block;
    /* The panel parks at translateX(100%) — one panel-width past the right
       edge. Clipped here, because 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 during those 360ms is a
       horizontal scrollbar that appears and leaves for no visible reason. */
    overflow: hidden;
}
.menu__scrim {
    position: absolute; inset: 0;
    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__head img { display: block; width: 66px; height: 28px; }
.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; }

/* The search field. Sticky, because the register below it is forty-six rows
   and a field that scrolls away is a field you have to hunt for again. */
.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);
    border: 1px solid var(--line); 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; }
.menu__clear {
    position: absolute; right: 1.75rem; 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: 50%;
    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__body {
    flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain;
    padding: 1.3rem 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: .7rem;
    font-size: .58rem; font-weight: 700; letter-spacing: .22em; text-transform: uppercase;
    color: var(--ink-55); margin: 0 0 .75rem;
}
.menu__h::after { content: ''; height: 1px; background: var(--line); flex: 1 1 auto; }

/* A row: the shape the console uses for its actions, in hairlines. */
.menu__row {
    display: flex; align-items: center; gap: .85rem; width: 100%;
    padding: .68rem .2rem; text-align: left;
    background: transparent; border: 0; border-bottom: 1px solid var(--line-soft);
    font: inherit; color: var(--ink-77); text-decoration: none; cursor: pointer;
    transition: color .2s var(--ease), padding-left .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__i {
    flex: 0 0 auto; width: 26px; text-align: center;
    color: var(--accent); font-size: .62rem; line-height: 1;
}
.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: .72rem; font-weight: 400; color: var(--ink-55);
    margin-top: .1rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.menu__go { flex: 0 0 auto; color: var(--ink-55); font-size: .8rem; font-style: normal; }

/* The property tiles. Three across at the panel's width, which is what fits a
   two-word name at this size without hyphenating it. */
.menu__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: .4rem; }
.menu__grid[hidden] { display: none; }
.menu__tile {
    position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .34rem; padding: .72rem .3rem;
    border: 1px solid var(--line); border-radius: 10px;
    text-decoration: none; color: var(--ink); background: var(--paper);
    transition: border-color .2s var(--ease), background .2s var(--ease);
}
.menu__tile:hover { border-color: var(--ink); background: rgba(30, 38, 43, .028); }
.menu__tile[hidden] { display: none; }
.menu__tile i { font-style: normal; color: var(--accent); font-size: .58rem; line-height: 1; }
.menu__tile b {
    font-size: .68rem; font-weight: 600; letter-spacing: -.01em; line-height: 1.2;
    text-align: center; overflow-wrap: anywhere;
}
/* The off-site marker sits in the corner rather than in the stack. In flow it
   is a third line, and only the fifteen external tiles have one — so a row
   with one in it stood taller than a row without. */
.menu__tile em {
    position: absolute; top: .34rem; right: .4rem;
    font-style: normal; font-size: .5rem; line-height: 1; color: var(--ink-55);
}

/* Surfaces at the top of the panel when the query looks like somebody trying
   to reach a person rather than a page. */
.menu__pin {
    display: flex; align-items: center; gap: .85rem;
    margin-bottom: 1.4rem; padding: .85rem 1rem;
    border: 1px solid var(--accent); border-radius: 10px;
    text-decoration: none; background: rgba(194, 65, 12, .045);
}
.menu__pin[hidden] { display: none; }
.menu__pin b { display: block; font-size: 1.05rem; font-weight: 500; letter-spacing: -.02em; color: var(--ink); }
.menu__pin span {
    display: block; font-size: .55rem; font-weight: 700; letter-spacing: .2em;
    text-transform: uppercase; color: var(--accent); margin-bottom: .15rem;
}

.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);
}
/* The footer's copy of the pill used to be stretched to the full width of the
   panel — on a phone that is a 375-pixel track with a 15-pixel sun adrift in one
   half of it and a moon in the other, which reads as a broken button rather than
   as the same control that is in the bar above. It 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. */
.menu__foot { display: flex; justify-content: center; }
.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%; }
    .menu__grid { grid-template-columns: repeat(2, 1fr); }
}

/* =============================================================================
   BUTTONS
   ========================================================================== */

.btn {
    display: inline-flex; align-items: 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--ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn--ghost:hover { background: var(--ink); color: #fff; border-color: var(--ink); }

/* The hero's call to action: uppercase, hairline box, arrow that steps out on
   hover. Deliberately not the filled button -- over a photographic field a
   solid block reads as a banner ad. */
.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); }
.btn:hover i { transform: translateX(3px); }

/* =============================================================================
   HERO
   ========================================================================== */

.hero { position: relative; min-height: min(84vh, 720px); display: flex; align-items: flex-end;
        background: var(--deep); overflow: hidden; }
/* The reference site opens on an aerial photograph. There is no photograph to
   use here, and borrowing theirs was never an option, so the field is drawn:
   a slow horizon of fine verticals on a cold charcoal ground, painted to a
   canvas. It carries the same weight as a photo -- something quiet happening
   behind the words -- without pretending to be one. */
.hero__field { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* The scrim exists to protect the headline, and it was doing considerably more
   than that: at 94/72/30 it covered the whole width, so the field behind it
   was washed out everywhere rather than only under the type. Now it is heavy
   where the words are and clears quickly across, and the bottom pass stops
   short of the figures instead of climbing into the middle of the frame. */
.hero__scrim {
    position: absolute; inset: 0;
    background:
        linear-gradient(90deg, rgba(17,22,28,.92) 0%, rgba(17,22,28,.86) 26%,
                               rgba(17,22,28,.42) 62%, rgba(17,22,28,.06) 100%),
        linear-gradient(0deg,  rgba(17,22,28,.80) 0%, rgba(17,22,28,0) 38%);
}
.hero__in { position: relative; width: 100%; padding-block: clamp(56px, 11vw, 118px); }
/* The headline is one of twenty, swapped in by applyLanding() after the markup
   has already laid out — so anything that changes its height between the
   shipped variant and the drawn one is a shift in the largest element on the
   page.

   Two rules stop that. The measure was 20ch, which is narrower than the longest
   of the twenty lines ("One Contract. Ninety Seconds.", 29 characters) — those
   variants silently wrapped to three lines while the rest sat at two, so the
   swap could add a whole display line. At 30ch every variant is the two lines
   its <br> asks for. And min-height holds those two lines whatever happens, so
   a narrow viewport that does wrap one of them grows downward into reserved
   air rather than pushing the standfirst.

   line-height is 1.09, so two lines is 2.18em. */
.hero h1 { color: #fff; max-width: 30ch; min-height: 2.18em; text-wrap: balance; }
/* Same argument, one element down: the standfirst runs 98-140 characters
   across the twenty, which is two lines at the short end and three at the long
   one. Held at three, the block never grows when the copy is replaced — the
   shortest variant leaves a line of air instead, which a hero has to spare. */
.hero p { color: rgba(255,255,255,.74); max-width: 52ch; margin: 1.5rem 0 2.2rem; font-weight: 300; }
#hero-sub { min-height: 4.9em; }

/* The reservations above are the worst case from about 460px up, where the
   twenty variants settle at two display lines and three of standfirst. Below
   that the box narrows faster than the type does and the worst of them takes
   another line, then another — measured across all twenty at each step rather
   than guessed, because a reservation that is one line short is exactly the
   shift it was added to prevent, and one line long is a hole in the hero. */
@media (max-width: 460px) { .hero h1  { min-height: 3.27em; } }   /* 3 display lines */
@media (max-width: 420px) { #hero-sub { min-height: 6.52em; } }   /* 4 body lines    */
@media (max-width: 344px) {
    .hero h1  { min-height: 4.36em; }                             /* 4 display lines */
    #hero-sub { min-height: 8.15em; }                             /* 5 body lines    */
}

/* The live strip along the bottom of the hero — the one place the old build's
   pulse survives, reduced to a line of figures. */
.hero__ticker {
    position: relative; z-index: 2;
    border-top: 1px solid var(--line-dark);
    display: flex; flex-wrap: wrap; gap: 0;
}
.hero__stat {
    flex: 1 1 190px; padding: 1.15rem 0 1.25rem;
    border-right: 1px solid var(--line-dark);
}
.hero__stat:last-child { border-right: 0; }
.hero__stat b {
    display: block; font-size: 1.55rem; font-weight: 300; letter-spacing: -.03em; color: #fff;
    font-variant-numeric: tabular-nums;
}
/* Direct child only. As `.hero__stat span` this also caught the counter's own
   span inside the <b>, which shrank the figure to label size -- the number and
   its caption rendered at the same 10px. */
.hero__stat > span {
    display: block; margin-top: .15rem;
    font-size: .625rem; font-weight: 600; letter-spacing: .17em; text-transform: uppercase;
    color: rgba(255,255,255,.5);
}
/* Two up rather than four down on a phone. At a 190px basis each figure took a
   full row, which pushed the last one most of a screen below the headline and
   turned a four-figure summary into a scroll. */
@media (max-width: 640px) {
    .hero__stat { flex: 1 1 42%; padding: .95rem 0 1rem; }
    .hero__stat:nth-child(2n) { border-right: 0; padding-left: 1.1rem; }
    .hero__stat:nth-child(n+3) { border-top: 1px solid var(--line-dark); }
    .hero__stat b { font-size: 1.32rem; }
}

.live-dot {
    display: inline-block; width: 6px; height: 6px; border-radius: 50%;
    background: var(--accent); margin-right: .5rem; vertical-align: middle;
    animation: pulse 2.6s ease-in-out infinite;
}
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }

/* =============================================================================
   SPLIT — the two-column band the reference uses for every prose section
   ========================================================================== */

.split { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: clamp(2rem, 5vw, 5.5rem); }
@media (max-width: 860px) { .split { grid-template-columns: 1fr; gap: 2rem; } }
.split--even { grid-template-columns: 1fr 1fr; }
@media (max-width: 860px) { .split--even { grid-template-columns: 1fr; } }

/* The counter. One oversized figure per cell, hairline-separated. */
.figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1px;
           background: var(--line); border-block: 1px solid var(--line); margin-top: clamp(2rem,4vw,3.4rem); }
.figures > div { background: var(--paper); padding: 1.6rem 1.4rem 1.5rem; }
.band--deep .figures { background: var(--line-dark); border-color: var(--line-dark); }
.band--deep .figures > div { background: var(--deep); }
.figure__n {
    font-size: clamp(2.1rem, 4vw, 3.05rem); font-weight: 200; letter-spacing: -.04em;
    line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums;
}
.band--deep .figure__n { color: #fff; }
.figure__l {
    margin-top: .6rem; font-size: .64rem; font-weight: 700; letter-spacing: .17em;
    text-transform: uppercase; color: var(--ink-55);
}
.band--deep .figure__l { color: rgba(255,255,255,.5); }
.figure__d { margin-top: .5rem; font-size: .855rem; line-height: 1.5; color: var(--ink-55); }
.band--deep .figure__d { color: rgba(255,255,255,.55); }

/* =============================================================================
   PRACTICES
   ========================================================================== */

.practices { display: grid; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); gap: 1px;
             background: var(--line); border: 1px solid var(--line); margin-top: clamp(2rem,4vw,3.2rem); }
.practice {
    background: var(--paper); padding: clamp(1.6rem, 2.4vw, 2.2rem);
    display: flex; flex-direction: column; min-height: 268px;
    text-decoration: none; transition: background .3s var(--ease);
}
.practice:hover { background: #FAFAFA; }
.practice__i {
    width: 34px; height: 34px; display: grid; place-items: center;
    border: 1px solid var(--line); border-radius: 50%; color: var(--ink);
    font-size: .78rem; margin-bottom: 1.4rem;
    transition: background .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);
}
.practice:hover .practice__i { background: var(--ink); color: #fff; border-color: var(--ink); }
.practice h3 { font-size: 1.16rem; font-weight: 600; letter-spacing: -.025em; }
.practice p { margin: .7rem 0 0; font-size: .9rem; line-height: 1.56; color: var(--ink-55); }
.practice__n {
    margin-top: auto; padding-top: 1.3rem;
    font-size: .64rem; font-weight: 700; letter-spacing: .17em; text-transform: uppercase;
    color: var(--ink-55);
}

/* =============================================================================
   PORTFOLIO INDEX
   -----------------------------------------------------------------------------
   A register, not a card wall. Forty-six properties on cards would be a grid of
   noise; as hairline-ruled rows they read the way a holdings table reads, which
   is the point of the whole restyle.
   ========================================================================== */

.filters { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 2.2rem; }
.filter {
    font: inherit; font-size: .755rem; font-weight: 600; letter-spacing: .02em;
    padding: .5rem .95rem; border-radius: 999px;
    border: 1px solid var(--line); background: transparent; color: var(--ink-77);
    cursor: pointer; transition: all .24s var(--ease);
}
.filter:hover { border-color: var(--ink-55); color: var(--ink); }
.filter[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: #fff; }
.filter span { opacity: .55; font-variant-numeric: tabular-nums; margin-left: .3rem; }

.register { margin-top: 2.2rem; border-top: 1px solid var(--line); }
.entry {
    display: grid; grid-template-columns: 2.3fr 3.6fr 1.5fr auto;
    align-items: baseline; gap: 1.6rem;
    padding: 1.15rem .4rem 1.2rem;
    border-bottom: 1px solid var(--line-soft);
    text-decoration: none; color: inherit;
    transition: background .22s var(--ease), padding-left .22s var(--ease);
}
.entry:hover { background: #FAFAFA; padding-left: 1rem; }
.entry__name { font-size: 1.02rem; font-weight: 600; letter-spacing: -.022em; color: var(--ink); }
.entry__sum  { font-size: .85rem; line-height: 1.5; color: var(--ink-55); }
.entry__sec  { font-size: .64rem; font-weight: 700; letter-spacing: .15em;
               text-transform: uppercase; color: var(--ink-55); }
.entry__go   { font-size: .8rem; color: var(--ink-55); transition: transform .22s var(--ease), color .22s var(--ease); }
.entry:hover .entry__go { transform: translateX(4px); color: var(--ink); }
.entry__host { display: block; margin-top: .2rem; font-size: .7rem; color: var(--muted); font-weight: 400;
               letter-spacing: 0; }
@media (max-width: 900px) {
    .entry { grid-template-columns: 1fr auto; gap: .5rem 1rem; }
    .entry__sum { grid-column: 1 / -1; }
    .entry__sec { grid-column: 1; }
}
.register__empty { padding: 3rem .4rem; color: var(--ink-55); font-size: .92rem; }

/* =============================================================================
   INSIGHTS
   ========================================================================== */

.insights { display: grid; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
            gap: clamp(1.4rem, 2.4vw, 2.2rem); margin-top: clamp(2rem,4vw,3.2rem); }
.insight { display: flex; flex-direction: column; text-decoration: none; }
.insight__t {
    aspect-ratio: 16 / 10; border-radius: 10px; overflow: hidden;
    background: var(--deep-soft); position: relative;
}
.insight__t canvas { width: 100%; height: 100%; display: block; }
.insight__k {
    margin: 1.1rem 0 .5rem; font-size: .625rem; font-weight: 700; letter-spacing: .17em;
    text-transform: uppercase; color: var(--ink-55);
}
.insight h3 { font-size: 1.06rem; font-weight: 600; letter-spacing: -.024em; line-height: 1.32; }
.insight p { margin: .55rem 0 0; font-size: .875rem; line-height: 1.55; color: var(--ink-55); }
.insight:hover h3 { text-decoration: underline; text-underline-offset: 3px; }

/* =============================================================================
   CONTACT + FOOTER
   ========================================================================== */

.offices { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
           gap: clamp(1.6rem, 3vw, 2.6rem); margin-top: 2.6rem; }
.office h3 { font-size: 1.16rem; font-weight: 700; letter-spacing: -.025em; }
.office p  { margin: .5rem 0 0; font-size: .875rem; line-height: 1.55; color: rgba(255,255,255,.55); }
.office a  { color: #fff; text-decoration: none; border-bottom: 1px solid var(--line-dark); }
.office a:hover { border-color: #fff; }

.form { display: grid; gap: .8rem; margin-top: 1.8rem; max-width: 460px; }
.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: .8rem; }
@media (max-width: 560px) { .form__row { grid-template-columns: 1fr; } }
.field {
    font: inherit; font-size: .9rem; width: 100%;
    padding: .78rem .9rem; border-radius: 8px; color: #fff;
    background: rgba(255,255,255,.045); border: 1px solid var(--line-dark);
    transition: border-color .24s var(--ease), background .24s var(--ease);
}
.field::placeholder { color: rgba(255,255,255,.38); }
/* Pointer focus gets the border lift and nothing else — a ring drawn every
   time somebody clicks into a field is noise. Keyboard focus gets a real ring:
   `.field:focus` outranks the sheet-wide :focus-visible rule below on
   specificity, so without this pair a tabbing visitor crossed the contact form
   with a one-pixel border tint on a dark panel as their only indicator. */
.field:focus { outline: none; border-color: rgba(255,255,255,.5); background: rgba(255,255,255,.08); }
.field:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 2px;
    border-color: rgba(255,255,255,.5); background: rgba(255,255,255,.08);
}
textarea.field { resize: vertical; min-height: 104px; }
/* The native select needs its own arrow once the platform chrome is removed,
   and color-scheme so the dropped-open list is dark too -- without it the
   options render as black-on-white over a dark form. */
select.field {
    appearance: none; -webkit-appearance: none;
    color-scheme: dark; cursor: pointer;
    padding-right: 2.4rem;
    background-image: linear-gradient(45deg, transparent 50%, rgba(255,255,255,.55) 50%),
                      linear-gradient(135deg, rgba(255,255,255,.55) 50%, transparent 50%);
    background-position: calc(100% - 18px) 51%, calc(100% - 13px) 51%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
}
select.field:invalid { color: rgba(255,255,255,.38); }
.form .btn { background: #fff; color: var(--ink); justify-content: center; }
.form .btn:hover { background: rgba(255,255,255,.86); }
.form__note { font-size: .74rem; color: rgba(255,255,255,.45); margin: 0; }
.form__status { font-size: .82rem; margin: 0; min-height: 1.2em; }
.form__status[data-state="ok"]  { color: #6EE7B7; }
.form__status[data-state="err"] { color: #FCA5A5; }

.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 house
   voice is calm; anything springier undoes the type.
   ========================================================================== */
.rise { opacity: 0; transform: translateY(14px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.rise.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
    .rise { opacity: 1; transform: none; transition: none; }
    .entry, .btn, .practice, .filter { transition: none; }
}

/* Focus, once, for everything. */
: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%);
}

.skip {
    position: absolute; left: -9999px; top: 0; z-index: 100;
    background: var(--ink); color: #fff; padding: .8rem 1.2rem; text-decoration: none;
}
.skip:focus { left: 0; }

/* =============================================================================
   THE STRIPS
   -----------------------------------------------------------------------------
   Carried over from 47th.llc, which took it from the console build before it.
   There it was two rows of chips crawling over a dark ground; here it sits on
   paper between the hero and the first band, held by a hairline top and bottom
   like every other division on the page.

   Two rows rather than one, running opposite ways at slightly different speeds,
   so they never march in lockstep and read as a single block. Each property gets
   an enclosed chip rather than a run of text between dots: at crawl speed a
   bare "name · practice" string reads as one continuous smear.

   Speed is not set here. The script measures the laid-out run and writes
   animation-duration, so the strip always crawls at a fixed pixels-per-second
   whatever ended up in it.
   ========================================================================== */
.strips {
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    background: #FCFCFC;
    overflow: hidden;
    padding: calc(1.1rem - 3px) 0;
}
.strip-row {
    display: flex; overflow: hidden;
    /* Three pixels of slack inside the clip. A row is as tall as one chip and a
       chip works out to a fractional height, so without this the clip rounds
       down and shaves the bottom line of pixels off every chip in the top row —
       which reads as the two rows overlapping rather than as one being cut. */
    padding: 3px 0;
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 5%, #000 95%, transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 5%, #000 95%, transparent 100%);
}
.strip-row + .strip-row { margin-top: calc(.7rem - 6px); }

.strip-track {
    display: flex; align-items: center; flex: 0 0 auto;
    gap: .7rem;
    /* Matches `gap` exactly. Without it the track is one gap short of twice a
       run, so the -50% keyframe lands half a gap off and the loop visibly
       hitches once per pass. */
    padding-right: .7rem;
    white-space: nowrap;
    animation: strip-run 120s linear infinite;
    backface-visibility: hidden;
    /* Parked until the script says the rows are actually on screen. The chips
       are built by JS in the first place, so a paused default costs a
       no-script visitor nothing — and it means the compositor is not being
       asked to run two infinite translations behind six screens of copy for
       the whole session. `will-change` rides the same switch: held permanently
       it is a promoted layer that never comes back. */
    animation-play-state: paused;
}
.strips.is-live .strip-track { animation-play-state: running; will-change: transform; }
/* The crawl deliberately does not pause on hover: a chip is a link, and
   stopping the row the moment the pointer arrives made every click stop the
   strip dead. */

.s-chip {
    display: inline-flex; align-items: center; gap: .6rem;
    flex: 0 0 auto;
    padding: .42rem .9rem;
    border: 1px solid var(--line); border-radius: 999px;
    background: var(--paper);
    font: inherit; text-align: left; cursor: pointer; text-decoration: none;
    transition: border-color .22s var(--ease), background .22s var(--ease);
}
.s-chip:hover { border-color: var(--ink); background: #fff; }
.s-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.s-chip::before {
    content: '◆'; color: var(--accent); font-size: .58rem; line-height: 1; flex: 0 0 auto;
}
.s-name {
    font-size: .82rem; font-weight: 600; letter-spacing: -.018em; color: var(--ink);
}
.s-meta {
    font-size: .58rem; font-weight: 700; letter-spacing: .15em; text-transform: uppercase;
    color: var(--ink-55);
    padding-left: .6rem; border-left: 1px solid var(--line);
}

@keyframes strip-run {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
}
@media (prefers-reduced-motion: reduce) { .strip-track { animation: none; } }

@media (max-width: 640px) {
    .strips { padding: calc(.8rem - 3px) 0; }
    .strip-row + .strip-row { margin-top: calc(.5rem - 6px); }
    .s-chip { gap: .45rem; padding: .36rem .7rem; }
    .s-name { font-size: .76rem; }
    .s-meta { font-size: .53rem; padding-left: .45rem; }
}
