/* ============================================================
   HUCKLEBERRY × Normal-is-Boring system — warm-adapted layer
   Sits ON TOP of normal-is-boring.css (load that first).
   - Loads the free fallback fonts the base file names.
   - Overrides the cool teal/blue accent -> Huckleberry honey.
   - Adds the page-composition components (nav, hero, stacks,
     index, dark panel, marquee, closer, footer).
   ============================================================ */

/* Font loading now happens via the <link> tag in each page's <head>
   (see docs/2026-08-02-nib-site-build-design.md §5) — the @import that
   used to live here duplicated that request with a different axis set. */

/* ---- Warm-adapt: keep the system, swap the temperature ----
   Converged brand system (wiki/brand-guide.md, 2026-08-06). All brand
   values are declared here and the base system's semantic aliases are
   repointed at them, so normal-is-boring.css stays vendored/upgradeable. */
:root{
  /* Brand palette */
  --hb-cream:#FBF6EC;          /* Cream / Paper — page canvas        */
  --hb-honey:#B0730A;          /* fine accents, links, signature dot */
  --hb-gold:#FFC94B;           /* highlight FILLS & CTAs only        */
  --hb-sage:#A3B18A;           /* secondary accent — rules, tags     */
  --hb-band:#EFECDE;           /* sage wash on cream — section band  */
  --hb-ink:#1F1F1F;            /* Ink / Espresso                     */

  --nib-teal:var(--hb-honey);  /* signature dot -> honey             */
  --nib-blue:#8A4B12;          /* strong accent -> roast/rust        */
  --nib-coral:#C25A3F;         /* warm brick (kept, retuned)         */
  --accent:var(--nib-teal);
  --accent-strong:var(--nib-blue);

  /* Repoint the base system's semantic aliases at the brand values.
     --nib-white stays true white: it is still needed for text on dark
     panels and the black page frame. */
  --canvas:var(--hb-cream);
  --ink:var(--hb-ink);

  /* wire the fallback families the base file already references */
  --font-display:"Fraunces","Cormorant Garamond",Georgia,serif;
  --font-sans:"Karla","Archivo","Helvetica Neue",Arial,sans-serif;

  /* RETIRED — pending owner review (2026-08-11)
     Was: --font-serif:"Playfair Display","Cormorant Garamond",Georgia,serif;
     Playfair Display retired: the brief calls for one display face. Aliased
     rather than removed so pages not yet converted on this branch fall back
     to Karla instead of to browser default serif. */
  --font-serif: var(--font-sans);

  --fs-body:    17px;
  --fs-body-sm: 16px;
  --lh-body:    1.6;

  /* Accent text. Gold is 1.4:1 on cream and can never carry text; it is a
     fill behind ink only (ink on gold = 10.76:1). Roast is 6.3:1. */
  --hb-roast:   #8A4B12;

  /* Content sits on a left axis rather than the page centre. */
  --hb-axis: clamp(20px, 8vw, 128px);
}
.nib-display{font-optical-sizing:auto}
/* overflow-x MUST be `clip`, never `hidden`.
   `hidden` turns body into a scroll container, which silently kills
   `position:sticky` for every descendant — the hero pin never pins, so the
   whole scroll-scrub animation runs while the hero scrolls away off-screen
   and the visitor sees no effect at all. `clip` gives the same protection
   against sideways scrollbars without establishing a scroll container.
   This was the root cause of "the animation doesn't work". */
body{-webkit-font-smoothing:antialiased;overflow-x:clip}

/* The vendored base paints <html> with canvas-invert ("black frame shows
   during transitions"). On this site that means a full-screen black flash
   on every load before the body paints — clearly visible in the owner's
   screen recording. The page is cream, so html should be cream too. */
html{background:var(--canvas)}

/* Neither stylesheet ever reset link decoration, so every <a> was picking up
   the browser default underline — including the solid CTA buttons and the
   category rows. The design already carries its own affordances (.hb-tlink
   has a border-bottom, .hb-nav-link.here has an ::after rule for the active
   page, which only means anything if links aren't all underlined by default).
   Prose links that set text-decoration inline are unaffected. */
a{text-decoration:none}
.hb-accent{color:var(--accent)}
.hb-serif{font-family:var(--font-serif)}

/* ---- Primary CTA: gold fill ----
   Gold is a FILL colour only — ink on gold measures ~10.6:1 so it is safe
   for buttons, but gold as text on cream would fail contrast badly, so it
   is never used that way. Secondary CTAs keep the outlined .nib-btn. */
.nib-btn--solid{background:var(--hb-gold);color:var(--hb-ink);border-color:var(--hb-gold)}
.nib-btn--solid:hover{background:var(--hb-ink);color:var(--hb-cream);border-color:var(--hb-ink)}

/* progressive-enhancement: content visible without JS */
.nib-reveal{opacity:1;transform:none}
.js .nib-reveal{opacity:0;transform:translateY(24px);
  transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease)}
.js .nib-reveal.is-visible{opacity:1;transform:none}
.rd1{transition-delay:.08s}.rd2{transition-delay:.16s}.rd3{transition-delay:.24s}.rd4{transition-delay:.32s}

/* ============================================================
   NAV
   ============================================================ */
.hb-nav{position:fixed;inset:0 0 auto 0;z-index:60;display:flex;align-items:center;
  justify-content:space-between;padding:20px var(--gutter);
  transition:background var(--dur-fast) var(--ease),padding var(--dur-fast) var(--ease),
             border-color var(--dur-fast) var(--ease);border-bottom:1px solid transparent}
/* Cream-tinted, not white — a white bar would read as a visible seam
   against the cream canvas once the nav goes sticky. */
.hb-nav.scrolled{background:rgba(251,246,236,.88);backdrop-filter:blur(14px) saturate(1.1);
  padding-top:13px;padding-bottom:13px;border-bottom-color:var(--line)}
.hb-logo{height:44px;width:auto;transition:height var(--dur-fast) var(--ease)}
.hb-nav.scrolled .hb-logo{height:36px}
/* Live open/closed pill — mirrors the Google Business "Open now" cue.
   Status is computed in the shop's timezone (see hb-data.js), so it reads
   correctly for visitors anywhere. */
.hb-open{display:inline-flex;align-items:center;gap:7px;margin-left:14px;flex:none;
  font-family:var(--font-sans);font-size:var(--fs-2xs);font-weight:var(--fw-medium);
  letter-spacing:var(--tracking-wide);text-transform:uppercase;white-space:nowrap;
  padding:5px 11px;border-radius:var(--radius-pill);border:1px solid var(--line)}
/* The nav CTA must never wrap to two lines — it collides with the pill. */
.hb-nav .nib-btn{white-space:nowrap}
.hb-open::before{content:"";width:7px;height:7px;border-radius:var(--radius-pill);flex:none;background:currentColor}
.hb-open.is-open{color:#1f7a3d;border-color:rgba(31,122,61,.32);background:rgba(31,122,61,.07)}
.hb-open.is-closed{color:var(--ink-muted);background:rgba(25,25,25,.04)}
.hb-nav-left{display:flex;align-items:center;min-width:0}
.hb-nav-right{display:flex;align-items:center;gap:clamp(16px,2.6vw,38px)}
.hb-nav-link{font-family:var(--font-sans);font-size:var(--fs-xs);font-weight:500;
  text-transform:uppercase;letter-spacing:var(--tracking-wide);color:var(--ink);
  position:relative;transition:color var(--dur-fast)}
.hb-nav-link:hover{color:var(--accent)}
.hb-nav-link.here::after{content:"";position:absolute;left:0;right:0;bottom:-6px;height:1px;background:var(--accent)}
.hb-nav-toggle{display:none;background:none;border:0;padding:6px;cursor:pointer;
  width:26px;height:20px;position:relative}
.hb-nav-toggle-bars,.hb-nav-toggle-bars::before,.hb-nav-toggle-bars::after{
  content:"";position:absolute;left:6px;right:6px;height:2px;background:var(--ink);
  transition:transform var(--dur-fast) var(--ease),opacity var(--dur-fast) var(--ease),top var(--dur-fast) var(--ease)}
.hb-nav-toggle-bars{top:9px}
.hb-nav-toggle-bars::before{top:-7px}
.hb-nav-toggle-bars::after{top:7px}
.hb-nav-toggle.open .hb-nav-toggle-bars{background:transparent}
.hb-nav-toggle.open .hb-nav-toggle-bars::before{top:0;transform:rotate(45deg)}
.hb-nav-toggle.open .hb-nav-toggle-bars::after{top:0;transform:rotate(-45deg)}
.hb-drawer{position:fixed;inset:0;z-index:55;background:var(--canvas-alt);
  transform:translateY(-100%);transition:transform var(--dur) var(--ease);
  display:flex;flex-direction:column;justify-content:center;gap:6px;padding:var(--gutter)}
.hb-drawer.open{transform:none}
.hb-drawer a{font-family:var(--font-display);text-transform:uppercase;font-weight:var(--fw-light);
  font-size:clamp(40px,11vw,72px);line-height:1;letter-spacing:var(--tracking-tight);
  color:var(--ink);padding:12px 0;border-bottom:1px solid var(--line)}
.hb-drawer a .nib-italic{color:var(--accent)}

/* ============================================================
   RETIRED — pending owner review (2026-08-11)
   Previous hero: three-line display heading plus an overlapping
   two-photo stack driven by nib.js. Superseded by the scroll-scrub
   hero below. Kept until the owner picks between master and
   redesign-v2; delete only after that call.

   .hb-hero itself is not kept here — the new hero reuses that class
   name. Its retired declaration was:
   .hb-hero{padding-top:clamp(120px,20vh,220px);padding-bottom:clamp(40px,7vw,90px)}

   .hb-eyebrow, .hb-actions, and .hb-tlink are NOT retired below —
   they're shared with about.html, contact.html, menu.html, and the
   new hero markup, and stay live.
   ============================================================ */
.retired .hb-hero-top{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  flex-wrap:wrap;padding-bottom:clamp(24px,5vw,56px)}
.retired .hb-hero-h{margin:0;display:flex;flex-direction:column;gap:.02em}
.retired .hb-hero-h .line{overflow:hidden;display:block}
.retired .hb-hero-h .line>span{display:block;transform:translateY(0)}
.retired .js .hb-hero-h .line>span{transform:translateY(104%);transition:transform var(--dur-slow) var(--ease)}
.retired .js .hb-hero.in .line>span{transform:translateY(0)}
.retired .hb-hero-h .nib-italic{text-transform:none;padding-left:.12em}
.retired .hb-lastword{display:inline-flex;align-items:flex-start;gap:.12em}
.retired .hb-lastword .dot{width:.16em;height:.16em;min-width:10px;min-height:10px;border-radius:999px;
  background:var(--accent);margin-top:.62em;flex:none}
.retired .hb-hero-foot{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(24px,5vw,64px);
  align-items:end;padding-top:clamp(30px,5vw,60px)}

/* ============================================================
   RETIRED — OVERLAPPING IMAGE STACK  (core NIB move)
   ============================================================ */
.retired .hb-stack{position:relative;display:grid;grid-template-columns:repeat(12,1fr);align-items:end;
  margin-top:clamp(30px,5vw,70px)}
.retired .hb-stack .big{grid-column:1/9;grid-row:1;aspect-ratio:4/3}
.retired .hb-stack .small{grid-column:8/13;grid-row:1;align-self:end;transform:translateY(9%);
  z-index:2;aspect-ratio:3/4;border:8px solid var(--canvas)}
.retired .hb-stack .cap{grid-column:1/6;grid-row:1;align-self:start;z-index:3;margin:14px 0 0 14px}
.retired .hb-stack img{width:100%;height:100%;object-fit:cover;display:block;transition:opacity .3s var(--ease)}
.retired .hb-stack .hb-figcap{transition:opacity .3s var(--ease)}
.retired .hb-figcap--small{position:absolute;left:14px;bottom:12px}

/* Editorial "contact sheet" ticker — the non-typical stand-in for
   slideshow dots/arrows. Text-only, numbered, matches the site's
   existing numbered-index motif (.hb-chap-n / .hb-ixrow .n).
   Sits bottom-LEFT: the .small figure overhangs bottom-right with
   z-index 2, so a right-aligned ticker renders behind it and is
   invisible at desktop widths. */
.retired .hb-stack-n{position:absolute;left:4px;bottom:-30px;z-index:3;font-family:var(--font-sans);
  font-size:var(--fs-xs);letter-spacing:.06em;color:var(--ink-muted)}
@media(max-width:860px){.retired .hb-stack-n{position:static;text-align:left;margin-top:14px}}

/* Phone-only directions button. Hidden on desktop, where the same link is
   already in the closer section and there is no room pressure. 44px is the
   minimum comfortable touch target. */
.hb-nav-icon{display:none;width:44px;height:44px;flex:none;
  align-items:center;justify-content:center;
  border:1px solid var(--line);border-radius:var(--radius-pill);
  color:var(--ink);background:none;
  transition:border-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}
.hb-nav-icon svg{width:19px;height:19px;fill:currentColor;display:block}
.hb-nav-icon:hover,.hb-nav-icon:focus-visible{border-color:var(--accent-strong);color:var(--accent-strong)}

.hb-eyebrow{display:inline-flex;align-items:center;gap:12px}

/* .nib-label has no flex and no gap, and .nib-dot is a bare inline-block, so
   a dot placed inside a label sits flush against the first letter ("•Come
   say hi"). .hb-eyebrow happened to avoid it with its own inline-flex+gap;
   every other label carrying a dot did not. Fixed here, not in the vendored
   base. Margin rather than :has()+gap so it cannot double up. */
.nib-label > .nib-dot{margin-right:12px;vertical-align:middle;position:relative;top:-1px}
.hb-actions{display:flex;gap:20px;align-items:center;flex-wrap:wrap}
.hb-tlink{font-family:var(--font-sans);font-size:var(--fs-sm);letter-spacing:.03em;color:var(--ink);
  display:inline-flex;align-items:baseline;gap:.5em;padding-bottom:3px;
  border-bottom:1px solid rgba(25,25,25,.3);
  transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast),gap var(--dur-fast) var(--ease)}
.hb-tlink .arw{color:var(--ink-muted);transition:transform var(--dur-fast) var(--ease),color var(--dur-fast)}
/* Roast, not honey. --accent is #B0730A = 3.67:1 on cream, which fails AA
   for 14px link text; --accent-strong is 6.3:1. Honey remains correct on
   display-scale type, where the 3:1 large-text threshold applies. */
.hb-tlink:hover,.hb-tlink:focus-visible{color:var(--accent-strong);border-color:var(--accent-strong);gap:.85em}
.hb-tlink:hover .arw,.hb-tlink:focus-visible .arw{transform:translateX(3px);color:var(--accent-strong)}

/* ============================================================
   HERO — static composition (default)
   This is what renders with no JS, with reduced motion, and on
   mobile. hb-hero.js upgrades it to a scrub; nothing here depends
   on JS running. Failure mode is "no animation", never "no content".
   ============================================================ */
.hb-hero{position:relative;padding-top:clamp(104px,14vh,152px);padding-bottom:clamp(40px,7vw,88px)}
.hb-hero__pin{padding-inline:var(--hb-axis)}

.hb-hero__head{display:flex;justify-content:space-between;align-items:baseline;
  gap:16px;flex-wrap:wrap;margin-bottom:clamp(16px,3vw,32px)}

/* The giant mark is the real logo lettering (2019x733 crop of the stacked
   lockup), NOT type. It must be the same letterforms as the header logo, or
   the shrink-into-header reads as serif capitals vanishing and a script logo
   appearing — which is exactly what it did when this was set in Fraunces. */
.hb-hero__word{margin:0;transform-origin:left top;line-height:0}
.hb-hero__word picture{display:block;line-height:0}
.hb-hero__word img{display:block;width:min(92vw,1180px);height:auto}

/* Phones get the full stacked lockup — mark above the script — not the mark
   alone. It reads as the complete brand and carries far more weight in the
   opening frame, which is what the empty space around it needed. Sized by
   WIDTH: the lockup is 1.21 (wider than tall), so sizing by height would
   push it past the screen edges. Matches the <source> in the markup. */
@media (max-width:700px){
  .hb-hero__word img{width:min(88vw,460px);height:auto;max-height:54svh;
    object-fit:contain}
}

/* "Bakes that linger" lives INSIDE the hero and is revealed as the wordmark
   shrinks away, so it arrives together with the lineup instead of sitting in
   a section of its own. Visible by default — only the scrub hides it. */
.hb-hero__linger{font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:clamp(34px,6vw,96px);line-height:1.04;
  letter-spacing:var(--tracking-tight);color:var(--ink);
  margin:clamp(20px,3vw,32px) 0 0;max-width:14ch}

/* The spans inside carry .nib-display / .nib-italic, which set their own
   font-size (--fs-display-lg, up to 142px). That silently beat every size
   set on the heading above — so the heading rendered at full display scale
   regardless, pushing the drinks and the buttons out of the pinned area
   entirely. Inherit so the heading's own sizing actually governs. */
.hb-hero__linger .nib-display,
.hb-hero__linger .nib-italic{font-size:inherit;line-height:inherit;
  letter-spacing:inherit}
.hb-hero__linger .nib-italic{text-transform:none;padding-left:.1em;
  color:var(--accent-strong)}
.hb-hero__linger .dot{display:inline-block;width:.11em;height:.11em;
  border-radius:50%;background:var(--accent-strong);margin-left:.07em}

/* Restored at the owner's request: the descriptive paragraph sits under
   "Bakes that linger" in the hero. It briefly lived in a separate section
   and was briefly removed altogether; this is its home. */
.hb-hero__sub{max-width:52ch;margin:clamp(16px,2.4vw,28px) 0 0;
  font-size:var(--fs-body);line-height:var(--lh-body)}

/* RETIRED — pending owner review (2026-08-11)
   "Bakes that linger" briefly had its own section after the hero. The owner
   asked for it to appear INSIDE the hero alongside the drinks as the opening
   animation resolves instead, so it is now .hb-hero__linger above. Prefixed
   so it cannot match; kept rather than deleted per the no-delete rule. */
.retired .hb-statement-h{font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:var(--fs-display-lg);line-height:var(--lh-tight);
  letter-spacing:var(--tracking-tight);margin:0;
  display:flex;flex-direction:column;gap:.02em}
.retired .hb-statement-h .line{display:block;overflow:hidden}
.retired .hb-statement-h .nib-italic{text-transform:none;padding-left:.12em}
.retired .hb-statement-sub{max-width:52ch;margin:clamp(24px,4vw,40px) 0 0;
  font-size:var(--fs-body);line-height:var(--lh-body)}

.hb-hero__line{display:flex;gap:24px;margin-top:clamp(32px,5vw,56px);
  list-style:none;padding:0;--hb-card-h:clamp(240px,42vh,420px)}

/* The image lives in its own clipping frame so the caption can sit BELOW it
   without being cut off. Putting height + overflow:hidden on the <figure>
   itself clips the caption to nothing — the image consumes 100% of the
   figure's height, so a flow-positioned caption starts exactly at the
   clipped edge. The frame also keeps Task 5's drift (scale 1.06) clipped;
   without it the image would visibly overflow while drifting.

   .hb-card had no explicit width, so it shrink-wrapped to max-content —
   below ~710px of viewport height the figcaption (uppercase, tracked-out)
   is wider than the 9:16 image, so the card and its frame stopped agreeing
   on width and the five cards rendered five different widths. Deriving
   both the frame height and the card width from one token keeps them
   locked together at every viewport size. */
.hb-card{margin:0;flex:none;width:calc(var(--hb-card-h) * 9 / 16)}
.hb-card__frame{position:relative;overflow:hidden;
  height:var(--hb-card-h);aspect-ratio:9/16}
.hb-card img{width:100%;height:100%;object-fit:cover;display:block}
.hb-card figcaption{font-family:var(--font-sans);font-size:var(--fs-2xs);
  letter-spacing:var(--tracking-wide);text-transform:uppercase;
  color:var(--ink-muted);margin-top:8px}

/* Marks a hero card as one of this month's specials. Ink on cream text,
   NOT gold: gold is reserved for things you can click, and a badge is not
   clickable. Ink also reads harder against a photo than gold would.
   Sits inside .hb-card__frame, which is already position:relative and
   overflow:hidden, so it rides along with the drift and the entry
   transform rather than floating free. */
.hb-card-badge{position:absolute;top:10px;left:10px;z-index:2;
  padding:5px 9px;border-radius:var(--radius-pill);
  background:var(--ink);color:var(--canvas);
  font-family:var(--font-sans);font-size:var(--fs-2xs);font-weight:var(--fw-medium);
  letter-spacing:var(--tracking-wide);text-transform:uppercase;line-height:1;
  pointer-events:none}

/* Phone-only; the media query below turns it on. */
.hb-line-count{display:none;font-family:var(--font-sans);font-size:var(--fs-2xs);
  letter-spacing:var(--tracking-wide);color:var(--ink-muted);margin-top:12px}

.hb-hero__actions{display:flex;align-items:center;gap:24px;flex-wrap:wrap;
  margin-top:clamp(28px,4vw,44px)}

/* Cards overflow the right edge on purpose — the lineup runs off the page
   rather than sitting centred in it. Horizontal scroll is the fallback on
   any viewport too narrow to show all five. */
@media (max-width:1100px){
  /* Scrollbar hidden: on a phone it sits as a grey slab under the drinks and
     reads as a rendering fault. The half-visible card at the right edge is
     what signals the strip scrolls, so nothing is lost by removing it. */
  .hb-hero__line{overflow-x:auto;scroll-snap-type:x mandatory;
    scrollbar-width:none;-ms-overflow-style:none;
    -webkit-overflow-scrolling:touch}
  .hb-hero__line::-webkit-scrollbar{display:none}
  .hb-card{scroll-snap-align:start}
}

/* ============================================================
   HERO — scrub upgrade
   Only applies when hb-hero.js has added .hb-hero--scrub, which it does
   only on wide viewports without reduced-motion. Every property here is
   transform or opacity, so it stays on the compositor.
   ============================================================ */
/* 200vh against a 100vh pin gives a full viewport of scrub. The earlier
   160vh (60vh of throw) resolved so fast it did not read as an animation
   at all — the owner reported seeing no effect. */
/* svh, not vh. On phones vh includes the space the URL bar occupies, so the
   pin jumps every time that bar shows or hides. svh is the stable "small"
   viewport height, which is what lets the scrub run on mobile at all. */
.hb-hero--scrub{height:200svh;padding-top:0;padding-bottom:0;--p:0}
/* `safe center` matters here. Plain `center` overflows equally off BOTH
   ends when the content is taller than the pin, so the top of the
   composition disappears behind the fixed header with no way to reach it.
   `safe` falls back to flex-start in exactly that case, so content is
   clipped only at the bottom, never hidden above the viewport. */
.hb-hero--scrub .hb-hero__pin{position:sticky;top:0;height:100svh;
  display:flex;flex-direction:column;justify-content:safe center;align-items:center;
  overflow:hidden;padding-top:clamp(88px,12svh,132px)}

/* Phones: the lineup stays a horizontal strip and everything shrinks to fit
   one small screen, but the pin and the wordmark hand-off still run. */
@media (max-width:1100px){
  /* align-self:stretch, not width:100%. The pin centres its children, so a
     flex item sizes to its content — a five-card strip, far wider than the
     screen. Combined with the negative bleed margins below, width:100% made
     it wider still and pushed the whole PAGE sideways, which is where the
     stray horizontal scrollbar came from. Stretch fills exactly the pin's
     inline size and the cards scroll inside it. */
  .hb-hero--scrub .hb-hero__line{align-self:stretch;width:auto;
    justify-content:flex-start}
  .hb-hero--scrub .hb-hero__word img{width:88vw}
  .hb-hero--scrub .hb-hero__linger{font-size:clamp(28px,8.5vw,48px)}
  .hb-hero--scrub .hb-hero__sub{font-size:var(--fs-body-sm);max-width:40ch}
}

/* The hero is centred at the owner's request — the rest of the page keeps
   its left axis. Deliberate exception to the "nothing centred" rule: this
   is the one screen where the composition is a single focal column, and it
   matches the reference site. */
.hb-hero--scrub .hb-hero__linger{text-align:center;max-width:none}
.hb-hero--scrub .hb-hero__sub{text-align:center}
.hb-hero--scrub .hb-hero__head{width:100%}

/* Out of flow while scrubbing. transform:scale() shrinks the wordmark
   visually but its layout box stays full size, which left a viewport-tall
   hole above "Bakes that linger" once it had shrunk away. Absolute removes
   it from flow entirely — the same thing the reference site does. */
/* Centred in the pin, not pinned to a fixed height near the top — at
   top:14svh the wordmark ran straight through the eyebrow row. Filling the
   pin and centring both axes keeps it clear of the header and the eyebrow
   at every window height. */
.hb-hero--scrub .hb-hero__word{position:absolute;inset:0;
  display:flex;align-items:center;justify-content:center;pointer-events:none}
.hb-hero--scrub .hb-hero__word img{transform-origin:left top}

.hb-hero--scrub .hb-hero__word img{
  transform:translate(calc(var(--dx,0px) * var(--p)), calc(var(--dy,0px) * var(--p)))
            scale(calc(1 - (1 - var(--s,.1)) * var(--p)));
  opacity:calc(1 - (var(--p) - .8) / .2)}

.hb-hero--scrub .hb-hero__word img{will-change:transform}

/* Reduced motion gets a clean, static hero: no giant wordmark at all, and no
   pin. The wordmark only earns its space by animating — sitting there
   full-screen and motionless it is just an obstacle between the visitor and
   the content. Hiding it also means the pin has nothing to reveal, so the
   scrub is skipped in JS and there is no dead scroll range.
   The header logo is untouched, so the brand is never absent. */
/* RETIRED — owner decision, 2026-08-12: the hero animation now runs for
   everyone and does not consult prefers-reduced-motion. These rules gave
   reduced-motion visitors a static hero with no giant wordmark. Prefixed so
   they cannot match; restore by removing `.retired` if reconsidered.
   See docs/go-live-checklist.md. */
@media (prefers-reduced-motion: reduce){
  .retired .hb-hero__word{display:none}
  .retired .hb-hero .hb-card{transform:none}
  .retired .hb-hero .hb-hero__linger{transform:none;opacity:1}
}

.hb-hero--scrub .hb-card{
  opacity:var(--e,1);
  transform:translateY(calc((1 - var(--e,1)) * 48px))}

/* "Bakes that linger" and its paragraph fade up as the wordmark leaves,
   landing with the cards. The paragraph trails the heading slightly so they
   arrive in reading order rather than together. Hidden ONLY while the scrub
   is running — the static composition shows both outright, so a no-JS
   visitor always sees them. */
.hb-hero--scrub .hb-hero__linger{
  opacity:clamp(0, calc((var(--p) - .35) / .3), 1);
  transform:translateY(calc((1 - clamp(0, calc((var(--p) - .35) / .3), 1)) * 28px))}
.hb-hero--scrub .hb-hero__sub{
  opacity:clamp(0, calc((var(--p) - .45) / .3), 1);
  transform:translateY(calc((1 - clamp(0, calc((var(--p) - .45) / .3), 1)) * 20px))}

/* The counter belongs to the lineup, so it arrives with it. It was sitting
   there reading "1 / 4" while the drinks it counts were still invisible. */
.hb-hero--scrub .hb-line-count{
  opacity:clamp(0, calc((var(--p) - .55) / .25), 1)}

/* Cards translate down 48px while entering, overlapping the actions row.
   At --e:0 they are invisible (opacity:0) but still hit-testable, which
   silently swallows clicks on the Order CTA for the first ~15% of the
   scroll. Nothing inside a card is interactive, so opting them out of
   hit-testing entirely is free. */
.hb-hero--scrub .hb-card{pointer-events:none}

/* The lineup is sized for the static composition, which is taller than the
   100vh pin. The squeeze is worst at p=0, where the wordmark is still full
   size — by p=1 it has shrunk into the header and freed ~139px. Buy the
   room back from the vertical rhythm rather than the cards, which are the
   point of the lineup. Measured at 1200x760, p=0: 626px into a 669px box. */
.hb-hero--scrub .hb-hero__line{--hb-card-h:clamp(200px,32svh,380px);margin-top:28px}

/* Phone sizing lives here, AFTER the desktop rhythm above — the earlier
   mobile block sets the same custom property, and at equal specificity the
   later rule wins regardless of the media query, so phones would otherwise
   inherit desktop-sized cards. */
@media (max-width:1100px){
  /* Bigger than a thumbnail — the drinks are the point of the lineup. The
     strip bleeds to both screen edges so a card is always half-visible at
     the right, which is what tells people it scrolls; scroll-padding keeps
     the first card on the same axis as the text above it. */
  .hb-hero--scrub .hb-hero__line{--hb-card-h:clamp(170px,27svh,250px);margin-top:20px;
    margin-inline:calc(var(--hb-axis) * -1);
    padding-inline:var(--hb-axis);scroll-padding-inline:var(--hb-axis);
    gap:10px}
}

/* Short windows, desktop and laptop. The hero is pinned to the viewport
   HEIGHT, so height is the constraint that matters here, not width — a wide
   but short laptop window is exactly where the composition ran out of room
   and the drinks and buttons fell off the bottom. */
@media (min-width:701px) and (max-height:860px){
  .hb-hero--scrub .hb-hero__linger{font-size:clamp(30px,4.4vw,64px)}
  .hb-hero--scrub .hb-hero__sub{font-size:var(--fs-body-sm);max-width:62ch;
    margin-top:12px}
  .hb-hero--scrub .hb-hero__line{--hb-card-h:clamp(150px,25svh,240px);
    margin-top:20px}
  .hb-hero--scrub .hb-hero__actions{margin-top:18px}
}
@media (min-width:701px) and (max-height:700px){
  .hb-hero--scrub .hb-hero__linger{font-size:clamp(28px,3.6vw,50px)}
  .hb-hero--scrub .hb-hero__line{--hb-card-h:clamp(130px,22svh,190px)}
}

/* Short PHONES. This counterpart to the two desktop rules above was missing,
   and the phone composition is the taller of the two — the wordmark, heading,
   paragraph, a full-width card, the counter and the actions all stack. On a
   360x640 handset that overflows the 100svh pin, and because the pin is
   overflow:hidden with justify-content:safe center (which only protects the
   START edge) the overflow is clipped at the BOTTOM and cannot be scrolled to.
   The last child is .hb-hero__actions — the Order button. So the primary
   call to action becomes invisible and unreachable on small phones.
   Everything here buys vertical room back. */
@media (max-width:700px) and (max-height:760px){
  .hb-hero--scrub .hb-hero__pin{padding-top:clamp(72px,10svh,96px)}
  .hb-hero--scrub .hb-hero__linger{font-size:clamp(24px,7vw,36px)}
  .hb-hero--scrub .hb-hero__sub{font-size:var(--fs-body-sm);margin-top:8px;
    max-width:34ch}
  .hb-hero--scrub .hb-hero__line{--hb-card-h:clamp(140px,26svh,200px);margin-top:12px}
  .hb-hero--scrub .hb-hero__actions{margin-top:12px}
  .hb-hero--scrub .hb-line-count{margin-top:6px}
}
/* Shorter still — drop the paragraph during the scrub. It is still present in
   the static composition below the fold, so nothing is lost; this only trades
   it against the Order button, and the button wins. */
@media (max-width:700px) and (max-height:660px){
  .hb-hero--scrub .hb-hero__sub{display:none}
  .hb-hero--scrub .hb-hero__line{--hb-card-h:clamp(120px,22svh,170px)}
}

/* Phones: one big drink at a time, swiped, rather than a row of tall
   slivers. Sized by WIDTH here (the desktop rule sizes by height), at a
   squarer 4:5 so each photo actually reads — 9:16 at this width would be
   taller than the screen. The next card stays part-visible so the swipe is
   discoverable. */
@media (max-width:700px){
  /* One card per screen, aligned to the same axis as the text above it, so
     it reads as a single photo you swipe rather than a strip you scroll. A
     76%-wide card with a quarter of the next one showing still looked like a
     strip — the whole point of this treatment is that it does not. */
  .hb-hero__line{gap:var(--hb-axis)}
  .hb-card{width:calc(100vw - var(--hb-axis) * 2);scroll-snap-align:center}
  /* 36svh: the cards are invisible at the start of the scrub but still
     reserve their height. That reserved block is both the gap under the
     logo and the reason the composition was overflowing the pin on shorter
     phones — measured at roughly 55px over on a 700px-tall screen. */
  .hb-card__frame{height:auto;aspect-ratio:4/5;max-height:36svh}
  .hb-card figcaption{margin-top:10px}
  .hb-line-count{display:block;margin-top:10px}

  /* The eyebrow row is pulled OUT OF FLOW on phones and faded in once the
     scroll starts.
     Two problems, one fix. Visually the owner did not want it in the
     opening frame. Structurally it was the victim of a flex-centring
     overflow: the pin centres its children, so when the content is taller
     than the pin it spills off BOTH ends — the eyebrow was pushed up behind
     the fixed header and clipped, which is why only half of it showed.
     Absolute positioning removes its height from the flow entirely, so the
     rest of the composition fits, and it sits just clear of the header. */
  .hb-hero--scrub .hb-hero__head{
    position:absolute;top:clamp(88px,12svh,132px);
    left:var(--hb-axis);right:var(--hb-axis);width:auto;
    flex-direction:column;align-items:center;gap:4px;text-align:center;
    opacity:clamp(0, calc((var(--p) - .08) / .22), 1)}

  /* Horizontal pan, driven by the page's vertical scroll. One card is
     visible at a time, so the strip travels three steps to show all four.
     A step is one card plus one gap = (100vw - 2*axis) + axis = 100vw - axis.
     Manual side-swiping is turned off while this runs — two things moving
     the same strip at once fight each other — so overflow goes visible and
     the pin's own overflow:hidden does the clipping. */
  .hb-hero--scrub .hb-hero__line{
    overflow:visible;scroll-snap-type:none;
    transform:translateX(calc(var(--pan,0) * -3 * (100vw - var(--hb-axis))))}
  .hb-hero--scrub .hb-card{scroll-snap-align:none}
  /* Four drinks, not five, at the owner's request — a shorter swipe reads
     as a curated set rather than an endless strip. All five stay in the
     markup and on desktop. */
  .hb-card:nth-child(n+5){display:none}
}
.hb-hero--scrub .hb-hero__sub{margin-top:16px}
.hb-hero--scrub .hb-hero__actions{margin-top:24px}

/* Only hide the nav logo while a scrub is actually running — never by
   default, or a no-JS visitor loses the header logo entirely. */
.hb-hero--scrub-active .hb-nav .hb-logo{opacity:var(--navlogo,1)}

/* On phones the header logo stays visible throughout. The fade exists so the
   giant wordmark appears to BECOME the header logo, but that hand-off needs
   room to read; on a small screen it just meant no logo in the header for
   the first 80% of the scroll, which looked like the header was missing.
   Visible from the start, the mark shrinking toward it still reads as
   convergence — and there is always a logo up there. */
@media (max-width:1100px){
  .hb-hero--scrub-active .hb-nav .hb-logo{opacity:1}
}

/* ============================================================
   HERO — image drift
   This stands in for the "slow moving video" in the brief. There is exactly
   one video clip in the whole project, and capheroasters.com — one of the
   cited references — serves no video at all, so the cinematic quality is
   carried by imagery instead.

   Applied to the <img>, NOT to .hb-card: the card carries the scrub's entry
   translateY, and two transforms on one element overwrite rather than
   compose. The img sits inside .hb-card__frame, which has overflow:hidden,
   so the scaled image stays clipped instead of bleeding past the card.
   ============================================================ */
@keyframes hb-drift{
  from{transform:scale(1)}
  to  {transform:scale(1.06)}
}
.hb-card img{animation:hb-drift 20s ease-in-out infinite alternate}

/* Negative delays start each card part-way through its cycle, so no two
   drift in unison and none of them sits waiting to begin. */
.hb-card:nth-child(2) img{animation-delay:-4s}
.hb-card:nth-child(3) img{animation-delay:-8s}
.hb-card:nth-child(4) img{animation-delay:-12s}
.hb-card:nth-child(5) img{animation-delay:-16s}

/* RETIRED — owner decision, 2026-08-12: the drift runs for everyone.
   Restore by removing `.retired`. See docs/go-live-checklist.md. */
@media (prefers-reduced-motion: reduce){
  .retired .hb-card img{animation:none}
}
/* Off below the scrub breakpoint too — continuous animation is a battery
   and data cost on phones for an effect that reads as ambient at best. */
@media (max-width:1100px){
  .hb-card img{animation:none}
}

/* Tickers double as a manual "next" control — hint that with an
   underline + arrow that appears on hover/focus. */
.is-clickable{cursor:pointer;display:inline-flex;align-items:center;gap:6px;
  border-bottom:1px solid transparent;transition:border-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}
.is-clickable::after{content:"→";opacity:0;transform:translateX(-4px);
  transition:opacity var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
.is-clickable:hover,.is-clickable:focus-visible{border-color:currentColor}
.is-clickable:hover::after,.is-clickable:focus-visible::after{opacity:1;transform:none}

/* ============================================================
   SECTIONS
   ============================================================ */
.hb-sec{padding-top:clamp(72px,11vw,168px);padding-bottom:clamp(72px,11vw,168px)}
.hb-sec--tight{padding-top:clamp(48px,7vw,110px);padding-bottom:clamp(48px,7vw,110px)}
.hb-split{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,6vw,90px);align-items:center}
.hb-labelrow{display:flex;align-items:center;gap:14px;margin-bottom:22px}
/* Sage as the secondary accent: decorative rules only, not structural
   hairlines — keeping it off the item/table dividers stops it reading as
   a large fill. */
.hb-labelrow .ln{flex:1;height:1px;background:var(--hb-sage)}
.hb-fig{aspect-ratio:3/4}
.hb-fig--wide{aspect-ratio:16/10}
.hb-fig img,.hb-fig{overflow:hidden}
.hb-fig img{width:100%;height:100%;object-fit:cover;display:block}
.hb-figcap{position:absolute;left:14px;bottom:12px;z-index:2;display:inline-flex;align-items:center;gap:10px;
  padding:7px 12px;background:rgba(25,25,25,.68);backdrop-filter:blur(2px);
  font-family:var(--font-sans);font-size:var(--fs-2xs);letter-spacing:var(--tracking-wide);
  text-transform:uppercase;color:var(--nib-white)}

/* ============================================================
   DARK BREAK PANEL  (statement)
   ============================================================ */
.hb-dark{background:var(--canvas-invert);color:var(--ink-invert);
  padding:clamp(90px,15vw,220px) var(--gutter)}
.hb-dark .stmt{font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:var(--fs-display-md);line-height:var(--lh-tight);letter-spacing:var(--tracking-tight);
  text-transform:uppercase;max-width:16ch;text-wrap:balance}
.hb-dark .stmt .nib-italic{text-transform:none;color:var(--accent)}
.hb-dark .sub{font-family:var(--font-sans);font-style:italic;color:rgba(255,255,255,.6);
  margin-top:26px;font-size:var(--fs-md)}

/* Narrow phones: --fs-display-md is clamp(3.5rem, 8vw + 1rem, 6.1rem), so its
   FLOOR is 56px and it stops scaling below ~500px wide. "HANDCRAFTED." sets
   427px at 56px against a 320px content box on a 360px phone - 107px of the
   word is thrown away, silently, because body is overflow-x:clip. The clamp
   lives in the vendored base and must not be edited, so override the size
   here instead. 11vw keeps the word inside the gutters all the way down and
   hands back to the 56px original once there is room for it (~509px). */
@media (max-width:560px){
  .hb-dark .stmt{font-size:clamp(2.25rem,11vw,3.5rem)}
}

/* ============================================================
   NUMBERED INDEX (menu as contents)
   ============================================================ */
.hb-ixhead{display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:12px;
  margin-bottom:10px}
.hb-ix{border-top:1px solid var(--line)}
.hb-ixrow{display:grid;grid-template-columns:70px 1fr auto;align-items:center;gap:clamp(14px,3vw,40px);
  padding:clamp(20px,2.6vw,34px) 4px;border-bottom:1px solid var(--line);position:relative;
  color:var(--ink);transition:padding-left var(--dur) var(--ease)}
.hb-ixrow:hover{padding-left:24px}
.hb-ixrow .n{font-family:var(--font-sans);font-size:var(--fs-sm);color:var(--ink-muted);
  letter-spacing:.04em}
/* Sentence case, not caps: these are a scannable list of categories, not
   eight headlines. Caps at this size competed with the section heading and
   is measurably slower to read. Kept in Fraunces so the section still reads
   as part of the editorial system rather than an app menu. Tracking eased
   off --tracking-tight, which is tuned for uppercase. */
.hb-ixrow .nm{font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:clamp(28px,4.2vw,56px);line-height:1.05;letter-spacing:-0.015em;
  transition:color var(--dur-fast) var(--ease)}
.hb-ixrow:hover .nm{color:var(--accent)}
.hb-ixrow .mt{font-family:var(--font-sans);font-size:var(--fs-2xs);letter-spacing:var(--tracking-wide);
  text-transform:uppercase;color:var(--ink-muted);text-align:right;max-width:22ch}
.hb-ixrow .go{position:absolute;right:4px;opacity:0;transform:translateX(-8px);color:var(--accent);
  transition:opacity var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
.hb-ixrow:hover .go{opacity:1;transform:none}

/* ============================================================
   VALUES (numbered, overlapping-ish trio)
   ============================================================ */
.hb-trio{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(20px,3vw,44px)}

/* RETIRED — pending owner review (2026-08-12)
   "On the board this month" was briefly its own section under the hero. The
   owner asked for the specials to be part of the hero lineup with a badge
   instead, so this is unused. Prefixed so it cannot match; kept rather than
   deleted per the no-delete standing instruction. */
.retired .hb-specials .hb-trio{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}
.retired .hb-specials .hb-val h3{margin:0 0 6px}
.retired .hb-specials-price{color:var(--ink);white-space:nowrap}
.hb-val .vfig{aspect-ratio:4/5;overflow:hidden}
.hb-val .vfig img{width:100%;height:100%;object-fit:cover;display:block}
.hb-val:nth-child(2){transform:translateY(clamp(20px,4vw,56px))}
.hb-val .vn{font-family:var(--font-sans);font-size:var(--fs-sm);color:var(--ink-muted);
  margin:18px 0 8px;letter-spacing:.04em}
.hb-val h3{font-family:var(--font-display);text-transform:uppercase;font-weight:var(--fw-light);
  font-size:var(--fs-xl);line-height:1;letter-spacing:var(--tracking-tight);margin:0 0 10px}
.hb-val p{font-family:var(--font-sans);color:var(--nib-ink-70);font-size:var(--fs-body);
  line-height:var(--lh-body);max-width:38ch}

/* ============================================================
   MARQUEE
   ============================================================ */
.hb-marq{overflow:hidden;border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:18px 0;white-space:nowrap;background:var(--canvas)}
.hb-marq-in{display:inline-flex;align-items:center;gap:34px;animation:hbslide 34s linear infinite;will-change:transform}
.hb-marq:hover .hb-marq-in{animation-play-state:paused}
.hb-mq{font-family:var(--font-serif);font-style:italic;font-size:clamp(20px,2.4vw,30px);color:var(--ink)}
.hb-mq-star{color:var(--hb-sage);font-size:14px}
@keyframes hbslide{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* The marquee keeps scrolling under prefers-reduced-motion, at the owner's
   explicit request — stopping it was why the banner went static on their
   machine.

   This IS a deliberate accessibility trade, not an oversight. Mitigations
   that stay in place: the band is aria-hidden="true" so it is invisible to
   screen readers, it carries no information that is not elsewhere on the
   page, and .hb-marq:hover pauses it. The gap is keyboard and touch users
   who have asked for reduced motion and cannot hover to pause.

   RETIRED — pending owner review (2026-08-12): the guard that stopped it.
   Restore by removing the .retired prefix if the trade is reconsidered. */
@media (prefers-reduced-motion: reduce){
  .retired .hb-marq-in{animation:none}
}

/* ============================================================
   REVIEWS (cream, off-centre, single cycling quote)
   ============================================================ */
/* Moved up to follow the menu index directly, and moved off the dark panel:
   two full-dark sections back to back reads heavy, and the brief asks for
   most of the page to stay neutral. Still deliberately restrained — quiet
   social proof between the louder sections, not a display-type statement.
   The star uses roast (6.3:1 on cream), never gold: gold is 1.4:1 on cream
   and cannot carry a mark, and gold is reserved for interactive elements
   regardless. */
/* Content sits inside .nib-container like every other section, so the left
   edge lines up with the rest of the page. Padding the section with
   --hb-axis instead put the reviews on a different vertical axis from
   everything above and below it, which read as inconsistent.

   The band tint is what finally gives sage a job. It marks the reviews as a
   distinct beat between the menu index and the dark panel without breaking
   the "mostly neutral" rule — it is a wash, not a colour block. Ink on it
   measures 13.9:1. */
.hb-reviews{background:var(--hb-band)}
.hb-reviews-in{max-width:620px}
.hb-rating{display:flex;align-items:baseline;gap:8px;margin:0 0 24px;
  font-family:var(--font-sans);font-size:var(--fs-body);color:var(--ink)}
.hb-rating b{font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:var(--fs-xl);line-height:1;letter-spacing:var(--tracking-tight)}
.hb-rating .star{color:var(--hb-roast);font-size:var(--fs-md)}

/* Reuses the hero-stack's crossfade-cycle pattern instead of a scrolling
   marquee — full sentences are hard to read mid-scroll, so this holds
   still and changes on a timer instead. */
.hb-quote{max-width:46ch;margin:24px 0 0}
.hb-quote-text{font-family:var(--font-sans);font-style:italic;font-size:clamp(18px,2vw,24px);
  line-height:1.5;color:var(--ink);margin:0;transition:opacity .3s var(--ease)}
.hb-quote-who{display:block;margin-top:12px;font-family:var(--font-sans);font-style:normal;
  font-size:var(--fs-2xs);letter-spacing:var(--tracking-wide);text-transform:uppercase;
  color:var(--nib-ink-70);transition:opacity .3s var(--ease)}
.hb-quote-n{font-family:var(--font-sans);font-size:var(--fs-2xs);
  letter-spacing:.06em;color:var(--nib-ink-70)}
.hb-quote-nav{display:flex;align-items:center;justify-content:flex-start;gap:16px;margin-top:20px}
.hb-quote-arrow{width:28px;height:28px;flex:none;border:1px solid var(--line);border-radius:var(--radius-pill);
  background:none;color:var(--ink);font-family:var(--font-sans);font-size:13px;line-height:1;cursor:pointer;
  transition:border-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}
.hb-quote-arrow:hover,.hb-quote-arrow:focus-visible{border-color:var(--hb-roast);color:var(--hb-roast)}
.hb-reviews .hb-tlink{margin-top:24px;font-size:var(--fs-body-sm)}

/* RETIRED — pending owner review (2026-08-11)
   .hb-tlink--invert styled the "Read them on Google" link against the dark
   reviews panel. Reviews are cream now and no live page uses this modifier
   (checked across all five). Selectors prefixed so they cannot match; kept
   rather than deleted per the owner's no-delete standing instruction. */
.retired .hb-tlink--invert{color:var(--nib-white);border-bottom-color:rgba(255,255,255,.35)}
.retired .hb-tlink--invert .arw{color:rgba(255,255,255,.6)}
.retired .hb-tlink--invert:hover{color:var(--accent);border-color:var(--accent)}
.retired .hb-tlink--invert:hover .arw{color:var(--accent)}

/* ============================================================
   FOLLOW (social CTA band)
   ============================================================ */
.hb-follow{display:flex;flex-direction:column;align-items:flex-start;gap:20px;
  max-width:640px;margin-inline:0}
.hb-follow .nib-heading{font-size:var(--fs-xl)}
.hb-follow .hb-actions{justify-content:flex-start}

/* ============================================================
   CLOSER
   ============================================================ */
.hb-closer{padding-top:clamp(80px,12vw,180px);padding-bottom:clamp(60px,9vw,140px)}
.hb-closer .big{font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:var(--fs-display-lg);line-height:var(--lh-tight);letter-spacing:var(--tracking-tight);
  text-transform:uppercase;margin:0;text-wrap:balance}
.hb-closer .big .nib-italic{text-transform:none;color:var(--accent)}
.hb-cgrid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(24px,5vw,70px);margin-top:clamp(30px,5vw,60px)}
.hb-cline{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;
  border-top:1px solid var(--line);padding:15px 0}
.hb-cline .k{font-family:var(--font-sans);font-size:var(--fs-2xs);letter-spacing:var(--tracking-wide);
  text-transform:uppercase;color:var(--ink-muted)}
.hb-cline .v{font-family:var(--font-sans);font-size:var(--fs-body);color:var(--ink)}

/* ============================================================
   FOOTER
   ============================================================ */
.hb-foot{background:var(--canvas-invert);color:var(--ink-invert);padding:clamp(56px,8vw,110px) var(--gutter) 40px;
  container-type:inline-size}
/* "HUCKLEBERRY®" must fit on one line at every width. Its rendered width is
   ~7.2x the font-size in this face, so sizing off the footer's own content
   box (cqw) keeps it in bounds instead of a fixed px floor, which overflowed
   by ~68px on a 375px screen and got clipped by body{overflow-x:hidden}.
   The vw line is the fallback for browsers without container queries. */
.hb-foot-word{font-family:var(--font-display);text-transform:uppercase;font-weight:var(--fw-light);
  font-size:clamp(28px,12vw,220px);
  font-size:min(220px,13.5cqw);
  line-height:.82;letter-spacing:var(--tracking-tight);color:var(--nib-white);white-space:nowrap}
.hb-foot-word .reg{font-size:.28em;vertical-align:super;color:rgba(255,255,255,.45)}
.hb-foot-top{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:40px;padding:clamp(36px,6vw,80px) 0}
.hb-foot-logo{height:32px;margin-bottom:20px}
.hb-foot h4{font-family:var(--font-sans);font-size:var(--fs-2xs);letter-spacing:var(--tracking-xwide);
  text-transform:uppercase;color:rgba(255,255,255,.5);margin:0 0 14px}
.hb-foot a,.hb-foot p{font-family:var(--font-sans);font-size:var(--fs-body-sm);color:rgba(255,255,255,.85);
  display:block;padding:6px 0;margin:0;transition:color var(--dur-fast)}
/* Gold on the dark footer, not honey: honey is 4.16:1 there. These are
   links, so gold is the sanctioned token. */
.hb-foot a:hover,.hb-foot a:focus-visible{color:var(--hb-gold)}
.hb-foot-bottom{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px;
  border-top:1px solid rgba(255,255,255,.14);padding-top:24px;
  font-family:var(--font-sans);font-size:var(--fs-2xs);letter-spacing:var(--tracking-wide);
  text-transform:uppercase;color:rgba(255,255,255,.5)}

/* ============================================================
   INTERIOR PAGE HEAD (Menu / About)
   ============================================================ */
.hb-pagehead{padding-top:clamp(130px,20vh,200px);padding-bottom:clamp(40px,6vw,80px)}
.hb-pagehead-top{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  flex-wrap:wrap;padding-bottom:clamp(20px,4vw,44px)}
.hb-pagehead h1{font-family:var(--font-display);font-weight:var(--fw-light);
  font-size:var(--fs-display-md);line-height:var(--lh-tight);letter-spacing:var(--tracking-tight);
  text-transform:uppercase;margin:0;text-wrap:balance}
.hb-pagehead h1 .nib-italic{text-transform:none}
.hb-pagehead .nib-body{margin-top:clamp(20px,3vw,34px);max-width:56ch}

/* ============================================================
   IMAGE DUO (two-up figure pair)
   ============================================================ */
.hb-duo{display:grid;grid-template-columns:1fr 1fr;gap:clamp(14px,2.4vw,28px);
  margin-bottom:clamp(28px,4vw,54px)}
.hb-duo .hb-fig{aspect-ratio:3/4}

/* ============================================================
   MENU CHAPTERS
   ============================================================ */
.hb-chap{padding-top:clamp(56px,8vw,120px)}
.hb-chap:first-of-type{padding-top:0}
.hb-chap-head{display:flex;gap:clamp(16px,3vw,32px);align-items:baseline;
  border-top:1px solid var(--line);padding-top:clamp(20px,3vw,36px);margin-bottom:clamp(28px,4vw,54px)}
.hb-chap-n{font-family:var(--font-sans);font-size:var(--fs-sm);color:var(--ink-muted);
  letter-spacing:.04em;flex:none}
.hb-chap-head h2{font-family:var(--font-display);text-transform:uppercase;font-weight:var(--fw-light);
  font-size:clamp(32px,4.6vw,60px);line-height:1;letter-spacing:var(--tracking-tight);margin:0 0 10px}
.hb-chap-head .nib-body{margin:0}
/* Roast, not honey: this tag is real information at 10.5px, and honey on
   cream measures 3.68:1 — under the 4.5:1 AA floor for text this size.
   Roast measures 6.3:1 and stays in-palette. (The brief's own note:
   "reserve honey text for large/accent use, not body copy.") */
.hb-flag{display:inline-flex;align-items:center;gap:8px;margin-top:12px;
  font-family:var(--font-sans);font-size:var(--fs-2xs);letter-spacing:var(--tracking-wide);
  text-transform:uppercase;color:var(--accent-strong)}

/* ============================================================
   ITEM LIST (editorial menu rows, with thumbnail)
   ============================================================ */
.hb-items{border-top:1px solid var(--line)}
.hb-item{display:grid;grid-template-columns:56px 1fr;gap:clamp(14px,2.6vw,24px);
  align-items:center;padding:clamp(14px,2vw,22px) 4px;border-bottom:1px solid var(--line)}
.hb-item-fig{width:56px;height:56px;overflow:hidden;background:var(--canvas-alt);flex:none}
.hb-item-fig img{width:100%;height:100%;object-fit:cover;display:block}
.hb-item-top{display:flex;justify-content:space-between;align-items:baseline;gap:16px}
.hb-item-name{font-family:var(--font-serif);font-size:var(--fs-md);color:var(--ink)}
/* 16px, not 14. This is reading copy, and the menu is the page with the most
   of it — it was the last place the owner's "body text 16–18px" brief had
   not been applied. Measure widened with the type so the line length stays
   comfortable rather than getting shorter. */
.hb-item-desc{font-family:var(--font-sans);font-size:var(--fs-body-sm);color:var(--nib-ink-70);
  line-height:var(--lh-body);margin-top:4px;max-width:56ch}
.hb-item-price{font-family:var(--font-sans);font-size:var(--fs-xs);letter-spacing:var(--tracking-wide);
  text-transform:uppercase;color:var(--ink-muted);white-space:nowrap}

/* Compact price list — the full long-tail lineup (standard café drinks,
   everyday pastries) where a thumbnail per row isn't realistic. Reuses
   .nib-label for its sub-heading. */
.hb-minilist{display:grid;grid-template-columns:1fr 1fr;gap:0 clamp(20px,4vw,48px);margin-top:16px}
.hb-minirow{display:flex;justify-content:space-between;align-items:baseline;gap:12px;
  padding:9px 0;border-bottom:1px solid var(--line)}
/* The item NAME in the compact price lists — content, not a label, so it
   takes the prose size. The price beside it (.hb-minirow .v) stays small. */
.hb-minirow .k{font-family:var(--font-sans);font-size:var(--fs-body-sm);color:var(--ink)}
.hb-minirow .v{font-family:var(--font-sans);font-size:var(--fs-2xs);letter-spacing:var(--tracking-wide);
  text-transform:uppercase;color:var(--ink-muted);white-space:nowrap}
.hb-subhead{margin:clamp(32px,4vw,48px) 0 4px;display:block}
@media(max-width:860px){.hb-minilist{grid-template-columns:1fr}}

/* ============================================================
   ORDER — SQUARE IFRAME WRAP
   ============================================================ */
.hb-frame-wrap{padding-top:clamp(130px,20vh,200px);padding-bottom:clamp(60px,9vw,120px)}
.hb-frame-head{margin-bottom:clamp(24px,4vw,48px)}
.hb-frame{width:100%;height:80vh;min-height:640px;border:1px solid var(--line);background:var(--canvas-alt)}
.hb-frame-fallback{margin-top:18px;display:flex;justify-content:flex-end}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media(max-width:860px){
  .hb-hero-foot,.hb-split,.hb-cgrid{grid-template-columns:1fr;gap:26px}
  .hb-trio{grid-template-columns:1fr;gap:44px}
  .hb-val:nth-child(2){transform:none}
  .hb-foot-top{grid-template-columns:1fr;gap:28px}
  .hb-stack{display:block}
  .hb-stack .small{width:66%;margin:-16% 0 0 auto;transform:none;display:block}
  .hb-stack .cap{margin:12px 0 0}
  .hb-ixrow{grid-template-columns:48px 1fr;gap:16px}
  .hb-ixrow .mt{display:none}
  .hb-nav-right .hb-nav-link{display:none}
  .hb-nav-icon{display:inline-flex}
  .hb-nav-toggle{display:inline-block}
  /* Tighten the header so logo + status pill + CTA + toggle all fit. */
  .hb-nav{gap:10px}
  .hb-logo{height:34px}
  .hb-nav.scrolled .hb-logo{height:30px}
  .hb-open{margin-left:9px;padding:4px 8px;gap:5px}
  .hb-nav .nib-btn{padding:10px 15px;font-size:var(--fs-xs)}
  .hb-duo{grid-template-columns:1fr}
  .hb-item{grid-template-columns:44px 1fr;gap:14px}
  .hb-item-fig{width:44px;height:44px}
  .hb-frame{height:75vh;min-height:520px}

  /* "What we hold to" — stacking three full-width 4:5 photos made this
     section roughly three screens tall on a phone. Side-by-side instead:
     a square thumbnail with the text beside it, so all three fit in about
     one screen and read as a list rather than three separate posters. */
  .hb-trio{gap:28px}
  /* minmax(0,1fr), NOT 1fr. A grid track sized 1fr keeps an implicit
     min-width:auto, so a long unbreakable word ("NEIGHBOURHOOD") widens the
     track past the viewport instead of wrapping — which is why the headings
     and body copy were running off the right edge. */
  .hb-val{display:grid;grid-template-columns:88px minmax(0,1fr);
    column-gap:16px;row-gap:2px;align-items:start}
  .hb-val .vfig{grid-column:1;grid-row:1 / span 3;aspect-ratio:1}
  .hb-val .vn,.hb-val h3,.hb-val p{grid-column:2;min-width:0}
  /* --fs-xl (33.75px) is a desktop display size; beside an 88px thumbnail
     it cannot fit. Fluid, and tracking back to normal so it does not push. */
  .hb-val h3{margin:0;font-size:clamp(19px,5.2vw,26px);letter-spacing:0;
    overflow-wrap:break-word}
  .hb-val p{max-width:none;margin-top:4px;font-size:var(--fs-body-sm);
    overflow-wrap:break-word}

  /* Address / hours rows — space-between pushed the value hard right and
     wrapped it raggedly under its own label. Label above value, both left
     aligned, is legible at any width. */
  .hb-cline{flex-direction:column;align-items:flex-start;gap:4px;flex-wrap:nowrap}
  .hb-cline .v{text-align:left}

  /* The closer heading is display-scale; keep it from crowding the edge. */
  .hb-closer .big{font-size:clamp(44px,13vw,80px)}
  .hb-cgrid{margin-top:32px}
}
/* Header space, phones. The directions icon adds ~50px to a row that was
   already tight, and the owner chose to keep the words on "Order online"
   rather than reduce the primary CTA to an unlabelled icon — unlabelled
   CTAs get tapped less, and ordering is what the site exists to drive.
   So the room comes from the logo and the pill instead. */
@media(max-width:700px){
  .hb-logo{height:28px}
  .hb-nav.scrolled .hb-logo{height:26px}
  /* 44px stays — see the touch-target note on .hb-nav-icon. */
  .hb-nav{gap:8px}
  .hb-nav-icon svg{width:17px;height:17px}
  .hb-open{margin-left:6px;padding:3px 7px;font-size:var(--fs-2xs)}
  .hb-nav .nib-btn{padding:9px 13px}
}

/* Below ~480px even that is not enough for logo + pill + directions + CTA +
   toggle. The pill goes first: it is the one item that is a nicety rather
   than an action, and the hours still appear on Home and on Contact.
   (Was 359px; the directions icon moved the threshold up.) */
@media(max-width:480px){
  .hb-open{display:none}
}
