/* Z1B: local variable fonts; ranges and metrics recorded in fonts/MANIFEST.json and QA Z1B.
   Width calibration uses the two rendered Home headline lines, not guessed percentages. */
@font-face {
  font-family:'Inter'; font-style:normal; font-weight:400 700;
  font-display:swap; src:url('fonts/inter-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family:'Playfair Display'; font-style:normal; font-weight:600 800;
  font-display:swap; src:url('fonts/playfair-display-700-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family:'Playfair Display Fallback'; src:local('Georgia'); font-style:normal; font-weight:400 500;
  size-adjust:88.788681%; ascent-override:121.862381%;
  descent-override:28.269369%; line-gap-override:0.000000%;
  unicode-range:U+0000-002F, U+0031-10FFFF;
}
@font-face {
  font-family:'Playfair Display Fallback'; src:local('Georgia Bold'); font-style:normal; font-weight:600 900;
  size-adjust:88.788681%; ascent-override:121.862381%;
  descent-override:28.269369%; line-gap-override:0.000000%;
  unicode-range:U+0000-002F, U+0031-10FFFF;
}
@font-face {
  font-family:'Inter Fallback'; src:local('Arial'); font-style:normal; font-weight:400 500;
  size-adjust:106.960118%; ascent-override:90.571141%;
  descent-override:22.551484%; line-gap-override:0.000000%;
}
@font-face {
  font-family:'Inter Fallback'; src:local('Arial Bold'); font-style:normal; font-weight:600 900;
  size-adjust:106.960118%; ascent-override:90.571141%;
  descent-override:22.551484%; line-gap-override:0.000000%;
}
/* HOWIE KNOWS — HOME 2.0 shell.
 *
 * Mobile-first and one-handed: every primary action sits in the lower half of a 390px screen,
 * nothing needs a second hand, and the tap floor is 44px throughout.
 *
 * THE PALETTE IS A SWITCH. All colour resolves through semantic tokens, so DARK / LIGHT /
 * HYBRID is one attribute on <html>. Default is HYBRID — the house is lit, the stage is dark —
 * which is the direction I recommended; changing it is `data-palette="DARK"`, nothing else.
 */

/* ---------- tokens: the three directions ---------- */
:root, [data-palette="HYBRID"] {
  --bg:#f7f5f1; --bg-2:#eeebe4; --surf-1:#fff; --surf-2:#f3f0ea; --surf-3:#e9e4da;
  --line:#e5e1d9; --line-2:#d2ccc0; --line-3:#bdb3a0; --line-4:#9c8a63;
  --text:#14120f; --text-2:#3a352d; --text-3:#5b544a; --text-4:#6d665b; --text-5:#7a7368;
  --accent:#f5a623; --accent-hot:#8f5705; --on-accent:#1a1206;
  --ok-bg:#e8f2e4; --ok-fg:#2f5c28; --ok-line:#bcd7b4;
  --warn-bg:#f7ebe2; --warn-fg:#8a4a22; --warn-line:#e0c4ad;
  --info-bg:#e9ecf7; --info-fg:#303c6b; --info-line:#c0c7e0;
  --bad:#a8301a; --good:#3d7a38; --badge-fg:#fff;
  --scrim:linear-gradient(180deg,rgba(0,0,0,0) 40%,rgba(0,0,0,.72) 100%);
  --tap:44px;
}
/* ---------- NI-2026-08-21-110 JOB D: TYPE SYSTEM ----------
   Three roles, never blurred:
   --font-ui      body/nav/card/label copy — the consumer reading face, everywhere by default.
   --font-display Anton — scoped to high-emotion identity moments (marquee, event/performer
                  name, team plates, section kickers) and never the default page face.
   --font-mono    IBM Plex Mono — scoped to dates, schedule columns, evidence/provenance and
                  short technical labels only (applied per-element below, never inherited from
                  body).
   The --type-* tokens are the shared scale from TYPOGRAPHY_UI_SCALE_BENCHMARK_2026-08-21.md —
   clamp() so desktop visibly scales up rather than stretching the mobile composition. */
:root{
  --font-ui:'Inter','Inter Fallback',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,system-ui,sans-serif;
  --font-display:'Anton',Impact,'Arial Narrow',sans-serif;
  --font-mono:'IBM Plex Mono',ui-monospace,Menlo,Consolas,monospace;
  --type-hero:clamp(44px,7.6vw,80px);
  --type-page:clamp(34px,5vw,54px);
  --type-section:clamp(25px,3vw,34px);
  --type-feature:clamp(20px,2vw,26px);
  --type-card:clamp(17px,1.5vw,20px);
  --type-body-lead:clamp(17px,1.25vw,19px);
  --type-body:16px;
  --type-utility:14px;
  --type-meta:12px;
}
[data-palette="DARK"] {
  --bg:#101113; --bg-2:#0a0b0c; --surf-1:#191b1e; --surf-2:#1e2124; --surf-3:#282b2f;
  --line:#26292d; --line-2:#343840; --line-3:#4a4638; --line-4:#6f5c31;
  --text:#f2f0ec; --text-2:#d3d0ca; --text-3:#b0aca4; --text-4:#918d85; --text-5:#75716a;
  --accent:#f5a623; --accent-hot:#ffc95c; --on-accent:#14100a;
  --ok-bg:#16241a; --ok-fg:#93cb96; --ok-line:#33513a;
  --warn-bg:#251d17; --warn-fg:#dba585; --warn-line:#57422f;
  --info-bg:#1a1f2b; --info-fg:#a4b2d0; --info-line:#343e57;
  --bad:#d6482a; --good:#57a05c;
  --scrim:linear-gradient(180deg,rgba(8,9,10,0) 38%,rgba(8,9,10,.85) 100%);
}
[data-palette="LIGHT"] { /* inherits the :root light values; kept named so the switch is explicit */ }

/* THE STAGE. Re-declares the same names with dark values, so any component inside renders
   dark with no component-level change. Hybrid only: in DARK the page already is this, and in
   LIGHT the whole point is that it is not. */
[data-palette="HYBRID"] .cine, [data-palette="HYBRID"] .cine * { --_stage:1 }
[data-palette="HYBRID"] .cine {
  --bg:#101113; --bg-2:#0a0b0c; --surf-1:#191b1e; --surf-2:#1e2124; --surf-3:#282b2f;
  --line:#2b2f34; --line-2:#3a3f47; --line-3:#4a4638; --line-4:#6f5c31;
  --text:#f2f0ec; --text-2:#d3d0ca; --text-3:#b0aca4; --text-4:#918d85; --text-5:#75716a;
  --accent-hot:#ffc95c; --on-accent:#14100a;
  --ok-bg:#16241a; --ok-fg:#93cb96; --ok-line:#33513a;
  --warn-bg:#251d17; --warn-fg:#dba585; --warn-line:#57422f;
  --info-bg:#1a1f2b; --info-fg:#a4b2d0; --info-line:#343e57;
  --scrim:linear-gradient(180deg,rgba(8,9,10,0) 38%,rgba(8,9,10,.85) 100%);
  background:var(--bg); color:var(--text);
  border-radius:16px; box-shadow:0 10px 30px rgba(20,16,10,.16);
}
[data-palette="HYBRID"] .cine-full{border-radius:0}

/* ---------- base ---------- */
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0;overflow-x:hidden;max-width:100%}
body{background:var(--bg);color:var(--text);
  font-family:var(--font-ui);
  font-size:var(--type-body);line-height:1.6;-webkit-font-smoothing:antialiased;padding-bottom:96px}
.wrap{max-width:430px;margin:0 auto;position:relative}
@media(min-width:900px){ .wrap{max-width:1080px} }
h1,h2,h3{margin:0}
a{color:inherit}
button{font:inherit;cursor:pointer}
.display{font-family:var(--font-display);font-weight:400;
  letter-spacing:.4px;line-height:.94;text-transform:uppercase}
.label{font-size:11px;letter-spacing:1.5px;text-transform:uppercase;color:var(--text-4)}
/* NI-2026-08-26-237 JOB D correction: promoting a section label from <div class="label"> to a
   real <h2 class="label">/<h3 class="label"> (for screen-reader heading structure) must not also
   change its rendered weight -- h1,h2,h3 above resets margin but the browser's own UA stylesheet
   still applies font-weight:bold to any heading tag, and .label never declared a weight of its
   own, so it always rendered at whatever the surrounding context's normal weight was (400) until
   now. Locking it explicitly, scoped to the tags this round's fix actually introduced, keeps
   every non-heading .label use (the "City"/"Explore" eyebrow divs, the "The move" <b> label)
   exactly as they always rendered. */
h2.label,h3.label{font-weight:400}
/* NI-2026-08-21-110 JOB D: the ONE place IBM Plex Mono is still a class consumers can reach
   for — short metadata/provenance text, e.g. an evidence timestamp or a raw fact string. Real
   body/UI copy never uses this class. */
.mono-s{font-size:var(--type-meta);color:var(--text-4);font-family:var(--font-mono)}
.sect{padding:20px 16px 8px}
.pad{padding:0 16px}
.note{font-size:13px;line-height:1.55;color:var(--text-4);margin:0}

/* ---------- masthead ---------- */
/* NI-2026-08-22-163 JOB A: the above-the-fold block (brand, location, headline, the ask box
   that now leads) wrapped in `.cine` — the same proven dark-stage class the billboard/event-hero
   already use — so Home opens on a calm, premium dark canvas instead of the light "house" the
   prior round deliberately kept lit (shell.css:129-137's own comment). Bottom padding only:
   every child inside already carries its own top/side spacing, this just keeps the stage from
   ending flush against the ask box's own edge. */
.home-stage{padding-bottom:16px;margin-bottom:2px}
.home-kicker{margin-bottom:6px}
/* Both .mast and .loc are used exactly once, on Home, so tightening them here is a scoped
   pacing fix — not a change to a shared component used elsewhere in the product. */
.mast{padding:9px 16px 4px;text-align:center;border-bottom:1px solid var(--line)}
.bulbs{display:flex;justify-content:center;gap:7px;margin-bottom:7px}
.bulb{width:5px;height:5px;border-radius:50%;background:var(--accent);opacity:.35}
.bulb.on{opacity:1;box-shadow:0 0 6px var(--accent)}
.brand{font-family:'Anton',Impact,sans-serif;font-size:17px;letter-spacing:3px;color:var(--text)}
.brand i{font-style:normal;color:var(--accent-hot)}

/* ---------- where and when ---------- */
.loc{display:flex;align-items:center;gap:9px;margin:8px 16px;background:var(--surf-2);
  border:1px solid var(--line-3);border-radius:12px;padding:8px 13px;font-size:13px;min-height:var(--tap)}
.loc b{color:var(--accent-hot)}
.loc .when{color:var(--text-4)}
.loc button{margin-left:auto;background:none;border:0;color:var(--accent-hot);font-size:12px;
  min-height:var(--tap);padding:0 4px}

/* ---------- ask ---------- */
.ask{display:flex;align-items:center;gap:9px;background:var(--surf-2);border:1px solid var(--line-2);
  border-radius:12px;padding:10px 12px;margin:0 16px;min-height:52px}
.ask input{flex:1;min-width:0;background:none;border:0;color:var(--text);font:inherit;font-size:14px;outline:none}
.ask input::placeholder{color:var(--text-5)}
/* the tap floor is 44px and it applies to the submit arrow too — it was 38 */
.ask .go{background:var(--accent);color:var(--on-accent);border:0;border-radius:9px;
  width:var(--tap);height:var(--tap);font-size:16px;flex:0 0 auto}

/* ---------- marquee ---------- */
/* ---------- THE BILLBOARD — Batch 1, Stage Kit + Home Marquee ----------
   Recovered, not redesigned. Surface recipe from css-v1.txt:26-35 / css-extra.txt:180 (a
   dark gradient under a top amber glow, inset glow, deep drop shadow — the actual comedy-era
   billboard, not a dark-mode token swap). Bulb layout from body.html:33-54: 22 top + 22
   bottom + 14 per side = 72, matched exactly rather than approximated. Motion from
   css-extra.txt:220-227 (bchase). HYBRID stays locked — this does not touch the palette
   tokens or `.cine`; it IS the black ground `.cine` was approximating, applied to the one
   surface the founder named: the marquee. The house (masthead, headline, everything below)
   stays lit, per doctrine. */
.billboard{position:relative;margin:0 16px 14px;padding:16px 15px 10px;border-radius:14px;
  background:
    radial-gradient(ellipse 72% 58% at 50% 0%, rgba(245,166,35,.16), transparent 70%),
    linear-gradient(180deg,#171208,#0a0806);
  box-shadow:inset 0 0 60px rgba(245,166,35,.10), 0 14px 40px rgba(0,0,0,.55);
  overflow:hidden}
.billboard .mq{margin:0;padding:6px 0 0;position:relative;z-index:1}
/* the secondary-events rail matches the hero card's 13px inset instead of .rail-s's default
   16px, so the row lines up with the poster above it rather than sitting slightly wider */
.billboard-rail{padding:0 13px 2px !important;margin-top:2px}
/* FOUNDER VISUAL-PASS ITEM 2: "read like small utility/admin cards." The 16/10 landscape ratio
   from the pacing round fixed the HEIGHT problem but, at 14px plain type with no rule, no
   kicker and no accent, a tile carried no more identity than a settings row. This keeps every
   dimension the pacing fix earned (compact, landscape, no portrait regression) and puts the
   marquee's own vocabulary back on it: a kicker line, a bigger condensed title, an amber top
   rule like a ticket stub, and a touch of lift — a mini-poster, not a list row. Still one line
   taller than before (4/3 vs 16/10 — about 22px) because a flat video-thumbnail ratio read as
   utility on its own; that's spent on identity, not spent back on height (see the regression
   guard below, re-measured, not assumed, after this change). */
.billboard-rail .tile{width:180px;box-shadow:0 5px 16px rgba(0,0,0,.4)}
.billboard-rail .tile .bb-tile{aspect-ratio:4/3}
.billboard-rail .tile .tile-b{padding:8px 10px 9px;border-top:2px solid var(--accent)}
/* TYPOGRAPHY_UI_SCALE_BENCHMARK_2026-08-21.md names this exact rail (9.5/15.5/10.5px) as the
   worked example of the anti-pattern: 9.5px meaningful copy and a card title under the 17px
   floor. Kicker/venue move into the MICRO LABEL floor (11-12px, short text only); the title
   matches every other tile's 17px STANDARD CARD TITLE floor rather than a bespoke smaller size. */
.billboard-rail .tile .w{display:block;font-size:11px;letter-spacing:1px;margin-bottom:1px}
.billboard-rail .tile .n{font-size:17px;line-height:1.12;margin:0 0 3px;letter-spacing:.2px}
.billboard-rail .tile .v{font-size:12px}
/* Same duplicate-title bug the hero fix already covered (FD-057), one layer down: the
   fallback bill already carries the act/matchup name in its own large type, so the tile body
   below it doesn't need to repeat it in 15px text right underneath — scoped to the marquee
   rail only, Worth The Trip's tiles are untouched. */
.billboard-rail .tile-img .bb-tile.bb-none + .tile-b .n{display:none}
/* the fallback bill's own category line already carries the kicker's job when there's no
   photo, so the redundant "TONIGHT"/date kicker only shows on real-photo tiles */
.billboard-rail .tile-img .bb-tile.bb-none + .tile-b .w{display:none}
/* NI-2026-08-24-218 JOB C -- majority-case desktop dead space: today's build (build-home-data.js)
   caps each horizon at 3 events, so the hero (slot 0) leaves at most 2 secondary tiles -- the
   1004px-wide stage measured with real data has 2 fixed-180px tiles using 371px (63% empty),
   left-aligned by `.rail-s`'s default flex-start with nothing to its right. `data-n` (views.js)
   is a count-aware hook scoped to `.billboard-rail` only -- Worth The Trip's and event-page's
   own bare `.rail-s`/`.tile` carry no such attribute and are untouched. Centering plus a modest,
   count-scaled width increase turns the accidental left-hugging gap into a deliberate compact
   lineup without inventing filler content. Mobile (390, below this breakpoint) keeps the
   existing horizontal-scroll behavior -- there is no dead-space problem on a narrow viewport,
   only a wide one. */
@media (min-width:768px){
  .billboard-rail[data-n]{justify-content:center}
  .billboard-rail[data-n="1"] .tile{width:min(320px,90%)}
  .billboard-rail[data-n="2"] .tile{width:min(230px,45%)}
  .billboard-rail[data-n="3"] .tile{width:min(200px,30%)}
}
/* NI-2026-08-21-110 JOB A — "Worth the Trip cards that look like blank beige boxes." The
   billboard rail already got the marquee's own warm stage recipe for its no-image fallback
   (below); every OTHER rail tile without a portrait — Worth the Trip chief among them — still
   fell through to the plain, flat `.bb-none` panel (var(--surf-2), a neutral grey/beige box)
   because the fix was scoped narrowly to `.billboard-rail` at the time ("out of scope for this
   pass," per that comment). Same recipe, now the actual TILE default rather than a billboard-
   only special case — a rail tile without a cleared portrait is one of the two states this
   product designs for, not a placeholder. */
.bb-tile.bb-none{
  background:
    radial-gradient(ellipse 70% 70% at 50% 0%, rgba(245,166,35,.15), transparent 65%),
    linear-gradient(180deg,#171208,#0a0806);
  border-color:rgba(245,166,35,.14)}
.bb-tile.bb-none .bb-rule{background:linear-gradient(90deg,transparent,var(--accent),transparent);opacity:.5}
/* NI-2026-08-25-236 JOB H — same always-dark-background fix as .bb-mono on the line below:
   this tile's background became always-dark (NI-110 JOB A, above), so the theme-tracking
   --accent-hot token resolved to the LIGHT-surface value (#8f5705, ~3.5:1) in the default
   HYBRID palette instead of the dark-stage value. Fixed light color, matching --accent-hot's
   own DARK-palette/.cine value, not a new color. */
.bb-tile.bb-none .bb-sub{color:#ffc95c;font-size:10.5px}
.bb-tile.bb-none .bb-mono{color:#f2f0ec;text-shadow:0 0 16px rgba(245,166,35,.2)}
/* the tile fallback gets the same warm-stage treatment as the hero fallback (item 3), scaled
   down — same reasoning, same recipe, kept out of the shared .bb-none rule so Worth The Trip's
   tile fallbacks (a different surface, out of scope for this pass) are untouched */
.billboard-rail .bb-tile.bb-none{
  background:
    radial-gradient(ellipse 70% 70% at 50% 0%, rgba(245,166,35,.15), transparent 65%),
    linear-gradient(180deg,#171208,#0a0806);
  border-color:rgba(245,166,35,.14)}
.billboard-rail .bb-tile.bb-none .bb-rule{background:linear-gradient(90deg,transparent,var(--accent),transparent);opacity:.5}
/* NI-2026-08-25-236 JOB H — same always-dark-background fix as the base .bb-tile.bb-none .bb-sub. */
.billboard-rail .bb-tile.bb-none .bb-sub{color:#ffc95c}
.billboard-bulbs{position:absolute;display:flex;justify-content:space-between;
  pointer-events:none;z-index:2}
.billboard-bulbs.top{top:8px;left:16px;right:16px}
.billboard-bulbs.bottom{bottom:8px;left:16px;right:16px}
.billboard-bulbs.side{flex-direction:column;top:32px;bottom:32px}
.billboard-bulbs.left{left:4px}
.billboard-bulbs.right{right:4px}
/* FOUNDER VISUAL-PASS ITEM 4 (superseded in part by NI-2026-08-23-171 JOB B below): the chase
   was correct in structure (4-group stagger, full perimeter) but read as a dim flicker — a .3
   resting opacity is nearly off, and a 17%-of-cycle "on" window (15-32%) meant most of the
   perimeter was dark most of the time. Brighter rest state, a longer and stronger "on" peak, and
   a touch more speed (1.3s -> 1.1s) so the sweep is legible as motion rather than a random
   twinkle — still four groups, still not simultaneous, so it can't read as strobing.

   NI-2026-08-23-171 JOB B — "no moving Hollywood lights before intent... once the plan is
   grounded, the billboard becomes the wow moment." That founder-visual-pass chase was always
   running, on every load, whether the user had asked Howie anything or just clicked in from
   Explore — exactly the defect this job names. The bulbs' RESTING state (no `.grounded`
   ancestor: a direct page load, a browse-in click) is now a calm, static amber glow with no
   motion at all. The animated chase — now bigger and brighter than the founder-visual-pass
   version, the actual "wow" upgrade — fires only under `.billboard.grounded`, the marker
   views.js applies exactly once per render when that render is the direct, immediate result of
   a submitted ask (H.groundedFromAsk(), intent.js). A "More" click, a direct city/team page
   visit, or reloading the same page never re-earns it. */
.billboard-bulbs span{width:6px;height:6px;border-radius:50%;flex:0 0 auto;
  background:#ffcf7a;box-shadow:0 0 8px 2px rgba(245,166,35,.55);opacity:.7}
.billboard.grounded .billboard-bulbs span{
  width:7px;height:7px;box-shadow:0 0 15px 4px rgba(245,166,35,.9);
  animation:billboard-chase-wow 1s linear infinite,billboard-ground-in .6s ease-out}
.billboard.grounded .billboard-bulbs span:nth-child(4n+1){animation-delay:0s,0s}
.billboard.grounded .billboard-bulbs span:nth-child(4n+2){animation-delay:.25s,.05s}
.billboard.grounded .billboard-bulbs span:nth-child(4n+3){animation-delay:.5s,.1s}
.billboard.grounded .billboard-bulbs span:nth-child(4n+4){animation-delay:.75s,.15s}
/* The "resolving" beat: Howie searching for the answer reads as the perimeter sweeping in and
   catching light for the first time, not a separate spinner competing for attention — then it
   settles straight into the same loop it ends on, so there is exactly one motion, not a cut
   between a "loading" animation and a "done" animation. */
@keyframes billboard-ground-in{
  0%{opacity:0;box-shadow:0 0 0 0 rgba(245,166,35,0);transform:scale(.4)}
  60%{opacity:1;transform:scale(1.15)}
  100%{opacity:1;transform:scale(1)}
}
@keyframes billboard-chase-wow{
  0%,100%{opacity:.5;box-shadow:0 0 8px 3px rgba(245,166,35,.5)}
  18%,40%{opacity:1;box-shadow:0 0 22px 7px rgba(255,207,122,1)}
  70%{opacity:.5;box-shadow:0 0 8px 3px rgba(245,166,35,.5)}
}
/* Grounded marquee/hero headline gets the same one-shot "resolving into focus" beat as the
   bulbs — the closest honest reading of "flipping-letter... while Howie resolves intent" this
   pass could ship without splitting headline text into per-character spans (a much larger,
   riskier change for a decorative flourish; left as a documented simplification, not silently
   downgraded). */
.billboard.grounded .mq-h,.billboard.grounded .bill-kick{
  animation:billboard-text-ground .5s ease-out both}
@keyframes billboard-text-ground{
  0%{opacity:0;transform:translateY(-5px);filter:blur(3px)}
  60%{opacity:1;filter:blur(0)}
  100%{opacity:1;transform:translateY(0)}
}
@media (prefers-reduced-motion: reduce){
  .billboard-bulbs span{animation:none;opacity:.85}
  .billboard.grounded .billboard-bulbs span{animation:none;opacity:.95}
  .billboard.grounded .mq-h,.billboard.grounded .bill-kick{animation:none;opacity:1;transform:none;filter:none}
}
@media (min-width:768px){
  /* FOUNDER VISUAL-PASS ITEM 1: at 1280px the padding alone wasn't the problem — the hero
     image's own aspect-ratio was uncapped (see .ev-hero .bb-hero below), so a 16/9 box at the
     ~980px content width this padding leaves scaled to 550px+ tall, a poster wall no amount of
     trimming here could fix. Tightened both: less padding, and (below) a hard cap on the hero
     itself so widening the viewport widens the stage without inflating its height. */
  .billboard{padding:20px 22px 8px}
  .billboard-bulbs.top,.billboard-bulbs.bottom{left:20px;right:20px}
}

/* THE LEAD CARD IS THE POSTER: Blueberry's own `hero` tier (already proven on event pages —
   full-width image or the designed no-image bill, with its own scrim and fallback typography)
   simply gets a vertical layout here instead of the horizontal thumbnail row, and the title
   steps up so it reads as the dominant element rather than one more list row. No new image
   system, no new fallback — the same one, sized up. */
.ev-hero.ev-img{flex-direction:column;gap:0}
.ev-hero .bb-hero{width:100%;aspect-ratio:16/9;max-height:none;border-radius:10px}
.ev-hero .ev-b{padding:9px 1px 2px}
/* FOUNDER VISUAL-PASS ITEM 1, the actual cap: at mobile widths 16/9 is already compact (~220px
   at 390px wide) so this is inert there; at desktop it's what stops a wide real photo from
   growing past a "premium widened mobile" hero into a full poster wall. object-fit:cover (base
   .bb img rule) means capping height here crops rather than squeezes, so the image never
   distorts. */
@media (min-width:768px){ .ev-hero .bb-hero{max-height:300px} }
.ev.ev-hero .n{font-size:clamp(27px,4.4vw,42px);line-height:1.02}
/* NI-2026-08-21-110 JOB A/D — the "What's worth doing" headline was a fixed 32px inline style
   with no desktop step, so it never grew past a stretched mobile size on a wide screen. This
   is the PAGE TITLE level of the type scale, mobile and desktop both. */
.home-headline{font-size:var(--type-page);padding-top:4px}
/* NI-2026-08-21-110 JOB D — the shared PAGE TITLE level: every non-home screen's own h1
   (Explore, City, Event, Set List, Saved, Me, Entity) used a fixed 26-30px inline style with
   no desktop step, one of the concrete "stretched mobile app" acceptance-test failures named
   in the typography benchmark. One class, one scale, real desktop growth. */
.page-title{font-size:var(--type-page);margin-top:4px;line-height:.98}
.section-title{font-family:var(--font-ui);font-weight:700;font-size:var(--type-section);
  text-transform:none;letter-spacing:0;line-height:1.2}
/* When the hero has no image, the designed fallback bill already carries the act name in its
   own large type (bb-mono / bb-team). Repeating it in the body below read as a duplicated
   title on first render. A real photo carries no text of its own, so the body title is kept
   in that case — this only suppresses the redundant copy, never the only copy. */
.ev-hero .bb-hero.bb-none + .ev-b .n{display:none}
/* REVIEW-2026-08-16-NI012 finding 1 -- the hero FIXTURE plate: two .tm colour/logo plates
   (the event billboard's own component, reused wholesale) side by side inside the hero's
   existing 16/9 box. The photo scrim is for legibility over photographs and would muddy the
   real team colours here, so this state opts out of it. The duplicate title line below is
   hidden exactly the way the bb-match bill's already is -- the plate itself carries the
   matchup text and is NOT aria-hidden, so it is the card's accessible name. */
.bb-fixture{display:flex;align-items:stretch;gap:0;overflow:hidden}
.bb-fixture::after{display:none}
.bb-fixture .tm{border-radius:0}
.bb-fixture .tm:first-child{border-radius:10px 0 0 10px}
.bb-fixture .tm:last-child{border-radius:0 10px 10px 0}
.bb-fixture .bill-vs-wrap{margin:0 -17px;position:relative;z-index:2}
/* NI-2026-08-16-013 JOB D -- REAL BUG FOUND VERIFYING THE HOME HERO LIVE, NOT JUST CHECKING
   THAT LOGOS LOADED: `.bb img` (line ~633, the generic full-bleed portrait rule every other
   Blueberry image on the site correctly wants: position:absolute;inset:0;width:100%) has higher
   specificity (0,1,1) than `.tm-logo` alone (0,1,0), so inside `.bb-fixture` -- which reuses the
   `.bb`/`.bb-hero` wrapper classes on purpose, to inherit the hero box's sizing -- it silently
   overrode the 44px centered-logo design with "absolutely positioned, 100% of an unpositioned
   .tm's containing block", which resolved both team logos to the EXACT SAME on-page coordinates
   (confirmed live: both images' getBoundingClientRect() were pixel-identical), one fully
   obscuring the other. `src`/`alt`/`.complete` all reported correctly for both images, which is
   exactly why "does the logo load" checks (this round's own logo-registry gate, the prior
   round's Fable review) never caught it -- the defect is POSITION, not existence. Never present
   on the event page's own `.bill-teams .tm-logo`, which sits outside any `.bb`-classed ancestor
   and was never affected. Fixed here with enough specificity to beat `.bb img` back to the
   correct, already-designed 44/64px centered-logo treatment, not a new design decision.

   NI-2026-08-24-218 JOB A -- the position/overlap fix above never addressed a second, separate
   `.bb img` property: `opacity:0` (shell.css ~line 1004), toggled to 1 only via `.bb.bb-in img`
   when H.Blueberry's own scan adds `.bb-in` to a `[data-bb]` node it created the <img> for
   (blueberry.js). The `.bb.bb-hero.bb-fixture` wrapper (views.js) carries no `data-bb` attribute
   -- its two `.tm-logo` crests are plain eager-loaded <img> markup, never scanned by Blueberry --
   so `.bb-in` never gets added and the crests stayed permanently transparent despite `src`/`alt`/
   `.complete` all reporting correctly loaded (same "position/opacity, not existence" defect class
   as the comment above). `.bb-fixture .tm-logo` already outranks `.bb img` on specificity
   (0,2,0) vs (0,1,1), so a plain `opacity:1` here is enough; `.bb img`, `.bb.bb-in img`, and
   Blueberry's own portrait fade are untouched -- this rule stays scoped to `.bb-fixture` only. */
.bb-fixture .tm-logo{position:static;inset:auto;width:44px;height:44px;object-fit:contain;
  margin:0 auto 2px;filter:drop-shadow(0 2px 4px rgba(0,0,0,.35));opacity:1}
@media (min-width:768px){ .bb-fixture .tm-logo{width:64px;height:64px} }
.ev-hero .bb-fixture + .ev-b .n{display:none}

.mq{margin:0 16px 18px;padding:12px 0 4px}
.mq-h{display:flex;align-items:baseline;padding:0 13px 8px}
.mq-h .c{font-size:11px;letter-spacing:1.4px;text-transform:uppercase;color:var(--text-4)}
.mq-h .more{margin-left:auto;font-size:12.5px;color:var(--accent-hot);min-height:var(--tap);
  display:flex;align-items:center;background:none;border:0}
.ev{display:block;width:calc(100% - 26px);margin:0 13px 10px;text-align:left;
  background:var(--surf-1);border:1px solid var(--line);border-radius:12px;padding:12px 13px;
  color:inherit;min-height:var(--tap)}
.ev .w{font-size:11px;letter-spacing:1.2px;text-transform:uppercase;color:var(--accent-hot)}
.ev .n{font-family:var(--font-display);font-size:clamp(20px,3vw,26px);letter-spacing:.4px;
  text-transform:uppercase;margin:3px 0 2px;color:var(--text)}
.ev .v{font-size:13px;color:var(--text-4)}
.ev .cta{display:inline-flex;align-items:center;gap:6px;margin-top:9px;font-size:14px;color:var(--accent-hot)}

/* ---------- rails ---------- */
.rail{margin:0 0 18px}
.rail-h{display:flex;align-items:baseline;padding:0 16px 9px}
.rail-h h2{font-family:var(--font-display);font-size:var(--type-section);text-transform:uppercase;letter-spacing:.4px}
.rail-s{display:flex;gap:11px;overflow-x:auto;padding:0 16px 4px;scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;scrollbar-width:none}
.rail-s::-webkit-scrollbar{display:none}
.tile{flex:0 0 auto;width:208px;scroll-snap-align:start;background:var(--surf-1);
  border:1px solid var(--line);border-radius:12px;padding:12px;text-align:left;color:inherit;min-height:var(--tap)}
.tile .w{font-size:11px;letter-spacing:1.2px;text-transform:uppercase;color:var(--accent-hot)}
.tile .n{font-family:var(--font-display);font-size:17px;text-transform:uppercase;margin:4px 0 3px}
.tile .v{font-size:12px;color:var(--text-4)}
/* NI-2026-08-16-012 JOB F.3 -- Worth the Trip's reason signal, quieter than the venue line
   (a supporting fact, not the headline) but still readable -- present only when a card
   actually carries e.reason (tripReason() in build-home-data.js), so this never renders an
   empty line for cards with no supportable signal. */
.tile .reason{font-size:11px;color:var(--accent-hot);margin-top:2px;font-weight:600}
/* NI-2026-08-26-245 JOB 2 -- the national occasion radar reuses the existing full `.ev` card
   (event/city pages already ship it), not `.tile`, so it needs its own `.reason` rule -- `.ev`
   never carried a reason field before this round. `.radar-lbl` reuses the exact micro-label
   type recipe `.asked-k` already established (9.5px/uppercase/letter-spaced/--text-4) rather
   than inventing a second one. */
.ev .reason{font-size:11px;color:var(--accent-hot);margin-top:2px;font-weight:600}
.radar-lbl{font-size:9.5px;letter-spacing:1.4px;text-transform:uppercase;color:var(--text-4);font-weight:600}
/* Single column (the existing `.ev` block-stacking, unchanged) at mobile so one radar item's
   WHY NOW / WHY THIS / CTA genuinely fits the first useful viewport after the heading, per the
   director synthesis's own 390 acceptance line. A 2-column grid at desktop -- the same
   min-width:900px breakpoint and 1fr/1fr column pattern `.grid2` already establishes elsewhere
   in this file, reused rather than a third layout system invented -- so 1440 uses width for
   comparing a small curated set, never one more equal-weight rectangle wall. */
@media(min-width:900px){
  .trip-radar-list{display:grid;grid-template-columns:1fr 1fr;gap:0 14px}
  .trip-radar-list .ev{width:auto;margin:0 0 14px}
}

/* ---------- chips ---------- */
.chips{display:flex;gap:8px;flex-wrap:wrap;padding:0 16px}
.chip{background:var(--surf-2);border:1px solid var(--line-2);color:var(--text-2);
  border-radius:999px;padding:0 14px;font-size:12.5px;min-height:var(--tap);
  white-space:nowrap;transition:background .12s ease,border-color .12s ease,color .12s ease}
/* BATCH 3, ITEM 2 (2026-08-14): selected used to be one shade of the same neutral surface —
   legible if you compared two chips side by side, easy to miss at a glance while scrolling a
   19-category row. Selected now takes the same accent fill the primary Book button and the
   active category already use elsewhere in the product, so "this is the one that's on" reads
   without having to compare it to its neighbours. */
.chip.on{background:var(--accent);border-color:var(--accent);color:var(--on-accent);font-weight:600}
.chip:active{transform:scale(.97)}
/* NINETEEN CATEGORIES WRAPPED TO FIVE ROWS pushed the requested results 400px down the page,
   which is the FD-042 failure wearing a different hat: the user asked for Italian and got a
   wall of furniture first. One scrolling row keeps every category reachable and costs 44px.
   The scroller is the element that overflows, never the body — qa-devices asserts that. */
.chips.row{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
  -webkit-overflow-scrolling:touch;scroll-snap-type:x proximity;scroll-padding-left:16px}
.chips.row::-webkit-scrollbar{display:none}
.chips.row .chip{flex:0 0 auto;scroll-snap-align:start}
/* TRAVELER ROUND, OBJECTIVE 4D (2026-08-14): a curated lead of 7 plus a "More" reveal reads
   as a chosen shortlist; a dashed, muted style keeps the expand control from competing with
   the real categories around it as one of the choices. The revealed row (#catrow-more) is
   the same .chips.row component, just a second one — no new pattern, and it gets its own
   scroll room rather than crowding the lead row's snap points. */
.chip-more{background:none;border:1px dashed var(--line-3);color:var(--text-4)}
.chip-more-n{color:var(--text-5);margin-left:2px}
#catrow-more{margin-top:8px}
/* CAUGHT ON SCREENSHOT, NOT ASSUMED WORKING (rule 0b): the `hidden` attribute's implicit
   display:none loses to `.chips{display:flex}` — a class selector in the author stylesheet
   beats the attribute-driven user-agent default regardless of which comes first, so the
   "more" row was rendering wide open the whole time, defeating the entire point of the
   reveal. A same-specificity-class-of-problem to the tap-target bug from the prior round:
   both were caught only by looking at what actually painted. */
.chips[hidden]{display:none}

/* ---------- cards ---------- */
/* ---------- HOWIE ASKS ----------
   The conversational module. It reads as Howie speaking, not as a form: a spoken question in
   sentence case, answers as tappable chips, and nothing else competing for attention. */
.nbq{margin:16px;padding:14px 14px 12px;border-radius:14px;background:var(--surf-2);
  border:1px solid var(--line-3)}
.nbq-h{display:flex;align-items:center;gap:8px;margin-bottom:7px}
.nbq-badge{font-size:9.5px;letter-spacing:2px;text-transform:uppercase;color:var(--on-accent);
  background:var(--accent);border-radius:999px;padding:3px 9px;font-weight:600}
.nbq-q{margin:0 0 11px;font-size:var(--type-body-lead);line-height:1.45;color:var(--text)}
.nbq-chips{display:flex;flex-wrap:wrap;gap:8px}
.nbq-c{background:var(--surf-1);border:1px solid var(--line-2);color:var(--text-2);
  border-radius:999px;padding:0 14px;min-height:var(--tap);font-size:12.5px;white-space:nowrap;
  transition:border-color .14s ease,color .14s ease}
.nbq-c:active{transform:scale(.97)}
@media (hover:hover){.nbq-c:hover{border-color:var(--line-4);color:var(--text)}}
.nbq-out:not(:empty){margin-top:12px}
.nbq-picks{display:flex;flex-direction:column;gap:7px}
.nbq-p{display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px;position:relative;width:100%;text-align:left;min-height:var(--tap);
  background:var(--surf-1);border:1px solid var(--line-2);border-radius:11px;padding:9px 37px 9px 12px;
  color:var(--text)}
.nbq-pn{font-family:'Anton',Impact,sans-serif;font-size:14px;text-transform:uppercase;
  letter-spacing:.3px;flex:1 0 auto;min-width:0;max-width:100%;overflow-wrap:normal;word-break:normal}
.nbq-pm{font-size:11px;color:var(--text-4);margin-left:auto;max-width:100%;white-space:normal}
.nbq-p svg{position:absolute;right:12px;top:50%;transform:translateY(-50%);color:var(--accent-hot)}
.nbq-none{margin:0;font-size:12.5px;line-height:1.5;color:var(--text-4)}

/* ---------- THE SPORTS BILLBOARD — Batch 2: Sports Event Gold Standard ----------
   BATCH 2 ITEM 1: the prior treatment was correct in structure (two club plates, no logos,
   driven entirely by e.matchup) and explicitly named "the floor, not the final result" — a
   light var(--bg-2) strip with two small colour chips read as a caption, not a marquee.
   This reuses Batch 1's actual dark-stage recipe wholesale (`.billboard`'s own background,
   glow and shadow, `billboardBulbs()`'s own 72-bulb perimeter helper, same chase animation) —
   REUSE, not reinvention, exactly as instructed. `.bill.billboard` overrides only what has to
   differ for a full-bleed event-page hero instead of a home-page card: no side margin, no
   corner radius, so it reads as the top of the page rather than a floating panel — matching
   how the comedy/music hero (H.Blueberry's `.bb-hero`) is already full-bleed on this same
   page. Still driven entirely by e.matchup; any league with a registry entry gets this free. */
.bill.billboard{margin:0;border-radius:0;padding:20px 16px 22px}
/* NI-2026-09-18 (FD-126 follow-up): founder-supplied team hero image. Same full-bleed billboard
   ground as the other heroes on this page, capped so a tall/portrait-shaped source image never
   pushes the real page content (schedule, actions) far below the fold -- the exact class of
   defect the college-card height fix caught this same morning. */
.team-hero-override-img{max-width:720px;margin:0 auto;max-height:56vh;overflow:hidden;border-radius:14px;
  box-shadow:0 18px 44px rgba(0,0,0,.4)}
.team-hero-override-img img{width:100%;height:100%;max-height:56vh;object-fit:cover;object-position:50% 20%;display:block}
/* FD-123: pair the portrait billboard with the decision on desktop. The full
   mobile portrait and stack stay untouched; desktop keeps the complete artwork. */
@media (min-width:900px){
  #app .q38-entity-heading:has(> .team-hero-override){
    display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);
    grid-template-rows:1fr auto auto auto 1fr;column-gap:48px;row-gap:16px;
    padding-top:28px!important;padding-bottom:28px;
  }
  .q38-entity-heading > .team-hero-override{grid-column:1;grid-row:1 / 6;min-width:0}
  .q38-entity-heading:has(> .team-hero-override) > .page-title{grid-column:2;grid-row:2;margin:0;overflow-wrap:anywhere}
  .q38-entity-heading:has(> .team-hero-override) > .q38-run-dates{grid-column:2;grid-row:3;margin:0}
  .q38-entity-heading:has(> .team-hero-override) > .q38-entity-primary{grid-column:2;grid-row:4;justify-self:start;margin:0}
  .q38-entity-heading .team-hero-override-img img{object-fit:contain}
}
/* NI-2026-08-25-236 JOB H — .bill.billboard's own gradient ground (shared with .billboard,
   146-below) is always dark by construction regardless of the page's light/dark palette, and
   this hero sits outside .cine's scope on the event page. var(--accent-hot) therefore resolved
   to the theme-tracked LIGHT-surface value (#8f5705) here, measured 3.53:1 against the real
   rendered dark backdrop -- below the 4.5:1 AA small-text floor. Fixed light colour, matching
   --accent-hot's own DARK-palette/.cine value (same colour, not a new one) -- the same
   always-dark-background fix already established elsewhere on this page (.bb-none .bb-mono
   etc). Kept distinct from the separate .bb-match .bb-team fallback contrast defect, fixed
   alongside it in the same pass, below. */
.bill-kick{font-size:10.5px;letter-spacing:1.9px;text-transform:uppercase;color:#ffc95c;
  text-align:center;position:relative;z-index:1}
/* NI-2026-08-22-163 JOB C -- margin:auto added. Below max-width (mobile, most of tablet) this
   is a no-op: width:100% already consumes the full row, nothing to center. Past 560px available
   width (desktop) it previously had no rule centering it at all, so the fixture hero's own two-
   team plate has been rendering flush-LEFT against a 1080px-wide dark stage this whole time --
   found while giving the team-hero plate (below) the same base-class width instead of its own
   narrower inline override, and confirmed live (Playwright, 1440x900): teamsLeft=206px,
   teamsRight=494px pre-fix, i.e. genuinely off-center, not merely visually close. Centering here
   fixes both callers identically from one shared rule, matching how they already share every
   other .bill-teams/.tm property. */
.bill-teams{display:flex;align-items:stretch;gap:0;width:100%;max-width:560px;margin:0 auto;position:relative;z-index:1}
.tm{flex:1 1 0;min-width:0;padding:16px 12px;text-align:center;
  background:linear-gradient(165deg,var(--tc1,#26292d) 0%,var(--tc2,#15171a) 195%);
  display:flex;flex-direction:column;justify-content:center;gap:3px;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.12)}
.tm-h{border-radius:12px 4px 4px 12px}
.tm-a{border-radius:4px 12px 12px 4px}
.tm-city{font-size:9.5px;letter-spacing:1.8px;text-transform:uppercase;color:rgba(255,255,255,.68)}
.tm-name{font-family:'Anton',Impact,sans-serif;font-size:clamp(21px,6.4vw,34px);line-height:1;
  text-transform:uppercase;color:#fff;letter-spacing:.4px;overflow-wrap:anywhere;
  text-shadow:0 2px 6px rgba(0,0,0,.45)}
/* NI-2026-08-15-010 JOB A — the logo sits ABOVE the existing city/name text, never replacing
   it, so a broken or slow-loading image degrades to exactly the colour-plate look this already
   was — the fallback is the unmodified pre-existing design, not a new empty state to build.
   NI-2026-08-27-252 JOB A — founder sports visual-identity closeout: this single shared rule
   drives both the team-hub solo plate (entity-page.js's teamHero()) and the exact-matchup two-
   team plate (views.js's plate()), so doubling it here strengthens real team/matchup identity
   on both surfaces from one change, per the instruction's own "reuse, don't invent a parallel
   language" discipline. Baseline was 44px/64px (measured live pre-change: `before-*-390.png`/
   `before-*-1440.png` in qa/screenshots/); real rendered .bill-teams height only grows by the
   same delta as the logo (124px->~168px at 390, 173px->~237px at 1440 — see
   verify-sports-identity-visual-weight.js), which live measurement against both real routes
   confirmed still leaves matchup/team name, date/time/venue and the primary next step above the
   390 fold, so no reduced/fallback multiplier was needed. `.bb-fixture .tm-logo` (the separate,
   denser home-marquee fixture variant, shell.css ~line 382) is deliberately untouched — it is a
   city/browse card, outside this round's team-hub/exact-matchup scope. */
.tm-logo{width:88px;height:88px;object-fit:contain;margin:0 auto 2px;
  filter:drop-shadow(0 2px 4px rgba(0,0,0,.35))}
@media (min-width:768px){ .tm-logo{width:128px;height:128px} }
/* THE FIGHT-POSTER VS: a stacked badge between the two plates instead of a stray word — the
   central, high-contrast mark the founder asked for, on the same amber the bulbs already use
   so it reads as part of the same lit system, not a third colour. */
.bill-vs-wrap{flex:0 0 auto;display:flex;align-items:center;z-index:1}
/* NI-2026-08-25-236 JOB H — same always-dark-background fix as .bill-kick above: this badge's
   own background (#0a0806) is a fixed dark value, not theme-tracked, so var(--accent-hot) was
   the same bug, measured 3.36:1. */
.bill-vs{width:34px;height:34px;border-radius:50%;background:#0a0806;
  border:2px solid var(--accent);box-shadow:0 0 14px 2px rgba(245,166,35,.5);
  display:flex;align-items:center;justify-content:center;
  font-family:'Anton',Impact,sans-serif;font-size:11px;letter-spacing:1px;
  text-transform:uppercase;color:#ffc95c}
/* TRAVELER ROUND, OBJECTIVE 4A: same class as .bb-hero.bb-none's title fix above — this sits
   on .billboard's own always-dark gradient, not a themed surface, so it needs a colour tied
   to THAT background rather than the page's current light/dark palette. */
.bill-venue{font-size:12px;letter-spacing:.4px;color:rgba(255,255,255,.7);text-align:center;
  position:relative;z-index:1;margin-top:2px}
@media (min-width:768px){
  .bill.billboard{padding:30px 26px 30px}
  .tm{padding:26px 18px}
  .bill-vs{width:44px;height:44px;font-size:13px}
}

/* THE MOVE — restored personality, gated to real data (see views.js: only renders above a
   genuinely top-tier pick). A small amber kicker, not a badge on the card itself, so it reads
   as Howie's own voice introducing the pick rather than another data-sourced credential like
   RECOMMENDED BY. */
.move-tag{font-family:'Anton',Impact,sans-serif;font-size:12px;letter-spacing:1px;
  text-transform:uppercase;color:var(--accent-hot);padding:0 16px 6px}

/* EAT AT THE GAME — the reserved stadium-food slot (RECOVERY_PLAN Batch 2). A visibly held
   position with an honest "Coming" chip, not a fake menu: bordered like a real module so the
   page grammar reads pregame → eat at the game → postgame, dashed so nobody mistakes it for
   filled content. */
.eatgame{margin:14px 16px 0;padding:12px 14px;border:1px dashed var(--line-3);border-radius:12px}
.eatgame .label{display:flex;align-items:center;gap:8px}
.eatgame-chip{font-size:9px;letter-spacing:1.2px;padding:2px 7px;border-radius:999px;
  background:var(--warn-bg);color:var(--warn-fg);border:1px solid var(--warn-line)}
.eatgame .note{margin-top:6px}
/* NI-009 AMENDMENT (Citi Field in-venue food layer) — a filled module (real registry content)
   drops the dashed "reserved slot" border for a solid one, same radius/spacing as the
   placeholder so the page grammar doesn't jump when a venue gets real content. */
.eatgame[data-state="ready"]{border-style:solid}
.eatgame-list{margin:8px 0 0;padding-left:18px}
.eatgame-list li{margin:4px 0;line-height:1.4}


/* ---------- quick actions ----------
   Save, Share, Plan — under the billboard, above any recommendation, because the founder's
   order is hero, then actions, then content. */
.qa{display:flex;gap:9px;padding:12px 16px 2px}
.qa-b{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;min-height:var(--tap);
  background:var(--surf-1);border:1px solid var(--line-2);border-radius:11px;color:var(--text-2);
  font-size:12.5px;transition:border-color .14s ease,color .14s ease}
.qa-b:active{transform:scale(.98)}
.qa-b.on{color:var(--accent-hot);border-color:var(--line-4)}

/* ---------- global header ----------
   Sticky and compact on mobile; the bottom nav stays. On desktop it widens into a real top
   navigation rather than a phone bar stretched across a monitor. */
.ghead{position:sticky;top:0;z-index:40;display:flex;align-items:center;gap:10px;
  padding:0 12px;min-height:52px;background:var(--surf-1);
  border-bottom:1px solid var(--line);backdrop-filter:saturate(1.2) blur(8px)}
.gh-back,.gh-act{background:none;border:0;color:var(--text-2);display:flex;align-items:center;
  justify-content:center;min-width:var(--tap);min-height:var(--tap);border-radius:10px}
.gh-back:active,.gh-act:active{background:var(--surf-3)}
.gh-brand{background:none;border:0;font-family:'Anton',Impact,sans-serif;font-size:14px;
  letter-spacing:1.1px;color:var(--text);text-transform:uppercase;padding:0;white-space:nowrap}
.gh-brand i{font-style:normal;color:var(--accent-hot)}
/* NI-2026-08-22-163 JOB F — .gh-ctx is a real <button> now (it opens the location overlay), so
   it needs the usual button resets on top of its prior span styling; the visible look is
   unchanged, min-height keeps it inside the tap-target floor even though its own box is text-sized. */
.gh-ctx{flex:1;min-width:0;font-size:11.5px;letter-spacing:1.1px;text-transform:uppercase;
  color:var(--text-4);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:right;
  background:none;border:0;font-family:inherit;min-height:var(--tap);padding:0}
.gh-act:last-child{margin-right:-4px}
@media (min-width:768px){
  .ghead{min-height:62px;padding:0 24px;gap:16px}
  .gh-brand{font-size:18px;letter-spacing:1.6px}
  .gh-ctx{text-align:left;font-size:12.5px;margin-left:4px}
}

/* ---------- global search overlay ----------
   NI-2026-08-20-087. The one reusable search entry every non-Home header's search icon opens
   instead of routing straight to Explore. Sits above everything (tabs, plan bar, header) so
   while it is open the only reachable controls are its own — closing it (backdrop tap, the
   X, or Escape) is the sole way back, never an accidental click-through to a stale surface.
   `.search-panel .ask` reuses `.ask`'s own input styling untouched — one input shape, not two. */
.search-overlay{position:fixed;inset:0;z-index:200;display:flex;justify-content:center}
/* `.search-overlay{display:flex}` above and the UA `[hidden]{display:none}` rule are equal
   specificity — an author rule always wins that tie, so without this the backdrop stayed laid
   out (and kept intercepting clicks) even while the `hidden` attribute was set. Caught live: a
   Playwright click on the still-hidden overlay's own trigger button timed out because the
   invisible backdrop sat on top of it. */
.search-overlay[hidden]{display:none}
.search-backdrop{position:absolute;inset:0;background:rgba(10,8,6,.5)}
.search-panel{position:relative;width:100%;max-width:560px;margin:64px 16px 0;
  background:var(--surf-1);border:1px solid var(--line);border-radius:14px;
  box-shadow:0 12px 32px rgba(20,16,10,.28);padding:14px}
.search-panel-h{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;
  font-size:12px;letter-spacing:1px;text-transform:uppercase;color:var(--text-4)}
.search-x{background:none;border:0;color:var(--text-2);font-size:22px;line-height:1;
  min-width:var(--tap);min-height:var(--tap);border-radius:10px}
.search-x:active{background:var(--surf-3)}
.search-panel .ask{margin:0}
body.search-open{overflow:hidden}
body.search-open #tabs,
body.search-open .planbar{pointer-events:none}
@media (min-width:768px){
  .search-panel{margin-top:88px}
}

/* ---------- location-change overlay ----------
   NI-2026-08-22-163 JOB F. Same shell as the search overlay directly above (backdrop, panel,
   panel-h, body.search-open reused for the shared overflow-lock — one open modal at a time is
   the only real state either can be in, so one lock class is correct, not two). `.loc-field
   input` deliberately does NOT reuse `.ask` — `.ask` implies "submit this text as a free-form
   query," and this input's only valid outcome is picking a REAL result from `.loc-results`,
   never submitting arbitrary typed text as if it were a city. */
/* align-items:flex-start is the same content-sized-panel rule signup-overlay already uses:
   without it a flex item with no explicit height stretches to the full 100vh overlay, which
   is the founder-facing "large Where are you? sheet" — a near-full-screen panel whose only
   obvious action reads as Use my location. Search stays first; the panel sizes to its field. */
.loc-overlay{position:fixed;inset:0;z-index:220;display:flex;justify-content:center;align-items:flex-start}
.loc-overlay[hidden]{display:none}
.loc-backdrop{position:absolute;inset:0;background:rgba(10,8,6,.5)}
.loc-panel{position:relative;width:100%;max-width:480px;margin:64px 16px 0;max-height:calc(100vh - 96px);
  overflow-y:auto;background:var(--surf-1);border:1px solid var(--line);border-radius:14px;
  box-shadow:0 12px 32px rgba(20,16,10,.28);padding:14px}
.loc-panel-h{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;
  font-size:12px;letter-spacing:1px;text-transform:uppercase;color:var(--text-4)}
.loc-x{background:none;border:0;color:var(--text-2);font-size:22px;line-height:1;
  min-width:var(--tap);min-height:var(--tap);border-radius:10px}
.loc-x:active{background:var(--surf-3)}
.loc-field{background:var(--surf-2);border:1px solid var(--line-2);border-radius:12px;
  padding:2px 12px;margin-bottom:10px}
.loc-field input{width:100%;background:none;border:0;color:var(--text);font:inherit;
  font-size:14px;min-height:48px;outline:none}
.loc-field input::placeholder{color:var(--text-5)}
/* Deliberately below the input, smaller and quieter — "secondary, not the only visible action"
   is a founder-named requirement, not a style preference. */
.loc-useme{display:flex;align-items:center;gap:8px;width:100%;background:none;
  border:1px solid var(--line-3);border-radius:10px;padding:9px 12px;margin-bottom:10px;
  color:var(--text-2);font-size:13px;min-height:var(--tap)}
.loc-useme:active{background:var(--surf-3)}
.loc-status{font-size:12.5px;color:var(--text-3);margin-bottom:10px;line-height:1.4}
.loc-recent-label{font-size:11px;letter-spacing:1px;text-transform:uppercase;
  color:var(--text-5);margin:2px 0 6px}
.loc-result{display:flex;align-items:center;gap:9px;width:100%;background:none;border:0;
  border-bottom:1px solid var(--line-2);padding:11px 4px;color:var(--text);font-size:14px;
  text-align:left;min-height:var(--tap)}
.loc-result:last-child{border-bottom:0}
.loc-result:active{background:var(--surf-2)}
.loc-result svg{flex:0 0 auto;color:var(--text-4)}
.loc-empty{color:var(--text-4);font-size:13px;line-height:1.5;padding:10px 4px}
@media (min-width:768px){
  .loc-panel{margin-top:88px}
}

/* ---------- planning horizons ----------
   NI-2026-08-24-207 JOB A/D. Reuses `.chip` untouched — one chip shape, not two — just a new
   row of them under the ask box. */
.horizon-chips{display:flex;gap:8px;flex-wrap:wrap;padding:10px 16px 0}
.horizon-chip{font-size:12px;min-height:var(--tap)}
@media (max-width:430px){
  .horizon-chips{display:grid;grid-template-columns:1fr 1fr;gap:8px}
  .horizon-chip{width:100%;justify-content:center;text-align:center}
}

/* ---------- planning moment (anticipation) ----------
   NI-2026-08-24-207 JOB B. Same fixed-overlay shell as the search/location overlays, one tier
   above them (z-index) since it can appear while either of those has just closed. No invented
   progress bar or step list — three pulsing dots and one honest line, nothing this product
   cannot actually back with real work happening underneath it. */
.planning-moment{position:fixed;inset:0;z-index:210;display:flex;align-items:center;
  justify-content:center;background:rgba(10,8,6,.72)}
.planning-moment[hidden]{display:none}
.planning-moment-inner{position:relative;text-align:center;padding:26px 30px;border-radius:14px;
  background:linear-gradient(180deg,#171208,#0a0806);box-shadow:0 12px 32px rgba(20,16,10,.4);
  min-width:200px}
.pm-dots{display:flex;gap:6px;justify-content:center;margin-bottom:12px}
.pm-dots span{width:8px;height:8px;border-radius:50%;background:var(--accent-hot);
  animation:pm-bounce 1s ease-in-out infinite}
.pm-dots span:nth-child(2){animation-delay:.15s}
.pm-dots span:nth-child(3){animation-delay:.3s}
@keyframes pm-bounce{0%,80%,100%{opacity:.35;transform:translateY(0)}40%{opacity:1;transform:translateY(-4px)}}
.pm-line{font-size:13px;letter-spacing:.3px;color:#f3ece1;margin:0}
@media (prefers-reduced-motion:reduce){
  .pm-dots span{animation:none;opacity:.9}
}

/* ---------- save-your-plan overlay ----------
   NI-2026-08-24-207 JOB C. Same shell as the search/location overlays (backdrop, panel,
   panel-h, body.search-open reused for the shared overflow-lock). */
/* align-items:flex-start is deliberate, not the flex default -- without it a flex item with no
   explicit height stretches to the container's full cross-axis extent (here, the whole 100vh
   overlay), which is what made this panel render as a near-full-screen white rectangle with
   its real content pinned to the top rather than a panel sized to its own content. */
.signup-overlay{position:fixed;inset:0;z-index:205;display:flex;justify-content:center;align-items:flex-start}
.signup-overlay[hidden]{display:none}
.signup-backdrop{position:absolute;inset:0;background:rgba(10,8,6,.5)}
.signup-panel{position:relative;width:100%;max-width:420px;margin:96px 16px 0;
  background:var(--surf-1);border:1px solid var(--line);border-radius:14px;
  box-shadow:0 12px 32px rgba(20,16,10,.28);padding:18px}
.signup-panel-h{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;
  font-size:12px;letter-spacing:1px;text-transform:uppercase;color:var(--text-4)}
.signup-x{background:none;border:0;color:var(--text-2);font-size:22px;line-height:1;
  min-width:var(--tap);min-height:var(--tap);border-radius:10px}
.signup-x:active{background:var(--surf-3)}
.signup-sub{font-size:12.5px;color:var(--text-3);line-height:1.5;margin:0 0 12px}
.signup-go{width:100%;min-height:var(--tap)}
@media (min-width:768px){
  .signup-panel{margin-top:120px}
}

/* BATCH 3 CARD QUALITY PASS (2026-08-14). Same fields as before, restructured so the card
   reads as a scanned recommendation, not a record: a soft lift instead of a flat outline, a
   badge row instead of a floated distance number, an editorial quote for the why line, a
   quiet "trust" row for evidence instead of a bordered pill, one line for the difficulty
   estimate instead of a dashed form box. */
.card{background:var(--surf-1);border:1px solid var(--line);border-radius:14px;
  margin:0 16px 12px;overflow:hidden;position:relative;
  box-shadow:0 1px 2px rgba(20,16,10,.04),0 6px 16px rgba(20,16,10,.05)}
.card-top{padding:13px 14px 0;padding-right:52px}
.lodging-card .card-top{padding-right:14px}
.lodging-note{margin:0 16px 10px}
.card-badges{display:flex;align-items:center;gap:6px;margin-bottom:7px;flex-wrap:wrap}
.badge-mi{display:inline-flex;align-items:center;gap:3px;font-size:10.5px;letter-spacing:.3px;
  color:var(--text-4);background:var(--surf-2);border:1px solid var(--line-2);
  border-radius:6px;padding:3px 8px 3px 6px}
.badge-mi svg{width:10px;height:10px;flex:0 0 auto}
/* TRAVELER ROUND, OBJECTIVE 2 (2026-08-14) — PROTOTYPE. Deliberately NOT styled like `.tier`
   (which is Howie's own quality call — Bucket List, Best Realistic) or `.badge-mi` (a
   measured fact) — this is a third, distinct claim: regional scarcity. A thin accent
   outline on the accent colour itself (no fill) reads as "worth noticing" without
   competing with the tier pill for the eye's first stop on the card. */
.badge-traveler{display:inline-flex;align-items:center;font-size:10px;letter-spacing:.6px;
  text-transform:uppercase;font-weight:600;color:var(--accent-hot);
  background:none;border:1px solid var(--accent);border-radius:6px;padding:3px 8px}

/* NI-2026-08-15-009 JOB D — same visual weight and shape as .badge-traveler immediately
   above (a distinct fact about the place, not a quality claim), deliberately not identical
   in color so a "Top Grossing" badge and a "Traveler Institution" badge never read as the
   same kind of fact when both happen to appear together. */
.badge-topgrossing{display:inline-flex;align-items:center;font-size:10px;letter-spacing:.6px;
  text-transform:uppercase;font-weight:600;color:var(--text-3);
  background:none;border:1px solid var(--line-3);border-radius:6px;padding:3px 8px}

/* NI-2026-08-16-016 JOB B — D-40. Deliberately the ONLY badge that uses --bad (the same
   theme-aware negative-state color .timing.no already uses) — every other badge on this card
   is a neutral fact or a quality claim; this one is the single "do not treat this as
   currently bookable" signal and needs to read as different at a glance, in both themes. */
.badge-closed{display:inline-flex;align-items:center;font-size:10px;letter-spacing:.4px;
  font-weight:600;color:var(--bad);
  background:var(--warn-bg);border:1px solid var(--bad);border-radius:6px;padding:3px 8px}

/* ---------- the save heart ----------
   Outline unsaved, filled saved. Top-right of every place card, same position every time, so
   the gesture is learned once. */
.sv{position:absolute;top:8px;right:8px;z-index:2;background:var(--surf-1);border:1px solid var(--line-2);
  color:var(--text-4);display:flex;align-items:center;justify-content:center;
  min-width:var(--tap);min-height:var(--tap);border-radius:50%;
  transition:color .14s ease,transform .14s ease,background .14s ease,border-color .14s ease;
  opacity:.88}
.sv:active{transform:scale(.88)}
/* NI-2026-08-16-012 JOB D.2 -- was color-only (outline vs filled icon, one accent-hot shift):
   too subtle on a small corner icon to read as an intentional state on a quick glance, mobile
   or desktop. A filled background pill on the ON state, reusing the same --accent-hot/
   --on-accent tokens the icon already used (nothing new introduced), makes saved vs unsaved a
   real background-presence difference, not only a color/scale nuance. Kept to the existing
   circular shape and existing tokens on purpose -- a state-clarity fix, not a redesign. */
.sv.on{background:var(--accent-hot);border-color:var(--accent-hot);color:var(--on-accent);opacity:1}
.sv .ic-heart{transition:transform .16s cubic-bezier(.3,1.6,.5,1)}
.sv.on .ic-heart{transform:scale(1.12)}
@media (hover:hover){.sv:hover{color:var(--text-2);border-color:var(--line-3)}.sv.on:hover{color:var(--on-accent);opacity:.88}}
/* NI-2026-08-21-110 JOB B/D — restaurant identity is a name, not a marquee title: Anton
   condensed all-caps reads as diagnostic/database on a real brand name ("UCHI"), where the
   Dining spec wants the room to feel desirable and editorial. Proportional sans, real case,
   sized to the DINING spec's own card-level target (18-22px) and above the 17px card-title
   floor either way. Anton stays reserved for event/performer/marquee identity moments only. */
.card h3{font-family:var(--font-ui);font-weight:700;font-size:clamp(19px,1.6vw,22px);
  text-transform:none;letter-spacing:0;line-height:1.2}
/* CUISINE / NEIGHBOURHOOD / PRICE, ONE SCANNABLE LINE. Price breaks out of the middot chain
   and sits right-aligned in its own weight — it is the number someone's eye jumps to, not one
   more clause in a list. */
.card .meta{display:flex;align-items:baseline;gap:0;font-size:13px;color:var(--text-4);
  margin-top:5px;letter-spacing:.1px}
.meta-price{margin-left:auto;padding-left:8px;color:var(--text-3);font-weight:600;font-size:12.5px}
/* THE WHY LINE, AS A QUOTE, NOT A DESCRIPTION FIELD. This is DINING_AUTHORITY spec's Level 2
   — "why this place for this person/occasion" — the one sentence a reader actually needs, so
   it is primary explanatory copy (14px floor) rather than metadata; bumped from 13.5px. */
.card .why{font-size:15px;line-height:1.5;color:var(--text-2);font-style:italic;
  margin:10px 0 0;padding-left:11px;border-left:2px solid var(--line-4)}
.tier{display:inline-block;font-size:10px;letter-spacing:1.1px;text-transform:uppercase;
  border-radius:6px;padding:3px 8px;font-weight:600}
/* NI-2026-08-16-012 JOB D.1 -- was the same pale outline treatment as every other tier badge
   (realistic/planb/access), so Bucket List read as ordinary metadata rather than the premium
   Howie signal it actually is. Solid gold fill using the existing --accent/--on-accent tokens
   already used for the highest-weight badge elsewhere (.evb-creator-partner) -- reuses an
   existing design primitive rather than inventing a new palette decision, per this round's own
   JOB G constraint. */
.tier.bucket{background:var(--accent);color:var(--on-accent);border:1px solid var(--accent);font-weight:700}
.tier.realistic{background:var(--ok-bg);color:var(--ok-fg);border:1px solid var(--ok-line)}
.tier.planb{background:var(--warn-bg);color:var(--warn-fg);border:1px solid var(--warn-line)}
.tier.access{background:var(--info-bg);color:var(--info-fg);border:1px solid var(--info-line)}

/* NI-2026-08-20-089 JOB E — entity-page tour schedule, compact and grouped by month.
   Replaces one full-width .card per date (a long tour like Jeff Arcuri's 54 upcoming dates
   read as a wall of oversized bubbles, mobile or desktop) with a scannable DATE | VENUE/CITY |
   ACTION row. Reuses the same tokens every other card on this page already uses -- no new
   palette decision. */
.sched-month{padding:16px 16px 6px;font-size:11px;letter-spacing:1.2px;text-transform:uppercase;
  color:var(--text-4);font-weight:700}
.sched-row{display:flex;align-items:flex-start;gap:10px;margin:0 16px;padding:10px 2px;
  border-bottom:1px solid var(--line-2)}
.sched-row:last-child{border-bottom:none}
/* NI-2026-09-18: the mini per-row thumbnail Howard asked for on entity "Upcoming" lists.
   H.Blueberry.slot()'s '.bb'/'.bb-tile' classes carry no size of their own outside the rails
   that already size them (.billboard-rail .tile .bb-tile), so this row needs its own explicit
   box; object-fit:cover on the shared .bb img rule (shell.css ~1218) does the cropping. */
.sched-thumb{flex:0 0 40px;width:40px;height:40px;align-self:flex-start}
.sched-thumb .bb{width:100%;height:100%;border-radius:8px}
/* dates get the scoped mono accent per TYPOGRAPHY benchmark's "schedule columns" rule — the
   rest of this row (venue, city, action) is proportional sans (unchanged, already inherited). */
.sched-date{flex:0 0 46px;font-family:var(--font-mono);font-size:12px;font-weight:600;
  color:var(--text-3);line-height:1.35;padding-top:1px}
.sched-body{flex:1 1 auto;min-width:0}
.sched-where{font-size:13.5px;font-weight:600;color:var(--text);line-height:1.3}
.sched-city{font-size:12px;color:var(--text-4);margin-top:1px}
.sched-ticket{display:inline-block;margin-top:4px;font-size:11.5px;font-weight:600;color:var(--accent-hot)}
.sched-not-plannable{font-size:11.5px;color:var(--text-4);margin-top:4px}
.sched-action{flex:0 0 auto;font-size:12px;font-weight:600;color:var(--accent-hot);padding-top:1px;
  white-space:nowrap}
.sched-row[data-plannable="false"]{cursor:default}
/* NI-236 LIVE EVIDENCE ADDENDUM 10 — team-hub Upcoming previously gave the very next game the
   same byte-identical visual weight as a game months away. Additive only: the soonest real
   future date (index 0 of an already-chronological, future-only list) gets a quiet accent
   border and a plain chronological "Next up" fact-badge, same shape/weight as .badge-traveler
   but its own color so it never reads as a quality claim. Every other row is unchanged. */
.sched-row-next{border-left:3px solid var(--accent-hot);margin-left:13px;padding-left:5px}
.badge-nextgame{display:inline-block;font-size:10px;letter-spacing:.6px;text-transform:uppercase;
  font-weight:700;color:var(--accent-hot);margin-bottom:3px}

/* NI-2026-08-26-245 ADDENDUM JOB 1 — team-intent trip choices (FOUNDER_TEAM_TRIP_PLANNING_
   DIRECTIVE_2026-08-26.md / FOUNDER_SPORTS_TEAM_INTENT_HOME_AWAY_VISUAL_RULE_2026-08-26.md).
   Reuses the existing --ok-* / --accent-hot tokens this page already uses (.tier.realistic,
   .sched-row-next) rather than inventing a new palette decision. HOME and AWAY never rely on
   color alone -- .trip-badge always carries its own uppercase text label; the border-left tint
   is a second, additive cue for someone scanning quickly, same pattern as .badge-nextgame. */
.trip-choices{display:flex;flex-direction:column;gap:10px;margin:0 16px 4px}
@media (min-width:760px){.trip-choices{flex-direction:row;align-items:stretch}}
.trip-choice{flex:1 1 0;min-width:0;background:var(--surf-2);border:1px solid var(--line-3);
  border-radius:12px;padding:14px 16px 16px;cursor:pointer}
.trip-home{border-left:4px solid var(--ok-line)}
.trip-away{border-left:4px solid var(--accent-hot)}
.trip-teams{gap:6px;margin:0 0 12px;max-width:none;align-items:stretch}
.trip-teams .trip-team{border-radius:8px;padding:12px 8px;gap:8px}
.trip-teams .tm-name{font-size:clamp(17px,4.5vw,23px);line-height:1.15}
.trip-teams .tm-logo{width:64px;height:64px;max-width:100%;margin:0 auto}
.trip-team-rel{align-self:center;font-size:12px;font-weight:700;color:var(--text-3)}
.trip-badge{display:inline-block;font-size:10px;letter-spacing:1px;text-transform:uppercase;
  font-weight:700;border-radius:6px;padding:3px 8px;margin-bottom:9px}
.trip-badge-home{color:var(--ok-fg);border:1px solid var(--ok-line);background:var(--ok-bg)}
.trip-badge-away{color:var(--accent-hot);border:1px solid var(--accent-hot);background:none}
.trip-opponent{font-family:'Anton',Impact,sans-serif;font-size:17px;letter-spacing:.3px;
  text-transform:uppercase;color:var(--text);line-height:1.2}
.trip-where{font-size:13px;color:var(--text-3);margin-top:4px}
.trip-when{font-size:12px;color:var(--text-4);margin-top:1px;font-family:var(--font-mono)}
.trip-value{font-size:13.5px;line-height:1.45;color:var(--text-3);margin:9px 0 0}
.trip-plan-btn{width:100%;margin-top:12px}

/* "See the full schedule" -- the season stays completely real and reachable, just collapsed
   behind one explicit control once a trip-choice hierarchy already leads the page. Same
   aria-expanded/chevron-rotate shape as .why-btn's .chev (views.js). */
.sched-toggle{display:flex;align-items:center;gap:6px;width:calc(100% - 32px);margin:4px 16px 8px;
  background:none;border:1px solid var(--line-3);border-radius:9px;padding:9px 12px;
  font-size:12.5px;font-weight:600;color:var(--accent-hot);cursor:pointer}
.sched-toggle .chev{transition:transform .15s ease;margin-left:auto;flex:0 0 auto}
.sched-toggle[aria-expanded="true"] .chev{transform:rotate(180deg)}

/* NI-2026-08-16-014 JOB B -- Broadway cards. Same .card/.card-top/.sv/.tier/.meta primitives
   every other card already uses (see the comment block above); only two new, generic, reusable
   pieces: .grid-cards (a simple wrap layout, since Broadway lanes are not a horizontal rail like
   Worth the Trip) and .card-actions/.btn-sm (a compact two-button footer row a future card type
   can reuse rather than a Broadway-only class). */
.grid-cards{display:flex;flex-direction:column}
.card-broadway .card-top{padding-bottom:12px}
/* B29, Howard-approved 2026-08-16 -- the theatre-aware nearby-restaurants line. Quiet, small,
   secondary to the title/theatre -- a fact in support of the card, not a competing headline. */
.theatre-near{font-size:13px;line-height:1.45;color:var(--text-3);margin-top:8px;
  padding-top:8px;border-top:1px solid var(--line-2)}
.theatre-near b{color:var(--text-2);font-weight:600}

/* NI-2026-08-16-015 JOB D -- What To Order V3 block on place cards. Same quiet-secondary
   treatment as .theatre-near; the small evidence tag keeps claim provenance visible without
   shouting (restaurant's own words vs menu fact vs Howie's call -- never conflated). */
.wto{margin-top:10px;padding-top:8px;border-top:1px solid var(--line-2)}
.wto-k{display:block;font-size:11px;letter-spacing:1.1px;text-transform:uppercase;
  font-weight:600;color:var(--text-3);margin-bottom:4px}
/* what-to-order lines are a consumer recommendation ("order the X"), not metadata — same
   14px primary-copy floor as .card .why. */
.wto-line{font-size:14px;line-height:1.48;color:var(--text-2);margin:0 0 4px}
.wto-tag{display:inline-block;margin-left:6px;font-size:10.5px;color:var(--text-4);
  border:1px solid var(--line-2);border-radius:5px;padding:1px 5px;white-space:nowrap}
.card-actions{display:flex;gap:8px;padding:0 14px 14px}
.btn-sm{flex:1;display:flex;align-items:center;justify-content:center;gap:5px;
  font-size:12.5px;font-weight:600;letter-spacing:.2px;text-decoration:none;
  padding:9px 10px;border-radius:9px;border:1px solid var(--line-2);
  background:var(--surf-2);color:var(--text-2);min-height:var(--tap);box-sizing:border-box}
.btn-sm svg{width:13px;height:13px}
@media (hover:hover){.btn-sm:hover{border-color:var(--line-3);color:var(--text-1)}}

/* THE TRUST ROW. Evidence used to sit under a free-floating pill button that did nothing but
   log a tap; it now reads as one quiet row — a small-caps label doing double duty as the same
   tappable control (.js-why, unchanged), the badges immediately beside it, a hairline above
   to separate it from the editorial copy it is backing up. Six evidence classes stay exactly
   as visually distinguishable as before. */
.trust{margin:10px 13px 0;padding-top:10px;border-top:1px solid var(--line)}
/* NI-2026-08-21-110 JOB B — DINING_AUTHORITY_CONSUMER_EXPERIENCE_SPEC's Level 4: a readable
   evidence SENTENCE leads ("Multiple trusted food voices independently recommend it.",
   "Recommended by Dave Portnoy.") built client-side from the same p.ev/p.consensus data the
   chips already render from — no new claim, only a compressed restatement of what the chips
   already say. Never present unless evidenceSummaryLine() (views.js) found something
   honestly sayable; a card with only a sighting-class fact (SEEN HERE) shows no sentence,
   only the honestly-labelled chip below, exactly as before. */
.trust-line{font-size:14px;line-height:1.45;color:var(--text-2);margin:0 0 8px}
/* Evidence detail is now progressively disclosed, not the row's own visual lead — collapsed
   by default, opened by the same "Why Howie" control that used to only fire telemetry. */
.trust-detail{display:flex;align-items:center;flex-wrap:wrap;gap:7px;padding-top:9px}
.trust-detail[hidden]{display:none}
/* The tap floor is 44px throughout, per this file's own header — a borderless label button is
   still a button. min-height keeps the full hit area; align-items keeps the small-caps text
   vertically centred in it, so the fix is invisible: same look, real tap target underneath. */
.why-btn{background:none;border:0;color:var(--text-4);font-size:11px;letter-spacing:1px;
  text-transform:uppercase;font-weight:600;padding:0;min-height:var(--tap);
  display:inline-flex;align-items:center;gap:5px;flex:0 0 auto}
.why-btn .chev{transition:transform .16s ease}
.why-btn[aria-expanded="true"] .chev{transform:rotate(180deg)}
/* NI-2026-08-14-006 JOB B — restrained by design: same quiet weight as .why-btn, not a loud
   badge, and never present unless build-home-data.js found 2+ genuinely distinct authorities
   for this exact place. Sits between the "Why Howie" control and the evidence chips it is
   summarising, in the same .trust row, so it never competes with the chips for attention. */
.consensus{color:var(--ok-fg);font-size:11px;letter-spacing:.3px;font-weight:600;
  flex:0 0 auto}
.evs{display:flex;flex-wrap:wrap;gap:6px;flex:1 1 auto;margin-top:8px;width:100%}
.evb{font-size:10.5px;letter-spacing:.4px;border-radius:6px;padding:3px 8px;border:1px solid}
.evb b{font-weight:600}
.evb-first-hand{background:var(--ok-bg);color:var(--ok-fg);border-color:var(--ok-line)}
.evb-recommended-by{background:var(--surf-3);color:var(--accent-hot);border-color:var(--line-4)}
.evb-featured-by{background:var(--surf-2);color:var(--text-3);border-color:var(--line-2)}
.evb-seen-here{background:var(--info-bg);color:var(--info-fg);border-color:var(--info-line)}
.evb-favourite-of{background:var(--warn-bg);color:var(--warn-fg);border-color:var(--warn-line)}
.evb-creator-partner{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}

/* ESTIMATE, SAID ONCE, QUIETLY. The dashed form-field box read as "fill this in"; a single
   line with the verdict word given its own weight says the same thing without asking to be
   filled out. Still never implies live availability — the copy is unchanged. */
.est{margin:9px 13px 0;padding-top:9px;border-top:1px solid var(--line);
  font-size:13px;line-height:1.5;color:var(--text-4)}
.est-k{color:var(--accent-hot);font-weight:600;margin-right:5px}
.est-none{color:var(--text-5)}
.actions{display:flex;gap:8px;padding:11px 13px 13px;flex-wrap:wrap}
.btn{flex:1 1 auto;background:var(--surf-2);border:1px solid var(--line-2);color:var(--text);
  border-radius:10px;min-height:var(--tap);display:inline-flex;align-items:center;
  justify-content:center;gap:7px;font-size:13px;text-decoration:none;padding:0 12px}
.btn.primary{background:var(--accent);color:var(--on-accent);border-color:var(--accent);font-weight:600}
.btn.on{background:var(--ok-bg);color:var(--ok-fg);border-color:var(--ok-line)}

/* ---------- timing: SAFE FOR THE SHOW / TIGHT WINDOW / DON'T DO IT ---------- */
.timing{display:flex;flex-direction:column;gap:2px;margin:0 16px 12px;padding:10px 12px;
  border-radius:10px;border:1px dashed}
.timing .k{font-size:11px;letter-spacing:1.2px;text-transform:uppercase;font-weight:600}
.timing .v{font-size:14px;line-height:1.5}
.timing.safe{background:var(--ok-bg);border-color:var(--ok-line);color:var(--ok-fg)}
/* UNKNOWN IS NOT GOOD NEWS. A green box reads as a verdict; this state is the absence of one. */
.timing.info{background:var(--surf-2);border-color:var(--line-2);color:var(--text-2)}
.timing.tight{background:var(--warn-bg);border-color:var(--warn-line);color:var(--warn-fg)}
.timing.no{background:var(--warn-bg);border-color:var(--bad);color:var(--bad)}

/* ---------- coverage states ---------- */
.cov{margin:0 16px 14px;background:var(--surf-1);border:1px solid var(--line-2);
  border-radius:12px;padding:13px}
.cov b{display:block;margin-bottom:5px}
.cov p{font-size:14.5px;line-height:1.5;color:var(--text-2);margin:0 0 10px}

/* ---------- set list stages ---------- */
.stage{margin:0 16px 10px;background:var(--surf-1);border:1px solid var(--line);border-radius:12px;overflow:hidden}
.stage-h{display:flex;align-items:center;gap:9px;padding:10px 13px;background:var(--surf-2);
  border-bottom:1px solid var(--line)}
/* NI-2026-08-22-163 JOB D -- sequence badge, outline by default; the SHOW stage (the real event)
   fills solid, the same "the real thing gets the accent" rule .anchor-dot/.is-anchor already use
   one level down, so the two signals read as one consistent language rather than two rules. */
.stage-num{width:19px;height:19px;border-radius:50%;flex:0 0 auto;display:flex;align-items:center;
  justify-content:center;font-family:'Anton',Impact,sans-serif;font-size:10.5px;
  border:1.5px solid var(--line-3);color:var(--text-3)}
.stage[data-stage="show"] .stage-num{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
.stage-k{font-family:'Anton',Impact,sans-serif;font-size:15px;letter-spacing:.5px;
  text-transform:uppercase;color:var(--accent-hot)}
.stage-s{font-size:10.5px;letter-spacing:1.1px;text-transform:uppercase;color:var(--text-4)}
.stage-row{display:flex;align-items:flex-start;gap:11px;padding:11px 13px;border-top:1px solid var(--line)}
.stage-row:first-of-type{border-top:0}
.stage-dot{width:8px;height:8px;border-radius:50%;background:var(--text-5);margin-top:6px;flex:0 0 auto}
.anchor-dot{background:var(--accent)}
.stage-b{flex:1;min-width:0}
.stage-n{font-size:14.5px;color:var(--text)}
.is-anchor .stage-n{font-family:'Anton',Impact,sans-serif;font-size:17px;text-transform:uppercase;color:var(--accent-hot)}
.stage-sub{font-size:11.5px;color:var(--text-4);margin-top:2px}
.stage-row .sl-why-howie,
.stage-row .pb-why-howie{color:var(--text-3);margin-top:8px;max-width:42ch;line-height:1.4}
.stage-row .sl-why-howie em,
.stage-row .pb-why-howie em{display:block;margin-bottom:2px;font-family:'IBM Plex Mono',ui-monospace,monospace;font-size:10.5px;font-style:normal;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--text-4)}
.stage-row .sl-why-src{display:block;margin-top:4px;font-size:11px;color:var(--text-4)}
.anchor-tag{font-size:9.5px;letter-spacing:1.1px;text-transform:uppercase;color:var(--text-4);
  border:1px solid var(--line-2);border-radius:5px;padding:2px 6px;align-self:center}
.stage-gap{margin:0 16px 10px;padding:10px 13px;border:1px dashed var(--line-2);border-radius:10px;
  font-size:12.5px;color:var(--text-4)}
.sl-row{display:flex;align-items:center;gap:9px;margin:0 16px;padding:10px 0;border-top:1px solid var(--line)}
.sl-row .info{flex:1;min-width:0}
.sl-row .nm{font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sl-row .sub{font-size:11px;color:var(--text-4)}
.sl-title-block{margin-top:4px}
.sl-title-edit{
  display:inline-flex;align-items:center;min-height:44px;margin-top:6px;padding:0 2px;
  border:0;background:none;color:var(--accent-hot);font:600 13px/1.2 var(--font-ui);
  text-decoration:underline;cursor:pointer
}
.sl-title-form,.sl-user-add{display:flex;flex-direction:column;gap:8px;margin-top:12px;max-width:420px}
.sl-title-form[hidden],.sl-user-add[hidden]{display:none}
.sl-title-label{font-size:12px;color:var(--text-4);letter-spacing:.04em;text-transform:uppercase}
.sl-title-input{
  min-height:44px;width:100%;padding:10px 12px;border:1px solid var(--line-2);
  border-radius:8px;background:var(--surf-1);color:var(--text);font:500 16px/1.2 var(--font-ui)
}
.sl-title-form .btn,.sl-user-add .btn,.sl-add-row .btn{min-height:44px}
.sl-add-more{padding-bottom:8px}
.sl-add-more .note{margin:0 16px 10px}
/* NI-2026-08-24-218 JOB E -- was width:34px;height:21px (34x21, no gap change), short of the
   product's own --tap:44px target discipline every other control in this file follows. */
.mv{display:flex;flex-direction:column;gap:2px}
.mv button{width:var(--tap);height:var(--tap);background:var(--surf-2);border:1px solid var(--line-2);
  color:var(--text-3);border-radius:6px;font-size:11px;padding:0}
.x{width:var(--tap);height:38px;background:var(--surf-2);border:1px solid var(--line-2);
  color:var(--text-3);border-radius:9px;font-size:15px}

/* ---------- plan bar + tabs ---------- */
.planbar{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(72px + env(safe-area-inset-bottom, 0px));
  width:min(406px,calc(100% - 24px));background:var(--surf-3);border:1px solid var(--line-4);
  border-radius:14px;display:flex;align-items:center;gap:10px;padding:10px 13px;z-index:59;
  box-shadow:0 8px 24px rgba(0,0,0,.22);min-height:var(--tap)}
.planbar .n{font-family:'Anton',Impact,sans-serif;font-size:17px;color:var(--accent-hot)}
.planbar .l{flex:1;min-width:0;font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.planbar .go{font-size:11.5px;color:var(--accent-hot)}
body.has-plan{padding-bottom:150px}
.tabs{position:fixed;bottom:0;left:0;right:0;display:flex;background:var(--surf-1);
  border-top:1px solid var(--line);padding:6px 4px calc(6px + env(safe-area-inset-bottom));z-index:60}
.tab{flex:1;background:none;border:0;color:var(--text-4);font-size:11px;letter-spacing:.2px;
  text-transform:uppercase;min-height:var(--tap);display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:3px;border-radius:10px}
.tab.on{color:var(--accent-hot);background:var(--surf-2)}
.tab .ti{position:relative;display:flex}
.badge{position:absolute;top:-6px;right:-9px;background:var(--bad);color:var(--badge-fg);
  font-size:9px;min-width:15px;height:15px;border-radius:8px;display:flex;align-items:center;
  justify-content:center;padding:0 3px}

/* ---------- empty states ---------- */
.nores{margin:16px;background:var(--surf-1);border:1px solid var(--line-2);border-radius:13px;padding:16px}
/* NI-2026-08-21-110 JOB D — this is honest recovery/explanation copy ("Nothing we can stand
   behind tonight"), not an identity moment, so it reads as a friendly proportional headline
   rather than a shouted condensed one; Anton stays reserved for event/performer/marquee text. */
.nores h3{font-family:var(--font-ui);font-weight:700;font-size:20px;text-transform:none;margin-bottom:7px}
.nores p{font-size:14.5px;line-height:1.55;color:var(--text-2);margin:0 0 11px}

/* ---------- desktop ---------- */
/* NI-2026-08-21-110 JOB C — a mobile-shaped bottom tab bar was still the desktop navigation:
   the prior rule only swapped `position:fixed` for `position:sticky` and rounded the top
   corners, so the same five icon-over-label buttons still sat pinned to the BOTTOM of a wide
   desktop window — "observed mobile-style bottom navigation remains visible on wide desktop."
   `#tabs` is one component mounted once for every view (H.renderTabs, wire.js) with exactly
   the product's five real destinations — reusing it as the desktop top nav (repositioned and
   relaid-out, same markup, same data-v/.js-nav click routing, nothing duplicated) is a real
   desktop pattern instead of inventing a second nav surface that could drift from the first. */
@media(min-width:900px){
  .grid2{display:grid;grid-template-columns:1fr 1fr;gap:0 18px;align-items:start}
  .tabs{position:fixed;top:0;bottom:auto;left:0;right:0;max-width:none;margin:0;
    border-radius:0;border-top:0;border-bottom:1px solid var(--line);
    justify-content:center;gap:6px;padding:9px 16px calc(9px + env(safe-area-inset-bottom)) 16px;
    z-index:70}
  .tab{flex:0 0 auto;flex-direction:row;align-items:center;gap:8px;padding:0 16px;
    font-size:13.5px;letter-spacing:.2px;text-transform:none}
  .tab .ti{position:relative}
  body{padding-bottom:40px;padding-top:126px}
  body.has-plan{padding-bottom:40px}
  /* NI-2026-08-21-110 JOB C — `position:sticky` measured double its own `top` offset here
     (verified live: a `top:60px` override rendered the header's box at y=120, not 60) —
     `html,body{overflow-x:hidden}` with no `overflow-y` set computes overflow-y to `auto` per
     the CSS Overflow spec's mixed-visible rule, which put `.ghead` inside a real NESTED
     scroll-container chain sticky positioning does not handle predictably here. Fixed
     positioning, matching how `.tabs` already solves the exact same problem two rules up, is
     unambiguous: the per-page header (back/brand/context/search) becomes its own full-width
     bar sitting directly below the fixed primary nav, not a second sticky layer competing with
     it for the same offset math. */
  .ghead{position:fixed;top:63px;left:0;right:0;z-index:65}
  .planbar{bottom:16px}
}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}
/* NI-2026-08-26-237 JOB C -- the single global focus ring, real-rendered-pixel-measured
   ~2.93:1 against the fill of any control whose own background is var(--accent) (.btn.primary,
   .chip.on, .tier.bucket...), below the 3:1 WCAG SC 1.4.11 non-text-contrast floor. var(--accent-
   hot) itself has ample contrast against var(--bg) in every palette (~5.5:1 HYBRID/LIGHT, ~12:1
   DARK/.cine) -- outline-offset's 2px gap is the same colour as the page, but a plain `outline`
   is measured directly against whatever the ring is nearest to, and on an accent-filled control
   that is the control's own accent fill, not the page. Fixed with the standard accessible
   "moat" technique: a real, guaranteed `var(--bg)` box-shadow ring sits between the control and
   the visible ring, so the ring's own immediate neighbour is always the page background (always
   >=3:1 against var(--accent-hot) by construction) regardless of what is focused. `outline:2px
   solid transparent` is kept, not removed -- Windows/OS forced-colors mode draws its own system
   outline for any element whose outline-style isn't none, ignoring author colour, so a fully
   invisible-until-focus author outline still gets a real forced-colours-mode ring. */
:focus-visible{
  outline:2px solid transparent;outline-offset:2px;
  box-shadow:0 0 0 2px var(--bg),0 0 0 4px var(--accent-hot)
}

/* ---------- THE BLUEBERRY EFFECT ----------
   Four tiers, each with a no-image state that is a DESIGN rather than a hole. Every box
   reserves its aspect ratio before anything loads, so an arriving portrait never shifts the
   layout under someone's thumb. */
.bb{position:relative;overflow:hidden;background:var(--bg-2);border-radius:11px;flex:0 0 auto}
.bb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  /* 3:4 portraits, never cropped to a letterbox — that is what cut people's heads off */
  object-position:50% 22%;opacity:0;transition:opacity .32s ease}
.bb.bb-in img{opacity:1}
.bb-ph{position:absolute;inset:0;background:
  linear-gradient(100deg,var(--bg-2) 30%,var(--surf-2) 50%,var(--bg-2) 70%);
  background-size:220% 100%;animation:bbshim 1.4s linear infinite}
.bb.bb-in .bb-ph,.bb.bb-failed .bb-ph{display:none}
@keyframes bbshim{to{background-position:-120% 0}}
@media (prefers-reduced-motion:reduce){.bb-ph{animation:none}.bb img{transition:none}}

/* THE HERO — one per screen at most, behind the headline */
.bb-hero{width:100%;aspect-ratio:3/4;max-height:58vh;border-radius:0}
.bb-hero::after{content:"";position:absolute;inset:0;background:var(--scrim);pointer-events:none}
/* THE CARD — standard discovery card, portrait to one side */
.bb-card{width:74px;aspect-ratio:3/4}
/* THE TILE — rail tile, portrait above the label */
.bb-tile{width:100%;aspect-ratio:3/4;border-radius:9px 9px 0 0}

/* THE NO-IMAGE STATE: the generated bill. Type on a rule, like a printed programme. This is
   what most cards will show until the likeness review clears, and it has to look intentional
   on its own — a design that only works once the pictures arrive is not a design. */
/* NI-2026-08-22-163 JOB B -- POST-INTENT MARQUEE, "zero giant empty panels" extended to the
   remaining bare fallback tier. The hero (.bb-hero.bb-none, below) and the billboard-rail tile
   (.billboard-rail .bb-tile.bb-none, above) already got the marquee's own warm-stage recipe in
   place of a flat neutral panel -- but that fix was scoped to those two selectors only, so any
   OTHER surface using this base rule directly (.bb-card.bb-none, the standard discovery-card
   fallback used across every city/event picks list, and any plain rail tile outside
   .billboard-rail, e.g. "Also on that night") still fell through to a flat var(--surf-2) box --
   the same class of gap the tile fix's own comment ("Worth the Trip cards that look like blank
   beige boxes") already named, one layer further out. Promoting the recipe to the base rule
   fixes every remaining bare tier at once; the two already-fixed selectors are unaffected since
   both are higher-specificity and already declare their own background. */
.bb-none{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;
  background:
    radial-gradient(ellipse 70% 70% at 50% 0%, rgba(245,166,35,.15), transparent 65%),
    linear-gradient(180deg,#171208,#0a0806);
  border:1px solid rgba(245,166,35,.14)}
.bb-none .bb-rule{width:44%;height:1px;background:var(--line-4);opacity:.55}
/* FOUND DURING THE VISUAL PASS, NOT INTRODUCED BY IT: max-width/overflow-wrap were only ever
   declared on the HERO variant below (line ~536). A flex item with align-items:center sizes
   itself by shrink-to-fit, and `max-width:100%` alone left the browser painting the unwrapped
   line WIDER than the box it laid out for it — verified by outlining the element's own
   background: the paint bled past its measured, measured-correct-looking box on both sides
   ("Guns N' Roses World Tour" ran off the edge of a 180px tile, past the point max-width alone
   fixed). A hard `width:100%` removes the ambiguity: the box can no longer be narrower than its
   container regardless of content, so wrapping is forced by construction, not by a fit-content
   calculation that produced the wrong number for this content. Promoted to the base rule so
   every tier using this fallback — not just the one that happened to need it first — is safe. */
/* Fixed light colors, not the palette-tracking --text-4/--text-5 tokens: the same fix the hero
   fallback's own comment above already made for the identical reason (this box's background is
   now always-dark by construction, like the hero/tile fallbacks, so a token that flips light in
   the default HYBRID/light palette would print near-illegible dark-on-dark text again). */
.bb-none .bb-mono{font-family:'Anton',Impact,sans-serif;font-size:11px;letter-spacing:1.4px;
  text-transform:uppercase;color:#f2f0ec;text-align:center;padding:0 6px;line-height:1.15;
  width:100%;box-sizing:border-box;max-width:100%;overflow-wrap:anywhere;word-break:break-word}
/* NI-2026-08-25-236 JOB H — same always-dark-background reasoning as .bb-mono above: this
   base rule's own background became always-dark by construction (NI-163 JOB B comment, above),
   so var(--accent-hot) resolving to the theme-tracked LIGHT value here was the same bug class,
   just never patched on this specific rule when the background was promoted. */
.bb-none .bb-sub{font-size:8.5px;letter-spacing:1.2px;text-transform:uppercase;color:#ffc95c}
/* THE HERO FALLBACK IS A DESIGNED STATE, NOT AN EMPTY BOX.
   A 400px grey gradient with 19px type floating in it reads as a failed image, which is the
   opposite of the intent: this treatment exists so a card looks finished with no photograph at
   all. It is now a marquee bill — big condensed type, a rule above and below, the venue town
   underneath, on the house ground rather than a gradient. */
/* THE SCRIM IS FOR PHOTOGRAPHS. It exists so white type survives a bright image; laid over the
   generated bill it produced exactly the grey gradient the founder objected to. No photo, no
   scrim. The bill is also capped, because a 16/9 band on a wide screen is a landing page, not a
   header. */
.bb-hero.bb-none::after{display:none}
/* FOUNDER VISUAL-PASS ITEM 3: "structurally correct but visually too empty." The flat
   var(--surf-2) panel was a plain dark GREY regardless of whether a gradient sat on it — the
   objection was the colour, not the shape of the background rule. Replaced with the billboard's
   OWN recipe (same radial amber top glow + same near-black gradient the stage itself uses, from
   shell.css:112-114) instead of a neutral panel colour, plus a very faint vertical rule pattern
   for texture — no new imagery, no logo, nothing that needs rights clearance, just the marquee's
   existing owned visual language turned up on the one surface that had none of it. */
.bb-hero.bb-none{aspect-ratio:16/9;max-height:38vh;
  background:
    radial-gradient(ellipse 65% 60% at 50% 0%, rgba(245,166,35,.17), transparent 65%),
    repeating-linear-gradient(90deg, rgba(255,201,92,.035) 0 1px, transparent 1px 26px),
    linear-gradient(180deg,#171208,#0a0806);
  box-shadow:inset 0 1px 0 rgba(255,201,92,.16), inset 0 -36px 54px -24px rgba(0,0,0,.55);
  border-bottom:1px solid rgba(245,166,35,.16);gap:9px;padding:22px 20px}
@media (min-width:768px){ .bb-hero.bb-none{max-height:300px} }
/* TRAVELER ROUND, OBJECTIVE 4A (2026-08-14). This bill's own background (line ~610) is a
   fixed near-black gradient — "the billboard's own recipe" — always dark regardless of the
   page's light/dark palette. Its title text used `--text`, a token that tracks the PAGE'S
   palette, not this element's own always-dark ground: in light/HYBRID mode (the default)
   that resolved to near-black-on-near-black, because this hero sits outside `.cine`'s scope
   (the wrapper that would otherwise flip `--text` light for a dark surface). Fixed with a
   colour tied to this element's own background, not the page's theme — the marquee
   aesthetic (condensed type, amber rule, glow) is unchanged, only made legible. */
.bb-hero.bb-none .bb-mono{font-size:clamp(24px,7.4vw,42px);line-height:1.03;text-align:center;
  color:#f2f0ec;letter-spacing:.6px;max-width:100%;overflow-wrap:anywhere;
  text-shadow:0 0 24px rgba(245,166,35,.22)}
.bb-hero.bb-none.bb-mid .bb-mono{font-size:clamp(21px,6.1vw,34px)}
.bb-hero.bb-none.bb-long .bb-mono{font-size:clamp(17px,4.9vw,27px)}
.bb-hero.bb-none .bb-rule{width:34%;background:linear-gradient(90deg,transparent,var(--accent),transparent);opacity:.55}
/* NI-2026-08-25-236 JOB H — same always-dark-background fix as .bb-mono directly above:
   var(--accent-hot) was never patched on this sibling rule when the hero fallback's own
   background was fixed to always-dark. */
.bb-hero.bb-none .bb-sub{font-size:10.5px;letter-spacing:1.6px;color:#ffc95c}

/* A MATCHUP IS TWO TEAMS, STACKED, WITH THE FIXTURE WORD BETWEEN THEM.
   NI-2026-08-25-236 JOB H — .bb-match only ever renders alongside .bb-none (see blueberry.js
   bill()), whose own background is always-dark by construction (NI-163 JOB B, above). --text
   and --accent-hot are theme-tracked tokens, so on a real rendered NYC sports category card
   (outside .cine, default HYBRID/light palette) this measured 1.07:1 (--text, effectively
   invisible) and 3.36:1 (--accent-hot) contrast against the card's own dark ground -- below
   the 4.5:1 AA floor and, for the team names, unreadable. Same fixed-light-color fix as every
   other always-dark .bb-none descendant on this page (.bb-mono, .bb-sub) -- not a new colour,
   matching --text/--accent-hot's own DARK-palette/.cine values exactly, so this is a no-op
   everywhere .bb-match already rendered correctly (inside .cine) and a real fix everywhere it
   did not. */
.bb-match{gap:2px}
.bb-match .bb-team{font-family:'Anton',Impact,sans-serif;text-transform:uppercase;
  text-align:center;color:#f2f0ec;line-height:1.02;overflow-wrap:anywhere;max-width:100%}
.bb-match .bb-vs{font-size:11px;letter-spacing:2.4px;text-transform:uppercase;
  color:#ffc95c;margin:3px 0}
.bb-hero.bb-match .bb-team{font-size:clamp(26px,8vw,46px)}
.bb-hero.bb-match.bb-mid .bb-team{font-size:clamp(22px,6.6vw,38px)}
.bb-hero.bb-match.bb-long .bb-team{font-size:clamp(18px,5.2vw,30px)}
.bb-card.bb-match .bb-team{font-size:9px}
.bb-tile.bb-match .bb-team{font-size:10px}
.bb-card.bb-match .bb-vs,.bb-tile.bb-match .bb-vs{font-size:7.5px;letter-spacing:1.4px;margin:1px 0}
/* "A NAME IS NEVER CUT" — this file's own history (see the comment on bill(), a few screens
   up) already litigated this exact question: an 18-character JS slice once printed "GUNS N'
   ROSES WORL" and the fix was wrap-and-shrink, not truncation. Ellipsis truncation was tried
   here during the visual pass and reverted for the same reason — it's a different mechanism
   for the same forbidden outcome, a name the user can't read in full. The real cause of the
   overflow at the tile's compact width was 1.4px letter-spacing on a 24-character string:
   23 gaps x 1.4px is 32px of fixed-width padding INSIDE the words that a smaller font-size
   alone can't reclaim (letter-spacing doesn't scale with font-size). Tightened spacing plus a
   real per-length size step (mirroring the hero's own .bb-mid/.bb-long pattern, which this
   tier never had) gets a 24-character title on one line without cutting a single character. */
.bb-tile.bb-none .bb-mono{font-size:10px;letter-spacing:.3px}
.bb-tile.bb-none.bb-mid .bb-mono{font-size:8px;letter-spacing:.2px}
/* NI-2026-08-25-236 JOB H, LIVE EVIDENCE ADDENDUM 10 — the .long step used to drop a further
   half-point below .mid (7px tile / 6.5px card), which independent visual evidence on real
   mid/long real event titles (agent-lab/visual-design/32880668466-a1.json) flagged as below a
   genuinely legible floor. Measured on the real rendered box geometry (a 154px rail tile / 74px
   card, both with enormous unused vertical slack — a wrapped 47-character real title such as
   "School Girls; Or, The African Mean Girls Play" only ever fills a fraction of the box height
   at any size tested up to and including .mid's own size), holding the .long step at .mid's
   size introduces zero clipping risk while raising the floor — so .long no longer needs its own
   smaller step and now reads at the same legible size .mid already proved safe. Wrap, never
   truncate, per this file's own "A NAME IS NEVER CUT" rule directly above. */
.bb-tile.bb-none.bb-long .bb-mono{font-size:8px;letter-spacing:.2px}
.bb-card.bb-none .bb-mono{font-size:8.5px;letter-spacing:.3px}
.bb-card.bb-none.bb-mid .bb-mono{font-size:7px;letter-spacing:.15px}
.bb-card.bb-none.bb-long .bb-mono{font-size:7px;letter-spacing:.15px}

/* the image-led card layout: portrait beside the text, and identical without it */
.ev-img{display:flex;gap:11px;align-items:flex-start}
.ev-img .ev-b{flex:1;min-width:0}
/* the image-led rail tile */
.tile-img{padding:0;overflow:hidden}
.tile-img .tile-b{padding:10px 11px 12px}

/* ---------- "you asked for" ----------
   The router's understanding, said back above the fold. On a phone the billboard is tall enough
   that the requested block starts off-screen; without this the user cannot tell they were heard. */
.asked{display:flex;flex-wrap:wrap;align-items:center;gap:9px;margin:0 16px 4px;padding:10px 12px;
  background:var(--surf-2);border:1px solid var(--line-3);border-radius:11px}
.asked-k{font-size:9.5px;letter-spacing:1.8px;text-transform:uppercase;color:var(--text-4)}
.asked-v{font-family:'Anton',Impact,sans-serif;font-size:14px;letter-spacing:.5px;
  text-transform:uppercase;color:var(--accent-hot)}
.asked-no{flex:1 0 100%;font-size:14px;line-height:1.45;color:var(--text-3)}

/* ch-based headline max-width uses the zero advance, independently of text advances.
   Calibrated from the real Playfair 700 / Georgia Bold zero glyph (see Z1B/width-metrics.json). */
@font-face {
  font-family:'Playfair Display Fallback'; src:local('Georgia Bold'); font-style:normal; font-weight:600 900;
  unicode-range:U+0030; size-adjust:91.988858%;
  ascent-override:117.622941%; descent-override:27.285913%; line-gap-override:0%;
}
