/* The brand page (/) only: its sections, and the three interactions. Shared chrome - tokens,
   type, buttons, nav, the age gate, the footer - stays in site.css. */

/* ---- Hero ---- */
/* overflow-x: clip, and only x: the gold ring below ends its expansion at 1.5x the cap, which on a
   320px phone reaches about 13px past each edge of the viewport and made the whole page scroll
   sideways once the cap had popped. `clip` rather than `hidden` so this never becomes a scroll
   container, and x rather than both so the cap's own vertical travel is untouched. On any screen
   with room for the ring, nothing is clipped at all. */
.hero { position: relative; min-height: 100svh; display: grid; align-content: center; justify-items: center; text-align: center; padding: 5rem var(--gutter) 4rem; overflow-x: clip; }
/* The seal's own stacking context, so the ring that rings out can sit behind the cap (z-index: -1)
   without falling behind the page. The floor on the height is not decoration: the cap settles at
   .68 scale and the full seal turns to mush below 120px, so the pre-pop size can never go under
   120 / .68 = 177px. 11.5rem settles at 125px. */
.hero-art {
  /* One definite size for the seal, shared by the frame, the button and the image: a percentage
     chain here resolved against the SVG's intrinsic 1000px instead and overflowed the hero.
     The 72vw cap is load-bearing, not tidying: .cap-btn is a FIXED width, so on a short wide-ish
     phone 42vh alone came out larger than the hero's content box (302px inside 272px at 320x720),
     the grid column stretched to the button, and the whole hero sat 15px right of centre with 6px
     clipped by body's overflow-x. The clamp floor still wins, so the seal can never drop under the
     177px the full mark needs. */
  --cap-size: clamp(11.5rem, min(42vh, 72vw), 24rem);
  position: relative; isolation: isolate; display: grid; place-items: center;
  /* The frame stays --cap-size and the margin under it is what came down. The hero used to carry
     ~105px of empty navy between the settled seal and "Good drinks," at every width, worst under
     reduced motion where the settled state is all anyone ever sees - and it was three things:
     ~43px of spent .cap-hint still holding its line box, 25.6px of margin here, and about 3% of
     frame below where the settled cap actually ends. The hint is out of the flow now (see below)
     and this margin is 1.25rem.

     The frame's own slack below the settled cap - ((1 - .68) / 2 - .07) = .09 of --cap-size, 34px
     at 1440 - was tried as a negative margin on .hero.popped and taken back out: measured settled,
     it closed the gap to -4px and put the seal on the headline. The number that misled me into it
     was read from a hero that had .popped but not .settled, i.e. mid-tumble, where the cap's box
     is nowhere near where it lands. Measure the SETTLED state or measure nothing.

     DO NOT shrink this height to "fit" the settled cap. Tried on 2026-10-31 at .80 of --cap-size,
     which is where the settled cap's visual box ends: .cap-btn is a FIXED --cap-size square, so a
     shorter frame does not scale it - the grid simply overflows, and an overflowing grid item
     aligns to the START rather than staying centred. The cap moved DOWN by the whole overflow and
     ended up 22px over the headline (measured at 1282x653). The settled cap's visual bottom sits
     at about 97% of the frame, so there is roughly 3% to reclaim here, not 20%.

     apple-design 16.6: hierarchy comes from spacing, and 105px of nothing is not hierarchy. */
  width: 100%; height: var(--cap-size); margin-bottom: 1.25rem;
}
.hero h1 { margin-bottom: .7rem; }
.hero .lede { margin: 0 auto 1.7rem; }
.cta { display: flex; gap: .7rem; justify-content: center; flex-wrap: wrap; }
.badges {
  display: flex; gap: 1.6rem; justify-content: center; flex-wrap: wrap;
  margin-top: 2.4rem; font-family: "EB Garamond", serif; font-size: .8125rem;
  letter-spacing: .18em; text-transform: uppercase; color: var(--muted);
}
.badges span { display: flex; align-items: center; gap: .5rem; }
.badges i { width: .3rem; height: .3rem; border-radius: 50%; background: var(--gold); display: block; }

/* ---- Pop the cap ----
   The seal is a crown cap, so tapping it pops one: the cap is pried up, turns about once, and
   settles smaller and tilted - it never leaves the frame, because the mark has to stay on screen.
   A gold ring rings out once from where it sat, and the tagline and lede land beneath it.
   Everything is driven from .hero.popped; js/cap-pop.js only adds that class, on a tap, on a key,
   or on its own after a couple of seconds. The cap itself carries the motion and the button stays
   put, so the ring expands from where the cap sat rather than from where it ended up. */
.cap-btn {
  position: relative; display: block; width: var(--cap-size); height: var(--cap-size);
  padding: 0; border: 0; background: none; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.cap { display: block; width: 100%; height: 100%; }
/* The compositor is warned while the motion is coming AND while it is happening, and only let go
   once the cap has landed.
   It used to say `.hero:not(.popped)`, which dropped the hint at the exact moment the animation
   started - the one moment it is for. Without it the cap is not on a layer of its own, so Chrome
   re-rasterises the seal as it turns and shrinks: an ornate 1000x1000 SVG, rotated 372 degrees,
   redrawn frame by frame. That is a compositor cost, which is why main-thread frame timings read
   a clean 60fps while the picture stuttered (Dylan, 2026-10-29).
   `.settled` is added by js/cap-pop.js when the animation ends, because a permanent will-change is
   a permanent layer, and that is a cost of its own. */
.js .hero:not(.settled) .cap { will-change: transform; }
/* Feedback on press, not on release. */
.cap-btn:active .cap { transform: scale(.975); transition: transform .1s var(--settle); }
/* Spent: the pop happens once, so the control stops taking taps (cap-pop.js takes it out of the
   tab order and relabels it as the mark at the same moment). */
.hero.popped .cap-btn { pointer-events: none; cursor: default; }

/* A quick pry, about one turn, then a settle that eases down to the landing.
   The pry's exit curve is deliberate: it used to be cubic-bezier(.26, .78, .3, 1), whose end
   tangent is horizontal, and the segment after it starts slowly too, so the cap came to a near dead
   stop at the 22% waypoint (104 degrees, 0.03 degrees per 11.5ms sample) and then lurched away.
   The owner saw that as a freeze "at about 135 degrees". Nothing dropped a frame - the tumble is
   composited and the frame timings were clean - the motion stopped by instruction. The exit control
   point is now (.45, .75), so the pry still decelerates into the apex but leaves it at about 2 degrees
   per sample instead of 0. The start of the curve is unchanged, so the opening pry is exactly as it was.
   The second segment runs on `ease`, written out literally: it is what has always run there (see the
   trap noted at the 22% keyframe), and its first step, 1.59 degrees per sample, is the floor of the
   apex exit. Swapping in the damped --settle curve would start that segment near 9.5 degrees per
   sample, a jerk forward against the pry's 2; that changes how the tumble feels and is the owner's
   call, not a side effect of removing the stall. */
.hero.popped .cap { animation: cap-pop 1.15s both; }
@keyframes cap-pop {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); animation-timing-function: cubic-bezier(.26, .78, .45, .75); }
  /* A TRAP: var() is not resolved inside a keyframe's animation-timing-function. It is accepted,
     it sits in the rule, and it does nothing - the segment silently takes the animation's own easing,
     which here is the default `ease` because .hero.popped .cap names none (measured: var(--settle)
     and no timing function at all give identical rotation; the literal curve does not). So this is
     written as `ease`, what actually runs, rather than a --settle that reads as if it applied.
     --settle does resolve in ordinary `transition` declarations, which is where this file uses it. */
  22%  { transform: translate3d(0, -13%, 0) rotate(104deg) scale(.93); animation-timing-function: ease; }
  100% { transform: translate3d(0, 7%, 0) rotate(372deg) scale(.68); }
}

/* The ring: one expansion, never a pulse. Gold is metal here too - a lit gradient masked to a
   ring, not a flat stroke - and it sits behind the cap so it never crosses the mark. */
.cap-btn::after {
  content: ""; position: absolute; left: 50%; top: 50%; z-index: -1;
  width: 100%; aspect-ratio: 1; translate: -50% -50%;
  border-radius: 50%; opacity: 0; pointer-events: none;
  background: linear-gradient(135deg, var(--gold-hi), var(--gold) 46%, var(--gold-lo));
  /* A line, not a halo: the mask is cut in pixels off the unscaled box, so the ring stays a
     hairline the whole way out instead of blooming into a soft disc. */
  -webkit-mask: radial-gradient(closest-side, transparent 0 calc(100% - 3px), #000 calc(100% - 2px));
  mask: radial-gradient(closest-side, transparent 0 calc(100% - 3px), #000 calc(100% - 2px));
}
.hero.popped .cap-btn::after { animation: cap-ring .8s var(--settle) both; }
@keyframes cap-ring {
  from { opacity: .95; scale: .52; }
  to   { opacity: 0; scale: 1.5; }
}

/* The invitation, and only while there is something to tap.
   Out of the flow, because it is spent the moment the cap pops: .hero.popped hides it with
   visibility, which keeps its 19.2px line box and 24px of margin reserved forever - 43px of the
   105px hole the hero used to carry under the seal. It now lives INSIDE .hero-art (which is the
   positioning context) and is pinned just under the frame, so it occupies no flow space at all,
   before or after. */
.cap-hint {
  position: absolute; bottom: -1.3rem; left: 0; right: 0; display: block; margin: 0;
  font-family: "EB Garamond", serif; font-size: .75rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--muted); transition: opacity .32s var(--settle), visibility .32s;
}
html:not(.js) .cap-hint { display: none; }   /* without JS there is nothing to tap */
.hero.popped .cap-hint { opacity: 0; visibility: hidden; }

/* The tagline and the lede land behind the cap. Both are static text in index.html and visible
   with JS off - only the .js class opts them into the landing, and the cap always lands.
   --settle, not --spring: nothing the user did carried momentum into this - on the auto-pop path
   there is no gesture at all - so it damps rather than overshooting. (Task 5's brief asked for
   --spring here; amended by ruling, because CLAUDE.md's curve rule wins.) */
.js .hero .tagline, .js .hero .lede { opacity: 0; transform: translateY(1.15rem); }
.js .hero.popped .tagline, .js .hero.popped .lede {
  opacity: 1; transform: none;
  transition: opacity .46s var(--settle), transform .66s var(--settle);
}
.js .hero.popped .tagline { transition-delay: .3s; }
.js .hero.popped .lede { transition-delay: .4s; }

/* The Lineup grid went with its section (Dylan, 2026-10-28): .lineup, .drink, .swatch, .tag and
   .drink .find. Three cards summarising drinks the carousels show in full a screen below were a
   duplicate, and a coloured disc standing in for a product we now have a can for was strictly
   worse than the can. bars.html's sheet rows are .drink-sheet in css/bars.css and are untouched. */

/* The two feature panels that lived here - .feature, .facts, .fact and the glass that carried the
   pour - went with the panels themselves (see "Retired: the scroll-linked pour" in website-spec.md).
   .art stays: the trade section still uses it. */
.art { display: grid; place-items: center; }

/* ---- The two drink bands ----
   The band itself is css/carousel.css. What belongs here is the page's side of the join: how a can
   is sized inside a cell, the status label that sits with it, and the static cards that are these
   sections with JavaScript off. */

/* The art row absorbs the slack so the name and the position sit on the same line across the whole
   band. Without this a badge that wraps to two lines - "Concept - not yet in market" does at phone
   widths - pushed that one card's name down and broke the row. The can is pinned to the bottom of
   its row so the cans stand on a common line rather than floating at different heights. */
#beers .carousel-can,
#sodas .carousel-can { height: 100%; grid-template-rows: 1fr auto auto; }
.can-figure { display: grid; justify-items: center; align-content: end; gap: .7rem; height: 100%; }

/* Sized from the HEIGHT, not the width: the can is 440x940, so width:100% in a 265px cell would
   make it 565px tall and swallow a phone screen. Beats can.css's .can-render (0,1,0) on
   specificity, and only where a .can-figure wraps it. */
.can-figure .can-render {
  width: auto; height: clamp(18rem, 56vw, 21rem); max-width: 100%; margin: 0 auto;
}

/* Status, in the same box as the can so a screenshot of the can carries it. Never in the artwork:
   a print file must never have CONCEPT set into it. */
.can-flag {
  font-family: "EB Garamond", Georgia, serif; font-weight: 600;
  font-size: .6875rem; line-height: 1.3; letter-spacing: .16em; text-transform: uppercase;
  text-align: center; text-wrap: balance;
  padding: .32rem .6rem; border: 1px solid rgba(192, 168, 128, .42); border-radius: 2px;
  color: var(--gold);
}
/* A drink someone can order today is the one fact in the band worth stating positively. */
.can-flag-pouring { color: var(--navy); background: var(--gold-hi); border-color: var(--gold-hi); }

/* Only true once there is something to tap, exactly like the cap hint. */
.band-hint {
  margin-top: 1rem;
  font-family: "EB Garamond", Georgia, serif; font-size: .75rem;
  letter-spacing: .22em; text-transform: uppercase; color: var(--muted);
}
html:not(.js) .band-hint { display: none; }

/* ---- The facts: the card's back face ----
   Read at a card's width (190px of text at 320px wide), not at a band's, which is what the change
   on 2026-10-29 was for. So the gaps close up a little and the label column narrows: at 4.5rem it
   left "In development - not pouring yet" wrapping to four lines. */
.can-detail { display: grid; gap: .7rem; align-content: start; }
.can-back-name {
  font-size: 1.15rem; line-height: 1.15; letter-spacing: -.01em;
  padding-bottom: .55rem;
  border-bottom: 1px solid rgba(192, 168, 128, .28);
}
.can-facts { display: grid; gap: .6rem; }
.can-fact { display: grid; grid-template-columns: 3.6rem 1fr; gap: .7rem; align-items: baseline; }
.can-fact dt {
  font-family: "EB Garamond", Georgia, serif; font-weight: 600;
  font-size: .75rem; letter-spacing: .16em; text-transform: uppercase; color: var(--gold);
}
.can-fact dd { font-size: .95rem; color: var(--muted); }
.can-note { font-size: .95rem; color: var(--muted); }
.can-cta { margin-top: .4rem; }

/* On a card's back face these same facts have 190px to live in rather than a reading measure, so
   the rows close up and the values come down a notch. Measured, not guessed: it is what keeps the
   longest in the range - the one drink carrying a flagged ABV, a tasting note AND a call to
   action - inside its card at 320px instead of 8px over. The static no-JS cards keep the larger
   type, because there they have a card's full width. */
.carousel-details .can-detail { gap: .55rem; }
.carousel-details .can-back-name { font-size: 1.1rem; padding-bottom: .45rem; }
.carousel-details .can-facts { gap: .5rem; }
.carousel-details .can-fact { grid-template-columns: 3.2rem 1fr; gap: .6rem; }
.carousel-details .can-fact dd,
.carousel-details .can-note { font-size: .9rem; line-height: 1.45; }
.carousel-details .can-cta { margin-top: .1rem; }

/* ---- The same two bands with JavaScript off ----
   A plain grid of cards. js/page.js replaces this list wholesale, the way initLocator replaces the
   static account rows, so nothing here is ever on screen beside the rendered band. */
.can-static {
  list-style: none; display: grid; gap: 1.1rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  max-width: 70rem; margin: 0 auto; padding: 0 var(--gutter);
}
.can-card {
  display: grid; gap: .5rem; align-content: start;
  padding: 1.4rem 1.3rem;
  border: 1px solid rgba(192, 168, 128, .55); border-radius: 4px;   /* 3.00:1 on the panel, matching .carousel-can */
  background: linear-gradient(180deg, var(--navy-lift), var(--navy-deep));
}
.can-card h3 { font-size: 1.3rem; }
.can-card .can-facts { margin-top: .3rem; }

/* The dashed "More to come" card went with the lineup. Room to grow is now said by a band of
   the full range, which says it better than an empty card did. */

/* ---- Where to find it: the primary action, and a cream band ----

   THE PAGE'S GROUND RULE (Dylan, 2026-10-31): cream where a section is led by TYPE or
   PHOTOGRAPHS, navy wherever rendered can artwork sits, plus the hero. It is a rule rather than
   strict alternation because strict alternation is not available: the hero and the trade band
   both carry the seal, and the seal must never sit on cream (assets/merch/README.md). The two
   drink bands are navy by Dylan's own ruling, "leave drinks navy then". That leaves this band and
   the collage's indoor half as the cream ones, which is where the page stands:

     hero navy -> #where CREAM -> #beers navy -> #sodas navy
       -> #out navy outdoor half / CREAM indoor half -> #bar navy -> trade navy -> footer navy

   #bar stays navy although it is type-led, because it sits immediately under the collage's cream
   half and two cream bands in a row would read as one band. (#cans was the other type-only
   candidate and is not on the list because it no longer exists: it was folded into #bar on
   2026-10-31.)

   Gold is 1.1:1 on cream, so nothing in a cream band may lean on the gold that works on navy -
   the eyebrow, the focus ring and the board's shelf all need cream-ground variants and they are
   below. This band carried exactly these overrides until Task 5 (65e92fc) took it navy on
   2026-10-30; Dylan asked for the cream back on 2026-10-31, so they are recovered rather than
   reinvented. The same set, for the same reasons, is in css/collage.css under .collage-cream. */
#where { background: var(--cream); color: var(--navy); }
/* The page ring is --gold-hi, which is #F0E0B8 on #F5EBD2: 1.1:1. A keyboard user would lose the
   cursor entirely, so the whole band re-colours it. Navy on cream is 13:1. */
#where :focus-visible { outline-color: var(--navy); }
#where .eyebrow { background: none; -webkit-background-clip: border-box; background-clip: border-box; color: #6E5733; /* 5.76:1 on cream; --gold-lo is only 4.46:1 */ }
#where h2 { color: var(--navy); }
#where .lede, #where .note, #where .greeting { color: rgba(22,37,63,.78); }
#where .spots li { border-bottom-color: rgba(22,37,63,.2); color: var(--navy); }
/* --muted (#9FADC4) is tuned for navy and measures 1.9:1 on cream. */
#where .spots .city, #where .spots .spot-meta { color: rgba(22,37,63,.66); }
/* Rendered rows: the whole row is one link to the phone's maps app, 44px tall at minimum. */
.spots li.spot { padding: 0; }
/* The name sits on its own line at every width: an 18-cell board plus the meta cannot share one
   line inside a 23rem grid column without shrinking the cells past reading size. */
.spots .spot a { display: flex; flex: 1; flex-direction: column; align-items: flex-start; gap: .45rem; min-height: 44px; padding: 1.35rem 0; color: inherit; text-decoration: none; }
.spots .spot a:focus-visible { outline-offset: 2px; } /* colour comes from the #where block above, not the page's gold ring */
.spots .spot-meta { font-family: "EB Garamond", serif; font-size: .75rem; letter-spacing: .14em; text-transform: uppercase; }
.spots .spot-meta { color: var(--muted); } /* #9FADC4: 6.7:1 on navy */
/* Resting tap cue: an arrow glyph (decorative) plus a thin underline on the name. The underline is
   a second, non-colour signal; it thickens on hover/focus. */
.spots .spot-meta { white-space: nowrap; }
/* Navy, not --gold-hi: the arrow was gold while the band was navy and is 1.1:1 on cream. */
.spots .spot-go { font-family: "Libre Franklin", sans-serif; font-size: .95rem; letter-spacing: 0; color: var(--navy); }
/* The underline was cream at 50% on the navy band. Navy at 40% is its cream-ground twin. */
.spots .spot-name { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .25em; text-decoration-color: rgba(22,37,63,.4); }
.spots .spot a:hover .spot-name, .spots .spot a:focus-visible .spot-name { text-decoration-thickness: 2px; text-decoration-color: currentColor; }
@media (prefers-contrast: more), (forced-colors: active) { .spots .spot-name { text-decoration-color: currentColor; } }

/* ---- The split-flap board ----
   Real cells: a deep-navy cell with a cream character and the hairline split across the middle that
   gives a split-flap its name. Spare by rule - no bevel, no shadow, no distress - because the seal
   is the only ornate thing on the page. The cell is sized from the gutter so 16 of them fit a
   320px phone without ever shrinking the count; a name too wide wraps to a second line of cells. */
.spots {
  /* The cell size is written here by initScoreboard, which measures the band's content box and
     decides the characters per line with it: 16 cells on a phone, up to 26 - the longest account
     name - on a desktop, where the leftover width goes into bigger cells instead. The value below
     is only the pre-measurement fallback. */
  --flap-w: 1.25rem;
  --flap-fs: calc(var(--flap-w) * .85);
  /* The hairline is a LIGHT line (--muted at 30%) because the cell is --navy-deep, which a dark
     line cannot show on: it sits on #0B1726 at about 1.8:1, subtle but visible as the split.
     THE CELL COLOUR IS A FUNCTION OF ITS GROUND, and so is this line: change one, change the
     other. The full history, because it moved four times in two days and every move had a reason:
     --navy cell on the cream band; --navy-deep in Task 5 (65e92fc) when the band went navy;
     cream with a dark hinge in Task 5b (614d552) because --navy-deep on --navy was 1.18:1 and
     the cells disappeared; back to --navy-deep with this light hinge on 2026-10-31, when Dylan
     took the band back to cream and 614d552's reason for existing went with it. */
  --hinge: linear-gradient(to bottom, transparent calc(50% - 1px), rgba(159, 173, 196, .3) 50%, transparent calc(50% + 1px));
}
.spots .board {
  display: inline-flex; flex-direction: column; gap: 2px;
  /* The shelf the cells stand on. A text underline cannot cross the cells (they are atomic
     inlines), so this is the resting tap cue in its place: a line, not a colour. */
  padding-bottom: 5px; border-bottom: 2px solid rgba(22, 37, 63, .55); /* navy at 55%: 3.4:1 on cream. Gold at 60% was the navy-band version and is 1.2:1 here */
}
.spots .board-line { display: flex; gap: 1px; }
.spots .flap {
  flex: none; width: var(--flap-w); font-size: var(--flap-fs);
  font-family: "Libre Franklin", system-ui, sans-serif; font-weight: 600; letter-spacing: 0;
  line-height: 1.5; text-align: center; white-space: pre; /* a blank cell keeps its width */
  /* Deep-navy cell with a cream character on the cream band: the cell is 12.9:1 off the ground
     and the character is 15.9:1 off the cell. It is the darkest thing in the section, which is
     also the right read here - the row is the one interactive thing in the band, and on a light
     ground weight draws the eye where light did on the dark one (apple-design 12). */
  color: var(--cream); border-radius: 1px;
  background-color: var(--navy-deep); background-image: var(--hinge);
}
/* An unused slot on the board is empty, not a dark cell: six rows of solid dark cells would bury
   the band. */
/* The blanks are visible as a grid, not as cells. Every board is padded to the longest account
   name - 22 cells, 500px, on all six rows - and the gold shelf spans the padded board, so with
   invisible blanks the rule hung out past the end of every name: 296px of bare shelf under
   "Hangar 38", 410px under the second line of "The Hub at Feather Oaks". It read as an unfinished
   form field rather than a scoreboard. The hairline is drawn on the BAND, not on a cell, so it
   follows the ground like everything else here: it was cream at 10% (1.32:1) while the band was
   navy, and it is navy at 14% on cream, which measures 1.31:1 - the same whisper, inverted.
   Present as structure, nowhere near the inked cell's 12.9:1, so it never competes with a name.
   (The alternative was shortening the shelf to the ink, which loses the fixed-cell conceit the
   whole board is built on.) */
.spots .flap-blank { background: none; box-shadow: inset 0 0 0 1px rgba(22, 37, 63, .14); }
/* The unconfirmed highlight must wrap the WHOLE board. The board is an inline-flex box, an atomic
   inline, so the .confirm around it stays one line box tall (about 20px) and painted behind line one
   of a wrapped name only - box-decoration-break cannot help, there is no fragment to clone.
   inline-block makes the highlight as tall as the board, so every line sits on the yellow. */
.spots .spot-name .confirm { display: inline-block; }
/* The per-cell edge for a board inside a yellow .confirm highlight was removed on 2026-10-31 with
   the cell colour it existed for: a CREAM cell on #FFF1A8 is 1.04:1 and needed a boundary drawn
   round it, but a --navy-deep cell is 12.2:1 on that yellow and needs nothing. Northside was also
   the only account whose NAME was ever highlighted, and it came off the page the same day, so
   there is no board inside a highlight today. .spot-name .confirm above still earns its place:
   js/scoreboard.js can highlight any name, and locator.test.js covers an unverified address. */
.spots .spot a:hover .board, .spots .spot a:focus-visible .board { border-bottom-color: var(--navy); }
/* Mid-flip the cell lifts a shade, so the clatter reads as the board working. Stepped, critically
   damped, no bounce: it is a mechanism, not a spring. (--navy-lift, a palette token. The cream
   cell's mid-flip shade was #E0D2AB, invented for 614d552 and off-palette; it went with the cell.) */
.spots .flap.spinning {
  background-color: var(--navy-lift);
  animation: flap-tick var(--flap-step, 46ms) steps(2, end) infinite;
}
@keyframes flap-tick {
  from { transform: translateY(-8%); }
  to   { transform: none; }
}
/* The full name, for assistive tech: the cells themselves are aria-hidden because per-character
   spans get announced letter by letter. Clipped, so it is never seen and never measured. */
.spots .vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
#where .greeting { max-width: 48rem; margin: 0 auto 1.5rem; font-family: "Playfair Display", serif; font-size: 1.25rem; color: rgba(22,37,63,.78); }
/* Two columns from 600px, one below, made by CSS alone. column-count fills the first column top
   to bottom and then the second, so the DOM stays in data order: a screen reader and a sighted
   visitor read the same sequence, which a row-major grid reordered by script would break. The list
   fills the band rather than shrinking to its content, because initScoreboard (js/page.js
   boardSpace) measures the column from this width and the column gap below. A row never splits
   across the columns, or a board's two lines of cells could land in different ones. */
.spots { list-style: none; counter-reset: none; display: block; width: 100%; margin: 0 auto; column-gap: 3rem; }
@media (min-width: 37.5rem) { .spots { column-count: 2; } }
.spots li { break-inside: avoid; }
.spots li { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; padding: 1.05rem 0; border-bottom: 1px solid rgba(192,168,128,.18); font-size: 1.0625rem; }
.spots .city { font-family: "EB Garamond", serif; font-size: .75rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.note { text-align: center; margin-top: 1.8rem; color: var(--muted); font-size: .95rem; }

/* ---- Out in Tallahassee ---- */
/* The .out-rail block lived here: ~20 lines styling a drag-and-flick photo rail that index.html
   stopped containing when the collage replaced it on 2026-10-30. Deleted 2026-10-31 with
   js/rail.js and tests/rail.test.js.

   One line of it was load-bearing and the reason is kept so it is not re-learned: the rail broke
   out of its column with width:100vw and a negative margin-inline, and Chrome let its scrollable
   overflow escape to the root - the whole PAGE scrolled 735px sideways at 320px wide, through
   body's overflow-x:hidden. Neither overflow on the rail nor overflow on an ancestor stopped it;
   `contain: paint` did. The collage is a grid and does not break out, so it needs none of this -
   but anything that ever bleeds full-width out of .wrap again will. */

.slot-empty {
  position: relative; aspect-ratio: 4 / 5; border-radius: 4px; overflow: hidden;
  border: 1px solid rgba(192,168,128,.34);
  background:
    radial-gradient(120% 80% at 50% 100%, rgba(240,224,184,.14), transparent 62%),
    repeating-conic-gradient(from 270deg at 50% 100%, rgba(192,168,128,.10) 0 .6deg, transparent .6deg 7.5deg),
    linear-gradient(180deg, var(--navy-lift), var(--navy-deep));
}
/* Four crop marks, drawn as one gradient hairline each, like a viewfinder framing the shot. */
.slot-empty::before {
  content: ""; position: absolute; inset: 1rem; pointer-events: none;
  --m: linear-gradient(135deg, var(--gold-hi), var(--gold) 45%, var(--gold-lo));
  background:
    var(--m) top left / 1.4rem 1px no-repeat, var(--m) top left / 1px 1.4rem no-repeat,
    var(--m) top right / 1.4rem 1px no-repeat, var(--m) top right / 1px 1.4rem no-repeat,
    var(--m) bottom left / 1.4rem 1px no-repeat, var(--m) bottom left / 1px 1.4rem no-repeat,
    var(--m) bottom right / 1.4rem 1px no-repeat, var(--m) bottom right / 1px 1.4rem no-repeat;
}
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* Sits directly under the place name, in the panel's quietest voice: this is a caption for an
   absence, so it must not compete with the name of the place it is promising. */
.slot-soon {
  display: block; margin-top: .5rem;
  font-family: "EB Garamond", Georgia, serif; font-size: .75rem;
  letter-spacing: .18em; text-transform: uppercase; color: var(--muted);
}
/* NO CREAM-BAND OVERRIDE HERE, deliberately. The obvious one - dark ink because the band is
   cream - was written and was wrong: the PANEL is not the band. .slot-empty above paints itself
   --navy-lift to --navy-deep on both halves (css/collage.css says so explicitly: "the empty
   panels stay dark on the cream band on purpose"), so navy ink on it measured 1.09:1 and the card
   read as blank. --muted is 7.9:1 on that panel and is correct on both grounds. Follow the
   surface a thing sits ON, not the section it sits in. */
/* .slot-n lived here: the "03 / 08" contact-sheet counter on an empty panel. It went on
   2026-10-31 with the placeholders it numbered - one remaining filler card has nothing to count
   against, and a lone "01 / 01" reads as a bug. The panel's frame and crop marks stay. */
/* ---- The bar ---- */
.photo-slot {
  display: grid; place-items: center; min-height: clamp(11rem, 30vw, 22rem);
  border: 1px solid rgba(192,168,128,.55); border-radius: 4px;      /* 3.00:1 on the panel, matching .carousel-can */
  background: linear-gradient(180deg, var(--navy-lift), var(--navy-deep));
}

/* ---- Trade ---- */
.trade { background: var(--navy-deep); }
.trade-inner { display: grid; grid-template-columns: 1fr 1fr; gap: 3.4rem; align-items: center; }

/* bottom: 3.5rem while this is here at all - at 1rem it covered the hero's "On draft now" badge
   at 390 and the "Brewed at Proof" bullet on bars.html. It is demo-only and comes off at launch. */
.demo-badge {
  position: fixed; right: .75rem; bottom: 3.5rem; z-index: 150;
  font-size: .625rem; letter-spacing: .16em; text-transform: uppercase;
  background: var(--confirm); color: #1B1D22; padding: .4rem .6rem; border-radius: 2px;
}

@media (max-width: 54rem) {
  .trade-inner { grid-template-columns: 1fr; gap: 2rem; }
}

/* ---- Accessibility preferences, for this page's sections ---- */
@media (prefers-reduced-motion: reduce) {
  /* The pop is the page's signature moment, but it is still motion: reduced motion gets the end
     state and nothing else - no tumble, no ring, no landing. cap-pop.js adds .popped on init here,
     so this IS what loads. The transform must stay identical to cap-pop's 100% keyframe. */
  .hero.popped .cap { animation: none; transform: translate3d(0, 7%, 0) rotate(372deg) scale(.68); }
  .hero.popped .cap-btn::after { animation: none; opacity: 0; }
  .cap-btn:active .cap { transform: none; }
  .cap-hint { transition: none; }
  .js .hero .tagline, .js .hero .lede { opacity: 1; transform: none; }
  .js .hero.popped .tagline, .js .hero.popped .lede { transition: none; }
  /* The board does not clatter: the names are simply there. flapIn also returns settled, so the
     .spinning class should never land here - this is the belt as well as the braces. */
  .spots .flap.spinning { animation: none; transform: none; background-color: var(--navy-lift); }
}
@media (prefers-reduced-transparency: reduce) {
  .can-card { border-color: #6F6246; background: var(--navy-deep); }
  .can-flag { border-color: #6F6246; }
}
@media (prefers-contrast: more) {
  .photo-slot, .slot-empty, .can-card { border-color: var(--gold); }
  .slot-empty, .can-card { background: var(--navy-deep); }
  /* Solid cells and no hairline: at high contrast the split line is just noise inside the cell.
     Blanks stay blank - :not() keeps this from filling the empty slots in with dark. */
  .spots .flap:not(.flap-blank) { background-color: var(--navy-deep); background-image: none; }
  .spots .board { border-bottom-color: var(--navy); }
  .can-flag { border-color: currentColor; color: var(--cream); }
  .can-flag-pouring { color: var(--navy); background: var(--gold-hi); border-color: var(--gold-hi); }
}

/* Print is the leave-behind. Browsers drop backgrounds by default, which would leave cream type on
   white paper, so the locator prints as dark ink on the sheet. The two columns survive, and a row
   (or a board) is never cut between them or between pages. The band is given its own white
   sheet so the dark ink is correct whether or not the reader prints background graphics. */
/* ---- Print: the whole brand page ----
   bars.html is the leave-behind and prints to 1.3 pages. index.html is not meant to be printed at
   all - and somebody will anyway, because the distribution is a QR code and a scan can land on a
   desktop. Before this block they got eleven near-blank sheets.

   THE CAUSE WAS ONE LINE in css/site.css: `.js .reveal` is `opacity: 0` until the observer arms
   it, and print renders the WHOLE document, including every section the visitor never scrolled
   past. So most of the page printed as empty paper with the odd heading on it. css/bars.css has
   carried the one-line answer since it was written and this page never got the back-port - the
   same gap as js/bars.js's start-up guards, found by the same audit.

   Everything here is inside @media print, so none of it can reach the screen. */
@media print {
  /* Browsers drop background graphics by default, so the navy ground prints as white and cream
     type on it prints as nothing at all. Ink on paper instead, said at the root so no band keeps
     a dark edge of its own. */
  html, body { background: #fff !important; color: #16253F !important; }
  h1, h2, h3, .tagline, .lede, .note, .can-note, .foot, dt, dd { color: #16253F; }
  .legal, .city, .band-hint { color: #4A5568; }
  /* The chrome. None of it can be used on paper, and the age gate would otherwise print as a full
     sheet of navy laid over the facts. */
  nav, .gate, .skip, .cap-hint, .badges { display: none !important; }
  /* THE ELEVEN SHEETS. A reveal that never armed is invisible on paper as well as on screen. */
  .reveal { opacity: 1 !important; transform: none !important; }
  /* A hero sized to the screen is a sheet of nothing: the page starts at the top of the paper. */
  .hero { min-height: 0 !important; padding: .5cm 0 .3cm !important; }
  .hero-art { max-height: 6cm; }
  section { padding: .45cm 0 !important; }
  /* Both photo strips scroll sideways on screen. A scroller on paper shows whatever sits in its
     box and silently drops the rest - twelve of fourteen frames - so they wrap onto the sheet. */
  .collage-grid { flex-wrap: wrap !important; overflow: visible !important; row-gap: .3cm; }
  /* And smaller than the screen's clamp(16rem, 52vw, 27rem). Unwrapped at screen height, fourteen
     photographs took about five sheets on their own; at 4.5cm four fit across a sheet and the
     whole strip is under two. The tile is sized by HEIGHT and the image follows its own ratio, so
     this shrinks the frames without cropping one - the same guarantee the strip makes on screen. */
  .tile { break-inside: avoid; height: 4.5cm !important; }
  footer { background: none !important; }
  footer::before { display: none !important; }

  #where { background: #fff; }
  #where, #where .lede, #where .note, #where .greeting, #where h2, .spots li { color: #16253F; }
  #where .eyebrow { background: none; color: #6E5733; }
  .spots .spot-meta, .spots .spot-go, .spots .city { color: #16253F; }
  .spots li { border-bottom-color: rgba(22, 37, 63, .3); break-inside: avoid; }
  /* Hard-coded rather than tokenised, deliberately: the cell has changed ground four times in two
     days and the leave-behind must not move with it. Dark ink on the sheet, whichever way the
     screen goes, and whether or not the reader prints background graphics. */
  .spots .flap:not(.flap-blank) { background-color: #16253F; background-image: none; color: #F5EBD2; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  .spots .board { border-bottom-color: #16253F; }
  /* An unconfirmed fact must never print looking confirmed. This is the same treatment as the
     `.confirm` rule in css/bars.css @media print, copied rather than reinvented so the two cannot
     drift: force the yellow, and add the dashed rule as the backstop for a browser printing with
     background graphics off, where the yellow is dropped and the rule alone carries it. */
  .confirm {
    background: #FFF1A8 !important; color: #1B1D22 !important;
    border-bottom: .75pt dashed #806840;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
}


/* ---- Follow along: the accounts, said inline under a section heading ----
   The footer group is a row of links in its own block. This one sits inside a sentence, so it is
   laid out as text: the links keep the page's underline-on-hover behaviour and do not become
   buttons, because a second pair of buttons under a headline would compete with the headline. */
.follow {
  margin-top: 1.1rem; font-family: "Libre Franklin", system-ui, sans-serif; font-size: .95rem;
  color: var(--muted); display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: center; gap: .5rem;
}
.socials-inline { display: inline-flex; flex-wrap: wrap; gap: 1.1rem; margin-top: 0; }  /* css/site.css gives .socials a top margin, which tips it off p.follow's baseline */
.socials-inline a {
  color: var(--gold-hi); text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: .25em; text-decoration-color: rgba(240,224,184,.45);
  min-height: 44px; display: inline-flex; align-items: center;   /* a real tap target on a phone */
}
.socials-inline a:hover, .socials-inline a:focus-visible { text-decoration-thickness: 2px; text-decoration-color: currentColor; }


/* ---- The concept ballot, on a concept card's back face ----
   Sits under the facts, in the same ghost-button language as "Where to find it" on a pouring
   card, so the two backs read as one family. The card back is the tightest box on the page
   (js/carousel.js records 363px of content inside a 436px card at 320px), so this stays to two
   short lines and never grows a third. */
.can-vote { margin-top: 1rem; }
.can-vote-ask {
  margin: 0 0 .5rem; font-family: "EB Garamond", Georgia, serif; font-size: .75rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
}
.can-vote-btn { width: 100%; }
/* The spent state replaces the button rather than sitting beside it: a ballot that still shows a
   control it will not honour is the thing the visitor complains about. */
.can-vote-said {
  margin: 0; font-family: "Libre Franklin", system-ui, sans-serif; font-size: .9375rem;
  color: var(--gold-hi);
}
/* An honest failure, and deliberately not styled as an error banner - the vote is a nicety, not a
   checkout. It was #e0446a (the Watermelon Lime melon) at 13px, which measures 3.18:1 on
   --navy-lift and 4.46:1 on --navy-deep: under AA on every part of the card, on the one string a
   visitor has to read to learn their vote did not land. Cream is 15.9:1. The melon survives as a
   rule beside it, where colour is decoration and carries no meaning on its own. */
.can-vote-failed {
  margin: .4rem 0 0; padding-left: .55rem; border-left: 2px solid var(--melon);
  font-family: "Libre Franklin", system-ui, sans-serif; font-size: .8125rem;
  color: var(--cream);
}
@media (prefers-contrast: more) { .can-vote-failed { color: var(--cream); text-decoration: underline; } }


/* The vote's own hint, under the band hint it follows. Gold rather than muted: it is the only
   thing on the page asking the visitor to DO something that is not finding a drink, and it was
   previously invisible - the ballot lived on the back of a concept card and nothing said so. */
.band-hint-vote { color: var(--gold-hi); }
/* With JS off nothing turns, so there is no ballot to advertise. html:not(.js) is the page's
   existing gate for exactly this, and css/brand.css already hides .band-hint the same way. */
html:not(.js) .band-hint-vote { display: none; }


/* ---- Press feedback, on pointer-DOWN ----
   apple-design 1: "Respond on pointer-down, not on release. Waiting for click to show feedback
   feels dead." The cap and the cans already did this; the locator rows and the collage tiles were
   the last two things on the page that waited for the tap to finish before acknowledging it.

   :active fires on pointer-down, so no JavaScript is involved and nothing can get stuck in a
   pressed state if a pointer is lost. 100ms and a small scale, because these are large targets -
   a photograph the width of a phone screen dropping to .97 is a big movement, and the point is
   acknowledgement, not animation. */
.tile-link {
  /* HEIGHT IS LOAD-BEARING, not decoration. `.tile img` is `height: 100%`, which resolves against
     this anchor now that the photograph is wrapped in one - and an auto-height parent makes that
     compute to `auto`, so every image fell back to its intrinsic size and was guillotined by the
     track's overflow. Measured before the fix: a 432px tile showing a 1350px image, 32% of the
     photograph visible, and a strip 6444px long instead of 1301. The section that exists to show
     the brand turning up was showing tent ceilings.
     border-box so the 5px hit padding below stays inside the tile instead of adding to it. */
  display: block; height: 100%; box-sizing: border-box; border-radius: 4px;
  /* ~10px of hit padding (apple-design 10) without moving the tile: the gap between frames is
     12-16px, so negative margin keeps the layout identical while the touch target grows. */
  padding: 5px; margin: -5px;
  transition: transform .14s var(--settle, cubic-bezier(.22, .61, .36, 1));
}
.tile-link:active { transform: scale(.97); transition-duration: .1s; }
.tile-link:focus-visible { outline-offset: 2px; }

/* The whole row is the target, and it is 44px tall at minimum, so it scales less than a tile:
   a big block of text moving .97 reads as a wobble rather than a press. */
.spots .spot a { transition: transform .14s var(--settle, cubic-bezier(.22, .61, .36, 1)); }
.spots .spot a:active { transform: scale(.985); transition-duration: .1s; }

/* Reduced motion keeps the acknowledgement and drops the movement - the feedback is the point,
   the scale is only how it is delivered. Opacity carries it instead. */
@media (prefers-reduced-motion: reduce) {
  .tile-link, .spots .spot a { transition: opacity .1s linear; }
  .tile-link:active, .spots .spot a:active { transform: none; opacity: .72; }
}
