/* GENERATED from design-tokens.css + src/styles/* — do not edit directly */

/* ── design-tokens.css ── */
@font-face {
  font-family: "Archivo";
  src: url("/fonts/Archivo-Variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Space Grotesk";
  src: url("/fonts/SpaceGrotesk-Variable.woff2") format("woff2-variations");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

:root {
  
  --paper: #FBFDFC;        
  --surface: #FFFFFF;      
  --terrazzo: #F0F4F1;     
  --terrazzo-2: #E6EDE8;   
  --ink: #0F2A2E;          
  --ink-2: #33494D;        
  --muted: #53686C;        
  --line: #D9E2DC;         
  --line-2: #C3D0C8;       

  
  --ocean: #0A6E8A;        
  --ocean-deep: #07536B;   
  --ocean-tint: #E1F0F4;   
  --flamingo: #E0446E;     
  --flamingo-text: #A81E48;
  --sun: #E9A13B;          
  --sun-text: #7E5A14;     

  
  --viz-ocean: #0072B2;      
  --viz-vermillion: #D55E00; 
  --viz-teal: #009E73;       
  --viz-amber: #E69F00;      
  --viz-sky: #56B4E9;        
  --viz-rose: #CC79A7;       
  --viz-ink: var(--ink);     

  
  --up: var(--viz-ocean);
  --up-text: #075A8C;        
  --down: var(--viz-vermillion);
  --down-text: #9E4400;      

  
  --font-ui: "Archivo", "Trebuchet MS", Verdana, sans-serif;
  --font-data: "Space Grotesk", "Archivo", sans-serif;

  
  --wdth-display: 118;
  --wdth-h1: 110;
  --wdth-h2: 106;
  --wdth-body: 100;
  --wdth-marquee: 125;      

  --fs-display: clamp(2rem, 6vw, 3.25rem);   --lh-display: 1.08;
  --fs-h1: clamp(1.6rem, 4.5vw, 2.4rem);     --lh-h1: 1.15;
  --fs-h2: clamp(1.3rem, 3vw, 1.7rem);       --lh-h2: 1.25;
  --fs-h3: 1.125rem;                          --lh-h3: 1.4;
  --fs-body: 1rem;                            --lh-body: 1.6;   
  --fs-ui: 0.9375rem;                         --lh-ui: 1.5;
  --fs-receipt: 0.8125rem;                    --lh-receipt: 1.45;
  --fs-stat: clamp(1.8rem, 5vw, 2.6rem);      --lh-stat: 1.1;

  
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;
  --max-w: 1120px;
  --measure: 68ch;
  --tap: 44px;              
  --bar-app: 56px;          
  --bar-filter: 48px;       

  
  --r-card: 14px;           
  --r-ui: 10px;             
  --r-chip: 8px;            
  --r-round: 999px;         

  
  --elev-1: 0 2px 0 var(--line);
  --elev-2: 0 2px 0 var(--line-2);

  
  --t-fast: 140ms;          
  --t-sheet: 240ms;         
  --ease-out: cubic-bezier(0.2, 0, 0, 1);   

  
  --z-bar: 100; --z-map-ui: 150; --z-popover: 200; --z-sheet: 300; --z-toast: 400;

  
  --map-land: #F7FAF8;
  --map-water: #CFE7ED;     
  --map-park: #E8F0E6;
  --map-road-casing: #E3E9E5;
  --map-dot: var(--ocean);        
  --map-dot-selected: var(--flamingo); 
  --map-cluster-bg: var(--surface);    
}



html {
  font-family: var(--font-ui);
  font-variation-settings: "wdth" var(--wdth-body);
  color: var(--ink);
  background: var(--paper);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

::selection { background: var(--ocean-tint); color: var(--ink); }

a { color: var(--ocean-deep); text-underline-offset: 2px; }
a:hover { color: var(--ocean); }

h1, h2, h3 { margin: 0; text-wrap: balance; }
h1 { font-size: var(--fs-h1); line-height: var(--lh-h1); font-weight: 700;
     font-variation-settings: "wdth" var(--wdth-h1); }
h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); font-weight: 650;
     font-variation-settings: "wdth" var(--wdth-h2); }
h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); font-weight: 600; }


.num, .receipt, .stat {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
}
.receipt { font-size: var(--fs-receipt); line-height: var(--lh-receipt); }
.stat { font-size: var(--fs-stat); line-height: var(--lh-stat); font-weight: 500; }


.marquee {
  font-variation-settings: "wdth" var(--wdth-marquee);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: 1.2; 
}


:lang(es) p { hyphens: auto; }


.delta-up   { color: var(--up-text); }
.delta-down { color: var(--down-text); }
.delta-flat { color: var(--muted); }


.stream-card { border: 1px solid var(--line); background: var(--surface);
               border-start-end-radius: var(--r-card); }
.stream-chip { border: 1px solid var(--line); background: var(--terrazzo);
               border-start-end-radius: var(--r-chip); }


@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* ── base.css ── */
/* ============================================================
   BASE — extra reset + a11y beyond design-tokens.css.
   design-tokens.css (miami-site root) already owns :root tokens,
   @font-face, and the bare html/body/a/h1-h3/.num/.receipt/.stat/
   .marquee/.delta-*/.stream-*/reduced-motion rules — this file adds
   what a static-site reset still needs on top of that (LTR only,
   no RTL plumbing — brand-separation + DESIGN-DIRECTION.md §14).
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
img, svg, video, canvas { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
ul, ol { list-style: none; padding: 0; }
p, ul, ol, figure { margin-block-end: var(--sp-4); }
p:last-child, ul:last-child, ol:last-child { margin-block-end: 0; }

:focus-visible {
  outline: 2px solid var(--ocean);
  outline-offset: 2px;
  border-radius: var(--r-chip);
}

/* ── Locale-aware quotation marks (DESIGN-DIRECTION.md §3: real quotes,
   never straight, per-locale glyphs). Content authors use <q>, not literal
   quote characters, where the language may switch. ────────────────────── */
q:lang(en) { quotes: "\201C" "\201D" "\2018" "\2019"; }
q:lang(es) { quotes: "\00AB" "\00BB" "\2039" "\203A"; }

/* ── Skip link ──────────────────────────────────────────────────────────── */
.skip-link {
  position: absolute;
  top: -100%;
  left: var(--sp-4);
  z-index: var(--z-toast);
  padding: var(--sp-2) var(--sp-4);
  background: var(--ink);
  color: var(--paper);
  font-weight: 700;
  border-start-end-radius: var(--r-ui);
}
.skip-link:focus { top: var(--sp-2); }

/* ── Screen-reader-only utility ───────────────────────────────────────────── */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* ── typography.css ── */
/* ============================================================
   TYPOGRAPHY — utility classes for the type scale (DESIGN-DIRECTION.md
   §3). design-tokens.css already styles the bare h1/h2/h3/.num/.receipt/
   .stat/.marquee elements; these classes apply the same scale to any
   element (e.g. a <p> used as a display line, or ES width headroom on
   chips/marquee strings).
   ============================================================ */

.display {
  font-family: var(--font-ui);
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  font-weight: 750;
  font-variation-settings: "wdth" var(--wdth-display);
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.text-ui {
  font-family: var(--font-ui);
  font-size: var(--fs-ui);
  line-height: var(--lh-ui);
  font-weight: 500;
}

.text-body { font-size: var(--fs-body); line-height: var(--lh-body); }

.lede {
  font-size: 1.125rem;
  line-height: var(--lh-body);
  color: var(--ink-2);
  max-width: var(--measure);
}

/* ES gets +25% width headroom on short chip/marquee strings so accented
   caps and longer Spanish labels never clip or force a mid-diacritic
   ellipsis (DESIGN-DIRECTION.md §3 bilingual rule). */
:lang(es) .marquee,
:lang(es) .receipt-chip { padding-inline: calc(var(--sp-3) * 1.25); }

/* Bilingual prose: hyphenate Spanish only (mirrors design-tokens.css's
   :lang(es) p rule for any prose container, not just <p>). */
:lang(es) .lede, :lang(es) .text-body { hyphens: auto; }

/* ── ledger.css ── */
/* ============================================================
   LEDGER — the evidence-grammar components (DESIGN-DIRECTION.md §10):
   answer block + receipt chip row, ledger tables, result rows. This is
   Miami's ownable identity kit — every important page opens with an
   answer sentence and its receipt, never a naked number.
   ============================================================ */

/* ── Answer block ─────────────────────────────────────────────────────── */
.answer-block { margin-block-end: var(--sp-6); }
.answer-block__sentence {
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: var(--fs-h1);
  line-height: var(--lh-h1);
  font-variation-settings: "wdth" var(--wdth-h1);
  color: var(--ink);
  margin-block-end: var(--sp-3);
}

/* ── Receipt chip row ─────────────────────────────────────────────────── */
.receipt-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center; /* flex's default (stretch) forced every chip to the
    row's tallest sibling's height, leaving visible empty vertical padding
    inside any shorter chip (DESIGN-SPEC-V2.md's "receipt-chip stretch"
    flag, W11-D2) -- each chip should size to its own content. */
  gap: var(--sp-2);
}
.receipt-chip {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--ocean-tint);
  border-start-end-radius: var(--r-chip);
  font-family: var(--font-data);
  font-size: var(--fs-receipt);
  line-height: var(--lh-receipt);
  color: var(--ink);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
  /* Most chips are short tags ("35 cities", "Ranked") where nowrap+ellipsis
     never engages. But the venue-profile answer-block's verdict chip is a
     full generated sentence — e.g. "This business is not currently ranked
     among 4 fast-food spots (not enough data to rank it), confidence
     insufficient evidence." (131 chars, live on thousands of r/*.html
     pages). At the ~340px column a 375px viewport gives this chip, nowrap
     forces that whole sentence onto one line and ellipsis then truncates
     it around the 40-45ch mark — cutting off the confidence/evidence
     conclusion the chip exists to carry. That is silent information loss
     on exactly the "receipt" this brand's whole premise is not to fake
     (PRODUCT.md: "every number here carries its receipt or it doesn't
     ship"). Wrapping instead of truncating costs nothing for the short
     chips (a single word/phrase never wraps regardless) and fixes the
     long-sentence case everywhere it occurs. */
  white-space: normal;
  overflow-wrap: anywhere;
  max-width: 100%;
}
.receipt-chip[data-confidence="low"] {
  background: transparent;
  border-color: var(--line);
  color: var(--muted);
}
.receipt-chip[data-confidence="medium"] {
  background: color-mix(in srgb, var(--terrazzo) 50%, var(--surface));
}
a.receipt-chip { cursor: pointer; }
a.receipt-chip:hover { background: var(--terrazzo); border-color: var(--ocean); color: var(--ink); }

/* ── Stream chip sizing (venue-profile tag rows) ──────────────────────────
   design-tokens.css defines `.stream-chip`'s border/background/corner only
   (a "streamline corner helper" shared with `.stream-card`); it was written
   before any page actually rendered a bare `.stream-chip`, so it carries no
   padding, display, or type size of its own. It is now live on every venue
   profile's "What diners mention" and "Amenities" rows (lib/entity's
   sections.mjs) as a plain inline `<span>` — with zero padding, the border
   sits flush against the text on every one of those pages, worst on mobile
   where the tag row is one of the first things a reader's thumb meets.
   Adding the missing box model here (not in design-tokens.css, out of this
   item's allowed paths) restores the chip look these tags were always
   meant to have, at any viewport. */
.stream-chip {
  display: inline-flex;
  align-items: center;
  padding: var(--sp-1) var(--sp-3);
  font-size: var(--fs-receipt);
  line-height: var(--lh-receipt);
  color: var(--ink-2);
}

/* ── Ledger table (rankings, comparisons) ─────────────────────────────── */
.ledger-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-ui);
}
.ledger-table th, .ledger-table td {
  padding: var(--sp-2) var(--sp-3);
  text-align: left;
  border-bottom: 1px solid var(--line);
}
.ledger-table th {
  background: var(--terrazzo);
  font-family: var(--font-data);
  font-size: var(--fs-receipt);
  font-weight: 500;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.ledger-table td.num, .ledger-table th.num { text-align: right; }
.ledger-table .num {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
}
.ledger-table tbody tr:hover { background: var(--terrazzo-2); }

/* ── Row anatomy (RD visual-redesign pass): a ranked row is not a bare
   hyperlink -- the rank gets a real fixed-width numeral column, the name
   carries a muted meta subline, and the rating renders as one designed
   right-aligned unit (star + value on top, review count small underneath)
   instead of a flat "4.8★ (1,580)" string. Same classes on <th> and <td>
   so header + data columns agree on width/alignment; the mobile card
   transform (@media block below) keeps the same classes, just re-flows
   them into the card's own line layout. */
.ledger-table .ledger-cell--rank {
  font-family: var(--font-data);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: 1.1em;
  color: var(--ocean);
  width: 3.5ch;
}
.ledger-table .venue-name-line {
  display: block;
  font-weight: 600;
  color: var(--ink);
}
.ledger-table .venue-meta-line {
  display: block;
  margin-block-start: 2px;
  font-family: var(--font-ui);
  font-size: var(--fs-receipt);
  font-weight: 400;
  color: var(--muted);
}
.ledger-table .rating-unit {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  line-height: 1.3;
}
.ledger-table .rating-unit__stars {
  font-weight: 600;
  color: var(--ink);
}
.ledger-table .rating-unit__count {
  font-family: var(--font-ui);
  font-size: var(--fs-receipt);
  font-weight: 400;
  color: var(--muted);
}

/* "Why this result?" receipt expander (leaderboards.mjs receiptHtml) — the
   native <summary> keeps its disclosure marker (display is never overridden
   here); padding-block alone stretches the tap target to ≥44px tall across
   the full row width (§10 tap law). */
details > summary {
  cursor: pointer;
  min-height: var(--tap);
  padding-block: var(--sp-3);
  font-weight: 600;
  color: var(--ocean-deep);
}
@media (hover: hover) {
  details > summary:hover { color: var(--ocean); }
}

/* ── Mobile leaderboard: table -> card-stack (DESIGN-DIRECTION.md §10,
   W11-D2 mobile pass). `.ledger-table--cards` is applied ONLY to the ranked
   + small-sample leaderboard tables (content/generators/leaderboards.mjs) —
   infoCard/ratingDist/comparisons tables elsewhere keep the plain <table>
   layout, which already fits at 375px (2-3 short columns, no wide receipt/
   expander cell to fight). The <thead> stays in the accessibility tree
   (clip, not display:none) — same technique base.css's .visually-hidden
   utility uses, inlined here since a <thead> can't carry that class without
   a markup edit this item doesn't need. ─────────────────────────────────── */
@media (max-width: 640px) {
  .ledger-table--cards thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    padding: 0;
    margin: -1px;
  }
  .ledger-table--cards,
  .ledger-table--cards tbody { display: block; width: 100%; }
  .ledger-table--cards tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--sp-3);
    row-gap: var(--sp-1);
    border: 1px solid var(--line);
    border-start-end-radius: var(--r-card);
    padding: var(--sp-3);
    margin-block-end: var(--sp-3);
  }
  .ledger-table--cards tr:last-child { margin-block-end: 0; }
  .ledger-table--cards td { display: block; padding: 0; border: 0; }
  /* .ledger-cell--rank's numeral styling (font/weight/color/size) is now
     the shared, non-gated .ledger-table rule above -- inherited here
     unchanged, so it isn't redeclared. */
  .ledger-table--cards .ledger-cell--name {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
  }
  .ledger-table--cards .ledger-cell--price {
    font-size: var(--fs-receipt);
    color: var(--ink-2);
  }
  /* The rating unit stacks star/value over the review count on desktop
     (flex-column, right-aligned); inside a wrapping card row that reads
     better as one inline baseline group instead of a floating stack. */
  .ledger-table--cards .rating-unit {
    flex-direction: row;
    align-items: baseline;
    gap: var(--sp-1);
  }
  .ledger-table--cards .ledger-cell--receipt { flex-basis: 100%; }
}

/* ── Result row (leaderboard/list rows — §10) ─────────────────────────── */
.result-row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: 48px;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  text-decoration: none;
  transition: background-color var(--t-fast) var(--ease-out);
}
.result-row:active { background: var(--terrazzo-2); }
@media (hover: hover) {
  .result-row:hover { background: var(--terrazzo-2); }
}
.result-row__rank {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--muted);
  min-width: 2ch;
  text-align: right;
}
/* flex:1/min-width:0/ellipsis so a long venue or category name truncates
   instead of forcing the row (and the page) to scroll horizontally — the
   hard rule, hit on the venue-page "nearby" rows (lib/entity/sections.mjs)
   where a real business name can run long. */
.result-row__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}
.result-row__meta { flex-shrink: 0; color: var(--muted); font-size: var(--fs-receipt); }
.result-row .receipt-chip { flex-shrink: 0; }
.result-row__signal {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Momentum glyphs — color NEVER alone; the ▲ ▼ · glyph is markup, not
   generated content, so it survives copy/paste and screen readers. */
.result-row__signal .delta-up,
.result-row__signal .delta-down,
.result-row__signal .delta-flat { font-weight: 600; }

/* High-confidence receipt chips carry the brand accent (DESIGN-SPEC-V2 brand-
   color application): the strongest evidence tier reads in ocean, everything
   else stays quiet ink. Attribute set by lib/entity/verdict.mjs from the
   engine's own confidence tier — never parsed client-side. */
.receipt-chip[data-confidence="high"] {
  background: var(--ocean-tint);
  color: var(--ocean-deep);
  border-color: var(--ocean);
}

/* A filtered-out row's [hidden] must always win: the mobile card transform's
   `display:flex` (0,1,1) outspecifies the UA's [hidden] rule (0,1,0), leaving
   hidden rows visually present on phones. (P1's live-verified finding.) */
.ledger-table--cards tr[hidden] { display: none; }

/* ── buttons.css ── */
/* ============================================================
   BUTTONS — the streamline rule (design-tokens.css §6): one rounded
   corner (start-end), hairline + offset-rule elevation, no blurred
   shadows, no scale-on-press (terrazzo tint instead — DESIGN-DIRECTION
   §13 "press state").
   ============================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid transparent;
  border-start-end-radius: var(--r-ui);
  font-family: var(--font-ui);
  font-size: var(--fs-ui);
  font-weight: 600;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out);
}

.btn--primary {
  background: var(--ocean);
  color: #FFFFFF;
  box-shadow: var(--elev-1);
}
@media (hover: hover) {
  .btn--primary:hover { background: var(--ocean-deep); }
}
.btn--primary:active { background: var(--ocean-deep); }

.btn--secondary {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line);
  box-shadow: var(--elev-1);
}
@media (hover: hover) {
  .btn--secondary:hover { border-color: var(--line-2); background: var(--terrazzo); }
}
.btn--secondary:active { background: var(--terrazzo); }

.btn:disabled {
  cursor: not-allowed;
  opacity: 0.5;
  box-shadow: none;
}

.btn--sm { min-height: 36px; padding: var(--sp-2) var(--sp-4); font-size: var(--fs-receipt); }

/* Quiet inline link that isn't the default <a> (nav items, "view all") */
.link-quiet {
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 1px solid var(--line-2);
}
.link-quiet:hover { color: var(--ocean-deep); border-color: var(--ocean-deep); }

/* Segmented control (EN|ES toggle — §10 sticky stack) */
.segmented {
  display: inline-flex;
  border: 1px solid var(--line);
  border-start-end-radius: var(--r-ui);
  overflow: hidden;
}
.segmented__option {
  min-width: var(--tap);
  min-height: var(--tap);
  padding-inline: var(--sp-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-ui);
  font-size: var(--fs-ui);
  font-weight: 600;
  color: var(--ink-2);
  background: var(--surface);
  border: 0;
  text-decoration: none;
  cursor: pointer;
}
.segmented__option + .segmented__option { border-left: 1px solid var(--line); }
.segmented__option[aria-pressed="true"] {
  background: var(--ocean-tint);
  color: var(--ocean-deep);
}

/* ============================================================
   FILTER SYSTEM (Kit I — src/kits/filter.mjs markup, public/filter.js
   behavior). Not wired into any page yet (no generator calls filterBar()/
   filterSheet() today) — styled ahead of that landing per the W11-D2
   mobile-pass mandate, so a future wave item only has to call the kit, not
   also design it. Mobile-first: the full-height bottom sheet is the primary
   mobile UI; `.filter-row--desktop`'s inline bar shows only >=641px,
   replaced below that by `.filter-row--mobile`'s single "Filter" trigger
   (public/filter.js already branches its own animation mode on this same
   641px `config.breakpoint`). Streamline corner + offset-rule elevation,
   terrazzo press-tint (never scale) — DESIGN-DIRECTION.md §6/§10/§13.
   ============================================================ */
.filter-bar {
  position: sticky;
  top: var(--bar-app);
  z-index: var(--z-bar);
  background: var(--paper);
  border-bottom: 1px solid var(--line);
  padding-block: var(--sp-3);
}

.filter-results {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding-block-end: var(--sp-3);
  margin-block-end: var(--sp-3);
  border-bottom: 1px solid var(--line);
}
.filter-results__count { font-size: var(--fs-receipt); color: var(--muted); }
.filter-results__lb {
  margin-inline-end: auto;
  font-size: var(--fs-receipt);
  color: var(--ocean-deep);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.filter-results__lb[hidden] { display: none; }

.filter-sort { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-receipt); color: var(--muted); }
.filter-sort__select {
  font: inherit;
  font-family: var(--font-ui);
  font-size: var(--fs-ui);
  color: var(--ink);
  min-height: var(--tap);
  padding-inline: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-start-end-radius: var(--r-ui);
  cursor: pointer;
}
.filter-sort__select:focus-visible { outline: 2px solid var(--ocean); outline-offset: 1px; }

.filter-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); }
.filter-row--desktop { display: none; }
.filter-row--mobile { display: flex; }
@media (min-width: 641px) {
  .filter-row--desktop { display: flex; }
  .filter-row--mobile { display: none; }
}

.filter-trigger, .filter-more {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding-inline: var(--sp-4);
  font-family: var(--font-ui);
  font-size: var(--fs-ui);
  font-weight: 600;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--ink);
  border-start-end-radius: var(--r-ui);
  cursor: pointer;
}
@media (hover: hover) {
  .filter-trigger:hover, .filter-more:hover { background: var(--terrazzo); }
}
.filter-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 1.4em;
  padding-inline: 0.3em;
  background: var(--ocean);
  color: #FFFFFF;
  border-start-end-radius: var(--r-chip);
  font-size: 0.8em;
  font-weight: 700;
}
.filter-badge[hidden] { display: none; }

/* toggle chip (rising/creator-pick) + locked static context chip — both
   layer onto .receipt-chip (filter.mjs always pairs the two classes). */
.filter-chip, .filter-toggle { cursor: pointer; }
.filter-chip[aria-pressed="true"], .filter-toggle[aria-pressed="true"] {
  background: var(--ocean-tint);
  border-color: var(--ocean);
  color: var(--ocean-deep);
  font-weight: 600;
}
.filter-chip--locked { cursor: default; background: var(--ocean-tint); color: var(--ocean-deep); }

/* ── popover (5+ option facets, desktop bar only — narrow screens fall
   through to the sheet's accordion instead of opening this) ───────────── */
.filter-popover { position: relative; display: inline-flex; }
.filter-popover__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding-inline: var(--sp-3);
  font-family: var(--font-ui);
  font-size: var(--fs-ui);
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-start-end-radius: var(--r-ui);
  cursor: pointer;
}
.filter-popover__trigger.is-filled,
.filter-popover__trigger[aria-expanded="true"] {
  border-color: var(--ocean);
  color: var(--ink);
  background: var(--ocean-tint);
}
.filter-popover__caret { font-size: 0.7em; color: var(--muted); }
.filter-popover__panel {
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: calc(100% + var(--sp-2));
  z-index: var(--z-popover);
  min-inline-size: 220px;
  max-inline-size: min(320px, calc(100vw - var(--sp-8)));
  max-block-size: 60vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-start-end-radius: var(--r-card);
  box-shadow: var(--elev-2);
  padding: var(--sp-3);
}
.filter-popover__panel[hidden] { display: none; }
.filter-popover__foot {
  display: flex;
  justify-content: flex-end;
  margin-block-start: var(--sp-2);
  padding-block-start: var(--sp-2);
  border-block-start: 1px solid var(--line);
}

.filter-checklist { display: flex; flex-direction: column; gap: var(--sp-1); }
.filter-optlist { display: flex; flex-direction: column; gap: 2px; }
.filter-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding-inline: var(--sp-2);
  font: inherit;
  font-size: var(--fs-ui);
  color: var(--ink-2);
  text-align: start;
  background: transparent;
  border: 0;
  border-start-end-radius: var(--r-chip);
  cursor: pointer;
}
@media (hover: hover) {
  .filter-option:hover { background: var(--terrazzo); color: var(--ink); }
}
.filter-option.is-active { background: var(--ocean-tint); color: var(--ocean-deep); font-weight: 600; }
.filter-option[hidden] { display: none; }
/* large-facet collapse: options past the top-N stay hidden until the group
   is expanded ("Show more") or being searched; an already-selected value
   (.is-active) always stays visible so a chosen filter never disappears
   behind the fold — mirrors public/filter.js's openExpand/applyFacetSearch. */
[data-facet]:not(.is-expanded):not(.is-searching) .filter-optlist > .filter-option--overflow:not(.is-active) {
  display: none;
}
.filter-expand {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  font-size: var(--fs-receipt);
  color: var(--ocean-deep);
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 0;
}
.filter-expand:hover { text-decoration: underline; text-underline-offset: 2px; }
.filter-search {
  inline-size: 100%;
  min-height: var(--tap);
  font: inherit;
  font-size: var(--fs-ui);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-start-end-radius: var(--r-ui);
  padding-inline: var(--sp-3);
  margin-block-end: var(--sp-2);
}
.filter-search:focus-visible { outline: 2px solid var(--ocean); outline-offset: 1px; }

/* ── applied ✕-chips row (public/filter.js appends .filter-applied__chip /
   --locked buttons at runtime; the empty container ships in the baked
   HTML) ───────────────────────────────────────────────────────────────── */
.filter-applied {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--sp-2);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin-block-start: var(--sp-3);
  padding-block-end: var(--sp-1);
}
.filter-applied[hidden] { display: none; }
.filter-applied__chip { flex-shrink: 0; cursor: pointer; }
.filter-applied__chip::after { content: '✕'; margin-inline-start: var(--sp-2); opacity: 0.7; }
.filter-applied__chip--locked { cursor: default; }
.filter-applied__chip--locked::after { content: none; }
.filter-applied__clear { order: -1; margin-inline-end: var(--sp-2); }

/* ── filter sheet — mobile-first full-height bottom sheet; desktop (>=641px)
   becomes a centered modal panel. JS toggles the [hidden] attribute
   synchronously on open/close (public/filter.js openSheet/closeSheet). ──── */
.filter-sheet { position: fixed; inset: 0; z-index: var(--z-sheet); display: none; }
.filter-sheet:not([hidden]) { display: block; }
.filter-sheet__scrim { position: absolute; inset: 0; background: color-mix(in srgb, var(--ink) 45%, transparent); }
.filter-sheet__panel {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  max-block-size: 86vh;
  max-block-size: 86dvh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-start-start-radius: var(--r-card);
  border-start-end-radius: var(--r-card);
}
@media (min-width: 641px) {
  .filter-sheet__panel {
    inset-block-start: 8vh;
    inset-block-end: auto;
    inset-inline: 0;
    margin-inline: auto;
    inline-size: min(720px, calc(100% - var(--sp-6)));
    max-block-size: 84vh;
    border-start-start-radius: 0;
    border: 1px solid var(--line-2);
  }
}
.filter-sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.filter-sheet__title { font-size: var(--fs-h3); font-weight: 700; }
.filter-sheet__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--tap);
  block-size: var(--tap);
  background: transparent;
  border: 0;
  color: var(--muted);
  font-size: 1.1rem;
  cursor: pointer;
}
.filter-sheet__body { padding-inline: var(--sp-5); flex: 1 1 auto; overflow-y: auto; }
.filter-sheet__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--line);
  background: var(--surface);
  flex-shrink: 0;
  position: sticky;
  inset-block-end: 0;
}
body.filter-lock { overflow: hidden; }

.filter-accordion { border-bottom: 1px solid var(--line); }
.filter-accordion__head { margin: 0; }
.filter-accordion__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  inline-size: 100%;
  min-height: var(--tap);
  font: inherit;
  font-weight: 600;
  font-size: var(--fs-ui);
  color: var(--ink);
  background: transparent;
  border: 0;
  padding-block: var(--sp-3);
  cursor: pointer;
  text-align: start;
}
.filter-accordion__trigger::after { content: '▾'; font-size: 0.75em; color: var(--muted); }
.filter-accordion__trigger[aria-expanded="true"]::after { content: '▲'; }
.filter-accordion__body { padding-block-end: var(--sp-4); }
.filter-accordion__body[hidden] { display: none; }

/* ── layout.css ── */
/* ============================================================
   LAYOUT — container, alternating section bands, sticky app bar,
   staging banner, brand lockup composition (DESIGN-DIRECTION.md §7/§8/
   §10). Mobile-first, single-column by default (§7).
   ============================================================ */

.container {
  max-width: var(--max-w);
  margin-inline: auto;
  padding-inline: var(--sp-4);
}
@media (min-width: 768px) {
  .container { padding-inline: var(--sp-5); }
}

/* Several generators emit the breadcrumb nav as `class="crumbs container"`
   nested INSIDE the page's own `.container` div (e.g. florida.html,
   florida/miami.html, every florida/<city>/<category>.html leaderboard,
   florida/miami/map.html — verified across EN + ES builds). Without this
   reset, the breadcrumb's own `.container` padding-inline stacks on top of
   its ancestor's, indenting it ~16px further than every sibling heading/
   paragraph on the same page (worst on mobile, where 16px is a bigger
   fraction of the ~340px content column). This is a defensive reset for
   ANY nested `.container`, not just the breadcrumb case — a static-HTML
   site with no component boundary to prevent double-nesting should never
   let two ancestors both claim max-width/padding. */
.container .container {
  max-width: none;
  padding-inline: 0;
  margin-inline: 0;
}

/* Breadcrumb nav (src/kits/shell.mjs's crumbs()) — quiet muted trail, no
   underline (nav, not in-prose links); current-page label reads as ink
   to mark it as "you are here". Hover gets the same terrazzo tint the
   rest of the nav/row family uses. */
.crumbs {
  padding-block: var(--sp-3);
  font-size: var(--fs-receipt);
  color: var(--muted);
}
.crumbs a {
  color: var(--muted);
  text-decoration: none;
  border-radius: var(--r-chip);
  padding-inline: 2px;
}
@media (hover: hover) {
  .crumbs a:hover { background: var(--terrazzo); color: var(--ink); }
}
.crumbs [aria-current="page"] { color: var(--ink); font-weight: 600; }

.section { padding-block: var(--sp-6); }
.section--band { background: var(--terrazzo); }
@media (min-width: 768px) {
  .section { padding-block: var(--sp-7); }
}

.stack { display: flex; flex-direction: column; gap: var(--sp-4); }
.row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }

/* ── Staging banner (shell.mjs already emits .staging-banner — this fills
   the previously-unstyled gap). Reads as a printed system notice, not a
   generic yellow warning box — ledger voice, instrument face. ────────── */
.staging-banner {
  background: var(--terrazzo-2);
  color: var(--ink-2);
  border-bottom: 1px solid var(--line-2);
  font-family: var(--font-data);
  font-size: var(--fs-receipt);
  line-height: var(--lh-receipt);
  text-align: center;
  letter-spacing: 0.02em;
  padding: var(--sp-2) var(--sp-4);
}

/* ── Sticky app bar (§10: mark, search, EN|ES toggle) ─────────────────── */
.app-bar {
  position: sticky;
  top: 0;
  z-index: var(--z-bar);
  min-height: var(--bar-app);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-inline: var(--sp-4);
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}

/* ── Brand lockup (mark + wordmark, lib/mark.mjs) — min-width:0 lets the
   name truncate instead of pushing the EN|ES switch off-screen at narrow
   widths (mobile masthead pass, W11-D2). ───────────────────────────────── */
.brand-lockup {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  /* The brand NEVER truncates (a wordmark reduced to "P." shipped live and
     the operator saw it — W13-RD). The nav is a scroll strip and absorbs
     every squeeze; the lockup is the one fixed element besides the switch. */
  flex-shrink: 0;
  color: var(--ink);
  text-decoration: none;
}
.brand-lockup__mark { display: block; width: 1.35em; height: 1.35em; flex-shrink: 0; }
.brand-lockup__name {
  font-size: 1.15rem;
  white-space: nowrap;
}
.brand-lockup__dateline { margin-inline-start: var(--sp-2); color: var(--muted); }

/* Language switch never shrinks/wraps away — it's the smallest, most
   important control in the bar (§10 tap law: both segments stay ≥44px). */
.app-bar .segmented { flex-shrink: 0; }

/* Main nav (5 links: Florida/Map/Markets/Methodology/About, longer still
   in Spanish: Florida/Mapa/Mercados/Metodología/Acerca de) is the third
   flex item sharing the bar with the brand lockup and the EN|ES switch.
   `.row`'s own flex-wrap:wrap (layout.css) is correct for the footer's
   legal nav, which owns a full line to itself — but as an app-bar flex
   ITEM competing for space against two non-shrinking siblings, wrapping
   just squeezes the 5 links into a narrow, multi-row column (verified:
   the nav's unwrapped content needs ~310-345px; at a 375px viewport the
   brand+switch alone already claim ~125-165px of the ~343px available,
   leaving less room than the nav needs even in the best case). A
   horizontally-scrolling strip keeps every link on one line, reachable,
   and never forces the page itself to scroll — at any width wide enough
   to fit all 5 unwrapped it behaves exactly like a normal row (no visible
   scrollbar, nothing to scroll). Base rule, not gated by a breakpoint:
   correct at every viewport down to the narrowest phones. */
.app-bar nav {
  flex-wrap: nowrap;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.app-bar nav::-webkit-scrollbar { display: none; }

/* Bare `<a>` nav links (header main nav + footer legal nav) have no
   sizing of their own beyond the inherited body line-box (~24px) — well
   under the ≥44px tap-target floor. `inline-flex` + `min-height` gives
   every link a real tap height (text stays vertically centered via
   align-items) without touching the shared `<a>` rule (design-tokens.css)
   other inline text links rely on. */
.app-bar nav a,
footer nav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  text-decoration: none;
}

/* Main nav reads as chrome, not a row of in-prose links: quiet ink-2,
   background-tint on hover/press (the same terrazzo press pattern
   .result-row/.filter-option already use), no underline. */
.app-bar nav a {
  padding-inline: var(--sp-2);
  border-radius: var(--r-chip);
  color: var(--ink-2);
  transition: background-color var(--t-fast) var(--ease-out),
              color var(--t-fast) var(--ease-out);
}
@media (hover: hover) {
  .app-bar nav a:hover { background: var(--terrazzo); color: var(--ink); }
}
.app-bar nav a:active { background: var(--terrazzo-2); color: var(--ink); }

/* ── Mark size presets (design-preview + favicon-adjacent uses) ───────── */
.mark--16 { width: 16px; height: 16px; }
.mark--24 { width: 24px; height: 24px; }
.mark--32 { width: 32px; height: 32px; }
.mark--160 { width: 160px; height: 160px; }

/* ── Map page chrome (content/generators/city-map.mjs + src/kits/maproom.mjs)
   Page chrome only — the map engine's own tap/hit-test discipline is
   src/assets/map.js's own concern (already ≥44px-padded query boxes there).
   Mobile-first: #map is a shorter frame on phones, taller on desktop, and
   the "+N more in this area" density control (map.js's makeDensityControl,
   documented there as unstyled until this pass) gets real Miami styling. ── */
.maproom h1 { margin-block-end: var(--sp-3); }
.maproom p { max-width: var(--measure); }

#map {
  inline-size: 100%;
  block-size: 60vh;
  min-block-size: 320px;
  max-block-size: 480px;
  margin-block: var(--sp-5);
  border: 1px solid var(--line);
  border-start-end-radius: var(--r-card);
  background: var(--terrazzo);
}
@media (min-width: 768px) {
  #map { block-size: 65vh; max-block-size: 640px; }
}

.map-fallback {
  display: grid;
  place-items: center;
  block-size: 100%;
  padding: var(--sp-5);
  color: var(--muted);
  text-align: center;
  font-size: var(--fs-ui);
}
.map-fallback button {
  margin-block-start: var(--sp-3);
  min-height: var(--tap);
  padding-inline: var(--sp-4);
  background: var(--ocean);
  color: #FFFFFF;
  border: 0;
  border-start-end-radius: var(--r-ui);
  font-family: var(--font-ui);
  font-weight: 600;
  cursor: pointer;
}

.map-note { margin-block-start: var(--sp-3); font-size: var(--fs-receipt); color: var(--muted); }

/* #map-scoped so these beat the vendor stylesheet (linked after styles.css
   in mapFrame()'s own markup order). */
#map .maplibregl-popup-content {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-2);
  border-start-end-radius: var(--r-ui);
  font-family: var(--font-ui);
  box-shadow: var(--elev-2);
}
#map .maplibregl-popup-close-button { color: var(--muted); font-size: 1.1rem; }
.map-popup__name { font-family: var(--font-ui); font-weight: 600; margin-block-end: 0.3em; }
.map-popup__meta { font-family: var(--font-data); font-size: var(--fs-receipt); color: var(--muted); }
.map-popup__link { display: inline-block; margin-block-start: var(--sp-2); color: var(--ocean-deep); }

#map .maplibregl-ctrl-group.map-density-ctrl {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-2);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-start-end-radius: var(--r-ui);
  box-shadow: var(--elev-1);
}
.map-density-ctrl__chip {
  font-family: var(--font-data);
  font-size: var(--fs-receipt);
  color: var(--muted);
  white-space: nowrap;
}
.map-density-ctrl__action {
  min-height: var(--tap);
  padding-inline: var(--sp-3);
  background: var(--ocean);
  color: #FFFFFF;
  border: 0;
  border-start-end-radius: var(--r-chip);
  font-family: var(--font-ui);
  font-size: var(--fs-receipt);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

/* ── maproom.css ── */
/* ============================================================
   MAPROOM — the city map explore surface (W11-M1, "learn from Zillow"
   operator mandate). Map-specific styles only; every other component here
   (.result-row, .receipt-chip, .btn, .filter-*, .segmented) is styled in its
   OWN kit file (ledger.css/buttons.css) and simply reused, per
   docs/waves/wave-w11-miami-site.md's fork-not-package convention.

   Wired into public/styles.css via build.mjs's KIT_FILES array
   (build.mjs:93 includes 'maproom.css') -- verified during W13-D3's mobile
   audit; an earlier revision of this comment said otherwise.

   Full-bleed breakout: lib/shell.mjs's renderPage() wrapper never forwards
   kits/shell.mjs's `wide` flag, so every page (including this one) renders
   inside `.container` (max-width: var(--max-w)). The split layout wants
   real viewport width, so `.maproom__layout` breaks out of that container
   with a width:100vw + margin-inline-start:calc(50% - 50vw) rule (see that
   rule's own comment for why), rather than editing either shell file.
   ============================================================ */

.maproom { margin-block-start: var(--sp-2); }
.map-note { color: var(--muted); font-size: var(--fs-receipt); margin-block-start: var(--sp-4); }

/* ── Full-bleed breakout (see header note). NOT the left/right:50% +
   margin:-50vw recipe -- that one only cancels out when the element's own
   containing block already spans the viewport; nested inside `.container`
   (max-width: var(--max-w), centered) it does not, and produces a box
   wider than the viewport (verified during this item's browser proof: a
   1280px viewport measured a 2400px-wide layout + 1100px+ of horizontal
   overflow). width:100vw + margin-inline-start:calc(50% - 50vw) is the
   correct recipe for breaking out of a centered, narrower ancestor -- BUT
   plain `100vw` itself is 1-2 scrollbar-widths WIDER than the real usable
   viewport in most browsers (100vw includes the vertical scrollbar
   gutter; window.innerWidth/clientWidth does not), which the same browser
   proof caught as a residual 40px horizontal-scroll overflow even after
   the formula fix. src/assets/map.js measures the real width
   (window.innerWidth) into --vw100 on load + resize (a file this item
   already owns) specifically so this rule never has to guess; `100vw`
   below is ONLY the pre-JS/no-JS fallback for the first paint. ─────────── */
.maproom__layout {
  /* Explicit, not just inherited from base.css's `*{box-sizing:border-box}`
     -- this box's width is an exact pixel budget (real viewport width via
     --vw100), so it self-defends against box-sizing:content-box (from any
     override, or the base rule somehow not applying) adding padding on top
     instead of inside; belt-and-suspenders, same instinct as the explicit
     `overflow:hidden` below. */
  box-sizing: border-box;
  width: var(--vw100, 100vw);
  max-width: var(--vw100, 100vw);
  margin-inline-start: calc(50% - (var(--vw100, 100vw) / 2));
  margin-block-start: var(--sp-4);
  display: flex;
  flex-direction: column;
  overflow: hidden; /* belt-and-suspenders for the brief pre-JS 100vw fallback */
}

/* ── Mobile (default): map fills the viewport below the app bar; the result
   panel is a draggable bottom sheet over it (peek/half/full, JS-driven
   inline `height`); a full-screen-map FAB sits on the map itself. Map
   ALWAYS visible (standing explore-surface ruling) — the sheet never
   fully covers it, even at its "full" snap point (92vh, per map.js's
   SHEET_SNAPS). ────────────────────────────────────────────────────────── */
.maproom__mapwrap {
  position: sticky;
  top: var(--bar-app);
  height: 56vh;
  height: 56dvh;
}
/* Explicit resets, not just position/inset: a concurrent mobile-pass commit
   (layout.css, outside this item's allowed paths) added its own unscoped
   `#map { block-size:60vh; margin-block:...; border:...; background:... }`
   rule (plus a `@media (min-width:768px) { #map { max-block-size:640px } }`
   companion), written against the OLD bare-map markup this item's split
   layout has superseded. `.maproom__mapwrap #map` (specificity 1,1,0)
   already beats layout.css's bare `#map` (1,0,0) for any property BOTH
   declare, so explicitly re-declaring every property that rule (and its
   768px media companion) sets -- not just leaving them uncontested --
   neutralizes it instead of silently inheriting it. Caught during this
   item's own browser proof: without the max-block-size reset, #map was
   still capped at a stale 640px tall regardless of its wrapper's real
   height. #map must fill `.maproom__mapwrap` edge to edge with no border/
   margin/background/size-cap of its own now that the wrapper owns framing. */
.maproom__mapwrap #map {
  position: absolute;
  inset: 0;
  inline-size: auto;
  block-size: auto;
  max-inline-size: none;
  max-block-size: none;
  min-inline-size: 0;
  min-block-size: 0;
  margin: 0;
  border: 0;
  background: transparent;
}

.maproom__panel {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--z-map-ui);
  height: 50vh; /* JS drag/FAB rewrite this inline once interacted with */
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border-start-start-radius: var(--r-card);
  border-start-end-radius: var(--r-card);
  box-shadow: var(--elev-2);
  transition: height var(--t-sheet) var(--ease-out);
  padding-inline: var(--sp-4);
  padding-block-end: max(var(--sp-3), env(safe-area-inset-bottom));
}

.maproom__sheet-handle {
  flex-shrink: 0;
  align-self: center;
  width: 40px;
  min-height: var(--tap);
  padding-block: var(--sp-2);
  background: transparent;
  border: 0;
  cursor: grab;
  touch-action: none;
}
.maproom__sheet-handle::before {
  content: '';
  display: block;
  width: 40px;
  height: 4px;
  margin-inline: auto;
  background: var(--line-2);
  border-radius: var(--r-round);
}
.maproom__sheet-handle:active { cursor: grabbing; }

.maproom__panel [data-filter-root] { flex-shrink: 0; }
/* The shared .filter-bar sticks to `var(--bar-app)` on a page's own scroll
   (buttons.css) -- nested here inside the panel's own scroll context there
   is no duplicate app bar above it, so it should stick to the panel's own
   top edge instead. Scoped override, not an edit to the shared rule. */
.maproom__panel .filter-bar { position: static; top: 0; border-bottom: 0; padding-block: var(--sp-2) 0; }

.maproom__toolbar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  padding-block: var(--sp-2);
}
.maproom__follow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  font-size: var(--fs-receipt);
  color: var(--ink-2);
  cursor: pointer;
}
.maproom__follow input { width: 18px; height: 18px; accent-color: var(--ocean); cursor: pointer; }

/* map.js toggles `researchBtn.hidden` at runtime (true on click, and again
   whenever "follow map" re-enables) — not just a static initial-load state.
   `.btn`'s own `display: inline-flex` (buttons.css) is an unconditional
   author rule, so without this it wins over the plain `[hidden]` the
   browser sets by default: the button silently stays visible after every
   click, defeating the show/hide interaction (same regression class as the
   `.ledger-table--cards tr[hidden]` fix above — any class that sets an
   unconditional `display` needs its own `[hidden]` carve-out). */
.maproom__research[hidden] { display: none; }

.maproom__count {
  flex-shrink: 0;
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-receipt);
  color: var(--muted);
  margin-block-end: var(--sp-2);
}

.maproom__cards {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.maproom__card { cursor: pointer; }
.maproom__card:focus-visible { outline: 2px solid var(--ocean); outline-offset: -2px; }
.maproom__card.is-hover { background: var(--ocean-tint); }
.maproom__card-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.maproom__card .result-row__name { white-space: normal; overflow-wrap: anywhere; }

.maproom__fab {
  position: absolute;
  inset-inline-end: var(--sp-4);
  inset-block-end: var(--sp-4);
  z-index: var(--z-map-ui);
  width: 56px;
  height: 56px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--ink);
  color: var(--paper);
  border: 0;
  border-radius: var(--r-round);
  box-shadow: var(--elev-2);
  font-size: 1rem;
  cursor: pointer;
}
.maproom__fab[aria-pressed="true"] span { transform: rotate(180deg); display: inline-block; }

/* ── Desktop (>=900px, must match map.js's SPLIT_BREAKPOINT): list column +
   sticky map pane, both plain in-flow -- the mobile sheet/FAB/handle retire
   entirely. Sticky right pane >= 440px (DESIGN-DIRECTION.md #9). ────────── */
@media (min-width: 900px) {
  .maproom__layout {
    flex-direction: row;
    align-items: flex-start;
    gap: var(--sp-5);
    padding-inline: var(--sp-5);
    overflow: visible;
  }
  .maproom__panel {
    position: sticky;
    inset-inline: auto;
    inset-block-end: auto;
    top: calc(var(--bar-app) + var(--sp-4));
    z-index: auto;
    flex: 0 0 400px;
    width: 400px;
    height: auto !important; /* clears any mobile drag-set inline height */
    max-height: calc(100vh - var(--bar-app) - var(--sp-6));
    max-height: calc(100dvh - var(--bar-app) - var(--sp-6));
    overflow-y: auto;
    border-radius: 0;
    box-shadow: none;
    border-inline-end: 1px solid var(--line);
    padding-inline: 0 var(--sp-4);
    padding-block-end: var(--sp-4);
    transition: none !important;
  }
  .maproom__sheet-handle, .maproom__fab { display: none; }
  .maproom__mapwrap {
    position: sticky;
    top: calc(var(--bar-app) + var(--sp-4));
    flex: 1 1 auto;
    height: calc(100vh - var(--bar-app) - var(--sp-6));
    height: calc(100dvh - var(--bar-app) - var(--sp-6));
    min-height: 440px;
    border-start-end-radius: var(--r-card);
    overflow: hidden;
  }
}

/* ── Density control (top-right MapLibre IControl: "+N more in this area" +
   "Search this area", src/assets/map.js's makeDensityControl()) ───────────
   The SAME concurrent layout.css commit above also styled this control,
   under the compound selector `#map .maplibregl-ctrl-group.map-density-ctrl`
   (specificity 1,2,0 — higher than a plain `.map-density-ctrl` class rule
   would be). Repeating that exact selector string here (rather than just
   `.map-density-ctrl`) ties the specificity; layout.css precedes
   maproom.css in build.mjs's KIT_FILES concat order, so on a tie THIS rule
   (later in the cascade) wins, cleanly superseding the stale one instead of
   silently losing to it. */
#map .maplibregl-ctrl-group.map-density-ctrl {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding-inline: var(--sp-2);
  background: var(--surface);
}
.map-density-ctrl__chip {
  font-family: var(--font-data);
  font-size: var(--fs-receipt);
  color: var(--ink-2);
  white-space: nowrap;
}
.map-density-ctrl__action {
  min-height: var(--tap);
  padding-inline: var(--sp-3);
  font-family: var(--font-ui);
  font-size: var(--fs-receipt);
  font-weight: 600;
  color: var(--ocean-deep);
  background: transparent;
  border: 0;
  cursor: pointer;
  white-space: nowrap;
}
.map-density-ctrl__action:hover { text-decoration: underline; text-underline-offset: 2px; }

/* ── Venue popup (MapLibre Popup content -- the wrapping .maplibregl-popup*
   chrome comes from the vendored maplibre-gl.css) ────────────────────────── */
.map-popup { padding: var(--sp-1) 0; font-family: var(--font-ui); }
.map-popup__name { font-weight: 700; color: var(--ink); margin-block-end: var(--sp-1); }
.map-popup__meta { font-family: var(--font-data); font-size: var(--fs-receipt); color: var(--muted); }
.map-popup__link {
  display: inline-block;
  margin-block-start: var(--sp-2);
  font-size: var(--fs-receipt);
  font-weight: 600;
  color: var(--ocean-deep);
}

/* ── Card rating chip (W13 RD round 2, visual only) ───────────────────────
   map.js's renderCards() emits the rating+vote pair as a plain
   `.receipt-chip` inside `.result-row__signal` ("4.6 · 1,234", no star
   glyph). Scoped to `.maproom__card` only -- never touches the shared
   `.receipt-chip` rule (ledger.css, out of this item's allowed paths) or
   its confidence-tier color variants. A star glyph is the only addition,
   matching the rating-display grammar used on the venue page and the
   platform-compare unit (venue.css) -- purely decorative, so it is a CSS
   generated-content star, not a DOM node a screen reader would announce
   out of context. ─────────────────────────────────────────────────────── */
.maproom__card .receipt-chip::before {
  content: '\2605';
  margin-inline-end: 0.3em;
  color: var(--sun);
}

/* ── No-WebGL / context-lost fallback ─────────────────────────────────────── */
.map-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: var(--sp-5);
  text-align: center;
  color: var(--ink-2);
  background: var(--terrazzo);
}
.map-fallback [data-map-reload] {
  display: inline-flex;
  margin-inline-start: var(--sp-2);
  color: var(--ocean-deep);
  font-weight: 600;
  background: transparent;
  border: 0;
  cursor: pointer;
}

/* ── brand.css ── */
/* ============================================================
   BRAND — the Platerian wordmark treatment (W13-D1 brand-mark pass).
   Styles the two-tone logotype src/kits/shell.mjs's masthead() now
   builds beside lib/mark.mjs's Solstep glyph (mark + word lockup,
   the same SHAPE the sibling brand's masthead uses -- a colored icon
   next to a styled word). Own tokens, own type family, own mixed-
   case treatment throughout (brand-separation hard rule, CLAUDE.md)
   -- never the sibling brand's palette, fonts, or script.
   ============================================================ */

.brand-word {
  font-family: var(--font-data);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.1;
}
.brand-word__seg--ink { color: var(--ink); }
.brand-word__seg--ocean { color: var(--ocean); }

/* ── venue.css ── */
/* ============================================================
   VENUE — venue-profile composition (W13 RD round 2): hero identity +
   display-type rating, the cross-platform comparison unit, and the shared
   section rhythm (heading + hairline + spacing) reused across every
   lib/entity/sections.mjs + divergence.mjs section. Answers the DESIGN.md
   audit findings: no heading element on most sections, no section banding,
   rating/reviews buried in a receipt-chip-sized line instead of carrying
   display weight.

   NOT YET WIRED: this file is new and must be added to build.mjs's
   KIT_FILES array (after 'maproom.css') before public/styles.css includes
   it — that edit is outside this item's allowed paths; the integrator
   commit adds the one line.
   ============================================================ */

/* ── Shared section rhythm ─────────────────────────────────────────────
   infoCard/topics/ratingDist/attributes/busyHours/comparisons/
   acrossPlatforms all carry this class: a top hairline + consistent block
   padding so the page reads as a sequence of exhibits, not one
   undifferentiated stack of headings and tables. methodology keeps its
   own `.section--band` treatment (layout.css) instead. ──────────────── */
.venue-section {
  padding-block: var(--sp-6);
  border-block-start: 1px solid var(--line);
}
.venue-section h2 {
  margin-block-end: var(--sp-3);
}

/* ── Hero identity + rating ─────────────────────────────────────────────
   Mobile-first: identity stacks above the rating stat; >=640px they sit
   side by side, rating right-aligned so the name still reads first. ──── */
.venue-hero {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  margin-block: var(--sp-4) var(--sp-2);
}
.venue-hero__identity { min-width: 0; }
.venue-hero__meta { margin-block-start: var(--sp-1); }

.venue-hero__rating {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
.venue-hero__rating-value {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
}
.venue-hero__rating-glyph {
  font-size: var(--fs-h2);
  color: var(--sun);
  line-height: 1;
}
.venue-hero__rating-num {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-stat);
  line-height: var(--lh-stat);
  font-weight: 700;
  color: var(--ink);
}
.venue-hero__rating-scale {
  font-family: var(--font-data);
  font-size: var(--fs-ui);
  color: var(--muted);
}
.venue-hero__rating-count {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-receipt);
  color: var(--muted);
}

@media (min-width: 640px) {
  .venue-hero {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
  }
  .venue-hero__rating { align-items: flex-end; text-align: right; }
}

/* ── Platform comparison unit ("Across platforms") ──────────────────────
   Two stat units side by side with a hairline divider — same
   label-over-numeral grammar as the hero rating above. The full sentence
   equivalent (divergence.mjs's unitHtml) renders via `.visually-hidden`
   (base.css) since the visible units here are aria-hidden fragments. ─── */
.platform-compare {
  display: flex;
  margin-block: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-start-end-radius: var(--r-card);
}
.platform-compare__unit {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--sp-4);
}
.platform-compare__unit + .platform-compare__unit {
  border-inline-start: 1px solid var(--line);
}
.platform-compare__platform {
  font-family: var(--font-ui);
  font-size: var(--fs-receipt);
  font-weight: 600;
  color: var(--ink-2);
}
.platform-compare__num {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-stat);
  line-height: var(--lh-stat);
  font-weight: 700;
  color: var(--ink);
}
.platform-compare__count {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-receipt);
  color: var(--muted);
}

/* ── home.css ── */
/* ============================================================
   HOME — RD visual redesign round 2 (docs/waves/wave-w13-us-platform-ia.md
   "RD" section). New home-page-only classes: hero search, the big-numeral
   stats band, the cross-market top-ranked module, and a denser city grid.

   INTEGRATION NOTE: this file is NOT yet in build.mjs's KIT_FILES array
   (build.mjs is outside this item's allowed paths) — add 'home.css' to that
   list so it gets concatenated into public/styles.css, or none of the rules
   below apply on the built site.

   Every selector here is scoped to a home-page-only wrapper class
   (.hero-search / .stats-band / .top-ranked / .city-grid) so nothing leaks
   onto other pages once concatenated into the shared bundle. Where a rule
   restyles a shared component (.result-row, .stream-card) it is written as
   a descendant override (e.g. `.city-grid .result-row`) rather than editing
   the shared class itself — content/pages/home.mjs's own comments explain
   why the markup underneath stays byte-identical to what ledger.css/
   test/home.test.mjs already expect.
   ============================================================ */

/* ── Hero search (client-side city filter, zero network) ─────────────── */
.hero-search {
  margin-block: var(--sp-5);
  max-inline-size: 32rem;
}
.hero-search__input {
  inline-size: 100%;
  min-height: var(--tap);
  padding-inline: var(--sp-4);
  font: inherit;
  font-size: var(--fs-ui);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-start-end-radius: var(--r-ui);
}
.hero-search__input::placeholder { color: var(--muted); }
.hero-search__input:focus-visible {
  outline: 2px solid var(--ocean);
  outline-offset: 1px;
  border-color: var(--ocean);
}
.hero-search__status {
  margin-block-start: var(--sp-2);
  font-size: var(--fs-receipt);
  color: var(--muted);
}
.hero-search__status a { color: var(--ocean-deep); }

/* ── Stats band — display-type numerals over hairlines ─────────────────
   Reuses --font-data (Space Grotesk, tabular) the way design-tokens.css's
   own .stat already does, at a larger clamp for this one hero moment. ──── */
.stats-band {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-5) var(--sp-4);
  margin-block: var(--sp-6);
}
@media (min-width: 640px) {
  .stats-band { grid-template-columns: repeat(4, 1fr); }
}
.stats-band__item {
  padding-block-start: var(--sp-3);
  border-top: 1px solid var(--line);
}
.stats-band__num {
  display: block;
  font-family: var(--font-data);
  font-size: clamp(2.5rem, 6vw, 3.5rem);
  font-weight: 600;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
/* The "updated" tile holds a date string, not a magnitude — the big clamp
   reads as shouting on a date, so it gets the h2 face instead. */
.stats-band__num--date {
  font-size: var(--fs-h2);
  font-family: var(--font-ui);
  font-weight: 700;
  font-variant-numeric: normal;
}
.stats-band__label {
  display: block;
  margin-block-start: var(--sp-1);
  font-size: var(--fs-receipt);
  color: var(--muted);
}

/* ── Top-ranked — 3-column by category (desktop), stacked (mobile) ─────
   content/pages/home.mjs's topRankedColumns() emits the SAME .result-row
   markup (name/meta spans, data-venue-name wrapper) test/home.test.mjs
   already asserts on; every rule below is a scoped override, never a
   markup change. ────────────────────────────────────────────────────── */
.top-ranked {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-5);
  margin-block-end: var(--sp-6);
}
@media (min-width: 768px) {
  .top-ranked { grid-template-columns: repeat(3, 1fr); gap: var(--sp-6); }
}
.top-ranked__col {
  padding-block-start: var(--sp-3);
  border-top: 1px solid var(--line-2);
}
.top-ranked__kind {
  margin-block-end: var(--sp-2);
  font-family: var(--font-data);
  font-size: var(--fs-receipt);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--muted);
}
.top-ranked .result-row {
  display: flex;
  flex-wrap: wrap;
  min-height: 0;
  padding: 0;
  border-bottom: 0;
}
.top-ranked .result-row__name {
  flex: 1 1 auto;
  font-family: var(--font-ui);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
.top-ranked .result-row__meta {
  flex-basis: 100%;
  margin-block-start: var(--sp-1);
  font-size: var(--fs-ui);
  color: var(--ink-2);
}
@media (hover: hover) {
  .top-ranked .result-row:hover .result-row__name { color: var(--ocean-deep); }
}

/* ── City grid — 2-col mobile, denser desktop, hairline-top tiles ──────
   cityTile() wraps the unchanged `.result-row.stream-card` anchor in a new
   `.city-tile` div (carries `data-search`, is what this grid lays out) so
   test/home.test.mjs's exact-opening-tag regex on the anchor never breaks. */
.city-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-3);
}
@media (min-width: 640px) {
  .city-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 960px) {
  .city-grid { grid-template-columns: repeat(4, 1fr); }
}
.city-tile[hidden] { display: none; }
/* Name-then-count STACKED (not side-by-side): a 4-col desktop / 2-col
   mobile grid gives a long city name ("North Miami Beach", "Sunny Isles
   Beach") too little width to share a line with the count. An earlier pass
   tried wrapping the name in place, but ledger.css's `.result-row__name`
   also carries `min-width: 0` (needed elsewhere so a flex sibling can
   ellipsis-truncate) — combined with `overflow: visible` here, the shrunk
   flex box let the wrapped glyphs spill outside their own box and visually
   collide with the count next to them. Giving both spans `flex-basis: 100%`
   removes the width fight entirely: full name on its own line, the count
   (still the tabular/bold "designed element" the count deserves) directly
   under it. */
.city-grid .result-row {
  flex-wrap: wrap;
  align-items: baseline;
  min-height: var(--tap);
  padding: var(--sp-3);
  border-bottom: 0;
  text-decoration: none;
}
.city-grid .result-row__name {
  flex-basis: 100%;
  font-weight: 600;
  color: var(--ink);
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
.city-grid .result-row__meta {
  flex-basis: 100%;
  margin-block-start: var(--sp-1);
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: var(--fs-ui);
  color: var(--ink-2);
}
@media (hover: hover) {
  .city-grid .result-row:hover { background: var(--terrazzo); }
}

/* ── motion.css ── */
/* ============================================================
   MOTION — site-wide motion layer. Pure CSS, zero JS. Reuses the
   tokens design-tokens.css already declares (--t-fast, --ease-out);
   no new duration/easing tokens invented here.

   Brand rule this file respects (DESIGN-DIRECTION.md §13, "Press
   state: Terrazzo press tint, no scale" — the explicit differentiator
   from the sibling IL brand's `:active scale(.97)`, echoed in buttons.css's own
   comment "no scale-on-press (terrazzo tint instead)"): NOTHING below
   uses transform-based press feedback. Every existing pressed state in
   this codebase (.btn--secondary:active, .result-row:active) is
   already a background tint with zero scale() calls anywhere in
   src/styles/ or design-tokens.css — this file extends that same
   convention to the chip/link family instead of introducing scale.

   `.result-row` already owns a full transition + hover/active tint in
   ledger.css (background-color, var(--t-fast), correct no-scale
   pattern) — intentionally NOT duplicated or overridden here.
   ============================================================ */

/* ── Page-level entrance (main content only, once per page load) ──────────
   @starting-style is the progressive-enhancement fade-in: browsers that
   don't support it simply render #main at its resting opacity:1/
   translateY(0) immediately (no broken/invisible state, no flash). Only
   opacity + transform are animated — both are compositor-only properties,
   so this can never shift layout or contribute to CLS. 200ms is a
   deliberate one-off for this single page-level moment, distinct from the
   --t-fast (140ms, small UI transitions) and --t-sheet (240ms, drawers)
   tokens already in use elsewhere. ────────────────────────────────────── */
#main {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}
@starting-style {
  #main { opacity: 0; transform: translateY(8px); }
}

/* ── Interactive: smooth what already reacts, add pressed feedback where
   there was none. All transitions reuse --t-fast (140ms) rather than a
   stray 150ms value, matching every other transition in this system. ──── */

/* Every <a> (nav links in the masthead's <nav class="row">, in-copy links,
   crumbs) already changes color on :hover/:active via design-tokens.css --
   this only adds the missing transition so that color change eases instead
   of snapping. */
a { transition: color var(--t-fast) var(--ease-out); }
a:active { color: var(--ocean); }

.link-quiet {
  transition: color var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out);
}
.link-quiet:active { color: var(--ocean-deep); border-color: var(--ocean-deep); }

/* .receipt-chip itself declares no transition in ledger.css (only
   `a.receipt-chip:hover` sets an instant color/border snap) -- this is
   purely additive. Also smooths .filter-chip/.filter-toggle, which layer
   onto .receipt-chip per buttons.css's own comment, at no extra cost. */
.receipt-chip {
  transition: background-color var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out),
              color var(--t-fast) var(--ease-out);
}
/* Pressed feedback via --terrazzo-2 (design-tokens.css's own "sunken
   wells, pressed states" tint) -- no scale, matching .result-row:active
   and .btn--secondary:active exactly. */
a.receipt-chip:active,
.filter-chip:active,
.filter-toggle:active {
  background: var(--terrazzo-2);
}

/* ── Reduced motion: hard requirement, disables everything this file adds.
   design-tokens.css already forces `*, *::before, *::after { transition:
   none !important; animation: none !important; }` globally under this same
   query -- this block is a self-contained, defense-in-depth repeat scoped
   to exactly what motion.css introduces, so this file's contract holds
   even if load order ever changes. #main's entrance in particular must
   never be skipped visually: disabling its transition here means it simply
   renders at its resting opacity:1/translateY(0) state with no fade, no
   jump. */
@media (prefers-reduced-motion: reduce) {
  #main,
  a,
  .link-quiet,
  .receipt-chip {
    transition: none;
  }
}
