/* Cheers
 * ---------------------------------------------------------------------------
 * Gatsby's lawn, if it ran down to a Sydney beach and he drank schooners.
 *
 * TWO WORLDS, ONE SITE. The palette follows the actual sun over the peninsula
 * (assets/js/daylight.js sets data-time on <html>):
 *
 *   data-time="day"    golden hour on the lawn, sun-bleached linen, gold leaf,
 *                      pool teal. Bright, open, deco. The default.
 *   data-time="night"  the party after dark, midnight harbour, lamplit brass,
 *                      champagne. Arrives at real local sunset.
 *
 * Tokens are SEMANTIC, never literal: --bg, --ink, --gold. Both worlds define
 * the same names, so no component knows or cares which one is on. Never write
 * a hex value outside this block.
 *
 * THE MARK is artwork, not type: assets/brand holds the option 6 kit (two
 * amber glasses, charcoal wordmark, cream foam), and assets/brand
 * the display-size copies the page loads. Nothing here recolours it.
 *
 * TYPE, 1920s, all three:
 *   Marcellus   headings. Roman capitals, warm, carved rather than printed.
 *   Jost        UI and every figure. A Futura revival, Futura is 1927, which
 *               is the whole point, with tabular lining numerals for prices.
 * ------------------------------------------------------------------------- */

:root {
  /* ================== GOLDEN HOUR (default) ==============================
   * Derived from the CHEERS brand tokens in assets/brand/brand-tokens.json,
   * and NOT copied from them. The distinction matters:
   *
   *   The logo palette is charcoal, beer #D9A25F, foam #F7EFE3, clink
   *   #E2A12A, burnt orange #C96832. Measured as TEXT on the foam ground,
   *   clink is 1.97:1 and beer is 1.98:1. They are artwork colours — they
   *   work because they are large flat shapes with a charcoal outline, and
   *   they are unreadable at 13px.
   *
   * So the neutrals are taken exactly (foam is the page, charcoal is the
   * ink, and both measure 13.95:1) and the accents are the brand hues walked
   * down in lightness until they clear 4.5:1 — same colour, legible weight.
   *
   * Every ratio below is measured, including against --sunk, which is the
   * surface the provenance strip and the drink chips sit on. The old palette
   * put gold at 4.47:1 there, just under the floor; this one clears it.
   * ==================================================================== */
  --bg:        #F7EFE3;  /* brand foam, taken exactly */
  --surface:   #FEFBF6;  /* one step up into the light */
  --sunk:      #EDE4D5;  /* recessed: inputs, wells, the provenance strip */
  --ink:       #26211F;  /* brand charcoal, the wordmark's own  13.95:1 on bg */
  --ink-soft:  #675E58;  /* secondary   5.6:1 bg · 5.0:1 sunk · 6.1:1 surface */
  --gold:      #885E12;  /* clink, darkened   5.0:1 bg · 4.6:1 sunk */
  --gold-lamp: #D9A25F;  /* brand beer. DECORATIVE ONLY — 1.98:1, never text */
  --sea:       #1F625F;  /* confirmed   6.2:1 bg · 5.6:1 sunk */
  --coral:     #9D5127;  /* burnt orange, darkened  5.1:1 bg · 4.6:1 sunk */
  --edge:      #DBD0BD;
  --edge-soft: #E7DDCB;
  --on-gold:   #FEFBF6;  /* text on a gold fill  5.6:1 */
  --on-coral:  #FFF8F5;

  --lift: 0 1px 0 rgba(255,255,255,.7) inset, 0 14px 30px -20px rgba(38,33,31,.35);
  --scrim: rgba(38,33,31,.44);
  color-scheme: light;

  /* ------------------------------------------------------------- washes */
  --gold-wash:  color-mix(in srgb, var(--gold) 12%, transparent);
  /* Gold that is legible ON gold-wash. Plain --gold over its own 12% wash
     lands under the 4.5 floor every other pair in this file clears - it
     affected the current nav item and the selected drink chip, both of them
     12-13px text. Pulling 20% of --ink into it fixes that and it is still
     recognisably gold. One declaration covers both worlds because --gold and
     --ink both move at sunset: by day it darkens toward charcoal, by night it
     lifts toward champagne, and either way it pulls away from the wash. */
  --gold-ink:   color-mix(in srgb, var(--gold) 80%, var(--ink));
  --sea-wash:   color-mix(in srgb, var(--sea) 11%, transparent);
  --coral-wash: color-mix(in srgb, var(--coral) 10%, transparent);

  /* Deco geometry, but only where the deco is doing the talking. Cards, panels
     and the map keep the 4px corner: straight lines and stepped corners are
     the period, and those are the surfaces that carry it.
     Controls do not. The brand mark is two glasses drawn entirely in soft
     curves, and a 3px corner next to it read as a default form widget - which
     is exactly the "basic and out of place" of it. 9px is enough to belong to
     the artwork and still short of a pill. */
  --r-object: 4px;
  --r-control: 9px;
  --r-pill: 999px;

  /* THE ACTION COLOUR.
     It was bottle green #0D3826 — the last thing on the page that belonged to
     The Froth Exchange rather than to Cheers. Nothing in the wordmark is
     green, so the primary button was quietly advertising the old brand.

     It is now the brand's own charcoal: the colour of four of the six letters,
     the most confident thing the identity owns, and 13.95:1 against the cream
     it carries. Amber was the obvious alternative and is wrong here — amber is
     what a PRICE is on this site, and a button in the same colour as the
     number it sits beside stops either of them meaning anything.

     Night cannot use charcoal: the page is #0B1F2A and one dark on another is
     invisible. Night takes the beer amber from the mark instead, where nothing
     else on a dark page is competing for it. */
  --action:    #26211F;  /* brand charcoal   13.95:1 with --on-action */
  --on-action: #F7EFE3;  /* brand foam, the wordmark's own cream */

  --font-display: "Marcellus", "Hoefler Text", Georgia, serif;
  --font-ui: "Jost", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-fig: "Jost", ui-sans-serif, system-ui, sans-serif;
  /* The hero price only, carved-cornerstone numerals, not UI type. Falls
     back to --font-fig everywhere it isn't explicitly opted in. */
  --font-fig-lg: "Big Shoulders Display", var(--font-fig);

  --gutter: 20px;
  /* Smallest comfortable hit area. Anything a thumb lands on gets this. */
  --tap: 44px;
}

/* -------------------------------------------------- THE PARTY AFTER DARK */
:root[data-time="night"] {
  --bg:        #0B1F2A;  /* midnight harbour */
  --surface:   #12303D;  /* lamplit */
  --sunk:      #07161E;
  --ink:       #F7EFE3;  /* brand foam         14.8:1 on bg */
  --ink-soft:  #9FB0B8;  /* secondary           7.2:1 on bg */
  --gold:      #E0B457;  /* brass lamp          9.6:1 on bg */
  --gold-lamp: #E0B457;
  --sea:       #4FB3A5;  /* confirmed           7.5:1 on bg */
  --coral:     #F0947A;  /* estimate            7.6:1 on bg */
  --edge:      #23485A;
  --edge-soft: #1A3947;
  --on-gold:   #201703;
  --on-coral:  #2A1108;
  /* The clink amber from the mark, exactly — the colour of the three marks
     above the glasses. 7.9:1 against --on-action, and distinct from the brass
     --gold that prices use, so a button and a price still read as different
     things after dark the way they do by day. */
  --action:    #E2A12A;
  --on-action: #201703;

  --lift: 0 1px 0 rgba(247,239,227,.05) inset, 0 18px 40px -24px rgba(0,0,0,.75);
  --scrim: rgba(3,12,18,.68);
  color-scheme: dark;
}

/* The switch is a change of light, not a jump cut. Kept off interactive
   feedback so it can't slow a tap. */
:root[data-time] body,
:root[data-time] .card,
:root[data-time] .masthead {
  transition: background-color .8s ease, color .8s ease, border-color .8s ease;
}

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
/* The masthead is sticky, so anything scrolled to by the bottom bar has to
   clear it or the heading lands underneath and looks like it was skipped. */
/* 78px of bar plus 20px of daylight. This number tracks .masthead-in's
   min-height: grow the bar and forget this, and every in-page link lands with
   its heading tucked under the header. */
:target, [id] { scroll-margin-top: 98px; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 0.9375rem/1.55 var(--font-ui);
  -webkit-font-smoothing: antialiased;
  padding-bottom: env(safe-area-inset-bottom);
}
h1, h2, h3 { margin: 0; font-weight: 400; line-height: 1.06; }
h1, h2 { font-family: var(--font-display); letter-spacing: .005em; }
/* Every figure on the site: lining, tabular, so columns align and prices read
   as prices rather than as output. This is the fix for "the numbers look AI" -
   Jost's numerals are geometric and period-correct, and no monospace is
   involved anywhere. */
.fig, .row-price, .dial-read, .dial-mark b, .radius output, .hh-when {
  font-family: var(--font-fig);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
a { color: var(--gold); text-underline-offset: 3px; }
svg { display: block; flex: none; }
button { font: inherit; }

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

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.wrap { max-width: 1180px; margin: 0 auto; padding: 0 var(--gutter); }

/* Keyboard users shouldn't have to tab the masthead and six stats to reach the
   thing they came for. Invisible until focused. */
.skip {
  position: fixed; top: 8px; left: 8px; z-index: 100;
  transform: translateY(-160%);
  background: var(--gold); color: var(--on-gold);
  font: 600 0.875rem/1 var(--font-ui); text-decoration: none;
  padding: 12px 16px; border-radius: var(--r-control);
  transition: transform .16s ease;
}
.skip:focus-visible { transform: none; }

/* Voice-layer slots. Hidden by default; assets/css/voice.css reveals them when
   <html data-voice="on">. Delete the voice files and these stay collapsed. */
.voice-signoff { display: none; }

/* ====================== THE LOCK-UP AFTER DARK ===========================
 * SOLVED IN THE ARTWORK NOW, NOT IN CSS.
 *
 * What used to be here: the old kit was bottle green on cream with no reversed
 * version, so on the night palette (#0B1F2A) the wordmark was very nearly
 * invisible. The workaround painted a cream tile behind it with a box-shadow
 * spread — the artwork's own ground, faked, so it stayed legible after dark.
 *
 * The CHEERS pack ships purpose-drawn reversed artwork, which is what that
 * hack was standing in for. cheers-wordmark.svg has charcoal letters for light
 * grounds; cheers-wordmark-reversed.svg redraws them cream and leaves the
 * amber beer, the foam and the clink marks exactly where they are.
 *
 * WHY TWO FILES RATHER THAN ONE FILTERED ONE. The wordmark is flat
 * multi-colour art. `filter: invert()` would turn the beer blue and the foam
 * charcoal along with the letters. The brand pack forbids CSS inversion for
 * precisely this reason, and it is right.
 *
 * Both files load. They are SVGs of a six-letter wordmark; the unused one is
 * not worth a media query that could not read data-time anyway — the theme
 * comes from daylight.js and the real sunset over the peninsula, not from
 * prefers-color-scheme, so <picture> cannot switch on it.
 * ====================================================================== */
/* Block, not the inline default a <picture> carries, so the anchor in the
   masthead and the centred img in the footer both size off it predictably.

   DO NOT set `display` on `.wordmark picture` or any other selector that
   reaches these elements: a class-plus-type selector outranks these plain
   classes, and the visible failure is both cuts of the wordmark rendering
   side by side rather than one of them vanishing — which reads as a markup
   bug and sends you to the wrong file. */
.wordmark-day   { display: block; }
.wordmark-night { display: none; }
:root[data-time="night"] .wordmark-day   { display: none; }
:root[data-time="night"] .wordmark-night { display: block; }

/* No glow, blur, drop-shadow, gradient or opacity may be applied to the
   wordmark or the paired-E mark. That is the brand pack's rule and it is
   stated here as well as observed, because the last version of this file grew
   a tile and a shadow one reasonable-looking commit at a time. */

/* ----------------------------------------------------------------- header */
.masthead {
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in srgb, var(--bg) 90%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--edge-soft);
}
/* TWO GROUPS, NOT FOUR ITEMS. The `margin-right:auto` used to sit on the
   wordmark, which pushed everything else hard against the right edge: on a
   1280px screen that left the mark alone at x=70 and a 609px hole before the
   nav, so the bar read as two unrelated things that happened to share a
   border. The auto margin now sits after the nav instead. Brand and
   navigation group on the left, where the eye starts and where "Today" is
   plainly a page of this site; the two controls anchor the right, where
   actions belong. Nothing was added or removed to do it. */
/* 78px, up from 62. The bar was sized around a 40px lock-up and nothing else,
   which made the brand the smallest thing on a page whose largest headings run
   to 40px+. The extra 16px is all artwork: the padding moves 10 to 12 so the
   lock-up still has air above and below it, and the controls are unchanged
   because they were never the constraint. */
.masthead-in {
  display: flex; align-items: center; gap: 10px;
  min-height: 78px; padding: 12px var(--gutter);
  max-width: 1180px; margin: 0 auto;
}
/* Sticky furniture is rent on every scroll, and a phone viewport has less to
   spend: the bar comes back down where the lock-up is the symbol and does not
   need the height. */
@media (max-width: 599.98px) { .masthead-in { min-height: 68px; } }
/* The one deliberate hole, and it is small: enough air that the nav is not
   crowding the artwork, not so much that it stops being the same group.

   The rest of this block used to live in exchange.css, which index and the
   Board load and the cellar does not - so on deep.html the nav fell back to
   two bare anchors, gold and underlined, run together with no gap. The nav is
   masthead furniture on all three pages, so it belongs in the stylesheet all
   three pages load. Nothing about the rules changed in the move. */
.mast-nav {
  display: flex; gap: 4px; align-items: center; flex: none;
  margin-right: auto; margin-left: 12px;
}
.mast-nav a {
  display: inline-flex; align-items: center; min-height: var(--tap);
  padding: 0 13px; border-radius: var(--r-control);
  font: 500 0.8125rem/1 var(--font-ui); color: var(--ink-soft);
  text-decoration: none; white-space: nowrap;
  border: 1px solid transparent;
  transition: color .14s ease, background .14s ease, border-color .14s ease;
}
.mast-nav a:hover { color: var(--ink); background: var(--surface); }
/* The current page was a filled beige box with a gold border and a 3px corner,
   which is the shape of a text input, not of a nav state - and the border in
   particular made it compete with the real button two items along. Same wash,
   no border, the softer corner, and the weight doing the work instead.
   Deliberately gold and not --action: charcoal means "this does
   something" on this site now, and the page you are already on does not.
   Gold is also the only one of the two that is tuned for both worlds - at 11%
   over cream, a charcoal fill desaturates into a grey pill. The border stays
   transparent so the item does not resize when it becomes current. */
.mast-nav a[aria-current="page"] {
  color: var(--gold-ink);
  background: var(--gold-wash);
  font-weight: 600;
}
@media (max-width: 460px) {
  .mast-nav a { padding: 0 10px; font-size: 0.75rem; }
}
/* The last 12px. On a 320px phone the nav, the info button and the CTA come to
   more than the bar, before the mark gets any of it at all - true of the old
   text wordmark too, which collapsed to 8px there. Three pixels off each nav
   link is what leaves the symbol somewhere to stand. */
@media (max-width: 360px) {
  .mast-nav a { padding: 0 7px; }
}
/* THE WORDMARK, the option 6 compact lock-up as artwork. It was a deco
   sunburst drawn in SVG next to Poiret One; both are gone, and with them the
   whole question of matching a typeface to a mark - the lock-up arrives with
   its own spacing already set. The rules that follow only give it a height and
   get out of the way: never a width, never a filter, never a crop. */
.wordmark {
  display: flex; align-items: center;
  text-decoration: none; color: inherit; min-width: 0;
}
/* NO `display` HERE, deliberately. It used to say `display: flex`, which is a
   class-plus-type selector and therefore beat the plain `.wordmark-night`
   class that hides the off-theme artwork — so both cuts of the wordmark
   rendered at once, side by side, in every theme. The flex was only ever
   buying shrink behaviour that `min-width: 0` on a block picture gives anyway.
   Whichever cut is showing gets its `display` from the theme rules above. */
.wordmark picture { min-width: 0; }
/* Height, and width follows whichever of the two files <picture> picked. The
   pair of them is why max-width and object-fit are both here: the artwork can
   never be wider than the space it is given, and if it is ever squeezed it
   letterboxes rather than squashing. The old text wordmark handled the same
   squeeze with an ellipsis; artwork has no ellipsis, so this is the floor. */
.wordmark-logo {
  display: block;
  height: 54px; width: auto; max-width: 100%;
  object-fit: contain;
}
/* The symbol band. Heights step down with the room actually left over once
   the nav, the info button and the CTA have taken theirs - measured, not
   guessed; see the numbers in the <picture> comment in index.html. */
@media (max-width: 599.98px) { .wordmark-logo { height: 40px; } }
/* 391-470px is the worst case on the whole site: the CTA still carries its
   full label here, and only goes icon-only below 390. Buying 24px back out of
   the three gaps is what keeps the symbol clear of the nav. */
@media (max-width: 470px) {
  .masthead-in { gap: 8px; }
  /* The 12px that groups nav with the mark is a luxury of a wide bar. Down
     here every pixel is already spoken for by the nav, the info button and
     the CTA, so the grouping gap goes and the flex gap carries it alone. */
  .mast-nav { margin-left: 0; }
  .wordmark-logo { height: 34px; }
}
@media (max-width: 360px) { .wordmark-logo { height: 28px; } }
/* The info button is a plain .btn now, not .btn-quiet. As a quiet button it
   had a transparent fill and a transparent border, so at rest it was a glyph
   floating between the nav and the CTA with nothing to say it could be
   pressed. A hairline on its own did not fix it either: --edge measures
   1.35:1 against the page by day and is invisible by night. The border and
   the surface fill together are what make "Use my location" read as a button,
   and this is the same control. .btn-quiet is left alone for the panel close
   buttons, which are correctly invisible until you go looking for them. */

/* Three items (wordmark, info, "Check a price") now share a header built for
   two. Below ~390px there isn't room for all three at full size, dropping the
   CTA to icon-only buys back ~70px, which is what keeps the lock-up at its
   own size rather than being squeezed. The label still exists for screen
   readers; only the visible text goes. */
@media (max-width: 390px) {
  .masthead-cta .cta-label {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }
  .masthead-cta { padding-left: 13px; padding-right: 13px; }
}

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font: 500 0.875rem/1 var(--font-ui); color: var(--ink);
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-control); padding: 11px 15px;
  /* --tap is the project's own floor and .btn was landing at ~38px, which
     covers the masthead info button and every panel close button. */
  min-height: var(--tap);
  cursor: pointer; text-decoration: none;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.btn:hover { border-color: var(--action); }
.btn-primary {
  background: var(--action); border-color: var(--action);
  color: var(--on-action); font-weight: 600;
}
/* Mixed toward --ink rather than a hard-coded #fff. The old rule always went
   toward white, which on the light page made the button recede exactly when
   it was being pointed at. --ink is dark by day and champagne by night, so
   this deepens the green and brightens the amber - either way it pulls
   further from the background it sits on, which is what hover should do. */
.btn-primary:hover { background: color-mix(in srgb, var(--action) 86%, var(--ink)); }
.btn-quiet { background: transparent; border-color: transparent; color: var(--ink-soft); }
.btn-quiet:hover { color: var(--ink); background: var(--surface); }
.btn-icon { padding: 10px; min-width: var(--tap); }
.btn-block { width: 100%; }

/* ============================ SECTION HEADS =============================
 * Deco: a rule, a centred cap-height title, a rule. Symmetry does the work. */
.sec-head { margin: 22px 0 12px; }
.sec-head h1, .sec-head h2 {
  /* Deliberately below .row-price. A section title is a signpost; the price is
     the answer, and the answer should be the largest thing on the screen. */
  font-size: clamp(1.375rem, 4.6vw, 1.75rem); line-height: 1;
  letter-spacing: .02em; color: var(--ink);
  display: flex; align-items: center; gap: 14px;
}
.sec-head h1::after, .sec-head h2::after {
  content: ""; flex: 1; height: 0; border-top: 1px solid var(--edge);
  border-bottom: 1px solid var(--edge); min-height: 3px;
}
.sec-head em { font-style: normal; color: var(--gold); }
.sec-head p { color: var(--ink-soft); font-size: 0.78125rem; margin-top: 5px; max-width: 56ch; }

.foot-note { margin-top: 14px; font-size: 0.78125rem; color: var(--ink-soft); }
.foot-note b { color: var(--ink); font-weight: 500; }

/* ------------------------------------------------------- near-me controls */
.near {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-bottom: 12px;
}
/* ROUND SIZE. Sits in the same row as location and radius because it is the
   same kind of thing — a fact about this trip that the whole page answers
   against. Borrows the drink picker's vocabulary rather than inventing a
   third control style. */
.party { display: flex; align-items: center; gap: 8px; border: 0; padding: 0; margin: 0; }
.party legend {
  padding: 0; float: left; line-height: var(--tap);
  font-size: 0.6875rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-soft); font-weight: 500;
}
.party-set { display: flex; gap: 4px; }
.party-n {
  min-width: var(--tap); min-height: var(--tap); padding: 0 10px;
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-control); color: var(--ink);
  font-family: var(--font-fig); font-size: 0.9375rem;
  font-variant-numeric: tabular-nums lining-nums; cursor: pointer;
  transition: background .14s ease, border-color .14s ease;
}
.party-n:hover { border-color: var(--action); }
.party-n.is-on {
  background: var(--gold-wash); border-color: var(--gold); color: var(--gold-ink);
  font-weight: 600;
}
/* What the round costs. Deliberately quieter than the unit price above it:
   the per-beer figure is what people compare, and this is the consequence. */
.row-round {
  display: block; font-family: var(--font-fig);
  font-variant-numeric: tabular-nums lining-nums;
  font-size: 0.8125rem; color: var(--gold-ink); margin-top: 3px;
}
.row-fig .row-round { text-align: right; }
@media (max-width: 400px) { .row-fig .row-round { text-align: left; } }

/* WE NEED A SCOUT. Sits on the sunk surface rather than in a coloured
   callout: it is an invitation, not a warning, and the page already uses
   coral for "this number is weak". Stacks on a phone so the button gets a
   full-width tap target instead of being squeezed beside the sentence. */
.scout {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap;
  margin-top: 12px; padding: 11px 13px;
  background: var(--sunk); border: 1px solid var(--edge);
  border-radius: var(--r-control);
}
.scout-ask { font-size: 0.8125rem; color: var(--ink); flex: 1 1 12rem; }
.btn-sm { min-height: 38px; padding: 8px 14px; font-size: 0.8125rem; }
@media (max-width: 420px) {
  .scout { flex-direction: column; align-items: stretch; }
  .scout .btn-sm { width: 100%; }
}

.btn-ghost { background: transparent; }
.radius {
  display: flex; align-items: center; gap: 12px; flex: 1 1 240px; min-width: 0;
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-control); padding: 0 15px; min-height: var(--tap);
}
.radius > span {
  font-size: 0.6875rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-soft); white-space: nowrap; font-weight: 500;
}
.radius input[type="range"] { flex: 1; min-width: 44px; accent-color: var(--gold); height: var(--tap); }
.radius output {
  font-family: var(--font-fig); font-variant-numeric: tabular-nums;
  font-size: 0.8125rem; color: var(--gold); min-width: 60px; text-align: right; font-weight: 500;
}
/* At 320px the label + range + output together are wider than the viewport
   has room for, and with .near already flex-wrapped there's no second line to
   fall back to. The word "Within" is decorative once the output already reads
   "5 km", drop it visually, keep it for screen readers. */
@media (max-width: 370px) {
  .radius > span { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .radius { gap: 10px; padding: 0 12px; }
}

/* =============================== THE MAP ===============================
 * Primary surface. Dark tiles so the brass pins are the only bright thing.
 * ====================================================================== */
.mapwrap {
  position: relative; border: 1px solid var(--edge);
  border-radius: var(--r-object); overflow: hidden; box-shadow: var(--lift);
  /* Leaflet stacks its own furniture high: panes at z-index 400, zoom controls
     and attribution at 1000. `position: relative` with `z-index: auto` creates
     NO stacking context, so those numbers were competing directly against the
     rest of the page in the root context - and beating the venue panel at 60.
     The result was tiles, pins and the zoom control drawing straight through an
     open sheet.

     `isolation: isolate` gives the map its own context, so everything Leaflet
     does is contained below whatever the page puts on top of it. Nothing about
     the map's internal layering changes. */
  isolation: isolate;
}
#map { height: 34vh; min-height: 260px; background: var(--sunk); }
@media (min-width: 760px) { #map { height: 48vh; min-height: 380px; } }

.map-legend {
  position: absolute; left: 12px; bottom: 12px; z-index: 500;
  display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: blur(8px);
  border: 1px solid var(--edge); border-radius: var(--r-pill);
  padding: 7px 13px; font-size: 0.75rem; color: var(--ink-soft);
}
.map-legend li { display: inline-flex; align-items: center; gap: 6px; }
.lg-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--gold); }
.lg-dot.cheap { background: var(--sea); }
.lg-dot.approx { background: transparent; box-shadow: inset 0 0 0 1.5px var(--ink-soft); }

/* Pins: a real price, legible at a glance, brass on dark. */
.pin {
  font-family: var(--font-fig); font-size: 0.75rem; font-weight: 500;
  white-space: nowrap; padding: 4px 9px; border-radius: var(--r-pill);
  background: var(--gold); color: var(--on-gold);
  box-shadow: 0 2px 10px rgba(0,0,0,.55);
  border: 1px solid rgba(0,0,0,.25);
  transition: transform .12s ease;
}
.pin:hover { transform: scale(1.08); }
.pin.is-cheap { background: var(--sea); }
/* Unplaced venues read as unplaced. No fake precision. */
.pin.is-approx {
  background: transparent; color: var(--ink-soft);
  border: 1px dashed var(--ink-soft); box-shadow: none;
}
/* Zoomed out, most venues are a dot. Only the cheapest quarter and the five in
   the list carry a price, so the map stays readable instead of a smear. */
.pin.is-dot {
  width: 9px; height: 9px; padding: 0; border-radius: 50%;
  display: block; font-size: 0; overflow: hidden;
  box-shadow: 0 1px 4px rgba(0,0,0,.6);
}
.pin.is-dot.is-approx { border-style: dashed; width: 8px; height: 8px; }
.leaflet-popup-content-wrapper {
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--edge); border-radius: var(--r-control); box-shadow: var(--lift);
}
.leaflet-popup-content { margin: 12px 14px; font-family: var(--font-ui); font-size: 0.8125rem; }
.leaflet-popup-content b { font-family: var(--font-display); font-size: 1rem; font-weight: 400; }
.leaflet-popup-tip { background: var(--surface); }
.leaflet-popup-close-button { color: var(--ink-soft) !important; }
.leaflet-container { font-family: var(--font-ui); background: var(--sunk); }
.leaflet-control-attribution {
  background: color-mix(in srgb, var(--bg) 80%, transparent) !important;
  color: var(--ink-soft) !important; font-size: 0.6875rem !important;
}
.leaflet-control-attribution a { color: var(--ink-soft) !important; }
.leaflet-bar { border: 1px solid var(--edge) !important; border-radius: var(--r-control) !important; overflow: hidden; }
.leaflet-bar a {
  background: var(--surface) !important; color: var(--ink) !important;
  border-bottom-color: var(--edge) !important;
}
.leaflet-bar a:hover { background: var(--edge) !important; }

/* =============================== THE FLOOR ================================
 * Not a directory. Five, ranked, with a way to act on each one.
 * ====================================================================== */
.rows { display: flex; flex-direction: column; gap: 8px; }

/* A ranked card: the whole thing opens the venue, and two explicit actions sit
   under a hairline, go there, or fix the price. */
.row {
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-object); overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.row:hover { border-color: var(--gold); box-shadow: var(--lift); }
.row:focus-within { border-color: var(--gold); }
/* The cheapest gets the gold rule across its top, a ribbon, not a badge. */
.row.is-first { border-color: color-mix(in srgb, var(--gold) 55%, transparent); }
.row.is-first::before {
  content: ""; display: block; height: 3px;
  background: var(--gold-lamp);
}
.row-open {
  display: grid; align-items: center; gap: 12px;
  grid-template-columns: 26px 1fr auto;
  padding: 12px 16px; width: 100%;
  background: none; border: 0; text-align: left; color: inherit; cursor: pointer;
}
/* The rank numeral is information, not ornament, it's the ranking. It was set
   in --edge at 1.5:1, which is a decorative treatment applied to content. */
.row-n {
  font-family: var(--font-display); font-size: 1.25rem; color: var(--ink-soft);
  line-height: .9; text-align: center;
}
.row.is-first .row-n { color: var(--gold); }

/* Actions strip */
.row-acts { display: flex; border-top: 1px solid var(--edge-soft); }
.row-act {
  flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: var(--tap); padding: 0 12px;
  font: 500 0.78125rem/1 var(--font-ui); color: var(--ink-soft);
  background: none; border: 0; cursor: pointer; text-decoration: none;
  transition: background .14s ease, color .14s ease;
}
.row-act + .row-act { border-left: 1px solid var(--edge-soft); }
.row-act:hover { background: var(--gold-wash); color: var(--gold); }

.seed-flag { display: inline-flex; align-items: center; gap: 6px; color: var(--coral); }
.seed-flag .mark { box-shadow: inset 0 0 0 1.5px var(--coral); }

.row-main { min-width: 0; }
/* Block, not inline, at the smaller size the name and the meta line that
   follows it are close enough in width to share a line by accident, which
   read as "Beacon Hill Bowling Club Bowling club ·" running together before
   wrapping mid-sentence. A block forces its own line regardless of how the
   text measures. */
.row-name { display: block; font-family: var(--font-display); font-size: 1.0625rem; letter-spacing: -.01em; line-height: 1.15; }
.row-meta { font-size: 0.75rem; color: var(--ink-soft); margin-top: 2px; }
.row-why {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 5px;
  font-size: 0.75rem; padding: 2px 8px; border-radius: var(--r-pill);
  color: var(--sea); background: var(--sea-wash);
  border: 1px solid color-mix(in srgb, var(--sea) 26%, transparent);
}

.row-fig { text-align: right; }
/* The price is the reason the page exists, and at 1.5625rem it was one pixel
   larger than the section heading above it - the most important number on the
   screen tying with page furniture. It now outranks every heading, which is
   what lets the eye run down the column of prices without reading anything
   else. Scales with the viewport so the ranked five still fit on a phone. */
.row-price {
  font-family: var(--font-fig-lg);
  font-size: clamp(2rem, 8vw, 2.25rem); font-weight: 700;
  color: var(--gold); letter-spacing: .01em; line-height: 1;
}
/* The dollar sign is a real element (.cur), not generated content: it stays
   optically smaller so the numerals align down the column, and, unlike a
   ::before, it is reliably announced. See .row-price .cur below. */
.row-proof { display: flex; align-items: center; justify-content: flex-end; gap: 6px; font-size: 0.75rem; color: var(--ink-soft); margin-top: 4px; }
/* inline-block because the marker is not always a flex item. Everywhere it is
   drawn by JS its parent is .row-proof, .seed-flag or .unplaced, all flex, so
   the <i> got blockified and the 9x9 applied. The one hand-written instance -
   the licence note in the cellar footer - sits in running prose, where an
   inline element ignores width and height and the marker rendered as nothing.
   Blockifying it costs the flex cases exactly nothing. */
.mark {
  display: inline-block; vertical-align: middle;
  width: 9px; height: 9px; border-radius: 50%; flex: none;
}
.row-proof[data-trust="confirmed"] { color: var(--sea); }
.row-proof[data-trust="confirmed"] .mark { background: var(--sea); }
/* The venue's own price — the top rung of the ladder.
   Deliberately NOT a fifth semantic colour. Every hue in this file has a
   measured ratio against three surfaces in two themes, and adding one to say
   "confirmed, but more so" would cost all of that and buy a distinction the
   eye reads better from shape anyway. Same trust colour, ringed marker: the
   two are tellable apart at a glance, in a screenshot, and by anyone who
   cannot distinguish the hues in the first place. */
.row-proof[data-trust="venue"] { color: var(--sea); font-weight: 600; }
.row-proof[data-trust="venue"] .mark {
  background: var(--sea);
  box-shadow: 0 0 0 1.5px var(--bg), 0 0 0 3px var(--sea);
}
.row-proof[data-trust="reported"] .mark { background: linear-gradient(90deg, var(--ink-soft) 50%, transparent 50%); box-shadow: inset 0 0 0 1.5px var(--ink-soft); }
.row-proof[data-trust="estimate"] { color: var(--coral); }
.row-proof[data-trust="estimate"] .mark { box-shadow: inset 0 0 0 1.5px var(--coral); }
/* The other half of the same fix: with no .row-proof above it the note's marker
   had a box but no colour. Carrying data-trust on the marker itself is the same
   vocabulary the rows use, so the sentence and the rows it describes are drawn
   by the same rule rather than by a one-off. */
.mark[data-trust="estimate"] { box-shadow: inset 0 0 0 1.5px var(--coral); }

@media (max-width: 400px) {
  /* Was targeting .row with a grid layout that lives on .row-open now, the
     mobile collapse silently stopped applying when the row markup split into
     card + button + actions strip. */
  .row-open { grid-template-columns: 22px 1fr; row-gap: 6px; padding: 10px 14px; }
  .row-fig { grid-column: 2; text-align: left; display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
  .row-proof { margin-top: 0; justify-content: flex-start; }
}

/* --------------------------------------------------------- happy hour list */
.hh-list { display: grid; gap: 8px; grid-template-columns: 1fr; margin-top: 10px; }
@media (min-width: 720px) { .hh-list { grid-template-columns: 1fr 1fr; } }
/* Card = the venue. Source link = the source. These were one <a> pointing
   off-site, so tapping a happy hour left the peninsula for somebody's blog
   instead of telling you about the pub. Now the card's primary action opens the
   venue, and the citation is a real, separate, still-prominent link.
   The hit overlay is a sibling button rather than a wrapping one, because an
   <a> nested inside a <button> is invalid and screen readers disagree on it. */
.hh-card {
  position: relative;
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-object); padding: 12px 16px; color: inherit;
  transition: border-color .15s ease;
}
.hh-card:hover { border-color: var(--gold); }
.hh-card:focus-within { border-color: var(--gold); }
.hh-hit {
  position: absolute; inset: 0; z-index: 0;
  border: 0; background: transparent; cursor: pointer;
  border-radius: var(--r-object); padding: 0;
}
.hh-card > *:not(.hh-hit) { position: relative; pointer-events: none; }
.hh-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.hh-name { font-family: var(--font-display); font-size: 1rem; }
.hh-when { font-family: var(--font-fig); font-size: 0.75rem; color: var(--gold); white-space: nowrap; }
.hh-deal { font-size: 0.78125rem; color: var(--ink); margin-top: 5px; }
.hh-src {
  font-size: 0.75rem; color: var(--ink-soft); margin-top: 8px;
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 24px;
}
/* Lifted back above the overlay so the citation stays clickable. The same
   applies to the directions link: .hh-row is a direct child and so is switched
   off by the rule above, and .hh-go sits inside it, which left the link
   focusable by keyboard but dead to a mouse or thumb. Both real links opt
   back in; only the inert text stays behind the overlay. */
.hh-card .hh-src, .hh-card .hh-go { pointer-events: auto; z-index: 1; }
.hh-go {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 24px; color: var(--ink-soft); text-decoration-color: var(--edge);
}
.hh-go:hover { color: var(--gold); text-decoration-color: var(--gold); }
.hh-src a { color: var(--ink-soft); text-decoration-color: var(--edge); }
.hh-src a:hover { color: var(--ink); text-decoration-color: var(--gold); }
.hh-on { color: var(--sea); }

/* ------------------------------------------------------- states / skeleton */
.state {
  text-align: center; padding: 44px 20px;
  border: 1px dashed var(--edge); border-radius: var(--r-object);
}
.state h3 { font-family: var(--font-display); font-size: 1.3125rem; margin-bottom: 8px; }
.state p { color: var(--ink-soft); font-size: 0.84375rem; max-width: 42ch; margin: 0 auto 18px; }
.state-actions { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }

/* ------------------------------------------------------------- sheets */
.scrim { position: fixed; inset: 0; background: rgba(6,18,16,.66); z-index: 50; backdrop-filter: blur(3px); }
.panel {
  position: fixed; z-index: 60; background: var(--bg);
  box-shadow: var(--lift); overflow-y: auto;
  left: 0; right: 0; bottom: 0; max-height: 92vh;
  border-radius: 18px 18px 0 0; border-top: 1px solid var(--edge);
  padding: 24px var(--gutter) calc(26px + env(safe-area-inset-bottom));
  animation: rise .24s cubic-bezier(.2,.8,.3,1);
}
@keyframes rise { from { transform: translateY(22px); opacity: 0; } }
@media (min-width: 760px) {
  .panel {
    left: auto; top: 0; width: min(470px, 100%); max-height: none;
    border-radius: 0; border-top: 0; border-left: 1px solid var(--edge);
    padding: 28px 28px calc(28px + env(safe-area-inset-bottom));
    animation: slidein .22s ease;
  }
  @keyframes slidein { from { transform: translateX(22px); opacity: 0; } }
}
.panel-close { position: absolute; top: 16px; right: 16px; z-index: 2; }
.panel h2 { font-size: 1.5625rem; padding-right: 46px; }
.panel-sub { color: var(--ink-soft); font-size: 0.8125rem; margin-top: 5px; }

.field { display: block; margin-bottom: 16px; }
.field > span { display: block; font-family: var(--font-fig); font-size: 0.6875rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: 7px; }
.field select, .field input[type="text"], .field input[type="number"], .field input[type="search"] {
  width: 100%; font: 400 0.9375rem/1 var(--font-ui); color: var(--ink);
  background: var(--sunk); border: 1px solid var(--edge);
  border-radius: var(--r-control); padding: 12px;
}
.field input[type="range"] { width: 100%; accent-color: var(--gold); height: var(--tap); }
.panel-foot {
  position: sticky; bottom: -26px; margin-top: 20px; padding: 14px 0 4px;
  background: var(--bg); box-shadow: 0 -18px 20px 8px var(--bg);
  display: flex; gap: 10px;
}

/* ============================== THE WELCOME ==============================
 * Opens on every load via ui.openDialog() in app.js, a real dialog (Escape,
 * Back, focus trap all come free from ui.js), just full-bleed instead of a
 * sheet. No scrim: at 100% of the viewport there's no "outside" to dim. */
.splash {
  position: fixed; inset: 0; z-index: 65;
  background: var(--bg); color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  overflow-y: auto; overflow-x: hidden;
  padding: 40px var(--gutter) calc(40px + env(safe-area-inset-bottom));
  animation: splashIn .45s cubic-bezier(.2,.8,.3,1);
}
@keyframes splashIn { from { opacity: 0; transform: scale(.98); } }
/* The medallion again, huge and faint, behind the copy, the same trick as
   the gold-wash sunburst everywhere else, just turned up for the one screen
   that's allowed to make an entrance. */
/* Fixed, not absolute. The medallion is far bigger than the panel, and as an
   absolutely-positioned child it counted toward the scroll container's content
   height, giving the welcome screen ~13px of phantom scroll on a short phone,
   so a screen that fits could still be nudged. A fixed element is out of the
   scroll flow entirely. Geometry is unchanged: .splash is itself fixed at
   inset 0, so the percentages resolve against the same box. */
/* NO OPACITY HERE, deliberately. The watermark PNG is drawn at 12% alpha in
   the file - that is what the "-12" in its name means. The old SVG needed
   opacity:.06 because it was solid ink; adding it back on top of the artwork
   would multiply the two and leave nothing on screen. */
/* THE ONE CSS OPACITY ON A BRAND IMAGE ANYWHERE ON THE SITE, and it is not
   the double-fade the kit warns about. That warning is about the intro/*-12
   and *-20 files, which arrive pre-faded and must be rendered at opacity 1.
   The file underneath this rule is the fully opaque symbol, and .12 of it is
   exactly the intro-symbol-watermark-12.png that should have been here: the
   kit's copy of that file arrived truncated at exactly 192KiB and no decoder
   would open it, so the effect is reproduced from the symbol instead. Nothing
   else in the kit works in this slot: the welcome screen already carries the full
   lock-up, and putting a second wordmark behind the first looks like a
   misprint at every size that was tried. */
.splash-bg {
  position: fixed; top: 44%; left: 50%; width: 560px; height: auto;
  transform: translate(-50%, -50%); object-fit: contain;
  opacity: .12;
  pointer-events: none;
}
.splash-in {
  position: relative; z-index: 1; max-width: 380px; width: 100%;
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
/* The stacked lock-up, which carries the name and the slogan itself. This is
   why there is no longer an <h2> under it. */
.splash-mark {
  /* Wider than the old lock-up at the same optical weight: this artwork is a
     single line of six letters, where the old one stacked a symbol over a
     wordmark over a slogan. */
  width: 210px; height: auto; object-fit: contain; margin-bottom: 18px;
}
/* The reason the thing exists, before the list of what it does. Sits between
   two hairlines so it reads as a statement rather than as more body copy. */
.splash-why {
  font-size: 0.84375rem; line-height: 1.6; color: var(--ink-soft);
  max-width: 38ch; margin: 16px 0 20px; padding: 15px 0;
  border-top: 1px solid var(--edge-soft);
  border-bottom: 1px solid var(--edge-soft);
}
.splash-points { display: flex; flex-direction: column; gap: 12px; text-align: center; align-self: stretch; margin-bottom: 26px; }
.splash-points li {
  display: flex; align-items: center; justify-content: center; gap: 11px;
  font-size: 0.84375rem; color: var(--ink);
}
.splash-points svg { flex: none; color: var(--gold); }
.splash-in .btn { max-width: 320px; }
/* The decline path. Quieter than the primary, but full-size and full-width:
   it is a real choice, and the peninsula-wide view is a legitimate way to use
   the site, not a consolation prize. */
.splash-alt { margin-top: 10px; }
/* Short viewports, the small iPhone SE and anything in landscape. The panel
   scrolls, so nothing is unreachable either way, but a welcome screen that
   needs scrolling to reach its own button reads as broken. Tighten the air
   rather than cutting the reason the app exists. */
@media (max-height: 660px) {
  .splash { padding-top: 24px; padding-bottom: calc(24px + env(safe-area-inset-bottom)); }
  .splash-mark { width: 104px; margin-bottom: 10px; }
  .splash-why { margin: 12px 0 16px; padding: 12px 0; font-size: 0.8125rem; }
  .splash-points { gap: 9px; margin-bottom: 20px; }
}

/* ------------------------------------------------------------ venue page */
.vp-head { padding-right: 46px; }
.vp-head h2 { font-size: 1.75rem; }
.vp-meta { color: var(--ink-soft); font-size: 0.8125rem; margin-top: 6px; }
.vp-price { display: flex; align-items: baseline; gap: 12px; margin: 20px 0 4px; }
.vp-price .p { font-family: var(--font-fig-lg); font-size: 2.25rem; font-weight: 700; color: var(--gold); letter-spacing: .01em; line-height: 1; }
.vp-price .p .cur { font-family: var(--font-fig); font-size: 1.125rem; font-weight: 500; color: var(--ink-soft); }
.vp-price .sd { font-family: var(--font-fig); font-size: 0.75rem; color: var(--ink-soft); }
.vp-actions { display: flex; gap: 8px; margin: 20px 0 26px; flex-wrap: wrap; }
.vp-section { margin-top: 26px; }
.vp-section h3 { font-family: var(--font-fig); font-size: 0.6875rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: 13px; }
.tap { display: flex; align-items: center; gap: 14px; padding: 12px 0; border-top: 1px solid var(--edge-soft); }
.tap:first-of-type { border-top: 0; }
.tap-name { flex: 1; min-width: 0; }
.tap-name b { display: block; font-weight: 500; font-size: 0.875rem; }
.tap-name span { font-family: var(--font-fig); font-size: 0.75rem; color: var(--ink-soft); }
.tap-fig { text-align: right; font-family: var(--font-fig); }
.tap-fig .p { font-size: 1rem; font-weight: 500; color: var(--gold); }
.tap-fig .sd { display: block; font-size: 0.75rem; color: var(--ink-soft); margin-top: 2px; }

/* ------------------------------------------------------- the data sheet
 * Replaces the old footer links that served raw .md files to a person standing
 * in a pub. Same honesty, readable on a phone. */
.prose { margin-top: 20px; }
.prose h3 {
  font-family: var(--font-fig); font-size: 0.6875rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-soft); margin: 26px 0 10px;
}
.prose p { font-size: 0.875rem; color: var(--ink-soft); margin-bottom: 10px; max-width: 54ch; }
.prose p b, .prose p strong { color: var(--ink); font-weight: 500; }
.prose .ledger { display: flex; flex-direction: column; gap: 1px; background: var(--edge-soft); border: 1px solid var(--edge); border-radius: var(--r-control); overflow: hidden; }
.prose .ledger div { display: flex; gap: 12px; justify-content: space-between; align-items: baseline; background: var(--surface); padding: 11px 13px; font-size: 0.8125rem; }
.prose .ledger dt, .prose .ledger span:first-child { color: var(--ink); }
.prose .ledger span:last-child { font-family: var(--font-fig); font-size: 0.75rem; text-align: right; flex: none; }
.prose .ledger .is-real { color: var(--sea); }
.prose .ledger .is-est { color: var(--coral); }

/* ---------------------------------------------------------------- footer */
/* Centred. The footer is the sign-off, not a column of content: left-aligned it
   read as a fourth section that had run out of things to say, with the lock-up
   stranded against the gutter and a lot of empty page to its right. Centring is
   one declaration here and inherits to every child; the blocks below only need
   their own auto margins, because text-align does nothing for an element that
   has already been given a width. */
.foot {
  border-top: 1px solid var(--edge-soft); margin-top: 30px;
  padding: 18px var(--gutter) 24px;
  font-size: 0.75rem; color: var(--ink-soft);
  text-align: center;
}
/* The horizontal padding is not decoration. `.foot` also carries `.wrap`, and
   the shorthand here used to zero the gutter `.wrap` had just set - it lands
   later in the file at equal specificity, so it won. Left-aligned that read as
   a slightly wider footer; centred it put the lock-up's night tile hard against
   both screen edges at 390px, and on the cellar page the licence note ran into
   the bezel. Every other band on the site is inset by --gutter; so is this. */
/* The full lock-up, slogan included. 400px is the point where "KNOW THE PRICE.
   ENJOY THE POUR." lands at roughly 7px and stops being decoration. */
.foot-logo {
  display: block; width: 100%; max-width: 400px; height: auto;
  object-fit: contain; margin: 0 auto 12px;
}
.foot-line { margin: 0; }
/* The two paragraphs that can run to more than one line. .foot-note is a
   general class - it also captions half the sections on the site, where it is
   correctly left-aligned - so this reaches only the copy of it that lives in
   the footer. .voice-signoff carries a 46ch measure from voice.css, and a
   measured block ignores text-align on its parent, hence the auto margins:
   without them both would sit centred-text-in-a-left-hand-column. */
.foot > .foot-note,
.foot .voice-signoff { margin-left: auto; margin-right: auto; max-width: 62ch; }

/* ----------------------------------------------------------------- toast */
.toast {
  /* Was anchored above the bottom nav bar via --bar-h, a token that no longer
     exists now the bar is gone, an invalid calc() was silently dropping the
     whole `bottom` declaration, so the toast had no vertical position at all. */
  position: fixed; left: 50%; bottom: calc(18px + env(safe-area-inset-bottom));
  transform: translateX(-50%); z-index: 90; max-width: calc(100vw - 32px);
  background: var(--ink); color: var(--bg);
  font-size: 0.8125rem; font-weight: 500; padding: 12px 18px;
  border-radius: var(--r-pill); box-shadow: var(--lift); animation: pop .2s ease;
}
@keyframes pop { from { transform: translate(-50%, 8px); opacity: 0; } }

/* ================================ THE DIAL ===============================
 * A real rotary dial. Prices sit around the rim, the rim turns, and whatever
 * reaches twelve o'clock is the selection. Each label is counter-rotated by
 * exactly the wheel's angle, so the type never tilts, you're turning a dial,
 * not reading a spinning plate. Deco through and through: concentric rings,
 * a marker at the top, a machined hub.
 * ====================================================================== */
.ph { padding-right: 46px; margin-bottom: 20px; }
.ph h2 { font-size: 1.875rem; letter-spacing: .01em; }
.ph-sub { color: var(--ink-soft); font-size: 0.84375rem; margin-top: 8px; line-height: 1.5; }

.venue-pick { display: flex; flex-direction: column; gap: 1px; background: var(--edge-soft);
  border: 1px solid var(--edge); border-radius: var(--r-object); overflow: hidden; margin-bottom: 20px; }
.venue-pick button {
  width: 100%; text-align: left; cursor: pointer; background: var(--surface);
  border: 0; padding: 13px 15px; min-height: var(--tap); color: inherit;
  display: flex; flex-direction: column; gap: 3px;
  transition: background .14s ease;
}
.venue-pick button:hover { background: var(--gold-wash); }
.vp-n { font-family: var(--font-display); font-size: 1.0625rem; }
.vp-m { font-size: 0.75rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

.dial-wrap { position: relative; display: flex; flex-direction: column; align-items: center; margin: 8px 0 20px; }
.dial-read {
  font-family: var(--font-fig-lg); font-variant-numeric: tabular-nums lining-nums;
  font-size: 2.75rem; font-weight: 700; color: var(--gold);
  line-height: 1; letter-spacing: .01em; margin-bottom: 8px;
  position: relative; z-index: 2;
}
/* The selection marker: a deco chevron pointing into twelve o'clock, where the
   selected price now actually sits. It lives inside .dial so it is anchored to
   the rim rather than to the readout's height; it used to overlap the live
   label once the geometry was corrected. */
.dial-marker {
  position: absolute; top: -5px; left: 50%; transform: translateX(-50%);
  width: 0; height: 0; z-index: 3;
  border-left: 7px solid transparent; border-right: 7px solid transparent;
  border-top: 9px solid var(--gold);
}
.dial {
  position: relative; width: min(300px, 78vw); aspect-ratio: 1;
  border-radius: 50%; cursor: grab; touch-action: none;
  background:
    radial-gradient(circle at 50% 50%, var(--surface) 38%, transparent 38.5%),
    conic-gradient(from 0deg, var(--gold-wash), transparent 25%, var(--gold-wash) 50%, transparent 75%, var(--gold-wash));
  border: 1px solid var(--edge);
  box-shadow: var(--lift);
  -webkit-user-select: none; user-select: none;
}
.dial:active { cursor: grabbing; }
.dial::before {
  content: ""; position: absolute; inset: 11%;
  border: 1px solid var(--edge); border-radius: 50%;
}
.dial-rim { position: absolute; inset: 0; }
.dial-hub {
  position: absolute; top: 50%; left: 50%; width: 26%; aspect-ratio: 1;
  transform: translate(-50%, -50%); border-radius: 50%;
  background: var(--surface); border: 1px solid var(--edge);
}
/* Each mark is rotated out to its place on the rim; the <b> inside is spun
   back by the same amount plus the wheel's own rotation, which is what keeps
   every price upright no matter where the dial is. */
.dial-mark {
  position: absolute; top: 50%; left: 50%; width: 50%; height: 0;
  transform-origin: 0 0; display: flex; align-items: center; justify-content: flex-end;
  pointer-events: none;
}
.dial-mark i { width: 9px; height: 1px; background: var(--edge); margin-right: 8px; flex: none; }
.dial-mark.is-live i { width: 15px; height: 2px; background: var(--gold); }
.dial-mark b {
  font-family: var(--font-fig); font-variant-numeric: tabular-nums lining-nums;
  font-size: 0.8125rem; font-weight: 400; color: var(--ink-soft);
  margin-right: 12px; display: block;
}
.dial-mark.is-live b { font-family: var(--font-fig-lg); color: var(--gold); font-weight: 700; font-size: 0.9375rem; }

.dial-foot { display: flex; gap: 10px; margin-top: 6px; }
.dial-fine { font-size: 0.75rem; color: var(--ink-soft); text-align: center; margin-top: 12px; }

/* Reduced motion removes travel, not feedback.
 *
 * The previous blanket `transition-duration: .01ms` on `*` also killed every
 * colour and border transition, so hovering a row or focusing a control snapped
 * with no sense that anything had responded, and it froze the loading spinner
 * and the skeleton mid-frame, turning two "we're working on it" signals into
 * two pieces of broken-looking furniture. Reduced motion means less movement,
 * not less information. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto !important; }

  /* Decorative entrances: arrive, don't travel. */
  .panel, .toast, .splash { animation: none !important; }

  /* Movement off; colour and border feedback stays. */
  .row, .row-open, .pin, .btn, .hh-card, .dial {
    transition-property: background-color, border-color, color !important;
    transition-duration: .12s !important;
  }
  .row:active, .pin:hover, .row:hover { transform: none !important; }

  }

@media (min-width: 760px) {
  .toast { bottom: 28px; }
}

/* ===================== FORCED COLOURS (Windows High Contrast) =============
 * The trust ladder carries its state in inset box-shadows and gradients, all
 * of which are discarded in forced-colours mode, so confirmed / reported /
 * estimate collapsed into three identical dots. Same for the "location
 * unconfirmed" pin and its legend key. Redraw the distinction with borders,
 * which survive, and let the system palette own the colours.
 * ====================================================================== */
@media (forced-colors: active) {
  .mark, .lg-dot {
    forced-color-adjust: none;
    border: 1px solid CanvasText; background: Canvas; box-shadow: none;
  }
  .row-proof[data-trust="confirmed"] .mark,
  .lg-dot.cheap { background: CanvasText; }
  /* Filled AND ringed. The ring is the only thing separating this from
     confirmed once the system palette owns the colours, so it is drawn with
     an outline rather than a shadow — outlines survive forced colours,
     box-shadows are discarded, which is the whole reason this block exists. */
  .row-proof[data-trust="venue"] .mark {
    background: CanvasText;
    outline: 1px solid CanvasText; outline-offset: 1.5px;
  }
  /* Half-filled: reported. A left border thick enough to read at 9px. */
  .row-proof[data-trust="reported"] .mark {
    background: linear-gradient(90deg, CanvasText 50%, Canvas 50%);
  }
  .row-proof[data-trust="estimate"] .mark,
  .mark[data-trust="estimate"],
  .lg-dot.approx { background: Canvas; border-style: dashed; }

  .pin { forced-color-adjust: none; background: Canvas; color: CanvasText; border: 1px solid CanvasText; }
  .pin.is-cheap { background: CanvasText; color: Canvas; }
  .pin.is-approx { background: Canvas; color: CanvasText; border-style: dashed; }
  .pin.is-dot { background: CanvasText; }
  .pin.is-dot.is-approx { background: Canvas; }

  /* Hairline rules and washes vanish; keep the structure legible. */
  .row, .hh-card, .panel, .venue-pick, .prose .ledger { border: 1px solid CanvasText; }
  .row.is-first { border-width: 2px; }
  .row.is-first::before { background: CanvasText; }
  .btn-primary { border: 1px solid CanvasText; }
  :focus-visible { outline: 2px solid CanvasText; }
}

/* ======================== REDUCED TRANSPARENCY ===========================
 * The masthead, the map legend and the attribution all sit on translucent,
 * blurred backdrops. Where the OS asks for less transparency, make them
 * opaque: the content underneath stops bleeding through the type.
 * ====================================================================== */
@media (prefers-reduced-transparency: reduce) {
  .masthead, .map-legend, .leaflet-control-attribution {
    backdrop-filter: none !important;
    background: var(--bg) !important;
  }
  .scrim { backdrop-filter: none; background: rgba(6,18,16,.86); }
}

/* The toast is a permanent live region now (see ui.js), so visibility is a
   class rather than `hidden` - the element has to stay in the accessibility
   tree for `role="status"` to announce anything at all. */
.toast:not(.is-up) { opacity: 0; visibility: hidden; pointer-events: none; }
.toast { display: flex; align-items: center; gap: 14px; transition: opacity .2s ease; }
.toast-act {
  flex: none; background: none; border: 0; cursor: pointer;
  font: 600 0.8125rem/1 var(--font-ui); color: var(--gold-lamp);
  text-decoration: underline; text-underline-offset: 3px;
  padding: 6px 2px; min-height: 32px;
}
:root[data-time="night"] .toast-act { color: var(--gold); }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }

/* ============================ WHICH DRINK =============================
 * Four benchmarks, one choice. Real radios underneath so the group is
 * announced as a group and arrow keys work; the input itself is hidden and
 * the label carries the whole target, which keeps it well past --tap.
 * ====================================================================== */
.drinks { border: 0; margin: 0 0 12px; padding: 0; min-width: 0; }
.drinks-legend {
  padding: 0; margin-bottom: 7px;
  font-size: 0.6875rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-soft); font-weight: 500;
}
.drinks-set { display: flex; gap: 6px; flex-wrap: wrap; }
.drink {
  flex: 1 1 auto; min-width: 132px; position: relative; cursor: pointer;
  display: flex; flex-direction: column; gap: 1px; justify-content: center;
  min-height: var(--tap); padding: 8px 13px;
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-control);
  transition: background .14s ease, border-color .14s ease;
}
.drink input { position: absolute; opacity: 0; width: 0; height: 0; }
.drink-size { font-family: var(--font-display); font-size: 0.9375rem; line-height: 1.1; }
.drink-kind { font-size: 0.6875rem; color: var(--ink-soft); letter-spacing: .04em; }
.drink:hover { border-color: var(--gold); }
.drink.is-on { border-color: var(--gold); background: var(--gold-wash); }
/* --gold-ink, not --gold: this is 15px text sitting on --gold-wash, the
   same pair the current nav item uses, and plain gold misses AA on it. */
.drink.is-on .drink-size { color: var(--gold-ink); }
.drink:has(input:focus-visible) { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ---------------------------------------------- price per standard drink */
.row-std, .vp-std {
  display: block; font-family: var(--font-fig);
  font-variant-numeric: tabular-nums lining-nums;
  font-size: 0.75rem; color: var(--ink-soft); margin-top: 3px;
}
.row-fig .row-std { text-align: right; }
@media (max-width: 400px) { .row-fig .row-std { text-align: left; } }

/* What the beer costs when the deal is not running. Only drawn when a happy
   hour is actually applied, so it is never a fake "was" price of the sort
   retail uses — it is the venue's own standard price, struck through because
   right now you would not pay it. */
.row-was {
  display: block; font-family: var(--font-fig);
  font-variant-numeric: tabular-nums lining-nums;
  font-size: 0.75rem; color: var(--ink-soft); margin-top: 2px;
  text-decoration: line-through; text-decoration-thickness: 1px;
}
.row-fig .row-was { text-align: right; }
@media (max-width: 400px) { .row-fig .row-was { text-align: left; } }
/* The "$" is a real element now, not a ::before. Generated content is not
   reliably announced, and this is the one figure on the page that matters. */
.row-price .cur, .vp-price .p .cur {
  font-family: var(--font-fig); font-size: 0.875rem; font-weight: 500;
  color: var(--ink-soft); margin-right: 1px;
}
.vp-price .p .cur { font-size: 1.125rem; }
.tap-fig .sd { display: block; font-size: 0.75rem; color: var(--ink-soft); margin-top: 2px; }
.tap.is-now { position: relative; }
.tap-now {
  display: inline-block; margin-left: 6px; padding: 1px 7px;
  font-size: 0.6875rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--gold); background: var(--gold-wash);
  border: 1px solid color-mix(in srgb, var(--gold) 30%, transparent);
  border-radius: var(--r-pill);
}
/* Was a title-attribute-only affordance, i.e. hover-only: invisible to touch
   and to keyboard. It is text now. */
.unplaced { display: inline-flex; align-items: center; gap: 4px; color: var(--ink-soft); }
.unplaced .mark { box-shadow: inset 0 0 0 1.5px var(--ink-soft); }

/* ------------------------------------------- the index and the saved list */
.saved, .allwrap { margin-top: 26px; }
.rows-slim { gap: 6px; }
.row-slim .row-open { padding: 11px 15px; grid-template-columns: 1fr auto; }
.row-slim .row-name { font-size: 1rem; }
.row-price-sm { font-size: 1.25rem; }
.row-none { font-size: 0.75rem; color: var(--ink-soft); }
.field-search { margin-bottom: 12px; }
.field-search input { min-height: var(--tap); }
.allwrap .foot-note { margin-top: 10px; }

/* THE PICKED VENUE. One card, and the restraint is the point: this is a
   summary, and a summary that lists everything is just the sheet again with
   worse typography. The price is the one thing set large, because it is the
   one thing the whole site is for. */
.vcard {
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-object); box-shadow: var(--lift);
  padding: 16px 17px; margin-top: 2px;
}
.vc-name { font-family: var(--font-display); font-size: 1.25rem; line-height: 1.15; }
.vc-meta { color: var(--ink-soft); font-size: 0.8125rem; margin-top: 5px; }
/* Price, what it buys, and how much to trust it, on one baseline. Wraps to
   its own line on a narrow phone rather than crushing the middle column. */
.vc-fig {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 14px;
  margin: 14px 0 12px; padding: 12px 0;
  border-top: 1px solid var(--edge-soft); border-bottom: 1px solid var(--edge-soft);
}
.vc-price {
  font-family: var(--font-fig-lg); font-variant-numeric: tabular-nums lining-nums;
  font-size: 2rem; font-weight: 700; color: var(--gold); line-height: 1;
}
.vc-price .cur { font-family: var(--font-fig); font-size: .55em; font-weight: 500; color: var(--ink-soft); }
.vc-what { font-size: 0.8125rem; color: var(--ink); flex: 1; min-width: 12ch; }
.vc-dim { color: var(--ink-soft); }
.vc-row {
  display: flex; align-items: center; gap: 8px;
  font-size: 0.8125rem; margin-bottom: 8px;
}
.vc-row svg { flex: none; color: var(--gold); }
.vc-bit { display: inline-flex; align-items: center; gap: 4px; margin-right: 14px; }
.vc-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.vc-actions .btn { flex: 1; min-width: 9rem; }
.vc-actions .btn[disabled] { opacity: .5; cursor: not-allowed; }

/* SIDE BY SIDE. Same table grammar as the board on exchange.html - it scrolls
   inside its own box so the page never scrolls sideways. */
.cmp { margin-top: 16px; }
.cmp-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.cmp-head h3 {
  font-family: var(--font-fig); font-size: 0.6875rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-soft);
}
.cmp-clear { min-height: 32px; padding: 4px 10px; font-size: 0.75rem; }
.cmp-scroll {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--edge); border-radius: var(--r-object); background: var(--surface);
}
.cmp-table { width: 100%; border-collapse: collapse; min-width: 30rem; }
.cmp-table th, .cmp-table td { padding: 10px 12px; text-align: left; vertical-align: middle; }
.cmp-table thead th {
  font: 500 0.6875rem/1 var(--font-fig); letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-soft); background: var(--sunk); border-bottom: 1px solid var(--edge);
}
.cmp-table tbody tr { border-top: 1px solid var(--edge-soft); }
.cmp-table tbody tr:first-child { border-top: 0; }
/* The cheapest row is tinted, not coloured-in: the flag says which one it is,
   and the tint is only there to find it again after you look away. */
.cmp-best { background: var(--gold-wash); }
.cmp-name {
  display: block; background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--font-display); font-size: 0.9375rem; line-height: 1.2;
  color: var(--ink); text-align: left;
}
.cmp-name:hover { color: var(--gold-ink); text-decoration: underline; }
.cmp-sub { display: block; font-size: 0.6875rem; color: var(--ink-soft); margin-top: 3px; }
.cmp-price { font-weight: 600; color: var(--gold); white-space: nowrap; }
.cmp-flag {
  font-family: var(--font-fig); font-size: 0.625rem; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--gold-ink);
}
.cmp-x {
  background: none; border: 0; cursor: pointer; color: var(--ink-soft);
  padding: 8px; min-width: var(--tap); min-height: var(--tap);
  display: inline-flex; align-items: center; justify-content: center;
}
.cmp-x:hover { color: var(--coral); }
@media (max-width: 400px) {
  .row-slim .row-open { grid-template-columns: 1fr; row-gap: 6px; }
  .row-slim .row-fig { grid-column: 1; text-align: left; }
}

/* The location button reads as on/off now that it toggles. */
#iCentre.is-on { border-color: var(--gold); color: var(--gold); background: var(--gold-wash); }
/* Saved state on the heart, which used to only be conveyed by re-rendering. */
[data-save].is-saved { color: var(--coral); border-color: var(--coral); }
[data-save].is-saved svg { fill: color-mix(in srgb, var(--coral) 22%, transparent); }

/* ---------------------------------------------- one-tap confirm (B8) */
.agree-fig {
  font-family: var(--font-fig-lg); font-variant-numeric: tabular-nums lining-nums;
  font-size: 3.5rem; font-weight: 700; color: var(--gold);
  line-height: 1; text-align: center; margin: 10px 0 26px;
}
.agree-fig .cur { font-family: var(--font-fig); font-size: 1.375rem; font-weight: 500; color: var(--ink-soft); }
.agree { display: flex; flex-direction: column; gap: 9px; }

/* ------------------------------------------- typed price entry (D33) */
.dial-type {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-top: 4px;
}
.dial-type label { font-size: 0.75rem; color: var(--ink-soft); }

/* The optional beer name. Stacked rather than inline, because on a 360px phone
   a label beside a text input leaves about 140px to type a beer name into.
   Visually quieter than the price row above it on purpose: this is the field
   nobody has to fill in, and it should not compete with the one that matters. */
.dial-note { flex-direction: column; align-items: stretch; gap: 5px; margin-top: 12px; }
.dial-note label { text-align: left; }
.dial-note .opt { opacity: .7; font-style: italic; }
.dial-note input {
  width: 100%; min-height: var(--tap);
  background: var(--sunk); color: var(--ink);
  border: 1px solid var(--edge); border-radius: var(--r-control);
  padding: 8px 12px; font: inherit; font-size: 0.9375rem;
}
.dial-note input::placeholder { color: var(--ink-soft); opacity: .8; }
.dial-type-in {
  display: inline-flex; align-items: center; gap: 2px;
  background: var(--sunk); border: 1px solid var(--edge);
  border-radius: var(--r-control); padding: 0 10px; min-height: var(--tap);
}
.dial-type-in span { font-family: var(--font-fig); font-size: 0.875rem; color: var(--ink-soft); }
.dial-type-in input {
  width: 5.5ch; border: 0; background: none; color: var(--ink);
  font-family: var(--font-fig); font-variant-numeric: tabular-nums lining-nums;
  font-size: 1rem; padding: 10px 0; min-height: var(--tap);
}
.dial-type-in input:focus { outline: none; }
.dial-type-in:focus-within { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ------------------------------------------------ palette control (D24) */
.timeset { border: 0; margin: 0; padding: 0; display: flex; gap: 6px; flex-wrap: wrap; }
.timeopt {
  flex: 1 1 auto; min-width: 96px; position: relative; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap); padding: 8px 12px; text-align: center;
  font-size: 0.8125rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-control);
  transition: background .14s ease, border-color .14s ease;
}
.timeopt input { position: absolute; opacity: 0; width: 0; height: 0; }
.timeopt:hover { border-color: var(--gold); }
.timeopt.is-on { border-color: var(--gold); background: var(--gold-wash); color: var(--gold); }
.timeopt:has(input:focus-visible) { outline: 2px solid var(--gold); outline-offset: 2px; }
#timeNote { margin-top: 9px; }

/* The CTA label was breaking across two lines in the 430-560px band, where
   there is room for the words but not much slack. It is two words; keep it on
   one line and let the wordmark take the squeeze, which it is already built
   to do. */
.masthead-cta { white-space: nowrap; flex: none; }
/* And the unplaced note travels with its own separator, so a wrap can't leave
   a dangling middot at the end of the line above. */
.unplaced { white-space: nowrap; }

/* ================================ THE SPREAD =============================
 * Where one price sits among all of them. The sentence carries the meaning;
 * the scale is the picture of it, and is aria-hidden because a screen reader
 * reading out 84 tick positions would be worse than useless.
 * ====================================================================== */
.spread { margin: 18px 0 4px; }
.spread-say { font-size: 0.8125rem; color: var(--ink-soft); line-height: 1.5; max-width: 46ch; }
.spread-say b { color: var(--ink); font-weight: 500; }

.spread-scale {
  position: relative; height: 34px; margin: 14px 0 6px;
  border-top: 1px solid var(--edge-soft); border-bottom: 1px solid var(--edge-soft);
}
/* The middle half, banded. "Normal" is a range, not a point. */
.spread-band {
  position: absolute; top: 0; bottom: 0;
  background: var(--gold-wash);
  border-left: 1px solid color-mix(in srgb, var(--gold) 28%, transparent);
  border-right: 1px solid color-mix(in srgb, var(--gold) 28%, transparent);
}
/* One tick per venue. Every one is a real price somebody charges. */
.spread-scale i {
  position: absolute; top: 9px; width: 1px; height: 16px;
  background: var(--ink-soft); opacity: .45; transform: translateX(-.5px);
}
.spread-mid {
  position: absolute; top: 4px; bottom: 4px; width: 1px;
  background: var(--ink-soft); opacity: .75;
}
/* This price. The one mark allowed to read as a mark. */
.spread-you {
  position: absolute; top: -3px; bottom: -3px; width: 3px;
  background: var(--gold); border-radius: 2px;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--bg) 70%, transparent);
  transform: translateX(-1.5px);
}
.spread-key {
  display: flex; justify-content: space-between; gap: 10px;
  font-family: var(--font-fig); font-variant-numeric: tabular-nums lining-nums;
  font-size: 0.6875rem; color: var(--ink-soft);
}
.spread-key-mid { color: var(--gold); }

@media (forced-colors: active) {
  .spread-band { forced-color-adjust: none; background: Canvas; border-color: CanvasText; }
  .spread-scale i, .spread-mid { background: CanvasText; opacity: 1; }
  .spread-you { background: Highlight; box-shadow: none; }
}

/* Why four benchmarks exist at all. Without this the picker reads as a filter
   rather than as "we hold the drink constant so venues are comparable". */
.drinks-why { font-size: 0.6875rem; color: var(--ink-soft); margin-top: 7px; max-width: 46ch; }

/* ------------------------------------------------------- right now (§3) */
.rightnow {
  margin: 0 0 14px; padding: 11px 14px;
  background: var(--surface); border: 1px solid var(--edge);
  border-left: 3px solid var(--gold);
  border-radius: var(--r-control);
  font-size: 0.8125rem; line-height: 1.5; color: var(--ink-soft);
}
.rightnow b { color: var(--ink); font-weight: 500; }

/* ------------------------------------------------- price history (§3) */
.spark {
  width: 100%; height: 54px; display: block; margin-bottom: 10px;
  color: var(--gold); overflow: visible;
}
.spark circle { fill: var(--bg); stroke: currentColor; stroke-width: 2; }
/* Your own observations read differently from everyone else's. */
.spark circle.is-mine { fill: var(--gold); }
.hist-mine { color: var(--ink-soft); }
@media (forced-colors: active) {
  .spark { color: CanvasText; }
  .spark circle { fill: Canvas; stroke: CanvasText; }
  .spark circle.is-mine { fill: CanvasText; }
}

/* --------------------------------------------------- the cellar (deep.html)
   The full register, ranked. Reuses .row/.row-slim wholesale; the only new
   shapes are the sort strip and the rank numeral, which .row-slim drops on
   the front page (the index there is alphabetical-by-distance, not a
   ranking) and this page needs back. */
.deepwrap .sec-head p { max-width: 60ch; }
.deep-controls { margin-bottom: 14px; }
.deep-sort { display: flex; flex-wrap: wrap; gap: 6px; }
.deep-sort .btn { font-size: 0.8125rem; padding: 8px 12px; min-height: 0; }
.deep-sort .btn[aria-pressed="true"] {
  border-color: var(--gold); color: var(--gold); background: var(--gold-wash);
}
/* Nothing on a row here opens a dialog, so the row is not a button. Same
   grid, no cursor or focus affordance promising an interaction that does
   not exist. */
.deepwrap .row-slim .row-open { grid-template-columns: 26px 1fr auto; cursor: default; }
.deepwrap .row-static { display: grid; }
/* Its own line. .row-meta is an inline span, so an inline-flex sibling
   trails it and "Take me there" ended up finishing the address line on any
   row whose meta happened to be short. */
.deepwrap .row-meta { display: block; }
.deepwrap .row-why {
  display: block; width: max-content;
  background: none; border: 0; padding: 0; margin-top: 4px; color: var(--ink-soft);
}
.deepwrap .row-why a { color: var(--sea); }
@media (max-width: 400px) {
  .deepwrap .row-slim .row-open { grid-template-columns: 22px 1fr; row-gap: 6px; }
  .deepwrap .row-slim .row-fig { grid-column: 2; text-align: left; }
}
@media (forced-colors: active) {
  .deep-sort .btn[aria-pressed="true"] { border: 2px solid Highlight; }
}
/* The door. Quiet by design - it is the last line of the index, not a call
   to action competing with the ranking at the top of the page. */
.deep-door { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--edge-soft); }
.deep-door a { color: var(--ink-soft); text-decoration-color: var(--edge); }
.deep-door a:hover { color: var(--gold); }
