/* =============================================================================
   47th.llc — DESIGN SYSTEM
   -----------------------------------------------------------------------------
   The look is labs.llc's, repurposed for the diamond directory: near-black
   surfaces, one orange accent, liquid glass panels, Orbitron/Space Grotesk
   display type over Inter body copy.

   Written by hand rather than as a purged Tailwind build. labs.llc ships
   assets/tailwind.css, and the comment in that file says what goes wrong when
   the purge output drifts from the markup — the contact section loses its
   columns at desktop widths. Nothing here can go stale against index.html,
   because there is no build step between the two.

   Theming works the way the CONTROL panel expects: everything colour-related
   resolves through --primary-orange and --glow-intensity, so applyTheme() only
   has to write two custom properties on :root to repaint the whole page.
   ========================================================================== */

:root {
    /* The single accent. Every other orange in the file derives from it. */
    --primary-orange: #ff8800;
    --accent-2:       #67e8f9;   /* the cyan half of the holo gradient */

    /* Surfaces — a deliberate ramp, not ad-hoc hex values */
    --bg:        #0a0a0a;
    --surface:   #101017;
    --surface-2: #14141d;
    --surface-3: #1b1b26;

    /* Lines */
    --line:        rgba(255, 255, 255, 0.08);
    --line-strong: rgba(255, 255, 255, 0.16);

    /* Text */
    --text:       #ededf2;
    --text-dim:   #94a3b8;
    --text-faint: #64748b;
    --text-mute:  #475569;

    /* Semantic */
    --ok:     #34d399;
    --warn:   #fbbf24;
    --danger: #f87171;

    /* Type */
    --font-sans:    'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-display: 'Space Grotesk', 'Inter', sans-serif;
    --font-tech:    'Orbitron', 'Space Grotesk', monospace;
    --font-mono:    'JetBrains Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;

    /* Space */
    --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
    --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px;

    /* Shape */
    --r-sm:   8px;
    --r:      14px;
    --r-lg:   22px;
    --r-pill: 9999px;

    /* Motion */
    --speed: 160ms;
    --ease:  cubic-bezier(0.23, 1, 0.32, 1);

    /* Driven by the CONTROL panel's GLOW INTENSITY slider. Every glow in the
       sheet multiplies by this, so one slider moves the whole page. */
    --glow-intensity: 1;

    --nav-h: 66px;
}

/* ==========================================================================
   RESET
   ========================================================================== */

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

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

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-sans);
    font-size: 15px;
    line-height: 1.55;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    /* Kills double-tap zoom without taking pinch magnification away. */
    touch-action: manipulation;
}

h1, h2, h3, h4 { margin: 0; font-weight: 700; letter-spacing: -0.02em; }
p  { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }

a { color: inherit; text-decoration: none; }
a:hover { color: var(--primary-orange); }

img { max-width: 100%; display: block; }

button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }

:focus-visible {
    outline: 2px solid var(--primary-orange);
    outline-offset: 2px;
    border-radius: 4px;
}

::selection { background: var(--primary-orange); color: #0a0a0a; }

/* Scrollbars, so the dark page does not get a bright system gutter. */
* { scrollbar-width: thin; scrollbar-color: #2a2a35 transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: #2a2a35; border-radius: 999px; border: 2px solid var(--bg); }
*::-webkit-scrollbar-thumb:hover { background: #3a3a48; }

/* ==========================================================================
   UTILITIES — the handful of Tailwind-shaped helpers the markup relies on
   ========================================================================== */

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border-width: 0;
}
.skip-link {
    position: absolute; left: -9999px; top: 0; z-index: 999;
    background: var(--primary-orange); color: #000; padding: 10px 16px;
    font-weight: 700; border-radius: 0 0 var(--r-sm) 0;
}
.skip-link:focus { left: 0; }

.hidden   { display: none !important; }
.wrap     { max-width: 1600px; margin: 0 auto; padding: 0 var(--s6); }
.wrap-narrow { max-width: 1120px; margin: 0 auto; padding: 0 var(--s6); }
.row      { display: flex; align-items: center; }
.row-gap  { gap: var(--s3); }
.spread   { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); }
.stack    { display: flex; flex-direction: column; }
.grow     { flex: 1 1 auto; min-width: 0; }
.nowrap   { white-space: nowrap; }
.mono     { font-family: var(--font-mono); }
.dim      { color: var(--text-dim); }
.faint    { color: var(--text-faint); }
.accent   { color: var(--primary-orange); }
.center   { text-align: center; }

.font-display { font-family: var(--font-display); font-weight: 600; }
.font-tech    { font-family: var(--font-tech); letter-spacing: 0.04em; }
.stat-number  { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   SIGNATURE TREATMENTS
   ========================================================================== */

/* Travelling orange→cyan gradient text. The second line of every hero
   headline uses it, which is what makes a landing variant read as branded
   rather than as a swapped string. */
.holo-text {
    background: linear-gradient(90deg, var(--primary-orange), var(--accent-2), var(--primary-orange));
    background-size: 200% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    animation: holo 3s ease infinite;
}
@keyframes holo {
    0%, 100% { background-position: 0% 50%; }
    50%      { background-position: 100% 50%; }
}

.section-header {
    font-family: var(--font-display);
    font-size: clamp(1.9rem, 3.4vw, 2.6rem);
    line-height: 1.06;
    font-weight: 800;
    letter-spacing: -0.035em;
}
.section-header-grad {
    background: linear-gradient(90deg, #ffffff, var(--primary-orange), #ffffff);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
}

.eyebrow {
    font-size: 10px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    font-weight: 700;
    color: var(--text-faint);
}

.premium-badge {
    background: linear-gradient(90deg, var(--primary-orange), var(--accent-2));
    color: #0a0a0a; font-weight: 700; font-size: 0.7rem;
    padding: 2px 10px; border-radius: var(--r-pill);
}

.lab-grid {
    background-image:
        linear-gradient(rgba(255,136,0,0.07) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,136,0,0.07) 1px, transparent 1px);
    background-size: 40px 40px;
}

.live-dot {
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--ok);
    box-shadow: 0 0 calc(8px * var(--glow-intensity)) var(--ok);
    animation: pulse-dot 2s ease-in-out infinite;
    flex: 0 0 auto;
}
@keyframes pulse-dot { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

/* ==========================================================================
   LIQUID GLASS
   --------------------------------------------------------------------------
   `.liquid-glass` marks a surface as glass; html.lg-on / html.lg-off decides
   globally whether it renders as glass, which is what the CONTROL panel flips.
   ON is genuinely translucent — the background canvas moves behind the panels.
   OFF is not "less glass": no blur at all and an opaque surface, so the cheap
   mode is genuinely cheap on a phone.
   ========================================================================== */

.liquid-glass {
    position: relative;
    overflow: hidden;
    border-radius: var(--r-lg);
    transition: background .45s ease, box-shadow .45s ease,
                border-color .45s ease, backdrop-filter .45s ease;
}

html.lg-on .liquid-glass {
    background:
        linear-gradient(135deg,
            rgba(255,255,255,0.09) 0%,
            rgba(255,255,255,0.02) 42%,
            rgba(255,255,255,0.06) 100%),
        linear-gradient(180deg, rgba(8,10,18,0.60), rgba(8,10,18,0.74));
    -webkit-backdrop-filter: blur(24px) saturate(165%) brightness(1.07) contrast(1.02);
    backdrop-filter:         blur(24px) saturate(165%) brightness(1.07) contrast(1.02);
    border: 1px solid rgba(255,255,255,0.15);
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.30),
        inset 0 -1px 0 rgba(255,255,255,0.06),
        inset 0 0 40px rgba(255,255,255,0.035),
        0 20px 56px -24px rgba(0,0,0,0.8),
        0 0 calc(26px * var(--glow-intensity)) color-mix(in srgb, var(--primary-orange) 12%, transparent);
}
html.lg-on .liquid-glass:hover {
    border-color: rgba(255,255,255,0.24);
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.42),
        inset 0 -1px 0 rgba(255,255,255,0.09),
        inset 0 0 48px rgba(255,255,255,0.055),
        0 26px 72px -26px rgba(0,0,0,0.85),
        0 0 calc(36px * var(--glow-intensity)) color-mix(in srgb, var(--primary-orange) 20%, transparent);
}

html.lg-off .liquid-glass {
    background: linear-gradient(180deg, var(--surface) 0%, #0b0b11 100%);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border: 1px solid var(--line);
    box-shadow: 0 10px 30px -12px rgba(0,0,0,0.7);
}
html.lg-off .liquid-glass::before { display: none; }

/* The travelling sheen — the wet part of "liquid" — glass mode only. */
html.lg-on .liquid-glass::before {
    content: '';
    position: absolute;
    top: -50%; left: -50%; width: 200%; height: 200%;
    background: linear-gradient(135deg,
        transparent 20%,
        rgba(255,255,255,0.13) 39%,
        rgba(255,255,255,0.03) 48%,
        transparent 62%);
    animation: liquid-flow 6s linear infinite;
    pointer-events: none;
    z-index: 0;
}
/* An IntersectionObserver parks .lg-idle on anything off-screen. With 400
   listing cards on the page that is 400 continuous compositor animations for
   an effect nobody can see. play-state keeps position, so a card resumes
   mid-sweep rather than snapping. */
html.lg-on .liquid-glass.lg-idle::before { animation-play-state: paused; }

@keyframes liquid-flow {
    0%   { transform: translateX(-30%) translateY(-30%) rotate(0deg); }
    50%  { transform: translateX(10%) translateY(10%) rotate(180deg); }
    100% { transform: translateX(-30%) translateY(-30%) rotate(360deg); }
}

/* Content sits above the sheen. */
.liquid-glass > * { position: relative; z-index: 1; }

/* The chrome follows the mode too, so the whole page changes state rather
   than just the cards in the middle of it. */
html.lg-on .site-nav {
    background: rgba(10,10,10,0.74);
    -webkit-backdrop-filter: blur(22px) saturate(160%) brightness(1.05);
    backdrop-filter:         blur(22px) saturate(160%) brightness(1.05);
}
html.lg-off .site-nav {
    background: #0a0a0a;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}
html.lg-on  #nav-menu-panel { background: rgba(11,11,18,0.80); -webkit-backdrop-filter: blur(28px) saturate(170%); backdrop-filter: blur(28px) saturate(170%); }
html.lg-off #nav-menu-panel { background: #0b0b12; -webkit-backdrop-filter: none; backdrop-filter: none; }

/* Without backdrop-filter, ON would be a pale wash with no blur — which reads
   as broken rather than plain. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    html.lg-on .liquid-glass { background: linear-gradient(180deg, rgba(16,16,23,0.96), rgba(11,11,17,0.98)); }
    html.lg-on .site-nav      { background: #0a0a0a; }
    html.lg-on #nav-menu-panel { background: #0b0b12; }
}

@media (prefers-reduced-motion: reduce) {
    html.lg-on .liquid-glass::before { animation: none; opacity: .35; }
    .liquid-glass { transition: none; }
}

/* ==========================================================================
   BACKGROUND CANVAS
   ========================================================================== */

#bg-canvas {
    position: fixed; inset: 0;
    width: 100%; height: 100%;
    z-index: -1;
    opacity: 0.35;
    pointer-events: none;
}

/* ==========================================================================
   BUTTONS + PILLS
   ========================================================================== */

.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
    padding: 0.6rem 1.15rem;
    border-radius: var(--r-pill);
    border: 1px solid var(--line-strong);
    background: var(--surface-2);
    color: var(--text);
    font-size: 0.82rem; font-weight: 600;
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease),
                transform var(--speed) var(--ease), color var(--speed) var(--ease);
    white-space: nowrap;
}
.btn:hover { border-color: color-mix(in srgb, var(--primary-orange) 60%, transparent); color: var(--text); }
.btn:active { transform: scale(0.985); }

.btn-primary {
    background: linear-gradient(135deg, var(--primary-orange), color-mix(in srgb, var(--primary-orange) 65%, #ffd08a));
    color: #0a0a0a; border-color: transparent; font-weight: 800;
    box-shadow: 0 0 calc(24px * var(--glow-intensity)) color-mix(in srgb, var(--primary-orange) 35%, transparent);
}
.btn-primary:hover { filter: brightness(1.08); color: #0a0a0a; }

.btn-ghost { background: transparent; }
.btn-sm    { padding: 0.4rem 0.8rem; font-size: 0.74rem; }
/* Bare glyph, not a framed control: it keeps the 34px hit target but drops the
   ring, so the ⟳ sits in the nav meta row as quietly as the ✆ beside it. The
   hover rule has to be restated because .btn:hover paints a border colour. */
.btn-icon  { padding: 0; width: 40px; height: 40px; border-radius: 50%;
             border-color: transparent; background: transparent;
             /* The glyph carries the whole control now that the ring is gone,
                so it is sized off .btn's 0.82rem rather than inheriting it. */
             font-size: 1.35rem; line-height: 1; }
.btn-icon:hover { border-color: transparent; color: var(--primary-orange); }

/* Marks the option you are currently on. The view and tile-size rows list every
   choice side by side, so their buttons are a state readout rather than an
   action, and the lit one has to say which state that is. Declared after
   .btn:hover — same specificity — so pointing at the active option does not
   wash the accent back out. */
.btn.is-active, .btn[aria-pressed="true"] {
    background: color-mix(in srgb, var(--primary-orange) 16%, transparent);
    border-color: color-mix(in srgb, var(--primary-orange) 55%, transparent);
    color: var(--primary-orange);
}
.btn.is-active:hover, .btn[aria-pressed="true"]:hover { color: var(--primary-orange); }

/* The pill bar: one row of segmented chips, exactly one lit at a time. */
.pillbar {
    display: flex; align-items: center; gap: 6px;
    overflow-x: auto; scrollbar-width: none;
    padding: 3px;
}
.pillbar::-webkit-scrollbar { display: none; }
.pill {
    flex: 0 0 auto;
    padding: 0.35rem 0.8rem;
    border-radius: var(--r-pill);
    border: 1px solid var(--line);
    background: rgba(255,255,255,0.03);
    color: var(--text-dim);
    font-size: 0.72rem; font-weight: 700;
    letter-spacing: 0.04em;
    transition: all var(--speed) var(--ease);
    white-space: nowrap;
}
.pill:hover { color: var(--text); border-color: var(--line-strong); }
.pill[aria-pressed="true"], .pill.is-active {
    background: color-mix(in srgb, var(--primary-orange) 16%, transparent);
    border-color: color-mix(in srgb, var(--primary-orange) 55%, transparent);
    color: var(--primary-orange);
}
.pill .pill-count {
    margin-left: 6px; font-family: var(--font-mono);
    font-size: 0.65rem; color: var(--text-faint);
}
.pill[aria-pressed="true"] .pill-count { color: inherit; opacity: 0.8; }

/* ==========================================================================
   SEGMENTED CONTROL
   --------------------------------------------------------------------------
   The successor to a .pillbar of detached buttons. Seven separate chips with a
   gap between them read as seven separate decisions; a department bar is one
   decision with seven answers, and a segmented control is what that shape looks
   like — a single recessed track, hairline rules between the answers, one lit.

   The count belongs to the label, not to the segment: it sits hard against the
   name with a hair of space, one step down in size and two steps down in
   contrast, so "Diamonds 259" reads as one thing rather than a word and a
   number sharing a button.

   .pill is deliberately left alone — the mobile sheet's tabs are built on it.
   ========================================================================== */

/* The track wraps rather than scrolls. A horizontal scroller inside a page that
   already scrolls vertically hides its own overflow: thirteen markets went in
   and five came out, with nothing to say the other eight existed. Wrapping
   costs a row of height and shows everything.

   Which is also why the radius is --r rather than --r-pill: on one row a 14px
   radius on a ~34px track still reads as a pill, and on two it reads as a
   rounded panel instead of a lozenge someone sat on. */
.segbar {
    display: flex; align-items: stretch; flex-wrap: wrap;
    width: fit-content; max-width: 100%;
    padding: 3px; row-gap: 2px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: color-mix(in srgb, var(--text) 2.5%, transparent);
}

.seg {
    position: relative;
    flex: 0 0 auto;
    display: inline-flex; align-items: stretch;
    border-radius: var(--r-pill);
    color: var(--text-dim);
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
    white-space: nowrap;
    transition: color var(--speed) var(--ease), background var(--speed) var(--ease),
                box-shadow var(--speed) var(--ease);
}

/* The rules live on the segment rather than between elements so they can be
   pulled either side of the lit one — a divider touching the active fill reads
   as a seam in it. */
.seg + .seg::before {
    content: ''; position: absolute; left: 0; top: 26%; bottom: 26%;
    width: 1px; background: var(--line);
    transition: opacity var(--speed) var(--ease);
    pointer-events: none;
}
.seg.is-active::before,
.seg.is-active + .seg::before,
.seg:hover::before,
.seg:hover + .seg::before { opacity: 0; }
/* First segment of a wrapped row: its rule would hang in the padding at the
   left edge of the track with nothing on the other side of it. Which segment
   that is depends on where the row happened to break, so it is measured rather
   than guessed — layoutSegRows() sets the class after layout. */
.seg.is-row-start::before { opacity: 0; }

.seg-btn {
    display: inline-flex; align-items: baseline; gap: 5px;
    padding: 0.34rem 0.72rem;
    color: inherit; font: inherit; letter-spacing: inherit;
    border-radius: inherit;
}
.seg-count {
    font-family: var(--font-mono); font-size: 0.62rem; font-weight: 600;
    letter-spacing: 0; color: var(--text-mute);
    font-variant-numeric: tabular-nums;
    transition: color var(--speed) var(--ease);
}

/* The market segments' second target: a small lamp that opens the market's
   modal, while the rest of the pill aims the strips and the directory.

   It is lit in the theme accent rather than in the ink, because it is the one
   thing on the pill that is a light rather than a label — and it breathes, on
   a slow cycle, so a row of thirteen reads as a panel of standby lamps instead
   of thirteen identical dots. The pulse lives on a ::before layer and moves
   only opacity and transform, which the compositor can run without touching
   layout; pulsing box-shadow on the button itself repaints thirteen elements
   every frame for the same picture.

   8px, pulled back into the label button's own right padding so there is one
   gap before it and one after, not three. It keeps a ~26px hit box through the
   ::after, so the thing that is 8px to look at is still thumbable — without
   that it is a target nobody lands on twice.

   Department pills keep their counts and never get one of these — a department
   has no modal behind it. */
.seg-info {
    position: relative;
    align-self: center;
    flex: 0 0 auto;
    width: 8px; height: 8px;
    /* A button's UA padding is 1px 6px, and under border-box that floors the
       box at 12px wide however small the width is set — the square came out a
       rectangle until this. */
    padding: 0;
    /* The pill centres on its own box; text sits a shade above that box's
       middle because of the descender space under it. One pixel puts the lamp
       on the same line as the label rather than a hair below it. */
    top: -1px;
    margin: 0 0.6rem 0 -0.2rem;
    border-radius: 2px;
    background: color-mix(in srgb, var(--primary-orange) 62%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--primary-orange) 90%, transparent);
    transition: background var(--speed) var(--ease), box-shadow var(--speed) var(--ease),
                transform var(--speed) var(--ease);
}

/* The glow. Sat behind the square rather than drawn on it so the pulse is one
   composited layer, and so hover can brighten the halo without touching the
   8px of colour at the centre of it. */
.seg-info::before {
    content: '';
    position: absolute; inset: -4px;
    border-radius: 5px;
    background: radial-gradient(circle,
        color-mix(in srgb, var(--primary-orange) 60%, transparent) 0%,
        transparent 70%);
    opacity: 0.4;
    pointer-events: none;
    animation: seg-lamp 3.2s var(--ease) infinite;
}
@keyframes seg-lamp {
    0%, 100% { opacity: 0.32; transform: scale(0.82); }
    50%      { opacity: 0.85; transform: scale(1.28); }
}
/* Offset in three groups, or thirteen lamps on one clock is a strobe rather
   than a bar breathing. Negative delays start each group mid-cycle, so nothing
   waits to join in on first paint. */
.seg:nth-child(3n)   .seg-info::before { animation-delay: -1.05s; }
.seg:nth-child(3n+1) .seg-info::before { animation-delay: -2.1s; }

/* Grown further vertically than sideways: up and down it only reaches into the
   segbar's own padding, whereas the left edge is eating into the label button's
   click area and has to stop short of the label text. */
.seg-info::after {
    content: ''; position: absolute; inset: -9px -7px;
}

.seg:hover .seg-info { background: color-mix(in srgb, var(--primary-orange) 85%, transparent); }
.seg:hover .seg-info::before { opacity: 0.75; }

/* Under the pointer it stops being a standby lamp and turns all the way on:
   solid accent, a white inner edge to read as a filament rather than a swatch,
   and two shadows — a tight one for the bulb and a wide soft one for the room
   around it. The breath runs at a third of the speed, so the thing you are
   pointing at is visibly more awake than its neighbours. */
.seg-info:hover, .seg-info:focus-visible {
    background: var(--primary-orange);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, #ffffff 55%, transparent),
                0 0 10px color-mix(in srgb, var(--primary-orange) 95%, transparent),
                0 0 24px color-mix(in srgb, var(--primary-orange) 55%, transparent);
    transform: scale(1.4);
}
.seg-info:hover::before, .seg-info:focus-visible::before {
    opacity: 1;
    animation-duration: 1.1s;
}

@media (prefers-reduced-motion: reduce) {
    .seg-info::before { animation: none; opacity: 0.5; transform: none; }
}

.seg:hover { color: var(--text); background: color-mix(in srgb, var(--text) 6%, transparent); }
.seg:hover .seg-count { color: var(--text-faint); }

.seg.is-active {
    color: var(--primary-orange);
    background: color-mix(in srgb, var(--primary-orange) 15%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--primary-orange) 45%, transparent);
}
.seg.is-active .seg-count { color: inherit; opacity: 0.75; }
/* The aimed market's lamp is on rather than on standby, and it stops breathing:
   a steady light next to twelve pulsing ones is what says "this one". */
.seg.is-active .seg-info {
    background: var(--primary-orange);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, #ffffff 45%, transparent),
                0 0 9px color-mix(in srgb, var(--primary-orange) 70%, transparent);
}
.seg.is-active .seg-info::before { opacity: 0.9; animation: none; transform: scale(1.1); }

/* BUTTON ANIMATION toggle — off by default, pulsing glow when on. .btn-icon is
   left out: the pulse draws a lit ring at the element's border radius, which on
   the round ⟳ reads as a circle around the glyph rather than a glow on a pill.
   A segmented bar glows as one track, not once per segment — thirteen separate
   pulses inside a single control is a strobe, not an accent. */
html.btn-anim-on .btn:not(.btn-icon),
html.btn-anim-on .pill,
html.btn-anim-on .segbar,
html.btn-anim-on #control-panel button {
    animation: btn-pulse 2.4s ease-in-out infinite;
}
@keyframes btn-pulse {
    0%, 100% { box-shadow: 0 0 0 0 transparent; }
    50%      { box-shadow: 0 0 calc(18px * var(--glow-intensity)) 1px var(--primary-orange); filter: brightness(1.1); }
}
@media (prefers-reduced-motion: reduce) {
    html.btn-anim-on .btn, html.btn-anim-on .pill, html.btn-anim-on .segbar { animation: none; }
}

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

.site-nav {
    position: sticky; top: 0; z-index: 50;
    border-bottom: 1px solid var(--line);
    min-height: var(--nav-h);
}
.nav-inner {
    max-width: 1600px; margin: 0 auto;
    padding: 10px var(--s6);
    display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
}

/* The 47th mark: an orange pill with a black wordmark, tilting in 3D. The
   wobble is what makes it read as an object rather than an image. */
.logo-link { position: relative; display: flex; align-items: center; perspective: 900px; }
#logo-3d {
    position: relative; width: 93px; height: 37px;   /* 395:157, the mark's own ratio */
    transform-style: preserve-3d;
    animation: logoTilt 5s ease-in-out infinite;
    filter: drop-shadow(0 0 calc(22px * var(--glow-intensity)) color-mix(in srgb, var(--primary-orange) 70%, transparent));
    transition: filter .2s var(--ease);
}
.logo-link:hover #logo-3d {
    filter: drop-shadow(0 0 calc(42px * var(--glow-intensity)) var(--primary-orange));
}
@keyframes logoTilt {
    0%, 100% { transform: perspective(600px) rotateY(-17deg) rotateX(7deg); }
    50%      { transform: perspective(600px) rotateY(17deg) rotateX(-5deg); }
}
@media (prefers-reduced-motion: reduce) { #logo-3d { animation: none; } }

.logo-halo {
    position: absolute; inset: -8px;
    border-radius: var(--r-lg);
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--primary-orange) 32%, transparent),
        transparent 60%,
        color-mix(in srgb, var(--accent-2) 28%, transparent));
    filter: blur(18px);
    opacity: calc(0.75 * var(--glow-intensity));
    pointer-events: none;
}

.nav-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }

/* Number and re-roll travel together. nowrap is the point: .nav-meta wraps, and
   without this the ⟳ was free to drop onto its own line under the number the
   moment the bar tightened. */
.nav-contact { display: inline-flex; align-items: center; gap: 6px; flex-wrap: nowrap; }

.nav-tagline {
    position: relative; display: inline-block; white-space: nowrap;
    font-size: 10px; letter-spacing: 0.2em; color: var(--text-faint);
}
.nav-tagline .tag-width { opacity: 0; }
.nav-tagline .tag-type  { position: absolute; inset: 0; }
.tagline-caret { color: var(--primary-orange); animation: caret 0.9s steps(1, end) infinite; }
@keyframes caret { 0%, 54% { opacity: 1; } 55%, 100% { opacity: 0; } }

.nav-phone {
    display: inline-flex; align-items: center; gap: 6px;
    font-family: var(--font-tech); font-size: 12.5px; font-weight: 700;
    letter-spacing: 0.04em; color: #e8e8ee;
}
.nav-phone:hover { color: var(--primary-orange); }

.nav-actions { display: flex; align-items: center; gap: var(--s2); }

/* The hamburger, with nothing written beside it. .btn-icon strips the ring and
   the fill so the ⟳ can sit quietly in the meta row; this is the opposite job —
   the burger is the loudest control in the bar — so the accent surface is put
   back on top of it, and squared off to a rounded rect rather than a circle so
   it reads as a button rather than another status dot. */
.nav-burger {
    width: 42px; height: 38px; border-radius: var(--r-sm);
    font-size: 1.05rem;
    background: var(--primary-orange); color: #0a0a0a;
    border: 1px solid transparent;
}
.nav-burger:hover {
    color: #0a0a0a; border-color: transparent;
    filter: brightness(1.08);
}
.nav-burger[aria-expanded="true"] { filter: brightness(0.92); }

/* Nav clock — digits in a tabular cell so the width never twitches. */
#nav-clock {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 5px 10px; border-radius: var(--r-pill);
    border: 1px solid var(--line); background: rgba(255,255,255,0.03);
}
#nav-clock .clk-face {
    font-family: var(--font-mono); font-size: 12px; font-weight: 600;
    font-variant-numeric: tabular-nums; color: var(--text);
}
#nav-clock .clk-d   { display: inline-block; min-width: 0.62em; text-align: center; }
#nav-clock .clk-sep { color: var(--primary-orange); }
#nav-clock .clk-sec { color: var(--text-faint); }
#nav-clock .clk-zone { font-size: 9px; letter-spacing: 0.12em; color: var(--text-faint); }
#nav-clock svg .clk-face-ring { stroke: var(--line-strong); fill: none; stroke-width: 2; }
#nav-clock svg .clk-h-hand,
#nav-clock svg .clk-m-hand { stroke: #e8e8ee; stroke-width: 2.5; stroke-linecap: round; }
#nav-clock svg .clk-s-hand { stroke: var(--primary-orange); stroke-width: 1.6; stroke-linecap: round; }
#nav-clock svg .clk-pin    { fill: var(--primary-orange); }

/* ==========================================================================
   NAV MENU (hamburger drawer)
   ========================================================================== */

#nav-menu { position: fixed; inset: 0; z-index: 300; }
#nav-menu-scrim { position: absolute; inset: 0; background: rgba(0,0,0,0.72); backdrop-filter: blur(3px); }
#nav-menu-panel {
    position: absolute; top: 0; right: 0; height: 100%;
    width: min(390px, 92vw);
    border-left: 1px solid var(--line);
    display: flex; flex-direction: column;
    transform: translateX(100%);
    transition: transform .28s var(--ease);
    padding-top: env(safe-area-inset-top);
}
#nav-menu.is-open #nav-menu-panel { transform: translateX(0); }

.menu-head { padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line); }
.menu-body { flex: 1; overflow-y: auto; padding: var(--s3) var(--s4) var(--s7); }
.menu-group-label {
    font-size: 9.5px; letter-spacing: 0.22em; text-transform: uppercase;
    color: var(--text-mute); font-weight: 700;
    padding: var(--s4) var(--s2) var(--s2);
}
.menu-row {
    display: flex; align-items: center; gap: 0.75rem; width: 100%;
    padding: 0.6rem 0.65rem; border-radius: var(--r);
    transition: background var(--speed) var(--ease), transform var(--speed) var(--ease);
    text-align: left; color: #e6e6e6; background: transparent;
    font-size: 0.86rem;
}
.menu-row:hover { background: rgba(255,255,255,.06); transform: translateX(3px); color: var(--text); }
.menu-row:active { transform: translateX(1px) scale(.99); }
.menu-chip {
    width: 2.3rem; height: 2.3rem; border-radius: 0.75rem; flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center; font-size: .9rem;
}
.menu-row .menu-tail { margin-left: auto; font-family: var(--font-mono); font-size: 0.68rem; color: var(--text-faint); }

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

/* padding-block only: `.hero` is also a `.wrap`, and a `padding` shorthand
   here would zero out the gutter that `.wrap` sets. */
.hero { padding-block: var(--s7) var(--s6); }
.hero-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s6);
    align-items: center;
}
@media (min-width: 1080px) {
    .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 46%); gap: var(--s8); }
}
/* The market panel is the grid's second row. The column gap is set wide to keep
   the copy off the image; the same figure between the copy and the panel below
   it just strands the panel, so the rows are pulled back in. */
.hero-grid { row-gap: var(--s5); }

#hero-headline {
    font-family: var(--font-display);
    font-size: clamp(2.6rem, 6.4vw, 5rem);
    font-weight: 900;
    line-height: 0.94;
    letter-spacing: -0.045em;
    margin-bottom: var(--s5);
}
#hero-subtext {
    max-width: 34rem;
    font-size: 1.05rem;
    color: var(--text-dim);
    margin-bottom: var(--s6);
}
.hero-badge-row { display: flex; align-items: center; gap: 10px; margin-bottom: var(--s4); }
#hero-cta-row { display: flex; flex-wrap: wrap; gap: var(--s3); margin-bottom: var(--s5); }
#hero-proof { color: var(--text-dim); font-size: 0.86rem; }
#hero-proof b, #hero-proof .hl { color: #fff; font-weight: 700; }

/* The landing image. 20 of them; one is drawn per load and paired with the
   copy variant, and clicking cycles to another. */
.hero-visual { position: relative; }
.hero-figure {
    position: relative; overflow: hidden;
    /* <figure> carries the browser's own `margin: 1em 40px`, and nothing in the
       reset above takes it off. That inset the image 40px inside its column
       while the dot strip below ran the column's full width, so the two never
       lined up — the strip started left of the picture and finished right of
       it. The margin was never asked for; the image fills its column. */
    margin: 0;
    border-radius: var(--r-lg);
    aspect-ratio: 3 / 2;
    cursor: pointer;
    border: 1px solid var(--line-strong);
    background: var(--surface);
}
#hero-image {
    width: 100%; height: 100%; object-fit: cover;
    transition: transform .9s var(--ease), filter .9s var(--ease);
}
.hero-figure:hover #hero-image { transform: scale(1.04); }

/* The facet mesh sits over the image and under the caption. z-index 1 clears
   .hero-figure::after, the gradient scrim, which would otherwise wash it out;
   the caption is already on 2. screen blending keeps it reading as light on
   the photograph rather than paint over it. */
#hero-mesh {
    position: absolute; inset: 0; z-index: 1;
    width: 100%; height: 100%;
    pointer-events: none;
    mix-blend-mode: screen;
    opacity: 0; transition: opacity .45s var(--ease);
}
.hero-figure:hover #hero-mesh, #hero-mesh.is-live { opacity: 1; }
@media (prefers-reduced-motion: reduce) { #hero-mesh { display: none; } }
html.epaper #hero-mesh { display: none; }

/* ---- markets in the landing --------------------------------------------
   A panel rather than a loose row of chips. The markets are the one thing on
   the landing that is both a fact about the book and a control over it, and a
   bare rail of buttons floating under the small print stated neither: it read
   as decoration until you happened to click one.

   So it gets a frame, a label, and a line that answers "what am I looking at"
   before you touch anything — and the picker itself collapses into a single
   segmented track, which is what a one-of-thirteen choice actually is.

   The panel takes the full hero column rather than a fixed 640px: the track
   wraps now, and every 40px of width taken away is another market pushed onto a
   third row.
   ------------------------------------------------------------------------ */
.market-panel {
    /* Third row of the hero grid, under both columns. */
    grid-column: 1 / -1;
    margin-top: var(--s2);
    padding: 10px 10px 9px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    /* Tinted off --text rather than off white: reader mode flips the palette,
       and a white wash on a cream page is no panel at all. */
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--text) 4%, transparent),
        color-mix(in srgb, var(--text) 1.5%, transparent));
}

.market-panel-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s3);
    padding: 0 4px 8px;
}
.market-panel-head .eyebrow { font-size: 0.62rem; letter-spacing: 0.18em; }

/* The readout. Reserved a line of its own height so switching markets does not
   reflow the hero — the longest label still has to fit without wrapping. */
.market-panel-note {
    font-size: 0.7rem; color: var(--text-faint);
    text-align: right; min-height: 1.1em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.market-panel-note b { color: var(--text-dim); font-weight: 700; }
.market-panel-note .mp-n {
    font-family: var(--font-mono); font-size: 0.66rem;
    color: var(--primary-orange); font-variant-numeric: tabular-nums;
}

/* The track fills the panel — a frame narrower than the head above it reads as
   a mistake — and on a desktop, where every market fits on one line, the
   segments are stretched to fill the track with it. Packed left they left one
   long run of dead track on the right; centred they left two shorter runs, one
   on each side. Both are the same admission that the row did not reach the
   edge. Stretched, there is no gap to explain: the bar is a segmented control
   the width of its frame, and every divider falls where a divider belongs.

   The stretch is gated on the row count because it is per flex line, not per
   track: on a phone, where thirteen markets break across three lines, a last
   line holding two of them would blow each up to half the width of the panel.
   layoutSegRows() measures what actually happened and sets .is-one-row, so the
   wrapped case keeps the centred treatment instead. */
.market-panel .segbar { width: 100%; justify-content: center; }
.market-panel .segbar.is-one-row .seg {
    /* Grown, then its own contents re-centred inside the growth — otherwise the
       label sits left and the lamp is flung out to the far right of a segment
       three times the width of the word in it. */
    flex: 1 1 auto;
    justify-content: center;
}

@media (max-width: 700px) {
    .market-panel-head { flex-direction: column; align-items: flex-start; gap: 2px; }
    .market-panel-note { text-align: left; }
}

/* ---- back to top --------------------------------------------------------
   Tinted rather than solid so it reads as an overlay on the listings it is
   floating above, not another card in the column. */
.to-top {
    position: fixed; z-index: 60;
    right: calc(var(--s5) + env(safe-area-inset-right));
    bottom: calc(var(--s6) + 58px + env(safe-area-inset-bottom));
    display: inline-flex; align-items: center; gap: 7px;
    padding: 0.5rem 0.9rem;
    border-radius: var(--r-pill);
    border: 1px solid color-mix(in srgb, var(--primary-orange) 45%, transparent);
    background: color-mix(in srgb, var(--primary-orange) 14%, rgba(10,10,16,0.72));
    -webkit-backdrop-filter: blur(14px) saturate(150%);
            backdrop-filter: blur(14px) saturate(150%);
    color: var(--primary-orange);
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
    box-shadow: 0 10px 30px -12px rgba(0,0,0,0.8),
                0 0 calc(22px * var(--glow-intensity)) color-mix(in srgb, var(--primary-orange) 26%, transparent);
    opacity: 0; transform: translateY(10px);
    transition: opacity .3s var(--ease), transform .3s var(--ease),
                background .3s var(--ease), border-color .3s var(--ease);
}
.to-top.is-in { opacity: 1; transform: none; }
.to-top:hover {
    background: color-mix(in srgb, var(--primary-orange) 26%, rgba(10,10,16,0.72));
    border-color: var(--primary-orange);
}
.to-top-arrow { font-size: 0.9rem; line-height: 1; }
@media (max-width: 640px) {
    .to-top { right: var(--s4); bottom: calc(var(--s5) + 64px + env(safe-area-inset-bottom)); padding: 0.5rem 0.7rem; }
    .to-top-label { display: none; }
}
@media (prefers-reduced-motion: reduce) { .to-top { transition: opacity .2s linear; transform: none; } }
.hero-figure::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(180deg, transparent 45%, rgba(5,5,8,0.88) 100%);
    pointer-events: none;
}
.hero-figure-cap {
    position: absolute; left: var(--s4); right: var(--s4); bottom: var(--s4);
    z-index: 2; display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s3);
}
.hero-figure-cap .cap-title { font-family: var(--font-display); font-weight: 700; font-size: 0.95rem; }
.hero-figure-cap .cap-sub   { font-size: 0.72rem; color: var(--text-dim); }
.hero-cycle-hint {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 4px 10px; border-radius: var(--r-pill);
    background: rgba(0,0,0,0.55); border: 1px solid var(--line-strong);
    font-size: 0.66rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-dim);
}
/* Centred under the picture rather than run from its left edge. Twenty dots
   never fill the column, so flex-start left the strip sitting off to one side
   of an image it is supposed to belong to. justify-content keeps it centred at
   every width, and centring the wrapped rows too so a second row cannot hang
   off the end of the first. */
.hero-dots {
    display: flex; flex-wrap: wrap;
    justify-content: center; align-items: center;
    gap: 5px; margin-top: var(--s3);
}
.hero-dot {
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--surface-3); border: 1px solid var(--line);
    transition: all var(--speed) var(--ease);
}
.hero-dot.is-active { background: var(--primary-orange); border-color: var(--primary-orange); width: 20px; border-radius: 999px; }

/* Off-screen home for <symbol> definitions. Not display:none — Safari will
   not resolve a <use> into a hidden tree. */
.svg-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Rolling marquee of listing names under the hero.
   -----------------------------------------------------------------------------
   Each listing is an enclosed chip carrying the 47th mark rather than a run of
   mono text separated by a dot: at ticker speed a bare "name · category" string
   reads as one continuous smear, and the two rows sat close enough together
   that the eye tried to read down the column instead of across it. The chip
   gives every listing its own edges, and the row gap below gives the pair some
   air.

   Speed is not set here. refreshTicker() measures the laid-out run and writes
   animation-duration so the strip always crawls at a fixed pixels-per-second,
   whatever the current filter left in it — the old flat 46s meant a filtered
   handful of names raced past while an unfiltered set trudged. */
.ticker {
    border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
    background: linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0.006));
    overflow: hidden;
    /* 3px of the outer padding has moved inside each row — see below — so this
       is the old var(--s4) minus what the top row now carries itself. */
    padding: calc(var(--s4) - 3px) 0;
}
.ticker-row {
    display: flex; overflow: hidden;
    /* The clip exists to hide the crawl, and with no padding it landed exactly
       on the chips' own border box. A row is as tall as one chip, and a chip
       works out to a fractional height (36.83px at the default size), so the
       clip rounded down and shaved 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. Three pixels of slack inside the clip, taken straight back off
       the gap below and the strip's own padding, so nothing moved. */
    padding: 3px 0;
    /* Chips fade out at both ends instead of being guillotined mid-word. */
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
}
.ticker-row + .ticker-row { margin-top: calc(var(--s3) - 6px); }

.ticker-track {
    display: flex; align-items: center; flex: 0 0 auto;
    gap: var(--s3);
    /* 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: var(--s3);
    white-space: nowrap;
    animation: ticker-run 120s linear infinite;
    /* Promoted so the crawl is composited rather than repainted — at these
       durations a repainting track shimmers on the sub-pixel steps. */
    will-change: transform;
    backface-visibility: hidden;
}
/* The crawl deliberately does NOT pause on hover. A chip is a link to a
   listing, and pausing the row the moment the pointer arrives made every click
   stop the strip dead — the thing was supposed to keep moving. */

.t-chip {
    display: inline-flex; align-items: center; gap: 10px;
    flex: 0 0 auto;
    padding: 7px 15px 7px 8px;
    border: 1px solid var(--line-strong); border-radius: var(--r-pill);
    background: var(--surface-2);
    line-height: 1.15;
    font: inherit; text-align: left; cursor: pointer;
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.t-chip:hover {
    border-color: color-mix(in srgb, var(--primary-orange) 60%, transparent);
    background: color-mix(in srgb, var(--primary-orange) 10%, var(--surface-2));
}
.t-chip:focus-visible { outline: 2px solid var(--primary-orange); outline-offset: 2px; }
.t-mark { width: 38px; height: 15px; flex: 0 0 auto; display: block; }
.t-name {
    font-family: var(--font-display);
    font-size: 0.84rem; font-weight: 600; letter-spacing: 0.012em;
    color: var(--text);
}
.t-cat {
    font-family: var(--font-mono);
    font-size: 0.6rem; font-weight: 500;
    letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--text-dim);
    padding-left: 10px; border-left: 1px solid var(--line-strong);
}

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

@media (max-width: 640px) {
    .ticker { padding: calc(var(--s3) - 3px) 0; }
    .ticker-row + .ticker-row { margin-top: calc(var(--s2) - 6px); }
    .t-chip { gap: 8px; padding: 6px 12px 6px 7px; }
    .t-mark { width: 33px; height: 13px; }
    .t-name { font-size: 0.78rem; }
    .t-cat { font-size: 0.56rem; padding-left: 8px; }
}

/* ==========================================================================
   STATS STRIP
   ========================================================================== */

.stats-strip { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: rgba(2,6,23,0.5); }
.stats-grid {
    max-width: 1600px; margin: 0 auto; padding: var(--s6);
    display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s5);
    text-align: center;
}
@media (min-width: 900px) { .stats-grid { grid-template-columns: repeat(4, 1fr); } }
.stat-tile { cursor: pointer; border-radius: var(--r); padding: var(--s3); transition: background var(--speed) var(--ease); }
.stat-tile:hover { background: rgba(255,255,255,0.03); }
.stat-value {
    font-family: var(--font-display); font-weight: 800;
    font-size: clamp(1.8rem, 3.2vw, 2.5rem);
    color: var(--primary-orange);
    text-shadow: 0 0 calc(22px * var(--glow-intensity)) color-mix(in srgb, var(--primary-orange) 40%, transparent);
    font-variant-numeric: tabular-nums;
}
.stat-label { font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); margin-top: 4px; }
.stat-sub   { font-size: 0.68rem; color: var(--text-mute); margin-top: 2px; }
.stat-status { font-size: 0.62rem; font-family: var(--font-mono); color: var(--text-mute); margin-top: 6px; }
.stat-status .live { color: var(--ok); }
.stat-status .sourced { color: var(--warn); }

/* ==========================================================================
   COMMAND BAR — search + facet popovers
   ========================================================================== */

.command { padding-block: var(--s6) var(--s4); }
.command-panel { padding: var(--s5); }

.search-wrap { position: relative; }
.search-input {
    width: 100%;
    background: rgba(2,6,23,0.72);
    border: 1px solid var(--line-strong);
    border-radius: var(--r-pill);
    padding: 0.85rem 2.6rem 0.85rem 2.8rem;
    font-size: 0.95rem;
    outline: none;
    transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
.search-input::placeholder { color: var(--text-mute); }
.search-input:focus {
    border-color: color-mix(in srgb, var(--primary-orange) 80%, transparent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-orange) 16%, transparent);
}
.search-icon { position: absolute; left: 1.05rem; top: 50%; transform: translateY(-50%); color: var(--text-mute); pointer-events: none; }

/* Chrome draws its own cancel button inside input[type=search], which landed
   under ours and gave the field two crosses a few pixels apart. Ours is the one
   that survives: it is themed, it is in the same place at every size, and it
   goes through runSearch() so the two inputs and the filter chips all update
   together instead of the value being wiped underneath them. */
.search-input::-webkit-search-cancel-button,
.search-input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; display: none; }

/* Hidden until there is something to clear — a cross on an empty field is a
   control that does nothing — but unmistakable once it is there. It used to be
   a grey disc with a grey glyph on a dark field, which read as decoration. */
.search-clear {
    position: absolute; right: 0.62rem; top: 50%; transform: translateY(-50%);
    display: none;
    align-items: center; justify-content: center;
    width: 28px; height: 28px; border-radius: 50%;
    background: color-mix(in srgb, var(--text) 10%, transparent);
    box-shadow: inset 0 0 0 1px var(--line-strong);
    color: var(--text-dim);
    font-size: 1.05rem; line-height: 1;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease),
                box-shadow var(--speed) var(--ease), transform var(--speed) var(--ease);
}
/* Toggled by applyFilters() as the field gains and loses a value. A class
   rather than an inline style, so display stays a decision the stylesheet
   makes and the flex centring survives being shown. */
.search-clear.is-on { display: flex; }
.search-clear:hover, .search-clear:focus-visible {
    background: color-mix(in srgb, var(--primary-orange) 22%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--primary-orange) 65%, transparent);
    color: var(--primary-orange);
    transform: translateY(-50%) scale(1.08);
}

.facet-rail { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); margin-top: var(--s4); }
.facet-btn {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 0.45rem 0.85rem; border-radius: var(--r-pill);
    border: 1px solid var(--line); background: rgba(255,255,255,0.03);
    font-size: 0.76rem; font-weight: 600;
    transition: all var(--speed) var(--ease);
}
.facet-btn:hover { border-color: var(--line-strong); }
.facet-btn[aria-expanded="true"] { border-color: color-mix(in srgb, var(--primary-orange) 60%, transparent); color: var(--primary-orange); }
.facet-btn .facet-label { color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.62rem; }
.facet-btn .facet-value { color: var(--text); }
.facet-btn::after { content: '▾'; font-size: 0.65rem; color: var(--text-faint); }

.count-display { margin-left: auto; font-size: 0.78rem; color: var(--text-dim); }
.count-display strong { color: var(--primary-orange); font-family: var(--font-mono); }

/* Always in the rail, never moving. The one that used to exist appeared inside
   the active-filters row, which meant it only turned up once two filters were
   already on and sat in a different place each time depending on how many chips
   were above it — so the way out of a filter you did not mean to set was the
   one control you could not find twice. Held at the end of the rail, disabled
   rather than hidden when there is nothing to clear: a control that vanishes
   teaches nobody it is there. */
.facet-clear {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 0.45rem 0.8rem; border-radius: var(--r-pill);
    border: 1px solid color-mix(in srgb, var(--primary-orange) 38%, transparent);
    background: color-mix(in srgb, var(--primary-orange) 10%, transparent);
    color: var(--primary-orange);
    font-size: 0.74rem; font-weight: 600; letter-spacing: 0.02em;
    transition: all var(--speed) var(--ease);
}
.facet-clear::before { content: '\00d7'; font-size: 0.95rem; line-height: 1; }
.facet-clear:hover:not(:disabled) {
    background: color-mix(in srgb, var(--primary-orange) 20%, transparent);
    border-color: color-mix(in srgb, var(--primary-orange) 70%, transparent);
}
.facet-clear:disabled {
    border-color: var(--line);
    background: transparent;
    color: var(--text-mute);
    cursor: default;
    opacity: 0.55;
}

.active-filters { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--s3); }
.chip {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 6px 3px 10px; border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--primary-orange) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--primary-orange) 40%, transparent);
    font-size: 0.7rem; color: var(--primary-orange);
}
.chip button { color: inherit; opacity: 0.75; line-height: 1; padding: 0 3px; }
.chip button:hover { opacity: 1; }

/* Popovers — anchored to their trigger, one open at a time. */
.popover {
    position: absolute; z-index: 120;
    min-width: 280px; max-width: min(440px, calc(100vw - 24px));
    border-radius: var(--r);
    border: 1px solid var(--line-strong);
    background: #0d0d14;
    box-shadow: 0 24px 60px -18px rgba(0,0,0,0.85);
    overflow: hidden;
}
html.lg-on .popover { background: rgba(13,13,20,0.92); -webkit-backdrop-filter: blur(22px); backdrop-filter: blur(22px); }
.pop-head {
    padding: 10px var(--s4); border-bottom: 1px solid var(--line);
    display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
    font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-faint);
}
.pop-body { padding: var(--s3) var(--s4) var(--s4); max-height: min(58vh, 460px); overflow-y: auto; }
.pop-divider {
    margin: var(--s3) 0 var(--s2); padding-top: var(--s3);
    border-top: 1px solid var(--line);
    font-size: 0.62rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-mute);
}
.pop-note { font-size: 0.68rem; color: var(--text-mute); margin-top: var(--s2); }

.opt {
    display: flex; align-items: center; gap: 10px;
    padding: 7px 8px; border-radius: var(--r-sm);
    font-size: 0.8rem; cursor: pointer;
}
.opt:hover { background: rgba(255,255,255,0.04); }
.opt input { accent-color: var(--primary-orange); width: 15px; height: 15px; }
.opt .opt-name  { flex: 1; min-width: 0; }
.opt .opt-count { font-family: var(--font-mono); font-size: 0.68rem; color: var(--text-faint); }

.pop-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--s2); }

/* CONTROL-style switch, reused for the popover toggles. */
.switch {
    position: relative; display: inline-flex; align-items: center;
    width: 38px; height: 21px; border-radius: 999px;
    background: #222; border: 1px solid var(--line); flex: 0 0 auto;
    transition: background var(--speed) var(--ease);
    cursor: pointer;
}
.switch::after {
    content: ''; position: absolute; left: 2px; top: 50%; transform: translateY(-50%);
    width: 15px; height: 15px; border-radius: 50%; background: #fff;
    transition: transform var(--speed) var(--ease);
}
.switch[aria-pressed="true"] { background: var(--primary-orange); border-color: transparent; }
.switch[aria-pressed="true"]::after { transform: translate(17px, -50%); }

.switch-row { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding: 7px 8px; font-size: 0.78rem; }

/* ==========================================================================
   DIRECTORY SECTIONS
   ========================================================================== */

.directory { padding-bottom: var(--s8); }

.dir-section { margin-top: var(--s7); scroll-margin-top: calc(var(--nav-h) + 16px); }
.dir-section-head {
    display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
    gap: var(--s3); margin-bottom: var(--s4);
    padding-bottom: var(--s3); border-bottom: 1px solid var(--line);
}
.dir-section-head h2 { font-family: var(--font-display); font-size: 1.55rem; letter-spacing: -0.03em; }
.dir-section-head .dir-count { font-family: var(--font-mono); font-size: 0.74rem; color: var(--text-faint); }
.dir-section-head .dir-sub { font-size: 0.78rem; color: var(--text-dim); }

/* --- card view --- */
.card-view { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); }
.card-view.size-compact { grid-template-columns: repeat(auto-fill, minmax(235px, 1fr)); gap: 11px; }
.card-view.size-tiny    { grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: 8px; }

.biz-card {
    display: flex; flex-direction: column; gap: 8px;
    padding: var(--s4);
    border-radius: var(--r);
    transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s var(--ease);
}
.biz-card:hover { transform: translateY(-6px); }
.biz-card.compact { padding: var(--s3); gap: 6px; font-size: 0.9em; }
.biz-card.tiny    { padding: 10px; gap: 4px; font-size: 0.82em; }
.biz-card.tiny .card-note, .biz-card.tiny .card-actions { display: none; }
.biz-card.compact .card-note { -webkit-line-clamp: 2; }

.biz-card .biz-name {
    font-family: var(--font-display); font-weight: 700;
    font-size: 1rem; line-height: 1.25; letter-spacing: -0.02em;
}
.biz-card.tiny .biz-name { font-size: 0.86rem; }
.biz-meta { font-size: 0.78rem; color: var(--text-dim); line-height: 1.5; }
.biz-meta a:hover { color: var(--primary-orange); }
.card-note {
    font-size: 0.75rem; color: var(--text-faint); line-height: 1.5;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.card-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: auto; padding-top: 6px; }
.card-actions .btn { padding: 0.3rem 0.7rem; font-size: 0.7rem; }

.badges { display: inline-flex; flex-wrap: wrap; gap: 4px; vertical-align: middle; }
.badge {
    display: inline-block; padding: 1px 7px; border-radius: var(--r-pill);
    font-size: 0.6rem; font-weight: 700; letter-spacing: 0.06em;
    border: 1px solid var(--line-strong); color: var(--text-dim);
    background: rgba(255,255,255,0.03);
}
.badge-verified {
    color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent);
    background: color-mix(in srgb, var(--ok) 12%, transparent);
}
.badge-fav { color: var(--primary-orange); border-color: color-mix(in srgb, var(--primary-orange) 45%, transparent); }

mark, .hl-match {
    background: color-mix(in srgb, var(--primary-orange) 30%, transparent);
    color: inherit; border-radius: 3px; padding: 0 2px;
}

/* --- table view --- */
.table-wrap { overflow-x: auto; border-radius: var(--r); border: 1px solid var(--line); }
table.dir-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; min-width: 900px; }
.dir-table thead th {
    position: sticky; top: 0; z-index: 2;
    background: #101017;
    text-align: left; padding: 10px var(--s3);
    font-size: 0.65rem; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--text-faint); font-weight: 700;
    border-bottom: 1px solid var(--line-strong);
    white-space: nowrap;
}
.dir-table th.sortable { cursor: pointer; user-select: none; }
.dir-table th.sortable:hover { color: var(--text); }
.dir-table th.sortable::after { content: ' ⇅'; opacity: 0.35; }
.dir-table th.sortable.asc::after  { content: ' ↑'; opacity: 1; color: var(--primary-orange); }
.dir-table th.sortable.desc::after { content: ' ↓'; opacity: 1; color: var(--primary-orange); }
.dir-table td { padding: 10px var(--s3); border-bottom: 1px solid var(--line); vertical-align: top; }
.dir-table tbody tr:hover { background: rgba(255,255,255,0.025); }

/* ---- the arrival flourish -----------------------------------------------
   A ticker chip drops you onto one listing out of fourteen hundred, so the
   landing has to be unmistakable. Three things fire at once: the card or row
   takes an accent frame, a single band of light crosses the card the way the
   glass sheen crosses everything else, and the name — the thing you actually
   clicked — lights up and shimmers. All of it keys off --primary-orange and
   --accent-2, so it repaints with the theme like the rest of the page.

   The frame is an outline rather than a border or a shadow because
   `html.lg-on .liquid-glass` owns both of those on a card and out-weighs any
   plain .biz-card rule; nothing competes for the outline. A <tr> takes
   neither, so the row is lit through its cells instead. */
.biz-card.is-revealed {
    outline: 2px solid var(--primary-orange);
    outline-offset: 3px;
    animation: reveal-frame 3.2s var(--ease);
}
.dir-table tbody tr.is-revealed > td {
    outline: 2px solid var(--primary-orange);
    outline-offset: -2px;
    background: color-mix(in srgb, var(--primary-orange) 12%, transparent);
    animation: reveal-frame 3.2s var(--ease);
}
/* A spine down the leading edge of the row, where the name sits. */
.dir-table tbody tr.is-revealed > td:first-child {
    box-shadow: inset 4px 0 0 var(--primary-orange);
}
@keyframes reveal-frame {
    0%, 20% { outline-color: var(--primary-orange); }
    100%    { outline-color: color-mix(in srgb, var(--primary-orange) 40%, transparent); }
}

/* One pass of light across the card. ::before is already the glass sheen, so
   this rides on ::after — and above the content rather than under it, since
   `.liquid-glass > *` lifts every child to z-index 1. */
.biz-card.is-revealed::after {
    content: '';
    position: absolute; inset: 0; z-index: 2;
    pointer-events: none;
    background: linear-gradient(102deg,
        transparent 40%,
        color-mix(in srgb, var(--primary-orange) 22%, transparent) 47%,
        rgba(255,255,255,0.22) 50%,
        color-mix(in srgb, var(--accent-2) 20%, transparent) 53%,
        transparent 60%);
    background-size: 260% 100%; background-repeat: no-repeat;
    animation: reveal-sweep 1.5s var(--ease) both;
}
@keyframes reveal-sweep {
    0%   { background-position: 150% 0; opacity: 1; }
    75%  { background-position: -55% 0; opacity: 1; }
    100% { background-position: -55% 0; opacity: 0; }
}

/* The name itself, on the same background-clip trick as .holo-text and the
   same keyframes — accent sliding through --accent-2 and back. Every frame of
   `holo` is a colour the theme already uses, so there is no resting position
   that looks wrong if the animation is cut short. Left inline: giving it a
   block box to hang an underline off would reflow the badges beside it the
   moment the class landed, and again when it lifted. */
.biz-card.is-revealed .biz-name,
.dir-table tbody tr.is-revealed .name {
    background: linear-gradient(90deg, var(--primary-orange), var(--accent-2), var(--primary-orange));
    background-size: 200% 100%;
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
            color: transparent;
    filter: drop-shadow(0 0 calc(16px * var(--glow-intensity))
            color-mix(in srgb, var(--primary-orange) 60%, transparent));
    animation: holo 2.4s ease infinite;
}

/* E-PAPER and reading mode are calm by contract: same accent, no gradient, no
   motion. --glow-intensity is already 0 under e-paper, so the drop-shadow
   collapses on its own. */
html.epaper .biz-card.is-revealed .biz-name,
html.epaper .dir-table tbody tr.is-revealed .name,
html.reader-on .biz-card.is-revealed .biz-name,
html.reader-on .dir-table tbody tr.is-revealed .name {
    background: none;
    -webkit-text-fill-color: var(--primary-orange);
            color: var(--primary-orange);
    animation: none;
}
html.epaper .biz-card.is-revealed::after,
html.reader-on .biz-card.is-revealed::after { display: none; }

@media (prefers-reduced-motion: reduce) {
    .biz-card.is-revealed,
    .dir-table tbody tr.is-revealed > td,
    .biz-card.is-revealed .biz-name,
    .dir-table tbody tr.is-revealed .name { animation: none; }
    .biz-card.is-revealed::after { display: none; }
}
.dir-table .name { font-weight: 600; }
.dir-table .category-tag {
    display: inline-block; padding: 1px 8px; border-radius: var(--r-pill);
    background: rgba(255,255,255,0.05); font-size: 0.68rem; color: var(--text-dim); white-space: nowrap;
}
.dir-table .compact-note { color: var(--text-faint); font-size: 0.74rem; }
.dir-empty { text-align: center; color: var(--text-mute); padding: 22px; }

/* ==========================================================================
   MAP
   ========================================================================== */

.map-panel { padding: var(--s4); }
#district-map { height: 420px; border-radius: var(--r); overflow: hidden; background: var(--surface-2); }
.map-footer { font-size: 0.68rem; color: var(--text-mute); margin-top: var(--s2); }
.leaflet-popup-content-wrapper, .leaflet-popup-tip { background: #12121a; color: var(--text); }
.leaflet-popup-content { font-family: var(--font-sans); font-size: 0.8rem; }
.leaflet-container { background: #0d0d13; }

/* ==========================================================================
   GALLERY
   ========================================================================== */

.gallery-grid {
    display: grid; gap: 10px;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
.gallery-tile {
    position: relative; overflow: hidden; border-radius: var(--r);
    aspect-ratio: 3 / 2; border: 1px solid var(--line); cursor: pointer;
}
.gallery-tile img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease), filter .5s var(--ease); }
.gallery-tile:hover img { transform: scale(1.08); filter: brightness(1.1) saturate(1.15); }
.gallery-tile figcaption {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 18px 10px 8px;
    background: linear-gradient(180deg, transparent, rgba(0,0,0,0.85));
    font-size: 0.68rem; color: var(--text-dim);
}

/* ==========================================================================
   CONTACT
   ========================================================================== */

#contact { padding: var(--s8) 0; border-top: 1px solid var(--line); }
.contact-grid {
    display: grid; grid-template-columns: minmax(0, 36rem);
    justify-content: center; justify-items: center; gap: 2.5rem;
}
.contact-rail, .contact-col { width: 100%; max-width: 36rem; }
@media (min-width: 1024px) {
    .contact-grid { grid-template-columns: minmax(0, 20rem) minmax(0, 46rem); gap: 3.5rem; align-items: start; justify-items: start; }
    .contact-rail { max-width: 20rem; }
    .contact-col  { max-width: 46rem; }
}
.contact-rail dl > div + div { margin-top: 1rem; }
.contact-card { width: 100%; padding: var(--s6); }
.contact-row { display: grid; grid-template-columns: 1fr; gap: var(--s4); }
@media (min-width: 640px) { .contact-row.two { grid-template-columns: 1fr 1fr; } }
.contact-field { display: block; }
.contact-label {
    display: block; font-size: .7rem; letter-spacing: .08em; text-transform: uppercase;
    font-weight: 700; color: var(--text-dim); margin-bottom: .4rem;
}
.contact-label .req { color: var(--primary-orange); margin-left: .15rem; }
.contact-label .opt-tag { color: var(--text-mute); font-weight: 500; text-transform: none; letter-spacing: 0; margin-left: .3rem; }
.contact-input {
    width: 100%; background: rgba(2, 6, 23, .72); color: #fff;
    border: 1px solid var(--line-strong); border-radius: 1rem;
    padding: .7rem .9rem; font-size: .875rem; line-height: 1.4;
    outline: none; transition: border-color .16s ease, box-shadow .16s ease, background .16s ease;
    -webkit-appearance: none; appearance: none;
}
.contact-input::placeholder { color: var(--text-mute); }
.contact-input:hover { border-color: rgba(255,255,255,.2); }
.contact-input:focus {
    border-color: color-mix(in srgb, var(--primary-orange) 80%, transparent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-orange) 16%, transparent);
    background: rgba(2, 6, 23, .9);
}
textarea.contact-input { min-height: 8.5rem; resize: vertical; }
/* Native select arrows are unstyleable and render light-on-light in some
   browsers, so draw our own and leave room for it. */
select.contact-input {
    padding-right: 2.4rem; cursor: pointer;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2394a3b8'%3E%3Cpath d='M4.5 6.5 8 10l3.5-3.5z'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right .8rem center; background-size: 1rem;
}
select.contact-input option { background: #0b0b12; color: #fff; }
/* Only paints once the field has actually been judged — :invalid alone would
   light up every required field before anyone has typed. */
.contact-input[aria-invalid="true"] {
    border-color: rgba(248, 113, 113, .85);
    box-shadow: 0 0 0 3px rgba(248, 113, 113, .14);
}
.contact-error { display: none; margin-top: .35rem; font-size: .72rem; color: #fca5a5; align-items: center; gap: .3rem; }
.contact-error.is-shown { display: flex; }
.contact-status { margin-top: var(--s3); font-size: 0.82rem; }
.contact-status.ok   { color: var(--ok); }
.contact-status.bad  { color: var(--danger); }

/* ==========================================================================
   FOOTER
   ========================================================================== */

.site-footer { border-top: 1px solid var(--line); background: #000; padding: var(--s8) 0 var(--s6); }
.footer-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--s6); }
.footer-col h3 {
    font-size: 0.68rem; letter-spacing: 0.2em; text-transform: uppercase;
    color: var(--text-mute); margin-bottom: var(--s3);
}
.footer-col a { display: block; padding: 3px 0; font-size: 0.84rem; color: var(--text-dim); }
.footer-col a:hover { color: var(--primary-orange); }
.footer-bottom {
    margin-top: var(--s7); padding-top: var(--s4); border-top: 1px solid var(--line);
    display: flex; flex-wrap: wrap; gap: var(--s3); justify-content: space-between;
    font-size: 0.74rem; color: var(--text-mute);
}
.newsletter-row { display: flex; gap: var(--s2); flex-wrap: wrap; }
.newsletter-row input {
    flex: 1 1 200px; min-width: 0;
    background: rgba(2,6,23,0.72); border: 1px solid var(--line-strong);
    border-radius: var(--r-pill); padding: 0.6rem 1rem; font-size: 0.82rem; outline: none;
}
.newsletter-row input:focus { border-color: color-mix(in srgb, var(--primary-orange) 80%, transparent); }

/* ==========================================================================
   MODALS + TOASTS
   ========================================================================== */

.modal {
    position: fixed; inset: 0; z-index: 200;
    display: flex; align-items: center; justify-content: center;
    padding: var(--s4);
    background: rgba(0,0,0,0.86);
    -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.modal-card {
    width: 100%; max-width: 620px; max-height: 86vh;
    display: flex; flex-direction: column;
    border-radius: var(--r-lg); border: 1px solid var(--line-strong);
    background: #0d0d14;
    animation: modalPop .3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
html.lg-on .modal-card { background: rgba(13,13,20,0.94); -webkit-backdrop-filter: blur(26px); backdrop-filter: blur(26px); }
@keyframes modalPop {
    from { opacity: 0; transform: scale(0.9) translateY(24px); }
    to   { opacity: 1; transform: scale(1) translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .modal-card { animation: none; } }
.modal-head {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
    padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line);
}
.modal-head h3 { font-family: var(--font-display); font-size: 1.1rem; }
.modal-body { padding: var(--s5); overflow-y: auto; font-size: 0.88rem; line-height: 1.6; }
.modal-body h4 { font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-faint); margin: var(--s4) 0 var(--s2); }
/* The market modal puts three actions plus Close in here, which is one more
   than the foot was built for — it wraps rather than pushing Close off the end
   on a phone. */
.modal-foot { padding: var(--s3) var(--s5) var(--s4); border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--s2); }
.modal-close { font-size: 1.4rem; line-height: 1; color: var(--text-faint); padding: 4px 8px; }
.modal-close:hover { color: var(--text); }

.modal-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
.modal-table td { padding: 5px 0; border-bottom: 1px solid var(--line); }
.modal-table td:last-child { text-align: right; font-family: var(--font-mono); color: var(--primary-orange); }

/* ---- the market modal ---------------------------------------------------
   Two kinds of content, kept visibly apart. The lede and the facts are written
   and will not change; everything under them is counted off the listings the
   moment the modal opens. The counted half is drawn as bars rather than as a
   table of numbers, because the question a market page is really being asked is
   "what is this place mostly", and a proportion answers that faster than a
   column of digits does.
   ------------------------------------------------------------------------ */
.mkt-modal h4 { margin-top: 0; }
.mkt-modal section { margin-top: var(--s5); }

/* The headline count, set against the orientation copy rather than above it —
   the number and the sentence explaining it belong on the same line of sight. */
.mkt-lede { display: flex; align-items: flex-start; gap: var(--s4); }
.mkt-lede-n {
    flex: 0 0 auto; width: 106px;
    padding: 10px 8px 9px;
    border-radius: var(--r-sm);
    border: 1px solid color-mix(in srgb, var(--primary-orange) 26%, transparent);
    background: color-mix(in srgb, var(--primary-orange) 9%, transparent);
    text-align: center;
}
.mkt-lede-n b {
    display: block;
    font-family: var(--font-tech); font-size: 1.5rem; line-height: 1.05;
    color: var(--primary-orange); font-variant-numeric: tabular-nums;
}
.mkt-lede-n span {
    display: block; margin-top: 3px;
    font-size: 0.6rem; letter-spacing: 0.12em; text-transform: uppercase;
    color: var(--text-faint);
}
.mkt-lede-copy p { font-size: 0.86rem; color: var(--text-dim); }
.mkt-where {
    font-size: 0.68rem !important; letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--primary-orange) !important; margin-bottom: 6px !important;
}

.mkt-facts { margin-top: var(--s4); display: grid; gap: 6px; }
.mkt-facts li {
    position: relative; padding-left: 16px;
    font-size: 0.8rem; color: var(--text-dim);
}
.mkt-facts li::before {
    content: '\25c7'; position: absolute; left: 0; top: 0;
    color: var(--primary-orange); font-size: 0.7rem;
}

.mkt-kpis {
    margin-top: var(--s5);
    display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: var(--s2);
}
.mkt-kpi {
    padding: 9px 10px;
    border: 1px solid var(--line); border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--text) 2.5%, transparent);
}
.mkt-kpi b {
    display: block;
    font-family: var(--font-mono); font-size: 1rem; color: var(--text);
    font-variant-numeric: tabular-nums;
}
.mkt-kpi span {
    display: block; margin-top: 2px;
    font-size: 0.64rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-faint);
}

/* Side by side once there is room for two readable columns, stacked below it.
   The breakpoint is on the modal's own width, not the viewport's, in spirit —
   the card tops out at 900px, so 760 is the point where two columns stop being
   cramped. */
.mkt-cols { display: grid; gap: var(--s5); }
@media (min-width: 760px) {
    .mkt-cols { grid-template-columns: 1fr 1fr; gap: var(--s5) var(--s6); }
    .mkt-cols > section { margin-top: var(--s5); }
}

.mkt-bars { display: grid; gap: 7px; }
.mkt-bars li {
    display: grid; grid-template-columns: minmax(0, 1fr) 34%  auto;
    align-items: center; gap: 10px;
}
.mb-label {
    font-size: 0.78rem; color: var(--text-dim);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mb-track {
    height: 5px; border-radius: 999px;
    background: color-mix(in srgb, var(--text) 13%, transparent);
    overflow: hidden;
}
.mb-fill {
    display: block; height: 100%; border-radius: inherit;
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--primary-orange) 55%, transparent),
        var(--primary-orange));
}
.mb-n {
    min-width: 42px; text-align: right;
    font-family: var(--font-mono); font-size: 0.72rem;
    color: var(--primary-orange); font-variant-numeric: tabular-nums;
}

.mkt-names { display: grid; gap: 4px; }
.mkt-name {
    width: 100%; text-align: left;
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
    padding: 7px 10px; border-radius: var(--r-sm);
    border: 1px solid transparent;
    transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.mkt-name:hover {
    background: color-mix(in srgb, var(--text) 5%, transparent);
    border-color: var(--line);
}
.mn-name { font-size: 0.82rem; font-weight: 600; color: var(--text); }
.mkt-name:hover .mn-name { color: var(--primary-orange); }
.mn-cat {
    flex: 0 0 auto;
    font-size: 0.62rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-mute);
}

.mkt-foot-note { margin-top: var(--s5); font-size: 0.72rem; }

@media (max-width: 560px) {
    /* Four actions right-aligned and wrapping produce a staircase. On a phone
       they go two-up on a grid instead, which is the same information without
       the ragged edge — and gives each one a thumb-sized target. */
    .modal-foot { justify-content: stretch; gap: 6px; }
    .modal-foot .btn { flex: 1 1 calc(50% - 3px); }

    .mkt-lede { flex-direction: column; gap: var(--s3); }
    .mkt-lede-n { width: 100%; display: flex; align-items: baseline; justify-content: center; gap: 8px; }
    .mkt-lede-n b, .mkt-lede-n span { display: inline; }
    .mkt-bars li { grid-template-columns: minmax(0, 1fr) 26% auto; gap: 8px; }
}

#toast-host {
    position: fixed; left: 50%; bottom: 92px; transform: translateX(-50%);
    z-index: 400; display: flex; flex-direction: column; gap: 8px;
    align-items: center; pointer-events: none;
    width: min(440px, calc(100vw - 32px));
}
.toast {
    pointer-events: auto;
    padding: 10px var(--s4); border-radius: var(--r-pill);
    background: #14141d; border: 1px solid var(--line-strong);
    font-size: 0.8rem; box-shadow: 0 16px 40px -18px rgba(0,0,0,0.9);
    animation: toast-in .25s var(--ease);
    max-width: 100%;
}
.toast.success { border-color: color-mix(in srgb, var(--ok) 50%, transparent); }
.toast.warn    { border-color: color-mix(in srgb, var(--warn) 50%, transparent); }
.toast.error   { border-color: color-mix(in srgb, var(--danger) 55%, transparent); }
@keyframes toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ==========================================================================
   CONTROL PANEL
   ========================================================================== */

#control-panel {
    position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
    z-index: 90;
    border: 2px solid var(--primary-orange) !important;
    border-radius: 16px;
    box-shadow: 0 0 calc(34px * var(--glow-intensity)) color-mix(in srgb, var(--primary-orange) 55%, transparent);
    width: 216px;
    font-family: var(--font-tech);
    overflow: hidden;
}
#control-head {
    position: relative; background: #111113;
    padding: 8px 14px; display: flex; align-items: center; justify-content: center;
    cursor: pointer; border-bottom: 1px solid var(--primary-orange);
}
#control-head .control-title { color: var(--primary-orange); font-size: 0.75rem; font-weight: 700; letter-spacing: 1.5px; }
#panel-arrow { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); color: var(--primary-orange); transition: transform .2s; }
#panel-content { display: none; padding: 14px; font-size: 0.75rem; }
#control-panel.is-open #panel-content { display: block; }
#control-panel.is-open #panel-arrow { transform: translateY(-50%) rotate(180deg); }

.ctl-block { margin-bottom: 10px; }
.ctl-block + .ctl-sep { padding-top: 8px; border-top: 1px solid #333; }
.ctl-label { color: #888; font-size: 0.65rem; margin-bottom: 4px; display: flex; justify-content: space-between; gap: 6px; }
.ctl-label .ctl-value { color: var(--primary-orange); }
.ctl-hint { font-size: 0.55rem; color: #555; }
#panel-content input[type="range"] { width: 100%; accent-color: var(--primary-orange); }
.ctl-scale { display: flex; justify-content: space-between; font-size: 0.55rem; color: #666; }

/* BACKGROUND ORBS — a segmented pill. Exactly one segment is lit, and the
   thumb slides between them; the selection is matched on the exact data-orb
   value, never on a substring of the label. */
.orb-seg {
    position: relative; display: grid; grid-template-columns: repeat(4, 1fr);
    background: #17171d; border: 1px solid #2a2a33;
    border-radius: 999px; padding: 3px; isolation: isolate;
}
.orb-seg__thumb {
    position: absolute; top: 3px; bottom: 3px; left: 3px;
    width: calc((100% - 6px) / 4);
    border-radius: 999px; z-index: -1;
    transform: translateX(calc(var(--orb-i, 3) * 100%));
    transition: transform .22s var(--ease);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--primary-orange) 72%, #ffffff) 0%,
        var(--primary-orange) 55%,
        color-mix(in srgb, var(--primary-orange) 86%, #000000) 100%);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--primary-orange) 50%, transparent);
}
.orb-btn {
    padding: 5px 0; border-radius: 999px;
    font-family: var(--font-tech); font-size: 0.6rem; font-weight: 700; letter-spacing: 0.06em;
    color: #8a8a96; transition: color .18s ease;
}
.orb-btn:hover { color: #e8e8ee; }
.orb-btn.active { color: #0a0a0a; }

.theme-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 5px; }
.theme-swatch { aspect-ratio: 1; border-radius: 4px; border: 1px solid rgba(255,255,255,0.6); cursor: pointer; }
.theme-swatch.is-active { border: 2px solid #fff; }

/* ==========================================================================
   FLOATING ACTION BAR (mobile)
   ========================================================================== */

#filter-summary {
    display: none;
    position: fixed; left: 50%; transform: translateX(-50%);
    bottom: calc(16px + env(safe-area-inset-bottom));
    z-index: 88;
    padding: 0.7rem 1.3rem; border-radius: var(--r-pill);
    background: var(--primary-orange); color: #0a0a0a;
    font-weight: 800; font-size: 0.82rem;
    box-shadow: 0 12px 34px -10px rgba(0,0,0,0.9);
}

/* Mobile filter sheet */
#m-scrim { position: fixed; inset: 0; z-index: 210; background: rgba(0,0,0,0.7); }
#m-sheet {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 220;
    max-height: 88vh; display: flex; flex-direction: column;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    border: 1px solid var(--line-strong); border-bottom: 0;
    background: #0d0d14;
    padding-bottom: env(safe-area-inset-bottom);
    transform: translateY(100%);
    transition: transform .3s var(--ease);
}
#m-sheet.is-open { transform: translateY(0); }
.m-grab { display: flex; justify-content: center; padding: 8px 0 2px; }
.m-grab span { width: 40px; height: 4px; border-radius: 999px; background: var(--surface-3); }
.m-sheet-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding: var(--s2) var(--s5) var(--s3); }
.m-tabs { display: flex; gap: 6px; padding: 0 var(--s5) var(--s3); border-bottom: 1px solid var(--line); }
.m-sheet-body { flex: 1; overflow-y: auto; padding: var(--s4) var(--s5); }
.m-sheet-foot { padding: var(--s3) var(--s5) var(--s4); border-top: 1px solid var(--line); }
.m-sheet-foot .btn { width: 100%; }

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */

@media (max-width: 1100px) {
    .nav-meta { display: none; }
}
@media (max-width: 760px) {
    .wrap, .wrap-narrow { padding: 0 var(--s4); }
    .nav-inner { padding: 8px var(--s4); }
    #nav-clock .clk-zone { display: none; }
    .hero { padding-block: var(--s5) var(--s4); }
    .stats-grid { padding: var(--s5) var(--s4); gap: var(--s4); }
    .command-panel { padding: var(--s4); }
    .card-view { grid-template-columns: 1fr; }
    .card-view.size-compact, .card-view.size-tiny { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
    #filter-summary { display: block; }
    #control-panel { bottom: 78px; width: 200px; }
    #toast-host { bottom: 140px; }
    .facet-rail { gap: 6px; }
    .count-display { margin-left: 0; width: 100%; }
    #district-map { height: 320px; }
}

/* E-PAPER — a reading mode rather than a look: paper-white surfaces, no glow
   and no motion. It is reachable by hand from the theme grid but is never
   rolled onto anyone, because landing in it by chance reads as a fault. */
html.epaper {
    --bg:        #efece4;
    --surface:   #f6f4ee;
    --surface-2: #eae7dd;
    --surface-3: #ded9cc;
    --text:       #1b1a17;
    --text-dim:   #4a473f;
    --text-faint: #6b675c;
    --text-mute:  #8a8577;
    --line:        rgba(0, 0, 0, 0.12);
    --line-strong: rgba(0, 0, 0, 0.24);
    --glow-intensity: 0;
}
html.epaper #bg-canvas { display: none; }
html.epaper .liquid-glass::before { display: none; }
html.epaper .liquid-glass {
    background: var(--surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border: 1px solid var(--line);
    box-shadow: none;
}
html.epaper .site-nav,
html.epaper .site-footer,
html.epaper .stats-strip { background: var(--surface); }
html.epaper .holo-text,
html.epaper .section-header-grad {
    background: none;
    -webkit-text-fill-color: var(--primary-orange);
    color: var(--primary-orange);
    animation: none;
}
html.epaper .btn-primary { color: #fff; }
html.epaper .dir-table thead th { background: var(--surface-2); }
html.epaper #logo-3d { animation: none; filter: none; }
html.epaper .logo-halo { display: none; }

/* Reading mode — larger type, no motion, flat surfaces. */
html.reader-on .liquid-glass::before { display: none; }
html.reader-on body { font-size: 16.5px; }
html.reader-on .card-note { -webkit-line-clamp: unset; }
html.reader-on #bg-canvas { display: none; }

@media print {
    .site-nav, #control-panel, #filter-summary, #bg-canvas, .site-footer,
    .command, .ticker, #toast-host, .hero-visual { display: none !important; }
    body { background: #fff; color: #000; }
    .biz-card { break-inside: avoid; border: 1px solid #ccc; }
}
