/* --------------------------------------------------------------------------
   WEBFONTS — all three are self-hosted from assets/fonts/ and preloaded in
   each page's <head>. Oswald and Epilogue used to come from Google Fonts;
   serving them from our own origin removes two extra DNS+TLS handshakes and
   a second round trip, which is what let headings paint in Arial Narrow and
   then jump to Oswald.

   Both are VARIABLE fonts — one file covers the whole weight range, which is
   why there is a range in font-weight rather than a single number. Don't
   replace these with static per-weight files; you'd ship more bytes.

   Only the `latin` subset is stored. Every character the site actually
   renders lives in it. The arrows and stars sit outside latin and already
   fell back to a system face under Google Fonts, so nothing has changed.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Oswald';
  src: url('assets/fonts/Oswald-Variable.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: 'Epilogue';
  src: url('assets/fonts/Epilogue-Variable.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: 'Beautifully Delicious';
  src: url('assets/fonts/BDScript-Regular.woff2') format('woff2'),
       url('assets/fonts/BDScript-Regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  /* block, not swap: swap paints Brush Script MT first and then jumps to this
     face, which is the flicker. block holds that one word invisible for up to
     ~3s instead, and since menu.html preloads the file it is normally in hand
     before first paint, so nothing is held at all. Only this word is affected
     — every other element paints immediately. */
  font-display: block;
}

/* ==========================================================================
   SILK ROAD CHAI & COFFEE — styles.css

   MOBILE-FIRST. Rules outside any @media block ARE the phone layout. Larger
   screens only ever add to it, never undo it.

   ┌── BREAKPOINTS — there are only three. Please don't invent a fourth. ────┐
   │                                                                        │
   │   (none)   0–599px    Phone. Single column, everything full width.      │
   │   600px    600–899    Large phone landscape / tablet portrait.          │
   │                       Two-column grids appear. Still hamburger nav.     │
   │   900px    900–1199   Tablet landscape / laptop. Desktop nav appears,   │
   │                       header grows, side-by-side layouts.               │
   │   1200px   1200+      Wide desktop. Three-column package grid.          │
   │                                                                        │
   │   900px is the important one: it's where the hamburger becomes the      │
   │   full nav, so every mobile-only behaviour keys off it.                 │
   └────────────────────────────────────────────────────────────────────────┘

   CONTENTS
     1. Design tokens          6. Packages
     2. Reset & base           7. Community / Events
     3. Layout helpers         8. Book / About / Menu
     4. Header & navigation    9. Footer & utilities
     5. Hero
   ========================================================================== */


/* ==========================================================================
   1. DESIGN TOKENS
   >> CHANGE BRAND COLOURS HERE and the whole site re-themes.
   ========================================================================== */
:root {
  /* ---- THE BRAND PALETTE — these five are the whole system --------------
     #FFFFFF  white          page background
     #EEEDEB  warm grey      quiet bands, hairlines
     #FFF9E9  cream          warm accent, button fill
     #4D5347  sage green     dark bands, primary buttons
     #000000  black          all body text
     --------------------------------------------------------------------- */
  --white:        #FFFFFF;
  --grey:         #EEEDEB;
  --cream:        #FFF9E9;
  --green:        #4D5347;
  --black:        #000000;

  /* Same sage as --green, as raw channels, so it can be used at partial
     opacity: rgba(var(--green-rgb), .9). Keep the two in sync. */
  --green-rgb:    77, 83, 71;

  /* Header glass. A near-neutral charcoal — deliberately NOT the brand green,
     which read as a solid bar rather than as glass. The two-point green cast
     stops it going cold against the warm photography. */
  --header-glass-rgb: 18, 20, 17;

  /* ---- Working aliases (what the rest of the file actually calls) ------- */
  --bg:           var(--white);
  --ink:          var(--black);   /* body text */
  --bone:         var(--grey);    /* the About band */

  /* ---- Derived shades — NOT new brand colours -------------------------
     Every value below is one of the five above, either darkened or set at
     partial opacity. Nothing here introduces a sixth colour.
     --------------------------------------------------------------------- */
  --green-dark:   #3A3F36;              /* sage at 76% — button/link hover  */
  --green-soft:   var(--grey);          /* quiet tinted background          */
  --cream-soft:   #FFFCF4;              /* cream lightened toward white     */
  --muted:        rgba(0, 0, 0, .62);   /* black at 62% — secondary text    */
  --line:         rgba(0, 0, 0, .13);   /* black at 13% — hairline borders  */

  /* ---- Typography — matches the Squarespace theme ----------------------
     Headings   Oswald
     Body/misc  Epilogue
     Base size  16px
     Both are open-source (OFL), so unlike an Adobe Fonts face they may be
     self-hosted. See the README if you want to drop the Google request.
     --------------------------------------------------------------------- */

  /* Arial Narrow sits first in the fallback because it's condensed like
     Oswald. Before the webfont arrives, headlines then occupy roughly the
     right width instead of ballooning and reflowing when it loads. */
  --font-display: 'Oswald', 'Arial Narrow', 'Helvetica Neue Condensed', 'Helvetica Neue', sans-serif;
  --font-body:    'Epilogue', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* Header height follows the logo — it grew when the logo doubled so the
     wordmark keeps breathing room rather than touching the edges. Everything
     that needs to clear the fixed header (anchor scrolling, hero top padding,
     the menu jump bar) reads this variable, so changing it here is enough. */
  --header-h:     70px;
  --radius:       10px;
  --ease:         cubic-bezier(.2, .8, .2, 1);
  --shell:        1240px;   /* max content width */

  /* Fluid page gutter: 20px on a phone, up to 48px on a desktop */
  --gutter:       clamp(20px, 5vw, 48px);
}

@media (min-width: 900px) { :root { --header-h: 96px; } }


/* ==========================================================================
   2. RESET & BASE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* Stops anchor links from tucking their target under the fixed header */
  scroll-padding-top: var(--header-h);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-body);
  /* 16px to match the Squarespace theme's base size. Epilogue has a large
     x-height, so it reads slightly bigger than its point size suggests. */
  font-size: 16px;
  line-height: 1.62;
  color: var(--ink);
  background: var(--bg);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* Locked while the mobile menu is open */
body.nav-open { overflow: hidden; }

img {
  max-width: 100%;
  height: auto;
  display: block;
}

a { color: inherit; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: .005em;
  margin: 0 0 .5em;
  text-wrap: balance;
}

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

strong { font-weight: 600; }

::selection { background: var(--green); color: var(--cream); }

/* Visible focus ring for keyboard users only */
:focus-visible {
  outline: 3px solid var(--cream);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--cream);
  color: var(--ink);
  padding: 12px 20px;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { left: 12px; top: 12px; }


/* ==========================================================================
   3. LAYOUT HELPERS
   ========================================================================== */
.container {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Vertical rhythm. The vw term makes this breathe with the screen; the 48px
   floor keeps phone sections from feeling cramped without wasting a third of
   the viewport on empty space. */
.section { padding-block: clamp(48px, 9vw, 110px); }

.section--dark {
  background: var(--green);
  color: #fff;
}

/* For pages that open on a section instead of a photo hero (Book, Menu):
   adds enough top padding to clear the fixed header. */
.section--page-top { padding-top: calc(var(--header-h) + clamp(36px, 7vw, 76px)); }

/* Fluid type scale — no media queries needed, it just breathes with the viewport */
.h2 {
  font-size: clamp(1.9rem, 5.2vw, 3.1rem);
  color: var(--ink);
}
.h2--light { color: #fff; }

.lead {
  font-size: clamp(1rem, 2.4vw, 1.08rem);
  color: var(--ink);
  max-width: 56ch;
}
.lead--light { color: rgba(255,255,255,.9); }

/* Hand-drawn style underline behind key words */
.u-mark {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 100% 3px;
  background-position: 0 92%;
  padding-bottom: 2px;
}
.u-mark--cream { color: var(--cream); }

.link-underline {
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1.5px;
  transition: opacity .25s var(--ease);
}
.link-underline:hover { opacity: .7; }


/* --------------------------------------------------------------------------
   BUTTONS — shared transition system.
   Every button lifts 2px, deepens its shadow, and shifts colour on hover.
   -------------------------------------------------------------------------- */
.btn {
  --btn-bg:     var(--cream);
  --btn-fg:     var(--ink);
  --btn-bg-alt: #fff;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5em;
  min-height: 48px;                 /* comfortable thumb target */
  padding: 13px 30px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--font-body);
  font-size: .95rem;
  font-weight: 500;
  letter-spacing: .01em;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: 4px;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0,0,0,.10);
  transition:
    background-color .3s var(--ease),
    transform        .3s var(--ease),
    box-shadow       .3s var(--ease),
    color            .3s var(--ease);
}

.btn:hover {
  background: var(--btn-bg-alt);
  transform: translateY(-2px);
  box-shadow: 0 8px 22px rgba(0,0,0,.22);
}
.btn:active { transform: translateY(0); box-shadow: 0 1px 2px rgba(0,0,0,.18); }

.btn--cream { --btn-bg: var(--cream);      --btn-fg: var(--ink); --btn-bg-alt: #fff; }
.btn--green { --btn-bg: var(--green);      --btn-fg: #fff;       --btn-bg-alt: var(--green-dark); }
.btn--sm    { min-height: 42px; padding: 10px 20px; font-size: .88rem; }

/* SECONDARY BUTTON ON A LIGHT BACKGROUND.
   A cream button on the cream closing band was cream-on-cream — a measured
   contrast of 1.00:1, i.e. completely invisible apart from its shadow. This
   outlined version reads at 7.6:1 and still sits back from the primary. */
.btn--outline {
  --btn-bg: transparent;
  --btn-fg: var(--green);
  --btn-bg-alt: var(--green);
  border-color: var(--green);
  box-shadow: none;
}
.btn--outline:hover,
.btn--outline:focus-visible { color: #fff; }

/* SECONDARY BUTTON ON A DARK BACKGROUND (the sticky bar). */
.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: #fff;
  --btn-bg-alt: rgba(255, 255, 255, .16);
  border-color: rgba(255, 255, 255, .6);
  box-shadow: none;
}


/* --------------------------------------------------------------------------
   SCROLL-REVEAL
   Elements start slightly down + transparent, then settle when they enter the
   viewport. Only opacity and transform animate, so this is GPU-composited and
   causes zero layout shift.
   -------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
  transition-delay: calc(var(--d, 0) * 110ms);
  will-change: opacity, transform;
}
.reveal.is-visible {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}
/* Once settled, release the compositor hint */
.reveal.is-done { will-change: auto; }

/* If JS is unavailable, never hide content */
.no-js .reveal { opacity: 1; transform: none; }


/* ==========================================================================
   4. HEADER & NAVIGATION
   ========================================================================== */
/* ┌── HEADER SCRIM ───────────────────────────────────────────────────────┐
   │  At the very top of a page the header is fully transparent, sitting   │
   │  over the hero photograph. The moment you scroll it fades in a green  │
   │  scrim that is dense at the top and tapers to nothing at the bottom,  │
   │  so there's no hard edge cutting across the page.                     │
   │                                                                       │
   │  Built from two stacked pseudo-elements, both extending 18px BELOW    │
   │  the header's layout box so the fade has somewhere to finish. Because │
   │  they're absolutely positioned inside a fixed parent, that overhang   │
   │  is purely visual — it changes no layout and no anchor offsets.       │
   │                                                                       │
   │    ::before  a tapered blur of whatever is behind                     │
   │    ::after   the tapered green gradient itself                        │
   │                                                                       │
   │  Density is driven by --header-tint, which app.js ramps 0 -> 1 over    │
   │  the first 180px of scroll, so the glass DARKENS as you travel down   │
   │  rather than snapping on at a threshold.                              │
   │                                                                       │
   │  CONTRAST FLOOR. Nav text is pure #fff. The gradient holds .66-.72    │
   │  through the top 50%, where the logo and links sit. Composited over   │
   │  pure white — the worst backdrop there is — that lands at 5.8:1,      │
   │  clear of the 4.5:1 minimum. Soften those first two stops and you     │
   │  drop below it: at .50 the same test gives 3.6:1 and fails.           │
   │                                                                       │
   │  The ramp only matters where it is still weak, i.e. the first ~180px  │
   │  of each page. That strip is a dark hero photo, the green --dark      │
   │  section or the terracotta panel on every one of the six pages, so    │
   │  white text is safe there even at tint 0. If a page ever opens on a   │
   │  light background, revisit this.                                      │
   └───────────────────────────────────────────────────────────────────────┘ */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  height: var(--header-h);
  background: transparent;
  /* 0 at the top of the page, 1 once fully darkened. app.js writes this on
     scroll; with JS off it stays 0 and the header is transparent, which is
     exactly how it behaved before. */
  --header-tint: 0;
}

.site-header::before,
.site-header::after {
  content: "";
  position: absolute;
  inset: 0 0 -18px 0;          /* the 18px is the visual overhang */
  pointer-events: none;
  opacity: var(--header-tint);
  /* No transition: the value is rewritten per animation frame, and a
     transition would lag a frame behind the scroll and feel rubbery. */
}

/* Blur layer. Masked with the same taper as the gradient — without the mask
   the blur would stop dead at the element's edge and draw a visible seam. */
.site-header::before {
  -webkit-backdrop-filter: blur(18px) saturate(180%);
          backdrop-filter: blur(18px) saturate(180%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 52%, rgba(0,0,0,.35) 78%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 0%, #000 52%, rgba(0,0,0,.35) 78%, transparent 100%);
}

/* The tint itself. Same taper as before so the bottom edge still dissolves
   instead of cutting a line across the page. */
.site-header::after {
  background: linear-gradient(
    180deg,
    rgba(var(--header-glass-rgb), .72) 0%,
    rgba(var(--header-glass-rgb), .66) 50%,
    rgba(var(--header-glass-rgb), .42) 74%,
    rgba(var(--header-glass-rgb), .16) 90%,
    rgba(var(--header-glass-rgb), 0)   100%
  );
}

/* Without backdrop-filter there is no glass to see through, so the tint has
   to carry the contrast on its own. Firefox before 103 and older Safari. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header::after {
    background: linear-gradient(
      180deg,
      rgba(var(--header-glass-rgb), .94) 0%,
      rgba(var(--header-glass-rgb), .90) 50%,
      rgba(var(--header-glass-rgb), .60) 74%,
      rgba(var(--header-glass-rgb), .24) 90%,
      rgba(var(--header-glass-rgb), 0)   100%
    );
  }
}

.header-inner {
  position: relative;   /* keeps logo and nav above both scrim layers */
  z-index: 1;
  height: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}

.brand { display: block; flex: none; }

/* LOGO SIZING — roughly double the previous on-screen size.
   Two thirds of that came for free: the source file had a big transparent
   margin baked in, so the wordmark only occupied 58% of its own canvas. At a
   40px box the visible mark was ~23px. The asset is now cropped to the artwork
   (1059×500 instead of 1257×837), so the box height IS the mark height.
   The remaining difference comes from the modest bump below.

   ⚠️ If you ever re-export the logo, keep it tightly cropped and update the
   width/height attributes on the <img> in all five HTML files — they set the
   reserved aspect ratio, and stale numbers distort it. */
.brand-logo {
  width: auto;
  height: 48px;
  /* Lifts the white wordmark off busy hero photography */
  filter: drop-shadow(0 1px 6px rgba(0, 0, 0, .45));
  transition: opacity .3s var(--ease);
}
.brand:hover .brand-logo { opacity: .8; }

@media (min-width: 900px) { .brand-logo { height: 70px; } }

/* ---------- Desktop nav ---------- */
.nav-desktop { display: none; }

@media (min-width: 900px) {
  .nav-desktop {
    display: flex;
    align-items: center;
    gap: clamp(16px, 2vw, 30px);
  }
  .nav-link {
    position: relative;
    color: #fff;
    font-size: 1rem;
    text-decoration: none;
    padding-block: 6px;
    transition: opacity .25s var(--ease);
  }
  /* Underline grows out from the centre on hover */
  .nav-link::after {
    content: "";
    position: absolute;
    left: 0; bottom: 0;
    width: 100%; height: 1.5px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: center;
    transition: transform .32s var(--ease);
  }
  .nav-link:hover::after,
  .nav-link.is-active::after { transform: scaleX(1); }
  .nav-link:hover { opacity: .85; }
}

/* The page you are currently on, in both navs */
.nav-mobile-link.is-active { color: var(--cream); }

/* ---------- Animated hamburger ---------- */
.hamburger {
  display: grid;
  place-items: center;
  width: 46px; height: 46px;
  margin-right: -10px;
  background: none;
  border: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
@media (min-width: 900px) { .hamburger { display: none; } }

.hamburger-box {
  position: relative;
  display: block;
  width: 26px; height: 18px;
}
.hamburger-bar {
  position: absolute;
  left: 0;
  width: 100%; height: 2px;
  background: #fff;
  border-radius: 2px;
  transition: transform .38s var(--ease), opacity .2s var(--ease);
}
.hamburger-bar:nth-child(1) { top: 0; }
.hamburger-bar:nth-child(2) { top: 8px; }
.hamburger-bar:nth-child(3) { top: 16px; }

/* Bars converge and cross into an X */
.hamburger.is-open .hamburger-bar:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.hamburger.is-open .hamburger-bar:nth-child(2) { opacity: 0; transform: scaleX(.3); }
.hamburger.is-open .hamburger-bar:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* ---------- Full-screen mobile menu ---------- */
.nav-mobile {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--header-h) var(--gutter) 40px;
  background: var(--green);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-12px);
  transition: opacity .38s var(--ease), transform .38s var(--ease), visibility .38s;
}
.nav-mobile[hidden] { display: flex; }     /* we animate it, so keep it in flow */
.nav-mobile.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
@media (min-width: 900px) { .nav-mobile { display: none !important; } }

.nav-mobile-list {
  list-style: none;
  margin: 0 0 auto;
  padding: 0;
}
.nav-mobile-list li {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
  /* Each link starts 60ms after the one above it */
  transition-delay: calc(var(--i) * 60ms + 90ms);
}
.nav-mobile.is-open .nav-mobile-list li { opacity: 1; transform: translateY(0); }

.nav-mobile-link {
  display: block;
  padding: 14px 0;
  color: #fff;
  font-family: var(--font-display);
  font-size: clamp(2rem, 9vw, 2.9rem);
  font-weight: 500;
  text-decoration: none;
  transition: color .25s var(--ease), transform .25s var(--ease);
}
.nav-mobile-link:hover,
.nav-mobile-link:focus { color: var(--cream); transform: translateX(6px); }

.nav-mobile-foot {
  color: rgba(255,255,255,.72);
  font-size: .92rem;
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
  transition-delay: calc(var(--i) * 60ms + 90ms);
}
.nav-mobile.is-open .nav-mobile-foot { opacity: 1; transform: translateY(0); }
.nav-mobile-foot a { color: var(--cream); }
.nav-mobile-foot p { margin: 6px 0 0; font-family: var(--font-display); letter-spacing: .04em; }


/* ==========================================================================
   5. HERO
   .hero-media is absolutely positioned and 120% tall so there is headroom for
   the parallax translate — the image never runs out of pixels at either end.
   ========================================================================== */
/* PHONE SIZING NOTE
   Heroes used to be 74–86svh, which on a phone meant the entire first screen
   was a photo and a headline — you had to scroll before seeing a single word
   about what the business does. They're now sized to end just below the
   button, so the next section peeks above the fold and invites a scroll.
   Full-height drama is reserved for 900px and up, where there's room for it. */
.hero {
  position: relative;
  display: flex;
  align-items: center;
  isolation: isolate;
  overflow: hidden;
  min-height: 62svh;                 /* svh dodges the iOS URL-bar resize jump */
  padding-block: calc(var(--header-h) + 28px) 44px;
  color: #fff;
}
@supports not (min-height: 62svh) { .hero { min-height: 62vh; } }

.hero--home { min-height: 72svh; }
@supports not (min-height: 72svh) { .hero--home { min-height: 72vh; } }

.hero--band { min-height: 42svh; }
@supports not (min-height: 42svh) { .hero--band { min-height: 42vh; } }

/* Roomier once there's screen to spare */
@media (min-width: 900px) {
  .hero        { min-height: 74svh; padding-block: calc(var(--header-h) + 40px) 60px; }
  .hero--home  { min-height: 86svh; }
  .hero--band  { min-height: 56svh; }

  @supports not (min-height: 74svh) {
    .hero       { min-height: 74vh; }
    .hero--home { min-height: 86vh; }
    .hero--band { min-height: 56vh; }
  }
}

.hero-media {
  position: absolute;
  inset: -10% 0;                     /* the parallax headroom */
  z-index: -2;
  will-change: transform;
}
.hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Dark wash so white text always clears WCAG contrast, whatever the photo */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg, rgba(0,0,0,.62) 0%, rgba(0,0,0,.38) 45%, rgba(0,0,0,.66) 100%);
}
.hero-scrim--left {
  background:
    linear-gradient(90deg, rgba(0,0,0,.78) 0%, rgba(0,0,0,.50) 55%, rgba(0,0,0,.28) 100%);
}

.hero-content { position: relative; text-align: center; }
.hero-content--left { text-align: left; }

/* The vw term is what does the work on phones; the rem floor/ceiling only
   catch the extremes. Tuned so the homepage headline lands on two lines at
   390px rather than three, which is what pushed the button off-screen. */
.hero-title {
  font-size: clamp(1.85rem, 7.6vw, 4.2rem);
  color: #fff;
  margin-bottom: .38em;
  text-shadow: 0 2px 20px rgba(0,0,0,.30);
  /* Prevents a lone orphan word on the last line */
  text-wrap: balance;
}
.hero-title--band { font-size: clamp(1.6rem, 6.4vw, 3.6rem); }

.hero-sub {
  max-width: 62ch;
  margin-inline: auto;
  font-size: clamp(1rem, 2.9vw, 1.28rem);
  color: rgba(255,255,255,.94);
  text-shadow: 0 1px 12px rgba(0,0,0,.35);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 30px;
}
.hero-actions--left { justify-content: flex-start; margin-top: 24px; }

.about-intro {
  max-width: 30ch;
  font-size: clamp(1.05rem, 3.4vw, 1.42rem);
  line-height: 1.5;
  color: #fff;
  text-shadow: 0 1px 14px rgba(0,0,0,.4);
}

/* Scroll cue */
.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 22px;
  translate: -50% 0;
  width: 26px; height: 42px;
  border: 2px solid rgba(255,255,255,.6);
  border-radius: 14px;
  display: none;
  transition: border-color .3s var(--ease);
}
.scroll-cue span {
  position: absolute;
  left: 50%; top: 8px;
  translate: -50% 0;
  width: 3px; height: 7px;
  background: #fff;
  border-radius: 2px;
  animation: cue 1.9s var(--ease) infinite;
}
.scroll-cue:hover { border-color: #fff; }
@keyframes cue {
  0%, 100% { transform: translateY(0);    opacity: 1; }
  60%      { transform: translateY(14px); opacity: 0; }
}
@media (min-width: 900px) { .scroll-cue { display: block; } }


/* ==========================================================================
   6. MOBILE COFFEE CATERING
   ========================================================================== */
/* ┌── WHY THESE PHOTOS ARE CROPPED RATHER THAN SCALED ────────────────────┐
   │                                                                      │
   │  Both source images are portrait (2:3 and 3:4). Previously they were │
   │  set to width:100% with height:auto, so their height was whatever    │
   │  the aspect ratio produced. On a 600px screen that made the first    │
   │  photo 460×690 — the two images accounted for 72–79% of the entire   │
   │  section's height, for three sentences of copy.                      │
   │                                                                      │
   │  Now the FIGURE owns the height and the image fills it with          │
   │  object-fit: cover. Height is a clamp() tied to viewport width, so   │
   │  it can never run away, and any replacement photo — portrait,        │
   │  landscape, square — drops in without moving the layout.             │
   └──────────────────────────────────────────────────────────────────────┘ */
.catering-grid {
  display: grid;
  gap: clamp(14px, 2.6vw, 24px) clamp(12px, 2.4vw, 24px);

  /* Phones and tablets: copy first, then the two photos as a pair.
     Leading with a 500px portrait meant scrolling past a photograph before
     reading a single word about what the business does. */
  grid-template-columns: 1fr 1fr;
  grid-template-areas:
    "copy copy"
    "a    b";
}
.catering-copy      { grid-area: copy; margin-bottom: clamp(4px, 1.5vw, 14px); }
.catering-photo--a  { grid-area: a; }
.catering-photo--b  { grid-area: b; }

.catering-photo {
  position: relative;             /* see the note on the <img> below */
  margin: 0;
  /* The figure owns the height — see the note above. */
  height: clamp(190px, 30vw, 280px);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 14px 38px rgba(0, 0, 0, .14);
}

/* The image is positioned absolutely rather than sized with height: 100%.
   That percentage only resolves against a parent with a DEFINITE height, and
   the tall photo at 900px+ gets its height from stretching across two grid
   rows — which is not definite at the time the image is laid out. The
   percentage silently fell back to the image's intrinsic ratio and rendered
   544×816 instead of the intended crop. Absolute fill has no such dependency,
   and it also keeps the image out of grid row sizing, so there's no
   circularity between the row height and the photo height. */
.catering-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Crop anchors. Both sources are portrait, so a wide box has to discard the
   top and bottom — these keep the subject in frame.
   >> If you swap a photo and the crop looks wrong, adjust here. */
.catering-photo--a img { object-position: center 30%; }  /* keeps the barista and cup */
.catering-photo--b img { object-position: center 52%; }  /* keeps the pink menu card in frame */

/* ---------- 900px and up: the original editorial layout ----------
   Tall photo down the left, copy top-right, second photo beneath it. Source
   order is unchanged; only the grid areas move. */
@media (min-width: 900px) {
  .catering-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas:
      "a copy"
      "a b";
    gap: clamp(20px, 2.4vw, 32px) clamp(28px, 4vw, 56px);
    align-items: stretch;
  }
  .catering-copy { padding-top: 6px; margin-bottom: 0; }

  /* Spans both rows, so it stretches to the height of copy + photo B.
     min-height stops it collapsing when the copy is short. */
  .catering-photo--a { height: auto; min-height: clamp(340px, 32vw, 480px); }
  .catering-photo--b { height: clamp(200px, 18vw, 280px); }
}


/* ==========================================================================
   7. CATERING PACKAGES
   ========================================================================== */
.packages .h2 { margin-bottom: clamp(30px, 5vw, 54px); }

/* ┌── PACKAGES LAYOUT — one switch point, at 720px ───────────────────────┐
   │                                                                      │
   │   ≤ 719px   Stacked. Photo beside the title, hairline between cards. │
   │   ≥ 720px   All three side by side, always. Never two.               │
   │                                                                      │
   │  720px rather than the site's usual 600px is deliberate. These are   │
   │  text-heavy cards: at a 600px viewport, three columns work out to    │
   │  about 170px each, which is roughly 15 characters a line — copy      │
   │  breaks into confetti. 720px is the narrowest width where three      │
   │  columns still hold a readable line. This is a component-level       │
   │  breakpoint, not a fourth site-wide tier.                            │
   │                                                                      │
   │  Everything between 720px and 1440px scales fluidly (see the clamp() │
   │  values below) so there's no jump — the circles and type simply grow │
   │  with the column.                                                    │
   └──────────────────────────────────────────────────────────────────────┘ */
.pkg-grid {
  display: grid;
  gap: clamp(38px, 5vw, 44px);
}

.pkg { display: flex; flex-direction: column; }

/* ---------- ≤719px: stacked, photo beside title ---------- */
/* Stacked in one column, three packages read as one continuous wall of text —
   there's no cue for where "The Espresso Bar" ends and "On Tap" begins. Each
   gets a hairline rule, and the photo sits beside the title rather than above
   it, saving roughly 150px of scrolling per card. */
@media (max-width: 719px) {
  .pkg-grid { gap: 0; }

  .pkg {
    display: grid;
    grid-template-columns: 104px 1fr;
    grid-template-areas:
      "media title"
      "body  body";
    column-gap: 16px;
    align-items: center;
    padding-block: 24px;
    border-top: 1px solid rgba(255,255,255,.18);
  }
  .pkg:first-child { border-top: 0; padding-top: 4px; }

  .pkg-media { grid-area: media; width: 104px; margin-bottom: 0; }
  .pkg-title { grid-area: title; margin-bottom: 0; }
  .pkg-body  {
    grid-area: body;
    margin-top: 12px;
    /* Full width, this runs to ~87 characters a line on a 700px screen —
       long enough that the eye loses its place returning to the next line. */
    max-width: 62ch;
  }
}

/* ---------- ≥720px: three across, fluid ---------- */
@media (min-width: 720px) {
  .pkg-grid {
    grid-template-columns: repeat(3, 1fr);
    column-gap: clamp(20px, 2.6vw, 44px);
    row-gap: 0;
  }
}

/* Align the three rows ACROSS the columns.
   Without this, "The Espresso Bar" (one line) and "The Silk Road Mobile
   Trailer" (two lines) push their body copy to different heights, and the
   row of circles no longer reads as a row. Subgrid makes all three cards
   share the grid's rows, so photos line up with photos and body copy starts
   on the same baseline in every column.
   Wrapped in @supports because browsers without subgrid fall back to the
   flex column above, which still stacks correctly — just less tidily. */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 720px) {
    .pkg-grid {
      grid-template-rows: auto auto auto;   /* photo | title | body */
      row-gap: clamp(12px, 1.5vw, 20px);
    }
    .pkg {
      display: grid;
      grid-row: span 3;
      grid-template-rows: subgrid;
    }
    .pkg-media, .pkg-title { margin-bottom: 0; }
  }
}

/* Perfect circle crop, matching the original design.
   The vw term is what scales it between 720px and 1440px: ~125px on a tablet,
   ~215px on a wide desktop, with no step change in between. */
.pkg-media {
  width: min(clamp(118px, 15vw, 220px), 62%);
  aspect-ratio: 1;
  margin-bottom: 22px;
  border-radius: 50%;
  overflow: hidden;
  background: rgba(255,255,255,.08);
}
.pkg-media img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform .6s var(--ease);
}
.pkg:hover .pkg-media img { transform: scale(1.07); }

.pkg-title {
  font-size: clamp(1.3rem, 3.4vw, 1.62rem);
  color: #fff;
  margin-bottom: .6em;
}
.pkg-body {
  font-size: .96rem;
  line-height: 1.66;
  color: rgba(255,255,255,.88);
}
.pkg-body p { margin-bottom: .95em; }

/* Once the cards are three across, both title and body have to live inside a
   third of the page rather than the whole of it, so they scale down with the
   column. Measured line lengths land at roughly 27–34 characters from tablet
   to desktop — tight, which is the nature of three columns, but comfortably
   above the ~25 mark where prose starts to feel choppy. */
@media (min-width: 720px) {
  .pkg-title { font-size: clamp(1.08rem, 1.9vw, 1.62rem); }
  /* .88rem floor, not .85rem — 14px is about as small as this body copy
     should get on a tablet, and it still leaves ~30 characters a line. */
  .pkg-body  { font-size: clamp(.88rem, 1.15vw, .96rem); line-height: 1.62; }
}
.pkg-body strong { color: #fff; }

.pkg-cta {
  display: flex;
  justify-content: center;
  margin-top: clamp(38px, 5vw, 56px);
}


/* ==========================================================================
   8. COMMUNITY STRIP
   ========================================================================== */
.community-grid {
  display: grid;
  gap: clamp(28px, 5vw, 60px);
  align-items: center;
}
@media (min-width: 900px) {
  .community-grid { grid-template-columns: minmax(0, 380px) 1fr; }
}

/* The arched crop from the original site */
.community-photo {
  margin: 0;
  max-width: 340px;
}
.community-photo img {
  width: 100%;
  border-radius: 999px 999px 8px 8px;
  box-shadow: 0 18px 46px rgba(0,0,0,.16);
}

.community-copy .btn { margin-top: 22px; }


/* ==========================================================================
   9. UPCOMING EVENTS
   ========================================================================== */
.events-body {
  padding-top: clamp(28px, 4vw, 48px);
  padding-bottom: clamp(48px, 7vw, 84px);
}

/* --------------------------------------------------------------------------
   ICONS
   One hidden <svg> sprite per page holds the symbols; each icon is a <use>.
   Stroke is currentColor, so icons inherit the surrounding text colour and
   re-theme with the palette for free.
   -------------------------------------------------------------------------- */
.ico-sprite { display: none; }

.ev-ico {
  width: 1.6em;
  height: 1.6em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Hangs in the padding-left reserved by .event-name */
.ev-ico--inline {
  position: absolute;
  left: 0;
  top: .2em;
  width: 1.1em;
  height: 1.1em;
  color: var(--green);
}

.events-head { margin-bottom: clamp(20px, 3vw, 30px); }
.events-head .lead { color: var(--muted); }

/* Standing note about hours, sat just under the section intro */
.events-note {
  margin: 14px 0 0;
  padding: 12px 16px;
  border-left: 3px solid var(--green);
  background: var(--cream);
  border-radius: 0 8px 8px 0;
  font-size: .92rem;
  color: var(--ink);
  max-width: 62ch;
}
.events-note strong { font-weight: 600; }

/* --------------------------------------------------------------------------
   WEEKLY REGULARS
   Standing spots don't need dates, so the day of the week is the headline and
   the card leads with it. Three across on tablet and up.
   -------------------------------------------------------------------------- */
.week-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: clamp(12px, 2vw, 20px);
}
@media (min-width: 600px) { .week-grid { grid-template-columns: repeat(3, 1fr); } }

.week-card {
  position: relative;
  /* Exposed as a variable so the paused banner can bleed to the card edges
     with a negative margin of exactly this value. */
  --week-pad: clamp(18px, 2.6vw, 26px);
  padding: var(--week-pad);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--white);
  overflow: hidden;                  /* keeps the banner inside the corners */
  transition: transform .35s var(--ease), box-shadow .35s var(--ease), border-color .35s var(--ease);
}
.week-card:hover {
  transform: translateY(-3px);
  border-color: transparent;
  box-shadow: 0 14px 38px rgba(0, 0, 0, .13);
}

.week-day {
  display: block;
  font-family: var(--font-display);
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--green);
  margin-bottom: 12px;
}
.week-card .ev-ico {
  width: 2.1em;
  height: 2.1em;
  color: var(--green);
  margin-bottom: 12px;
}

.week-venue {
  font-size: clamp(1.12rem, 2.6vw, 1.3rem);
  margin: 0 0 .3em;
}
.week-meta {
  margin: 0;
  font-size: .92rem;
  line-height: 1.45;
  color: var(--muted);
}
/* Opening hours — the thing people actually scan a schedule for, so it sits
   above the address and keeps full-strength ink. */
.week-time {
  font-weight: 600;
  color: var(--ink);
}

/* Street address — a notch quieter than the venue name above it */
.week-addr {
  font-size: .86rem;
  color: rgba(0, 0, 0, .55);
}
/* PAUSED-SPOT BANNER
   Added by app.js when a standing spot is off for a date that's currently
   visible in the schedule. It sits at the very TOP of the card and bleeds to
   the edges, because the whole point is that it must be read before the venue
   name and address — anything below those gets skipped.

   Solid sage with cream text: the loudest combination available in the palette
   without introducing an off-brand warning colour. */
.week-note {
  display: flex;
  align-items: center;
  gap: 9px;
  /* Negative margin equal to the card's padding, so it spans edge to edge */
  margin: calc(var(--week-pad) * -1) calc(var(--week-pad) * -1) var(--week-pad);
  padding: 11px var(--week-pad);
  background: var(--green);
  color: var(--cream);
  font-size: .84rem;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: .01em;
}
.week-note svg {
  flex: none;
  width: 1.15rem;
  height: 1.15rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.week-note .week-note-sub {
  display: block;
  font-weight: 400;
  opacity: .82;
}

/* The card as a whole flags itself too, so the difference is visible before
   you've read a word. */
.week-card.is-paused {
  border-color: var(--green);
  box-shadow: 0 0 0 1px var(--green);
}

.week-card .event-link { margin-top: 6px; }

/* --------------------------------------------------------------------------
   DATED EVENTS
   A date chip on the left, details on the right. Reads as a schedule rather
   than a stack of cards, which is what people are actually scanning for.
   -------------------------------------------------------------------------- */
.event-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 12px;
}

/* Keep the immediate week easy to scan; the additional dates are still in
   the same long-form list and can be revealed without leaving the page. */
.event-list .event-row--extra { display: none; }
.event-list.is-expanded .event-row--extra { display: grid; }

.events-more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .55em;
  width: 100%;
  min-height: 48px;
  margin-top: 18px;
  padding: 10px 18px;
  border: 1px solid var(--green);
  border-radius: 999px;
  background: transparent;
  color: var(--green);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background-color .25s var(--ease), color .25s var(--ease);
}
.events-more:hover,
.events-more:focus-visible {
  background: var(--green);
  color: #fff;
}
.events-more[hidden] { display: none; }

.event-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(14px, 2.4vw, 22px);
  align-items: center;
  padding: clamp(14px, 2.2vw, 20px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--white);
  transition: transform .35s var(--ease), box-shadow .35s var(--ease), border-color .35s var(--ease);
}
.event-row:hover {
  transform: translateY(-2px);
  border-color: transparent;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .12);
}

.date-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: clamp(56px, 9vw, 68px);
  aspect-ratio: 1;
  border-radius: 10px;
  background: var(--green);
  color: #fff;
  line-height: 1;
}
.date-chip-mon {
  font-family: var(--font-display);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .82;
}
.date-chip-day {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.4vw, 1.8rem);
  font-weight: 600;
  margin-top: 3px;
}

/* The icon hangs in the left margin rather than sitting in the text flow.
   As a flex item it became an orphan: a long name like "Carrollton Elementary
   School | Teacher Coffee Day" wrapped to three lines on a 320px screen and
   left the cup stranded alone on line one. Hanging it also gives multi-line
   names a clean indent, with every line aligned under the first. */
.event-name {
  position: relative;
  display: block;
  padding-left: 1.75em;
  font-size: clamp(1.1rem, 2.8vw, 1.32rem);
  margin: 0 0 .28em;
}
/* Divider inside a two-part event name ("Venue | What it is").
   Because .event-name is a flex row, the two halves are separate flex items —
   so on a narrow phone the name breaks cleanly at the divider instead of
   mid-phrase. Muted and thinner than the name so it reads as punctuation. */
.name-sep {
  font-weight: 300;
  color: rgba(0, 0, 0, .3);
}

.event-meta {
  margin: 0;
  font-size: .95rem;
  color: var(--muted);
}
.event-meta .dot { margin: 0 .5em; }

.event-link {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  min-height: 40px;
  margin-top: 2px;
  font-size: .92rem;
  font-weight: 500;
  color: var(--green);
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color .25s var(--ease);
}
.event-link:hover { color: var(--black); }

/* Private bookings: the venue is named, but three cues mark it as closed —
   a grey row, the padlock icon, and the badge below. No address, no map link. */
.event-row--private { background: var(--grey); }
.event-row--private .date-chip { background: var(--muted); }

/* Flows inline after the venue name, so it wraps naturally on a narrow phone
   rather than crushing the name into a column. */
.event-badge {
  display: inline-block;
  vertical-align: .14em;
  margin-left: .5em;
  padding: 3px 10px;
  border: 1px solid rgba(0, 0, 0, .2);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: .66rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--muted);
}

/* WHEN-IS-IT BADGES
   All three are added by app.js and move on their own as dates pass. Ranked
   by how much they matter to someone checking the page: today shouts,
   tomorrow speaks up, "next up" only appears when neither of those exists. */
.event-row--today .event-name::after,
.event-row--tomorrow .event-name::after,
.event-row--next .event-name::after {
  display: inline-block;
  vertical-align: .14em;
  margin-left: .5em;
  padding: 3px 9px;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: .66rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* TODAY — the whole row lifts, not just the badge. This is the one thing
   someone opening the page on their phone is usually looking for. */
.event-row--today {
  background: var(--cream);
  border-color: var(--green);
  box-shadow: 0 0 0 1px var(--green);
}
.event-row--today .event-name::after {
  content: "Today";
  background: var(--green);
  color: var(--cream);
}
/* Keep the private-booking grey from overriding the today treatment */
.event-row--private.event-row--today { background: var(--cream); }

/* TOMORROW — present but quieter: outlined rather than filled. */
.event-row--tomorrow { border-color: var(--green); }
.event-row--tomorrow .event-name::after {
  content: "Tomorrow";
  background: transparent;
  color: var(--green);
  box-shadow: inset 0 0 0 1px var(--green);
}

/* NEXT UP — only when nothing is on today or tomorrow. */
.event-row--next { border-color: var(--green); }
.event-row--next .event-name::after {
  content: "Next up";
  background: var(--cream);
  color: var(--green-dark);
}

/* Marks a row generated from a standing weekly spot, so a mixed list makes it
   obvious which entries repeat and which are one-offs. */
.event-badge--weekly {
  border-color: rgba(77, 83, 71, .35);
  color: var(--green);
}

/* Hidden by app.js once the date has passed */
.event-row[hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   EVENT MAP DIALOG
   Native <dialog>, so the browser handles focus trapping, Escape and making
   the background inert. Only the appearance is ours.
   -------------------------------------------------------------------------- */

/* A button styled to match the text links beside it */
.event-link--btn {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font-family: inherit;
  cursor: pointer;
  margin-left: 18px;
}

.map-dialog {
  width: min(94vw, 900px);
  height: min(92svh, 900px);
  max-height: 92svh;
  padding: 0;
  border: 0;
  box-sizing: border-box;
  border-radius: var(--radius);
  background: var(--white);
  color: var(--ink);
  overflow: hidden;
  z-index: 1001;
  /* Centred by the browser for a modal dialog; this just adds the lift */
  box-shadow: 0 24px 70px rgba(0, 0, 0, .45);
}
@supports not (height: 92svh) {
  .map-dialog {
    height: min(92vh, 900px);
    max-height: 92vh;
  }
}

.map-dialog::backdrop {
  background: rgba(0, 0, 0, .62);
  backdrop-filter: blur(3px);
}

/* Grid so the image area is the only thing that scrolls — the title bar and
   footer stay put, which matters on a phone where the map is tall. */
.map-dialog-inner,
.map-dialog[open] {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
}

/* The native dialog stylesheet hides closed dialogs. Keep that behavior even
   though the open state needs a custom grid display value above. */
.map-dialog:not([open]) { display: none; }

.map-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 14px clamp(14px, 2.4vw, 22px);
  background: var(--green);
  color: #fff;
}
.map-dialog-title {
  margin: 0;
  font-size: clamp(1rem, 2.6vw, 1.22rem);
  color: #fff;
}
.map-dialog-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;                       /* full tap target */
  height: 44px;
  margin-right: -8px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: #fff;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color .25s var(--ease);
}
.map-dialog-close:hover { background: rgba(255, 255, 255, .18); }

.map-dialog-body {
  min-height: 0;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--grey);
}
.map-dialog-body img {
  display: block;
  width: 100%;
  height: auto;
}

.map-dialog-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  padding: 12px clamp(14px, 2.4vw, 22px);
  border-top: 1px solid var(--line);
  background: var(--white);
}
.map-dialog-foot p {
  margin: 0;
  font-size: .86rem;
  color: var(--muted);
}

/* Stop the page behind scrolling while the dialog is open */
body.map-open { overflow: hidden; }


/* --------------------------------------------------------------------------
   INSTAGRAM CARD
   -------------------------------------------------------------------------- */
.ig-card {
  display: grid;
  gap: clamp(14px, 2.4vw, 22px);
  align-items: center;
  margin-top: clamp(36px, 5vw, 56px);
  padding: clamp(22px, 3.4vw, 34px);
  border-radius: var(--radius);
  background: var(--green);
  color: #fff;
  text-align: center;
  justify-items: center;
}
@media (min-width: 720px) {
  .ig-card {
    grid-template-columns: auto minmax(0, 1fr) auto;
    text-align: left;
    justify-items: start;
  }
}
.ig-card-ico {
  width: 40px;
  height: 40px;
  fill: none;
  stroke: var(--cream);
  stroke-width: 1.5;
  color: var(--cream);
}
.ig-card-body h3 {
  font-size: clamp(1.2rem, 3vw, 1.5rem);
  color: #fff;
  margin: 0 0 .3em;
}
.ig-card-body p {
  margin: 0;
  font-size: .95rem;
  color: rgba(255, 255, 255, .86);
  max-width: 52ch;
}
.ig-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}
@media (min-width: 720px) { .ig-card-actions { justify-content: flex-end; } }
.ig-card .btn { flex: none; }

.events-empty {
  padding: 40px 24px;
  text-align: center;
  color: var(--muted);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

/* --------------------------------------------------------------------------
   MISSING-IMAGE PLACEHOLDER
   Deliberately loud so it can never ship to production unnoticed.
   -------------------------------------------------------------------------- */
.img-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  aspect-ratio: 4 / 3;
  padding: 16px;
  text-align: center;
  border: 3px dashed var(--green);
  border-radius: 8px;
  background:
    repeating-linear-gradient(45deg,
      var(--cream) 0 12px,
      var(--cream-soft) 12px 24px);
}
.img-placeholder-tag {
  font-family: var(--font-display);
  font-size: .85rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--green);
}
.img-placeholder-name {
  font-size: .78rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted);
  word-break: break-all;
}


/* ==========================================================================
   10. BOOK AN EVENT
   ========================================================================== */
.book-grid {
  display: grid;
  gap: clamp(30px, 5vw, 56px);
  align-items: start;
}
@media (min-width: 900px) {
  .book-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 420px); }
}

.book-applet { margin-top: 30px; }

/* Reassurance strip under the booking form */
.book-assure {
  list-style: none;
  margin: clamp(20px, 3vw, 30px) 0 0;
  padding: 0;
  display: grid;
  gap: 10px;
  font-size: .92rem;
  color: rgba(255, 255, 255, .84);
}
.book-assure li {
  position: relative;
  padding-left: 26px;
}
/* Decorative tick drawn in CSS — no icon font, no extra request */
.book-assure li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: .42em;
  width: 9px;
  height: 5px;
  border-left: 2px solid var(--cream);
  border-bottom: 2px solid var(--cream);
  transform: rotate(-45deg);
}
.book-assure a { color: var(--cream); text-decoration: underline; text-underline-offset: 3px; }

@media (min-width: 600px) and (max-width: 899px) {
  .book-assure { grid-template-columns: 1fr 1fr; column-gap: 24px; }
}

.book-photo { margin: 0; }
.book-photo img {
  width: 100%;
  border-radius: 999px 999px 10px 10px;
  box-shadow: 0 20px 50px rgba(0,0,0,.25);
}

/* PHONE SIZING NOTE
   The arch photo goes first on phones so the page opens warm rather than
   with a form. But at full width it ran ~450px tall and swallowed the entire
   first screen — on the one page where the whole point is to reach the form.
   Capped to a small centred accent: enough to set the tone, and the headline
   plus the first form step now land above the fold. */
@media (max-width: 899px) {
  .book-photo {
    order: -1;
    /* Definite width, not max-width — see the note on .owner-photo. With
       `margin-inline: auto` a max-width-only figure shrink-to-fits to zero
       until its image loads and then shifts the page. */
    width: 190px;
    max-width: 100%;
    margin-inline: auto;
  }
  .book-photo img { box-shadow: 0 10px 26px rgba(0,0,0,.22); }
  .book-applet { margin-top: 22px; }
}


/* ==========================================================================
   11. ABOUT
   ========================================================================== */
.about-body {
  padding-block: clamp(44px, 6vw, 76px);
  background: var(--bone);
}
.about-body .h2 { font-size: clamp(1.5rem, 4vw, 2rem); color: var(--green-dark); }

.about-text {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.15rem, 3.4vw, 1.72rem);
  line-height: 1.34;
  color: var(--ink);
  max-width: 62ch;
}

/* --------------------------------------------------------------------------
   ABOUT THE OWNER
   Portrait beside the story from 720px up. Below that the photo leads, then
   the copy — a face before a wall of text is the right order on a phone.
   -------------------------------------------------------------------------- */
.owner-grid {
  display: grid;
  gap: clamp(26px, 4vw, 56px);
  align-items: start;
}
@media (min-width: 720px) {
  .owner-grid { grid-template-columns: minmax(0, 320px) minmax(0, 1fr); }
}
@media (min-width: 1200px) {
  .owner-grid { grid-template-columns: minmax(0, 380px) minmax(0, 1fr); }
}

/* ⚠️ WIDTH MUST BE DEFINITE — do not swap these for `max-width` alone.
   `margin-inline: auto` makes a figure shrink-to-fit. A lazy image that
   hasn't downloaded yet contributes zero intrinsic width, so the figure
   collapses to 0×0, reserves no space, and then jumps to full height the
   moment the image lands — a visible layout shift. Giving the figure a
   definite width lets the image's width/height attributes reserve the
   correct height up front. */
.owner-photo { margin: 0; width: 100%; max-width: 320px; }
@media (max-width: 719px) {
  .owner-photo { width: 240px; max-width: 100%; margin-inline: auto; }
}
.owner-photo img {
  width: 100%;
  border-radius: 999px 999px 10px 10px;   /* the arch crop used site-wide */
  box-shadow: 0 18px 46px rgba(0, 0, 0, .16);
}

.owner-copy p { max-width: 62ch; }

/* The closing line of Airyonna's story, lifted out as a pull quote — it's the
   emotional payoff of the section and gets lost as a third paragraph. */
.owner-quote {
  margin: 1.4em 0 0;
  padding-left: clamp(16px, 2.5vw, 24px);
  border-left: 3px solid var(--green);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.12rem, 2.8vw, 1.5rem);
  line-height: 1.35;
  color: var(--green);
  max-width: 46ch;
}
.owner-sign {
  margin-top: 1.1em;
  font-size: .92rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   "HOW CAN COFFEE ELEVATE YOUR EVENT?"  — green band closing the About page
   -------------------------------------------------------------------------- */
.elevate-inner { max-width: 760px; }
.elevate p {
  font-size: clamp(1rem, 2.3vw, 1.12rem);
  color: rgba(255, 255, 255, .9);
}


/* ==========================================================================
   12. MENU
   ========================================================================== */
/* --------------------------------------------------------------------------
   FALL MENU — seasonal feature panel above the standing menu
   The warm terracotta palette follows the supplied fall menu artwork, then
   eases into the site's green so the two menus feel like one scroll journey.

   On colour: the artwork's #e86625 is too light to carry cream body copy —
   it measured 2.6:1 against the small print, well under the 4.5:1 minimum.
   #b8480f is the same hue (20deg), just deeper, and clears 5:1. Every bit of
   text here is solid var(--cream) for that reason: the old .84-.9 alphas
   washed back toward the background and cost roughly 0.7 of contrast each.
   -------------------------------------------------------------------------- */
.fall-menu {
  position: relative;
  overflow: hidden;
  padding-top: calc(var(--header-h) + clamp(42px, 8vw, 88px));
  padding-bottom: clamp(78px, 11vw, 132px);
  background: linear-gradient(180deg, #b8480f 0%, #b8480f 76%, var(--green) 100%);
  color: var(--cream);
}

.fall-menu-inner { position: relative; z-index: 1; }

.fall-menu-kicker {
  margin: 0 0 12px;
  color: var(--cream);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
}

.fall-menu-title {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .18em .34em;
  margin: 0;
  color: var(--cream);
  font-size: clamp(4.8rem, 18vw, 10.5rem);
  font-weight: 700;
  letter-spacing: -.04em;
  line-height: .82;
  text-transform: uppercase;
}

.fall-menu-script {
  font-family: 'Beautifully Delicious', 'Brush Script MT', cursive;
  /* The script face fills only ~57% of its em box, against Oswald's ~81%,
     so it needs ~1.5em just to read as the same size as MENU. */
  font-size: 1.5em;
  font-style: normal;
  font-weight: 400;
  letter-spacing: .01em;
  text-transform: none;
}

.fall-menu-intro {
  max-width: 42ch;
  margin: clamp(28px, 4vw, 42px) 0 0;
  color: var(--cream);
  font-size: clamp(1.02rem, 2.4vw, 1.22rem);
  line-height: 1.55;
}

.fall-menu-grid {
  display: grid;
  gap: 0 clamp(28px, 5vw, 72px);
  margin-top: clamp(46px, 7vw, 76px);
}

@media (min-width: 700px) {
  .fall-menu-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.fall-drink {
  padding: 22px 0 26px;
  border-top: 1px solid rgba(255, 249, 237, .55);
}

.fall-drink h3 {
  margin: 0;
  color: var(--cream);
  font-size: clamp(1.2rem, 3vw, 1.72rem);
  font-weight: 500;
  letter-spacing: .13em;
  line-height: 1.2;
  text-transform: uppercase;
}

.fall-drink p {
  max-width: 39ch;
  margin: 12px 0 0;
  color: var(--cream);
  font-size: clamp(.94rem, 2vw, 1.06rem);
  line-height: 1.55;
}

.fall-menu-link {
  display: inline-flex;
  align-items: center;
  gap: .65em;
  margin-top: clamp(28px, 5vw, 52px);
  color: var(--cream);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: .2em;
  transition: opacity .25s var(--ease), transform .25s var(--ease);
}

.fall-menu-link:hover,
.fall-menu-link:focus-visible {
  color: #fff;
  text-decoration-thickness: 2px;
  transform: translateY(2px);
}

/* --------------------------------------------------------------------------
   MENU CATEGORY JUMP BAR  (phones and tablets only)
   The menu is the page people open while standing at the trailer, and on a
   phone it's ~2,700px long. Finding "Signatures" meant thumbing past every
   drink. This pins a scrollable row of category chips under the header so any
   section is one tap away.

   Hidden from 900px up, where the two-column layout already shows everything
   at once and a jump bar would just be clutter.
   -------------------------------------------------------------------------- */
.menu-jump {
  position: sticky;
  top: var(--header-h);
  z-index: 40;
  margin: 0 calc(var(--gutter) * -1) clamp(22px, 4vw, 34px);
  padding: 10px var(--gutter);
  background: var(--green);
  border-bottom: 1px solid rgba(255, 255, 255, .16);

  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* Chips shouldn't sit flush against the screen edge when scrolled */
  scroll-padding-inline: var(--gutter);
}
.menu-jump::-webkit-scrollbar { display: none; }

.menu-jump a {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 16px;
  border: 1px solid rgba(255, 255, 255, .38);
  border-radius: 999px;
  color: #fff;
  font-size: .82rem;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease);
}
.menu-jump a:hover,
.menu-jump a.is-current {
  background: var(--cream);
  border-color: var(--cream);
  color: var(--ink);
}

@media (min-width: 900px) { .menu-jump { display: none; } }

/* Landing offset for the jump links: clear the fixed header AND the chip bar,
   otherwise a tapped heading hides behind them. */
.menu-anchor { scroll-margin-top: calc(var(--header-h) + 76px); }
@media (min-width: 900px) { .menu-anchor { scroll-margin-top: calc(var(--header-h) + 20px); } }

.menu-title {
  font-size: clamp(3.6rem, 15vw, 8.4rem);
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: .92;
  color: #fff;
  margin-bottom: clamp(26px, 4vw, 44px);
}

.menu-grid {
  display: grid;
  gap: clamp(38px, 6vw, 70px);
}
@media (min-width: 900px) { .menu-grid { grid-template-columns: 1fr 1.25fr; } }

.menu-cat {
  font-size: clamp(1.7rem, 5.6vw, 2.7rem);
  font-weight: 700;
  color: #fff;
  margin: 0 0 .45em;
}
.menu-col > .menu-cat:not(:first-child) { margin-top: 1.5em; }

.menu-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.menu-list li {
  padding: 5px 0;
  font-size: 1.02rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: rgba(255,255,255,.92);
}

.menu-addons {
  margin: 0;
  font-size: .96rem;
  color: rgba(255,255,255,.92);
}
.menu-addons dt {
  margin-top: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--cream);
}
.menu-addons dt:first-child { margin-top: 0; }
.menu-addons dd {
  margin: 2px 0 0;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: rgba(255,255,255,.85);
}

.menu-note {
  margin: -.3em 0 1.6em;
  text-align: center;
}
.menu-note span {
  display: inline-block;
  padding-bottom: 3px;
  border-bottom: 1.5px solid rgba(255,255,255,.85);
  letter-spacing: .12em;
  text-transform: uppercase;
  font-size: .9rem;
  color: #fff;
}

.sig {
  padding: 16px 0;
  border-top: 1px solid rgba(255,255,255,.16);
}
.sig:first-of-type { border-top: 0; padding-top: 0; }

.sig h4 {
  font-family: var(--font-body);
  font-size: 1.06rem;
  font-weight: 500;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: #fff;
  margin: 0 0 .45em;
}
.sig h4 em {
  font-style: normal;
  font-weight: 300;
  opacity: .78;
  letter-spacing: .05em;
}
.sig p {
  margin: 0;
  padding-left: 22px;
  font-size: .88rem;
  line-height: 1.72;
  letter-spacing: .045em;
  text-transform: uppercase;
  color: rgba(255,255,255,.8);
}

/* Add class "is-new" to a .sig block to badge it as seasonal */
.sig.is-new h4::after {
  content: "Seasonal";
  display: inline-block;
  margin-left: 10px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--cream);
  color: var(--green-dark);
  font-size: .64rem;
  font-weight: 600;
  letter-spacing: .1em;
  vertical-align: 3px;
}


/* ==========================================================================
   12b. TESTIMONIALS

   Real Google reviews, quoted verbatim with the reviewer's name. That
   verbatim-ness is the whole point: a trimmed or tidied-up quote reads as
   marketing copy, and stops doing the job a review does.

   >> If you trim a quote, use an ellipsis and never reword it.
   >> Attribution is first name + last initial, matching how Google shows it.
   ========================================================================== */
.quotes { background: var(--grey); }

.quotes-head {
  text-align: center;
  margin-bottom: clamp(26px, 4vw, 44px);
}
.quotes-head .lead {
  margin-inline: auto;
  color: var(--muted);
}
.quotes-head .lead a {
  color: var(--green);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.quote-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: clamp(14px, 2.2vw, 22px);
}
@media (min-width: 720px) { .quote-grid { grid-template-columns: repeat(3, 1fr); } }

.quote {
  display: flex;
  flex-direction: column;
  height: 100%;
  margin: 0;
  padding: clamp(20px, 2.8vw, 28px);
  background: var(--white);
  border-radius: var(--radius);
  box-shadow: 0 10px 28px rgba(0, 0, 0, .07);
}

.quote-stars {
  color: var(--green);
  font-size: .95rem;
  letter-spacing: .12em;
  margin-bottom: 12px;
}

.quote blockquote {
  margin: 0 0 auto;
  padding: 0;
  font-size: clamp(.98rem, 1.5vw, 1.05rem);
  line-height: 1.6;
  color: var(--ink);
}
/* Hanging opening quote mark — decorative, so it sits outside the text flow
   and doesn't shift the first line's alignment. */
.quote blockquote p { margin: 0; }
.quote blockquote p::before {
  content: "\201C";
  margin-left: -.42em;
}
.quote blockquote p::after { content: "\201D"; }

.quote figcaption {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: .88rem;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--muted);
}

/* Screen-reader-only text (used to give the star row a readable rating) */
.u-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}


/* ==========================================================================
   12c. 404 PAGE
   ========================================================================== */
.error-page { min-height: 68svh; display: flex; align-items: center; }
@supports not (min-height: 68svh) { .error-page { min-height: 68vh; } }

.error-inner { max-width: 620px; text-align: center; margin-inline: auto; }

.error-code {
  font-family: var(--font-display);
  font-size: clamp(4rem, 16vw, 8rem);
  font-weight: 700;
  line-height: .9;
  color: rgba(255, 255, 255, .22);
  margin: 0 0 .1em;
}

.error-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 28px;
}
@media (max-width: 419px) {
  .error-actions { flex-direction: column; align-items: stretch; }
}

.error-links {
  list-style: none;
  margin: 34px 0 0;
  padding: 26px 0 0;
  border-top: 1px solid rgba(255, 255, 255, .18);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 26px;
}
.error-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: rgba(255, 255, 255, .86);
  font-size: .95rem;
  text-decoration: underline;
  text-underline-offset: 4px;
  transition: color .25s var(--ease);
}
.error-links a:hover { color: var(--cream); }


/* ==========================================================================
   13. NEXT-STEP BAND
   Every page used to stop dead at the footer. This closes each one with a
   single clear question and the one or two actions that answer it. Cream, so
   it separates the page body from the green footer instead of blurring into
   it.
   ========================================================================== */
.next-step {
  padding-block: clamp(44px, 7vw, 84px);
  background: var(--cream);
  color: var(--ink);
  text-align: center;
}
.next-step-inner {
  max-width: 640px;
  margin-inline: auto;
}
.next-step h2 {
  font-size: clamp(1.5rem, 4.6vw, 2.4rem);
  color: var(--ink);
  margin-bottom: .35em;
}
.next-step p {
  margin-bottom: 1.6em;
  color: rgba(0, 0, 0, .72);
}
.next-step-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
}
/* On the narrowest phones the two buttons stack; make them equal width so
   they read as a pair rather than two odd-sized blocks. */
@media (max-width: 419px) {
  .next-step-actions { flex-direction: column; align-items: stretch; }
}


/* ==========================================================================
   14. STICKY MOBILE CTA
   Booking is the point of the site, and on a phone the nav is behind a
   hamburger — so the primary action was always at least two taps away. This
   keeps it to one.

   Only below 900px (where the nav is collapsed), never on book.html (where
   it would point at the page you're already on), and it gets out of the way
   when the closing CTA band scrolls into view so the two don't stack up.
   ========================================================================== */
.sticky-cta {
  position: fixed;
  z-index: 92;
  left: 0;
  right: 0;
  bottom: 0;
  display: none;                     /* enabled in the media query below */
  padding: 10px var(--gutter) calc(10px + env(safe-area-inset-bottom));
  background: rgba(var(--green-rgb), .96);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow: 0 -4px 20px rgba(0, 0, 0, .22);
  transition: transform .32s var(--ease), opacity .32s var(--ease);
}

/* Two actions side by side, sharing the width evenly. */
.sticky-cta-inner {
  display: flex;
  gap: 10px;
  max-width: 520px;
  margin-inline: auto;
}
.sticky-cta .btn {
  flex: 1 1 0;
  min-width: 0;                      /* lets long labels shrink instead of overflowing */
  padding-inline: 10px;
  font-size: .86rem;
  white-space: nowrap;
}

/* Slides down out of the way rather than disappearing abruptly.
   The markup ships WITH this class: the bar is hidden at the top of the page
   and JS reveals it once you've scrolled. That also means no bar at all if JS
   fails, which is the right failure — better than one stuck on screen. */
.sticky-cta.is-hidden {
  transform: translateY(110%);
  opacity: 0;
  pointer-events: none;
}

@media (max-width: 899px) {
  .sticky-cta { display: block; }

  /* Stop the bar covering the last line of the footer */
  body.has-sticky-cta { padding-bottom: 76px; }

  /* Back-to-top has to clear the bar */
  .to-top { bottom: 84px; }
}


/* ==========================================================================
   15. FOOTER & UTILITIES
   ========================================================================== */
.site-footer {
  padding-block: clamp(44px, 6vw, 72px);
  background: var(--green);
  color: #fff;
}

.footer-grid {
  display: grid;
  gap: 26px;
}
/* Two columns on tablet portrait before going to four on desktop */
@media (min-width: 600px) {
  .footer-grid { grid-template-columns: repeat(2, 1fr); gap: 30px 40px; }
}
@media (min-width: 900px) {
  .footer-grid { grid-template-columns: 1.25fr .8fr .9fr 1.05fr; align-items: start; }
}

/* Footer page links.
   On a phone these five links stacked vertically added ~200px of dead
   scrolling to the bottom of every page, so below 600px they run in two
   columns. Each link keeps a 44px tap height. */
.footer-nav {
  list-style: none;
  margin: 0;
  padding: 0;
  columns: 2;            /* two short columns instead of one tall one */
  column-gap: 20px;
}
.footer-nav li { margin-bottom: 2px; break-inside: avoid; }

/* Comfortable tap rows by default. */
.footer-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

@media (min-width: 600px) {
  .footer-nav { columns: 1; }
}

/* Tighten to plain inline links ONLY for a mouse.
   Deliberately keyed off pointer type, not width. A 768px tablet is still a
   finger, and an earlier version of this rule used `min-width: 600px` — which
   silently shrank these to 18px tap targets on every tablet. */
@media (hover: hover) and (pointer: fine) {
  /* 26px, not 0. These are standalone navigation links rather than links
     inside a sentence, so WCAG 2.2's 24px minimum target size applies —
     a bare inline <a> only gives an 18px hit box. */
  .footer-nav a { min-height: 26px; }
  .footer-nav li { margin-bottom: 2px; }
}

.footer-tag {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 4.6vw, 2.3rem);
  font-weight: 600;
  line-height: 1.15;
  border-bottom: 2px solid #fff;
  padding-bottom: 6px;
  display: inline-block;
  margin: 0;
}

.footer-col { font-size: .96rem; }
.footer-col h4 {
  font-family: var(--font-body);
  font-size: .96rem;
  font-weight: 600;
  margin: 0 0 .3em;
}
.footer-col h4 + p { margin-bottom: 1.2em; }
.footer-col a {
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color .25s var(--ease);
}
.footer-col a:hover { border-color: currentColor; }

@media (min-width: 900px) {
  .footer-col--end { text-align: right; }
}
.footer-col--end p { margin-bottom: .2em; opacity: .92; }

/* Back-to-top */
.to-top {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 90;
  width: 46px; height: 46px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--green);
  color: #fff;
  font-size: 1.1rem;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(0,0,0,.3);
  opacity: 0;
  transform: translateY(10px) scale(.9);
  transition: opacity .3s var(--ease), transform .3s var(--ease), background-color .3s var(--ease);
}
.to-top[hidden] { display: grid; }        /* animated, not display-toggled */
.to-top.is-visible { opacity: 1; transform: translateY(0) scale(1); }
.to-top:hover { background: var(--green-dark); }


/* ==========================================================================
   14. REDUCED MOTION
   Honour the OS-level "reduce motion" setting: kill parallax, reveals and
   the scroll cue, but never hide content.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .reveal { opacity: 1 !important; transform: none !important; }
  .hero-media { transform: none !important; }
  .scroll-cue { display: none; }
}


/* ==========================================================================
   15. PRINT (handy — people print the menu)
   ========================================================================== */
@media print {
  .site-header, .nav-mobile, .to-top, .scroll-cue, .hero-media, .book-applet { display: none !important; }
  .section--dark, .site-footer { background: #fff !important; color: #000 !important; }
  .h2--light, .menu-title, .menu-cat, .sig h4, .sig p, .menu-list li { color: #000 !important; }
  .reveal { opacity: 1 !important; transform: none !important; }
}
