/* ==========================================================================
   GLAZED & CONFUSED — DESIGN SYSTEM
   One stylesheet, driven by design tokens. Every page uses the same tokens
   and component classes so the whole site feels like one hand-made thing.

   Palette is built around two colours from Sanzo Wada's "A Dictionary of
   Color Combinations":
     • Ecru       #c2ae93  (warm sandy beige)  — the earthy base
     • Nile Blue  #bce4e5  (soft muted blue)   — the calm accent
   ...plus a supporting terracotta/clay for warmth and clear calls-to-action.
   ========================================================================== */

/* ------------------------------------------------------------------ *
   0. SELF-HOSTED WEB FONTS — Oranienbaum (display) + Poppins (body)
   Both are SIL OFL 1.1, free for commercial use; licences at
   /assets/fonts/OFL-Oranienbaum.txt and /assets/fonts/OFL-Poppins.txt.
   Served same-origin (strict CSP: font-src 'self') as latin-subset woff2.

   Oranienbaum is the heading voice: a tall, high-contrast Didone-style
   serif (owner pick, 22 July 2026, replacing the commercial Perandory
   Condensed preview so nothing needs buying). Unlike Perandory it has a
   real lowercase, so the capital-letter look the site was designed around
   is applied with text-transform: uppercase on every display-font rule.
   It ships as one static weight; the @font-face below declares 400 ONLY so
   the browser synthesises the 600/700/800 the headings request (owner
   request: bold across the board). Poppins is the body voice: a clean
   geometric sans at reading volume. Three static cuts cover the
   400/500/600/700 the site uses (500 resolves to Regular per the CSS
   font-matching algorithm; there is a single, non-critical 500 use).

   FOUT handling: font-display: swap keeps text visible from first paint
   (never invisible headings); the "…Fallback" faces below are local fonts
   width-tuned with size-adjust so the fallback occupies roughly the width
   the webfont will, keeping re-wrap during the swap rare. Headings and
   display accents all set numeric line-heights, so the swap changes little,
   including during the hero's pure-CSS .page-intro animation (which only
   animates opacity/transform). font-display: optional was rejected: on a
   slow first visit it can skip the brand font entirely.
 * ------------------------------------------------------------------ */
/* LICENCE — Oranienbaum: SIL OFL 1.1, free for commercial use
   (/assets/fonts/OFL-Oranienbaum.txt). */
@font-face {
  font-family: "Oranienbaum";
  src: url("/assets/fonts/Oranienbaum-latin.woff2") format("woff2");
  /* Single-weight face declared at 400 ONLY, so the browser synthesises
     bold for the 600/700/800 the headings ask for (owner request: bold
     across the board). */
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* Approximate local stand-in while Oranienbaum loads. Oranienbaum is a
   normal-width serif (unlike the condensed face it replaced), so a
   serif-class local font at a slight size-adjust keeps heading widths close
   enough that lines rarely re-wrap during the swap. */
@font-face {
  font-family: "Oranienbaum Fallback";
  src: local("Georgia"), local("Times New Roman"), local("Liberation Serif");
  size-adjust: 97%;
}
/* Poppins body voice — three static cuts under one family name (the browser
   picks the right cut by weight; 500 falls back to 400, 800 to 700). */
@font-face {
  font-family: "Poppins";
  src: url("/assets/fonts/Poppins-Regular-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("/assets/fonts/Poppins-SemiBold-latin.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("/assets/fonts/Poppins-Bold-latin.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
/* Metric-compatible local stand-in while Poppins loads. size-adjust is tuned
   to the REGULAR weight (measured: Poppins 400 is ~16% wider than Arial),
   because multi-line paragraphs are where a swap could re-wrap; bold runs
   are short labels. ascent/descent are Poppins's metrics (1050/-350 @ upm
   1000) divided by size-adjust. The single italic use (an admin annotation)
   renders as a browser-synthesised oblique — acceptable at that size. */
@font-face {
  font-family: "Poppins Fallback";
  src: local("Arial"), local("Liberation Sans"), local("Helvetica Neue"),
    local("Roboto");
  size-adjust: 116.0%;
  ascent-override: 90.5%;
  descent-override: 30.2%;
  line-gap-override: 0%;
}

/* ------------------------------------------------------------------ *
   Pre-paint image hold (see preboot.js): while <html> carries .gnc-img-hold,
   every CMS-overridable image is kept invisible so a swapped-out built-in
   photo can never paint first. main.js lifts the class the moment the
   overrides are applied (cached synchronously pre-paint, or live config),
   with a 900ms safety cap. visibility keeps the layout box, so nothing
   shifts when the hold lifts.
 * ------------------------------------------------------------------ */
html.gnc-img-hold img[data-content-id$=".photo"],
html.gnc-img-hold img[data-content-id$=".before"],
html.gnc-img-hold img[data-content-id$=".after"] {
  visibility: hidden;
}

/* ------------------------------------------------------------------ *
   1. DESIGN TOKENS
 * ------------------------------------------------------------------ */
:root {
  /* --- Raw Wada colours --- */
  --wada-ecru: #c2ae93;
  --wada-nile: #bce4e5;

  /* --- Logo brown (the vase + lettering in the official logo) ---
     Used sparingly: logo-adjacent text (header brand name, QR/print brand
     lines) and print artifacts. NOT a replacement for the teal/clay accents. */
  --brown-logo: #745138;

  /* --- Ecru (sandy) ramp --- */
  --paper: #fbf7ef; /* warm cream page background */
  --paper-2: #f4ecdd; /* alternating section band */
  --surface: #fffdf8; /* cards / raised surfaces */
  --ecru-100: #efe7d7;
  --ecru-200: #e4d7c0;
  --ecru-300: #d6c4a6;
  --ecru-400: #c2ae93; /* base ecru */
  --ecru-600: #927b58;

  /* --- Nile Blue ramp --- */
  --nile-100: #e7f4f5;
  --nile-200: #cfeaeb;
  --nile-300: #bce4e5; /* base nile */
  --nile-500: #5a9ba0;
  --nile-700: #2c6e72; /* deep — primary interactive, AA on cream */
  --nile-800: #1e5155;

  /* --- Terracotta / clay accent --- */
  --clay-100: #f2ddd0;
  --clay-300: #dda487;
  --clay-500: #a84f2b; /* accent / buttons / eyebrow — AA on all light backgrounds */
  --clay-600: #93411f;

  /* --- Ink (warm near-black text) --- */
  --ink: #2e2620;
  --ink-soft: #5a4f44;
  --ink-faint: #68604f; /* AA (>=4.5) small hint text on cream, ecru & nile panels */
  --white: #ffffff;

  /* --- Semantic aliases --- */
  --color-bg: var(--paper);
  --color-surface: var(--surface);
  --color-text: var(--ink);
  --color-text-muted: var(--ink-soft);
  --color-primary: var(--nile-700);
  --color-primary-hover: var(--nile-800);
  --color-accent: var(--clay-500);
  --color-accent-hover: var(--clay-600);
  --color-border: var(--ecru-200);
  --color-border-strong: var(--ecru-300);
  --color-ring: var(--clay-500);

  /* --- Status colours (availability) --- */
  --ok-bg: #dcece0;
  --ok-text: #2f6b45;
  --low-bg: #f6e6c9;
  --low-text: #8a5a12;
  --full-bg: #f0dcd6;
  --full-text: #9a3d24;

  /* --- Typography --- */
  /* Display voice: headings, dates, prices and brand-adjacent lettering.
     Oranienbaum (see section 0), an elegant Didone-style serif rendered in
     capitals via text-transform on every display rule; the tuned fallback
     keeps widths stable while it loads, then the serif stack catches
     pathological cases. */
  --font-display: "Oranienbaum", "Oranienbaum Fallback", Georgia,
    "Times New Roman", serif;
  /* Body voice: paragraphs, forms, nav, buttons, labels — Poppins (see
     section 0), with its tuned fallback face, then the system stack. */
  --font-sans: "Poppins", "Poppins Fallback", system-ui, -apple-system,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;

  /* Fluid type scale (mobile-first, grows gently to desktop) */
  --step--1: clamp(0.82rem, 0.79rem + 0.15vw, 0.9rem);
  --step-0: clamp(1rem, 0.96rem + 0.2vw, 1.12rem);
  --step-1: clamp(1.2rem, 1.12rem + 0.4vw, 1.45rem);
  --step-2: clamp(1.45rem, 1.3rem + 0.75vw, 1.9rem);
  --step-3: clamp(1.75rem, 1.5rem + 1.25vw, 2.55rem);
  --step-4: clamp(2.1rem, 1.7rem + 2vw, 3.35rem);
  --step-5: clamp(2.5rem, 1.9rem + 3vw, 4.25rem);

  /* --- Spacing scale --- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;

  /* --- Section rhythm --- *
   * Vertical padding for page sections lives in these two tokens so the whole
   * site's inter-section spacing can be tuned in one place (rather than as
   * per-section one-offs). Tightened one step from the raw 4rem/3rem so pages
   * read a touch more compact without feeling crammed. */
  --section-pad: var(--space-7); /* standard section (was ~4rem) */
  --section-pad-tight: var(--space-6); /* tight section (was ~3rem) */

  /* --- Radii --- */
  --radius-sm: 6px;
  --radius: 14px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  /* --- Shadows (warm, soft) --- */
  --shadow-sm: 0 1px 2px rgba(74, 56, 34, 0.06), 0 2px 6px rgba(74, 56, 34, 0.05);
  --shadow: 0 4px 10px rgba(74, 56, 34, 0.08), 0 12px 28px rgba(74, 56, 34, 0.08);
  --shadow-lg: 0 10px 24px rgba(74, 56, 34, 0.1), 0 24px 60px rgba(74, 56, 34, 0.12);

  /* --- Layout --- */
  --container: 1080px;
  --container-narrow: 760px;

  /* --- Motion --- */
  --dur-fast: 160ms;
  --dur: 280ms;
  --dur-slow: 520ms;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
}

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.65;
  color: var(--color-text);
  background-color: var(--color-bg);
  /* subtle hand-made paper texture using two soft radial washes */
  background-image: radial-gradient(
      1200px 600px at 85% -10%,
      var(--nile-100),
      transparent 60%
    ),
    radial-gradient(900px 500px at -10% 110%, var(--ecru-100), transparent 55%);
  background-attachment: fixed;
  min-height: 100vh;
  overflow-x: hidden;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  /* Sticky footer: header, main and footer stack in a column and <main> grows to
     fill the viewport, so a short page (the 404, or any thin page) anchors the
     footer to the bottom instead of leaving a cream band beneath it. */
  display: flex;
  flex-direction: column;
}
body > main {
  flex: 1 0 auto;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  line-height: 1.12;
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 var(--space-4);
  /* Headings render as capitals (text-transform below): capitals want a
     little air, so a small positive tracking base keeps big headings crisp;
     the smaller levels get progressively more tracking below so capitals
     stay legible at reading sizes. */
  letter-spacing: 0.01em;
  text-transform: uppercase;
}

h1 {
  font-size: var(--step-4);
}
h2 {
  font-size: var(--step-3);
}
h3 {
  font-size: var(--step-2);
  letter-spacing: 0.02em;
}
h4 {
  font-size: var(--step-1);
  letter-spacing: 0.03em;
}

p {
  margin: 0 0 var(--space-4);
}

a {
  color: var(--color-primary);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1.5px;
  text-decoration-color: var(--nile-300);
  transition: color var(--dur-fast) var(--ease),
    text-decoration-color var(--dur-fast) var(--ease);
}
a:hover {
  color: var(--color-primary-hover);
  text-decoration-color: var(--color-primary);
}

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

ul,
ol {
  margin: 0 0 var(--space-4);
  padding-left: 1.3em;
}
li {
  margin-bottom: var(--space-2);
}

strong {
  font-weight: 700;
  color: var(--ink);
}

hr {
  border: 0;
  border-top: 1px solid var(--color-border);
  margin: var(--space-6) 0;
}

/* Visible focus for keyboard users everywhere */
:focus-visible {
  outline: 3px solid var(--color-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Screen-reader-only + skip link */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.skip-link {
  position: absolute;
  left: var(--space-3);
  top: -60px;
  z-index: 1000;
  background: var(--nile-700);
  color: var(--white);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus {
  top: var(--space-3);
  color: var(--white);
}

/* ------------------------------------------------------------------ *
   3. LAYOUT PRIMITIVES
 * ------------------------------------------------------------------ */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-5);
}
.container--narrow {
  max-width: var(--container-narrow);
}

.section {
  padding-block: var(--section-pad);
}
.section--tight {
  padding-block: var(--section-pad-tight);
}
/* Drops a section's TOP padding so it sits close under the previous one — the
   preceding section's bottom padding then supplies the whole (single, not
   doubled) gap. Used for the closing CTA row under the gift-voucher promo. */
.section--flush-top {
  padding-top: 0;
}
.section--band {
  background: var(--paper-2);
  border-block: 1px solid var(--color-border);
}
.section--nile {
  background: linear-gradient(180deg, var(--nile-100), var(--nile-200));
  border-block: 1px solid var(--nile-200);
}

.stack > * + * {
  margin-top: var(--space-4);
}
.stack-lg > * + * {
  margin-top: var(--space-6);
}

.grid {
  display: grid;
  gap: var(--space-5);
}
.grid--2 {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
}
.grid--3 {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}

.eyebrow {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--clay-500);
  margin-bottom: var(--space-3);
}

.lead {
  font-size: var(--step-1);
  color: var(--color-text-muted);
  max-width: 60ch;
}

.text-center {
  text-align: center;
}
.mx-auto {
  margin-inline: auto;
}
.measure {
  max-width: 62ch;
}

/* ------------------------------------------------------------------ *
   4. HEADER / NAV
 * ------------------------------------------------------------------ */
/* Smooth page-to-page navigation. Browsers with cross-document view
   transitions (Chrome, Edge, Safari) get a proper cross-fade between pages,
   with the practice banner and the site header snapshotted separately so
   they hold perfectly steady while only the content fades. Browsers
   without it (Firefox) fall back to a fast, shallow body fade. Both are
   skipped for reduced-motion users. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 180ms;
  }
  .dev-banner {
    view-transition-name: gnc-dev-banner;
  }
  .site-header {
    view-transition-name: gnc-site-header;
  }
  @supports not (view-transition-name: none) {
    body {
      animation: gnc-page-enter 170ms ease-out;
    }
    @keyframes gnc-page-enter {
      from {
        opacity: 0.5;
      }
      to {
        opacity: 1;
      }
    }
  }
}

.site-header {
  position: sticky;
  /* On the practice site the sticky dev banner sits above; its measured
     height arrives via --dev-banner-h (set by main.js). Absent on live, so
     this stays exactly top: 0 there. */
  top: var(--dev-banner-h, 0px);
  z-index: 100;
  background: rgba(251, 247, 239, 0.85);
  backdrop-filter: saturate(1.4) blur(10px);
  -webkit-backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--color-border);
}
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 68px;
}
.brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: var(--ink);
  flex-shrink: 0;
}
.brand:hover {
  color: var(--ink);
}
.brand__mark {
  display: block; /* block imgs: no inline baseline/descender gap, so the mark
                     and wordmark share one centreline with the nav links */
  width: 40px;
  height: auto; /* the logo mark is ~1.09:1, so height follows the width */
  flex-shrink: 0;
}
.brand__name {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 1.34rem; /* condensed caps read a touch smaller; nudge up to hold presence */
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.02em; /* condensed capitals want air, not negative tracking */
  color: var(--brown-logo); /* matches the logo lettering; AA on cream */
}
.brand__amp {
  color: var(--clay-500);
}
/* Header wordmark: the brown text-only lockup sits beside the mark (mark +
   wordmark reads as the horizontal lockup). Aspect ~7.11:1, so 26px tall is
   ~185px wide; explicit width/height on the <img> reserves the box to avoid
   layout shift. It carries the brand's accessible name (alt), so the mark is
   decorative (alt=""). */
.brand__wordmark {
  display: block;
  height: 26px;
  width: auto;
  flex-shrink: 0;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Each <li> flex-centres its link, with margin zeroed: the global list style
   gives content <li>s an 8px bottom margin, which (as a flex margin-box) sat the
   links' visual centre ~4px above the brand's block-image centreline. Reset to
   block in the mobile menu below. */
.nav-links > li {
  display: flex;
  align-items: center;
  margin: 0;
}
/* :not(.btn) so the CTA keeps its button colours (avoids the nav link colour
   winning on specificity and rendering dark text on the dark CTA fill). */
.nav-links a:not(.btn) {
  display: inline-block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  text-decoration: none;
  color: var(--ink-soft);
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.01em;
  /* Keep every label on ONE line at all desktop widths (NEW-2): a flex item can
     otherwise shrink and wrap "Sessions & booking" / "Gift a session" to two
     lines even at 1440. */
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
/* The "Book now" CTA also stays a single line on the desktop nav. */
.nav-links .btn {
  white-space: nowrap;
}
.nav-links a:not(.btn):hover {
  background: var(--ecru-100);
  color: var(--ink);
}
.nav-links a:not(.btn)[aria-current="page"] {
  background: var(--nile-200);
  color: var(--nile-800);
}
.nav-cta {
  margin-left: var(--space-2);
}

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--color-border-strong);
  background: var(--surface);
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--ink);
}
.nav-toggle:hover {
  background: var(--ecru-100);
}
.nav-toggle__bar,
.nav-toggle__bar::before,
.nav-toggle__bar::after {
  content: "";
  display: block;
  width: 20px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
  transition: transform var(--dur) var(--ease), opacity var(--dur-fast) var(--ease);
}
.nav-toggle__bar {
  position: relative;
}
.nav-toggle__bar::before {
  position: absolute;
  top: -6px;
}
.nav-toggle__bar::after {
  position: absolute;
  top: 6px;
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bar {
  background: transparent;
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bar::before {
  transform: translateY(6px) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bar::after {
  transform: translateY(-6px) rotate(-45deg);
}

/* Collapse to the burger below 1200px. The brand (40px mark + brown wordmark)
   plus the seven links and the "Book now" pill (with "Gift Vouchers" injected
   when payments are on) need ~1075px of nav content, which overflows the 1080
   container and only clears the viewport from ~1120px up. So the full horizontal
   nav is shown only at >=1200px (comfortable margin), and everything narrower
   uses the burger — no clipping and no sideways scroll at any width. The
   white-space:nowrap above stops any label wrapping at wider widths. */
@media (max-width: 1199px) {
  .nav-toggle {
    display: inline-flex;
  }
  .nav-links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--surface);
    border-bottom: 1px solid var(--color-border);
    box-shadow: var(--shadow);
    padding: var(--space-3);
    /* animated collapse */
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    visibility: hidden;
    transition: grid-template-rows var(--dur) var(--ease),
      opacity var(--dur) var(--ease), visibility var(--dur) var(--ease);
  }
  /* Reset the desktop flex-centred <li> so the mobile links stack full width. */
  .nav-links > li {
    display: block;
  }
  .nav-links > * {
    min-height: 0;
    overflow: hidden;
  }
  .site-header[data-open="true"] .nav-links {
    grid-template-rows: 1fr;
    opacity: 1;
    visibility: visible;
  }
  .nav-links a:not(.btn) {
    display: block;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    font-size: var(--step-0);
  }
  .nav-cta {
    margin-left: 0;
    margin-top: var(--space-2);
  }
  .nav-cta .btn {
    width: 100%;
  }
}

/* ------------------------------------------------------------------ *
   5. BUTTONS
 * ------------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 700;
  line-height: 1.1;
  padding: 0.75em 1.4em;
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  transition: transform var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
  will-change: transform;
}
.btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow);
}
.btn:active {
  transform: translateY(0);
}
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
/* Shared inline icon sizing for buttons/CTAs. Icons are decorative
   (aria-hidden) and always sit beside a visible text label. Sized in em so
   they track the button's font size and never dominate the label. */
.btn__icon {
  width: 1.15em;
  height: 1.15em;
  flex: 0 0 auto;
  display: inline-block;
  vertical-align: middle;
}
.btn--sm .btn__icon {
  width: 1.05em;
  height: 1.05em;
}
.btn--primary {
  background: var(--nile-700);
  color: var(--white);
  border-color: var(--nile-700);
}
.btn--primary:hover {
  background: var(--nile-800);
  border-color: var(--nile-800);
  color: var(--white);
}
.btn--accent {
  background: var(--clay-500);
  color: var(--white);
  border-color: var(--clay-500);
}
.btn--accent:hover {
  background: var(--clay-600);
  border-color: var(--clay-600);
  color: var(--white);
}
.btn--secondary {
  background: var(--surface);
  color: var(--nile-800);
  border-color: var(--nile-700);
}
.btn--secondary:hover {
  background: var(--nile-100);
  color: var(--nile-800);
}
.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--color-border-strong);
}
.btn--ghost:hover {
  background: var(--ecru-100);
  color: var(--ink);
}
.btn--block {
  display: flex;
  width: 100%;
}
.btn--sm {
  font-size: var(--step--1);
  padding: 0.5em 1em;
}
.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.btn-row--center {
  justify-content: center;
}

/* Text button styled like a link (used for the contact trigger, so no email
   address needs to appear in the markup). */
.link-btn {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--color-primary);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1.5px;
  text-decoration-color: var(--nile-300);
  transition: color var(--dur-fast) var(--ease),
    text-decoration-color var(--dur-fast) var(--ease);
}
.link-btn:hover {
  color: var(--color-primary-hover);
  text-decoration-color: var(--color-primary);
}
.site-footer .link-btn {
  color: var(--nile-300);
  text-decoration-color: transparent;
  font-weight: 600;
}
.site-footer .link-btn:hover {
  color: var(--white);
  text-decoration-color: var(--nile-300);
}

/* ------------------------------------------------------------------ *
   6. CARDS / SURFACES
 * ------------------------------------------------------------------ */
.card {
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}
.card--interactive:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow);
  border-color: var(--color-border-strong);
}
.card h3 {
  margin-bottom: var(--space-2);
}
.card__icon {
  width: 52px;
  height: 52px;
  margin-bottom: var(--space-4);
  color: var(--nile-700);
}

/* ------------------------------------------------------------------ *
   7. HERO
 * ------------------------------------------------------------------ */
.hero {
  position: relative;
  /* Top-of-page rhythm: the narrow/mobile end holds at --section-pad so the
     hero's header-to-eyebrow gap matches every other page's intro block
     (prices/faq); it still grows to --space-8 on wide desktop as before. */
  padding-block: clamp(var(--section-pad), 3.5vw + 1.5rem, var(--space-8));
  overflow: hidden;
}
.hero__grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  column-gap: var(--space-7);
  row-gap: var(--space-4);
  align-items: start;
}
/* Desktop: text spans both rows (vertically centred), the logo art sits top of
   the right column (nudged up), and the Next session tile fills the space right
   below it. */
.hero__intro {
  grid-column: 1;
  grid-row: 1 / 3;
  align-self: center;
}
.hero__art {
  grid-column: 2;
  grid-row: 1;
  align-self: start;
  margin-top: -0.25rem;
}
.hero__next {
  grid-column: 2;
  grid-row: 2;
  align-self: start;
}
/* The tile holds roughly the art's width, not full-bleed. */
.hero__next .next-session {
  max-width: 100%;
}
.hero__title {
  font-size: var(--step-5);
  margin-bottom: var(--space-4);
}
.hero__title .accent {
  color: var(--clay-500);
}
.hero__blurb {
  font-size: var(--step-1);
  color: var(--color-text-muted);
  margin-bottom: var(--space-5);
  max-width: 40ch;
}
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.hero__art {
  position: relative;
}
.hero__art svg {
  width: 100%;
  height: auto;
  /* No element-level drop-shadow here: in WebKit/iOS Safari a filter on the
     <svg> box casts a rectangular shadow of the SVG viewport (a faint grey
     square around the organic blob). The soft shadow now lives on the blob
     <path> itself via an in-SVG <feDropShadow>, so it follows the shape in
     every engine and no box appears. */
}
/* The official logo lockup (mark + wordmark) sits centred on the soft organic
   blob, sized generously but with breathing room around it. */
.hero__logo {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 62%;
  height: auto;
}

/* Next-session callout — a deliberate strip below the CTA row, aligned to the
   same left gutter and capped to the hero copy width so it reads as part of the
   hero composition rather than a chip floating below it. */
.next-session {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--space-3);
  row-gap: 2px;
  width: 100%;
  max-width: 42ch;
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-left: 5px solid var(--clay-500);
  border-radius: var(--radius);
  /* Snug padding + tight line-heights so the tile hugs its three lines instead
     of leaving a band of empty space around them. */
  padding: var(--space-3) var(--space-4);
  line-height: 1.3;
  box-shadow: var(--shadow-sm);
}
.next-session__label {
  flex-basis: 100%;
  font-size: var(--step--1);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--clay-500);
}
.next-session__date {
  flex-basis: 100%;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 700;
  font-size: var(--step-1);
  line-height: 1.2;
  color: var(--ink);
}
/* The time line and the availability chip share ONE row under the date: the
   time takes the left (wrapping within its own box if long), the chip sits
   inline on the right. A dedicated non-wrapping row keeps the chip beside the
   time instead of letting a long time line push it onto a stacked row. */
.next-session__timerow {
  flex-basis: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.next-session__time {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--step--1);
  line-height: 1.3;
  color: var(--ink-soft);
}
/* Location line: every next session names its venue now, as a clickable Maps
   link (one quiet line; the busy elements were removed). */
.next-session__location {
  flex-basis: 100%;
  font-size: var(--step--1);
  line-height: 1.3;
}
.next-session__location:empty {
  display: none;
}

/* ---- Shared clickable location line ----
   The location NAME TEXT itself is the link (opening Google Maps in a new tab)
   and takes the site's STANDARD link treatment from the base `a` rule — the
   same teal and soft underline as every other text link — at whatever small
   size its context sets. Deliberately no icon: an icon can strand an orphan
   glyph when a long address wraps, and the underline already says "link". */
.loc-link {
  font-weight: 500;
}

@media (max-width: 820px) {
  .hero__grid {
    grid-template-columns: 1fr;
    gap: var(--space-6);
  }
  /* Single column: reset the desktop grid placement. The intro text block
     (orange eyebrow + heading + copy + buttons) leads at the top, then the
     Next session tile directly under it, keeping the booking path together. */
  .hero__intro {
    grid-column: auto;
    grid-row: auto;
    align-self: auto;
    order: 0;
  }
  /* Retire the decorative logo art on mobile. The sticky header already carries
     the same brand lockup on this screen, so the blob only duplicated it — and,
     stranded and centred between the CTAs and the next-session tile while every
     sibling is left-aligned, it read as an orphaned island. Desktop keeps its
     art (it works there as the top of the right column). */
  .hero__art {
    display: none;
  }
  .hero__next {
    grid-column: auto;
    grid-row: auto;
    order: 1;
  }
}

/* ------------------------------------------------------------------ *
   8. BANNER (walk-ins welcome)
 * ------------------------------------------------------------------ */
.banner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  background: var(--nile-200);
  border: 1px solid var(--nile-300);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5);
  color: var(--nile-800);
}
.banner__icon {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  color: var(--nile-700);
}
.banner p {
  margin: 0;
  color: var(--nile-800);
}
.banner strong {
  color: var(--nile-800);
}

/* ------------------------------------------------------------------ *
   9. STEPS (how it works)
 * ------------------------------------------------------------------ */
.steps {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  counter-reset: step;
  list-style: none;
  padding: 0;
  margin: 0;
}
.step {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5) var(--space-5);
  box-shadow: var(--shadow-sm);
}
.step__num {
  counter-increment: step;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--clay-100);
  color: var(--clay-600);
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 1.3rem;
  font-weight: 700;
  margin-bottom: var(--space-3);
}
.step__num::before {
  content: counter(step);
}
.step h3 {
  font-size: var(--step-1);
  margin-bottom: var(--space-2);
}
.step p {
  margin: 0;
  color: var(--ink-soft);
}

/* ------------------------------------------------------------------ *
   10. PRICE LIST
 * ------------------------------------------------------------------ */
.price-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}
.price-item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-4) var(--space-5);
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}
.price-item__name {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--step-1);
  color: var(--ink);
  font-weight: 700;
  white-space: nowrap;
}
.price-item__leader {
  flex: 1;
  border-bottom: 2px dotted var(--ecru-300);
  transform: translateY(-4px);
  min-width: 12px;
}
.price-item__price {
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--nile-800);
  white-space: nowrap;
}
.price-item__blurb {
  flex-basis: 100%;
  margin: 0;
  color: var(--ink-faint);
  font-size: var(--step--1);
}

.includes {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-2);
}
.includes li {
  position: relative;
  padding-left: 2rem;
  margin: 0;
}
.includes li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.35em;
  width: 20px;
  height: 20px;
  background-color: var(--nile-700);
  -webkit-mask: var(--check-mask) center / contain no-repeat;
  mask: var(--check-mask) center / contain no-repeat;
}
:root {
  --check-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------------ *
   11. TIMETABLE / SESSIONS
 * ------------------------------------------------------------------ */
.sessions-list {
  display: grid;
  gap: var(--space-3);
  list-style: none;
  padding: 0;
  margin: 0;
}

/* Two stacked rows: the DATE always sits on its own line, and the TIME sits on
   the second line alongside the availability pill and the choose/book button.
   This keeps a long date (e.g. "Sunday, 27 September 2026") from pushing the
   time sideways or wrapping awkwardly, so nothing shifts at 320/375/768px. */
.session-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.session-row--past {
  opacity: 0.6;
  background: var(--paper-2);
}
/* Whole-tile selection on the booking timetable. The entire tile is the hit
   target and is keyboard-focusable; picking a date highlights the tile and its
   Choose control. Colours use design tokens and meet AA against the surface. */
.session-row--selectable {
  cursor: pointer;
}
.session-row--selectable:hover {
  border-color: var(--nile-500);
  box-shadow: var(--shadow);
}
.session-row--selectable:focus-visible {
  outline: 3px solid var(--color-ring);
  outline-offset: 2px;
}
.session-row.is-selected {
  border-color: var(--nile-700);
  box-shadow: 0 0 0 2px var(--nile-700), var(--shadow-sm);
  background: var(--nile-100);
}
.session-row.is-selected .session-row__date {
  color: var(--nile-800);
}
/* The Choose control switches to the filled primary look when its tile is the
   chosen one, so the selection reads at a glance. */
.session-row.is-selected .btn--secondary {
  background: var(--nile-700);
  color: var(--white);
  border-color: var(--nile-700);
}
.session-row.is-selected .btn--secondary:hover {
  background: var(--nile-800);
  border-color: var(--nile-800);
  color: var(--white);
}
.session-row__date {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 700;
  font-size: var(--step-1);
  color: var(--ink);
  line-height: 1.2;
}
/* Homepage "Coming up next" cards: the date and the venue link share the top
   row — date to the left, venue to the right — so the card loses the extra
   stacked venue row and reads tighter. On a narrow screen the venue wraps
   below the date (prefer same-row) rather than squashing it. */
.session-row__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-1) var(--space-3);
}
/* Small location line (homepage cards): the venue name as a subtle link,
   quieter than the date beside it. */
.session-row__location {
  font-size: var(--step--1);
  line-height: 1.3;
}
/* Narrow phones: longer date labels (September/November/December) can't share
   the top row with the venue without wrapping inconsistently card to card (some
   top-right, some dropped below). Stack them deliberately — date, then venue on
   its own line — for EVERY card, so the "Coming up next" list reads as one
   consistent construction rather than drifting between two. Above this width the
   venue stays pinned top-right, where it fits. */
@media (max-width: 430px) {
  .session-row__head {
    flex-direction: column;
    align-items: flex-start;
  }
}
.session-row__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}
/* Time pinned to the left of the second line; the pill + button group flows to
   the right of it. */
.session-row__time {
  color: var(--ink-soft);
  font-size: var(--step--1);
  margin-right: auto;
}
/* On the BOOKING timetable the day-tier pill sits bottom-LEFT (beside the time)
   and the action (Choose / Join the waitlist) is pushed bottom-RIGHT. */
#timetable .session-row__time {
  margin-right: 0;
}
#timetable .session-row__meta .btn {
  margin-left: auto;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--step--1);
  font-weight: 700;
  padding: 0.25em 0.85em;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.pill--open {
  background: var(--ok-bg);
  color: var(--ok-text);
}
.pill--low {
  background: var(--low-bg);
  color: var(--low-text);
}
.pill--full {
  background: var(--full-bg);
  color: var(--full-text);
}
.pill--past {
  background: var(--ecru-100);
  color: var(--ink-faint);
}

/* ---- Sold-out date card (every enabled slot full): reads "Fully booked" via
   the pill; the card is muted and not selectable, its action is the waitlist. */
.session-row--full {
  background: var(--full-bg);
  border-color: var(--full-bg);
}
.session-row--full .session-row__date {
  color: var(--full-text);
}

/* ---- Booking timetable: single-location context line (name + address, one
   clickable Maps line at the top, no grouping). ---- */
.timetable-loccontext {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px var(--space-2);
  padding: 0 var(--space-1) var(--space-1);
  border: 0;
  background: none;
  box-shadow: none;
}
.timetable-loccontext__lead {
  color: var(--ink-soft);
  font-size: var(--step--1);
}
.timetable-loccontext__link {
  font-size: var(--step--1);
}

/* ---- Booking timetable: venue filter chips + a per-card venue tag. Shown only
   when MORE THAN ONE venue runs this season; a single-venue season renders
   neither, so it stays byte-for-byte the pre-grouping design. ---- */
.venue-filter {
  margin-top: var(--space-4);
}
.venue-filter__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.venue-chip {
  font: inherit;
  font-weight: 700;
  font-size: var(--step--1);
  line-height: 1;
  padding: 0.55em 1.1em;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--nile-800);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.venue-chip:hover {
  background: var(--nile-100);
  border-color: var(--nile-500);
}
.venue-chip.is-active {
  border-color: var(--nile-700);
  background: var(--nile-700);
  color: var(--white);
  box-shadow: 0 0 0 2px var(--nile-200);
}
.venue-chip:focus-visible {
  outline: 3px solid var(--color-ring);
  outline-offset: 2px;
}
.venue-filter__count {
  margin: var(--space-2) 0 0;
  color: var(--ink-soft);
  font-size: var(--step--1);
}
/* The small venue line on a booking card (multi-venue seasons): the venue name
   as a subtle Google Maps link, quieter than the date above it. */
.session-row__venue {
  font-size: var(--step--1);
  line-height: 1.3;
}
.session-row__venue-lead {
  color: var(--ink-soft);
}

/* ------------------------------------------------------------------ *
   Time slots — the per-day availability summary + the booking picker
 * ------------------------------------------------------------------ */
/* The chips a visitor picks a slot from on the booking form. Same
   selected-state pattern as the date tiles (a teal border + fill). */
.slot-picker {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.slot-chip {
  flex: 1 1 8rem;
  min-width: 8rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border: 2px solid var(--color-border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease);
}
.slot-chip:hover:not(.slot-chip--full) {
  border-color: var(--nile-500, var(--nile-800));
  background: var(--nile-100, var(--ecru-100));
}
.slot-chip.is-selected {
  border-color: var(--nile-800);
  background: var(--nile-200);
}
.slot-chip__time {
  font-weight: 700;
  font-size: var(--step-0);
}
/* "stay until HH:MM" line under the arrival time. */
.slot-chip__until {
  font-size: var(--step--1);
  color: var(--ink-soft);
}
.slot-chip__avail {
  font-size: var(--step--1);
  color: var(--ink-soft);
}
.slot-chip--full {
  cursor: pointer;
  opacity: 0.85;
  border-style: dashed;
}
.slot-chip--full .slot-chip__time {
  text-decoration: line-through;
  color: var(--ink-faint);
}
.slot-chip__wl {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--nile-800);
}

/* ---- Availability grading colours (public tier labels) ----
   Each tier class sets a fg/bg pair as custom props; pills, slot summaries and
   chips read them. Calm ink/green when open, warming through clay/amber to a
   warm red for "last few" and full, consistent with the palette. */
.avail--open  { --avail-fg: var(--ok-text);   --avail-bg: var(--ok-bg); }
.avail--up    { --avail-fg: var(--nile-800);  --avail-bg: var(--nile-200); }
.avail--fast  { --avail-fg: var(--clay-500);  --avail-bg: #f4e3d6; }
.avail--last  { --avail-fg: var(--full-text); --avail-bg: var(--full-bg); }
.avail--full  { --avail-fg: var(--full-text); --avail-bg: var(--full-bg); }
/* Day-tier pill on the timetable + homepage upcoming list. */
.pill--avail {
  background: var(--avail-bg, var(--nile-200));
  color: var(--avail-fg, var(--nile-800));
}
/* The slot chip's availability line takes the tier colour (the chip carries the
   avail-- class, so the fg custom prop is in scope). */
.slot-chip__avail {
  color: var(--avail-fg, var(--ink-soft));
  font-weight: 600;
}
.slot-chip.avail--full .slot-chip__time {
  text-decoration: line-through;
  color: var(--ink-faint);
}
/* Generic colour-coded tier chip (homepage next-session tile). */
.avail-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--step--1);
  font-weight: 700;
  padding: 0.15em 0.6em;
  border-radius: var(--radius-pill);
  background: var(--avail-bg, var(--nile-200));
  color: var(--avail-fg, var(--nile-800));
  white-space: nowrap;
}
/* ONE compact availability chip, sitting inline on the RIGHT of the time line
   (owner: no stacked chip row, no empty band at the foot of the tile). It only
   drops below if the time line genuinely leaves no room. */
.next-session__avail {
  flex: 0 0 auto;
  align-self: center;
}
.next-session__avail:empty {
  display: none;
}

/* Homepage gift-voucher promo card (shown only when payments are on). */
.gift-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-6);
  justify-content: space-between;
  padding: var(--space-5) var(--space-6);
  background: var(--nile-100, var(--nile-200));
  border: 1px solid var(--nile-200);
  border-radius: var(--radius);
}
.gift-card__text { flex: 1 1 20rem; }
.gift-card__cta { flex: 0 0 auto; }

/* ------------------------------------------------------------------ *
   12. FORMS
 * ------------------------------------------------------------------ */
.form {
  display: grid;
  gap: var(--space-4);
}
.field {
  display: grid;
  gap: var(--space-2);
  /* Reset so a <fieldset> used as a field (e.g. the item picker) carries no
     default border/padding/margin and lines up flush with the fields around
     it. min-inline-size:0 lets a fieldset shrink instead of forcing overflow. */
  border: 0;
  padding: 0;
  margin: 0;
  min-inline-size: 0;
}
.field__label {
  font-weight: 700;
  color: var(--ink);
  font-size: var(--step-0);
}
/* A <legend> used as a field label behaves like any other label. */
legend.field__label {
  padding: 0;
  float: none;
  width: 100%;
}
.field__req {
  color: var(--clay-500);
}
.field__hint {
  font-size: var(--step--1);
  color: var(--ink-faint);
  margin: 0;
}
/* Hint/error lines are spaced by the field grid gap, not their own margin,
   and an empty one must never leave a phantom gap between fields. */
.field__hint:empty,
.field__error:empty {
  display: none;
}
.input,
.select,
.textarea {
  width: 100%;
  font: inherit;
  /* Explicit line-height so a date/time input (which has no text run of its
     own to inherit leading from) computes the SAME control height as a text
     input. Without this the native date value sits shorter and looks squashed. */
  line-height: 1.5;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius);
  padding: 0.7em 0.9em;
  box-sizing: border-box;
  transition: border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.input:hover,
.select:hover,
.textarea:hover {
  border-color: var(--ecru-600);
}
.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--nile-700);
  box-shadow: 0 0 0 3px var(--nile-200);
}
.textarea {
  min-height: 120px;
  resize: vertical;
}
/* Native date/time inputs otherwise shrink to their content width and sit
   squashed and misaligned next to the full-width text inputs (notably the hire
   quote's "Preferred date" and the stock "Log a delivery" form). Normalise them
   to the SAME box as a standard input at every width: full width, identical
   line-height (so the field height matches, not vertically squashed), the value
   left-aligned and vertically centred, and the picker icon pushed to the
   trailing edge. Inherits padding/border/radius from `.input` above. */
.input[type="date"],
.input[type="time"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  min-width: 0;
  line-height: 1.5;
}
.input[type="date"]::-webkit-date-and-time-value,
.input[type="time"]::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
  line-height: 1.5;
}
.input[type="date"]::-webkit-datetime-edit,
.input[type="time"]::-webkit-datetime-edit {
  padding: 0;
  line-height: 1.5;
}
.input[type="date"]::-webkit-calendar-picker-indicator,
.input[type="time"]::-webkit-calendar-picker-indicator {
  margin-inline-start: auto;
  opacity: 0.7;
}
/* Firefox: keep the value left-aligned and the field full width. */
@supports (-moz-appearance: none) {
  .input[type="date"],
  .input[type="time"] { text-align: left; }
}
/* ---- Custom date/arrival picker (booking form) ----
   One combobox/listbox component for the date and arrival choices on every
   device: a button styled like an input (current choice + its availability
   pill), opening an anchored option list on desktop and a full-width bottom
   sheet on small screens. Option pills reuse the .pill--avail tier colours. */
/* min-width: 0 lets the picker shrink inside the form's grid tracks — without
   it the button's no-wrap value line would widen the whole field and push the
   page into sideways scroll on narrow phones. */
.picker { position: relative; min-width: 0; }
.picker__btn {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  font: inherit;
  line-height: 1.5;
  text-align: left;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius);
  padding: 0.7em 0.9em;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.picker__btn:hover { border-color: var(--ecru-600); }
.picker__btn:focus {
  outline: none;
  border-color: var(--nile-700);
  box-shadow: 0 0 0 3px var(--nile-200);
}
.picker__btn:disabled { cursor: default; color: var(--ink-faint); }
/* The value area stacks the current choice over an optional venue line (the date
   picker fills it only in a multi-venue season). With one child it collapses to
   the old single-line control, so a one-venue season is visually unchanged. */
.picker__value-wrap {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.picker__value {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.picker__value.is-placeholder { color: var(--ink-soft); }
/* Which venue the chosen date is at, kept visible on the closed control. */
.picker__venue {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-top: 1px;
  font-size: var(--step--1);
  line-height: 1.25;
  color: var(--ink-soft);
}
.picker__pill { flex: 0 0 auto; display: inline-flex; }
.picker__chevron {
  flex: 0 0 auto;
  display: inline-flex;
  color: var(--ink-soft);
  transition: transform var(--dur-fast) var(--ease);
}
.picker__chevron svg { width: 1.05em; height: 1.05em; }
.picker.is-open .picker__chevron { transform: rotate(180deg); }
.field--invalid .picker__btn { border-color: var(--clay-500); }

/* The panel + backdrop are appended to <body> (the form's .reveal ancestors
   are transformed, which would otherwise trap the fixed bottom sheet); the
   anchored desktop position (left/top/width) is set by book.js from the
   button's rectangle. */
.picker__panel {
  position: absolute;
  z-index: 60;
  background: var(--surface);
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.picker__list {
  list-style: none;
  margin: 0;
  padding: 6px;
  max-height: min(19rem, 50vh);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.picker__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 0.65em 0.75em;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.picker__opt-label { min-width: 0; }
/* Date over a small secondary venue line (multi-venue seasons only). */
.picker__opt-main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.picker__opt-venue {
  font-size: var(--step--1);
  line-height: 1.25;
  color: var(--ink-soft);
}
.picker__opt.is-disabled .picker__opt-venue { color: var(--ink-faint); }
.picker__opt[aria-selected="true"] .picker__opt-label { font-weight: 700; }
.picker__opt:hover:not(.is-disabled) { background: var(--ecru-100); }
.picker__opt.is-active,
.picker__opt.is-active:hover { background: var(--nile-200); }
.picker__opt.is-disabled { cursor: default; color: var(--ink-faint); }
.picker__opt.is-disabled .picker__opt-label {
  text-decoration: line-through;
  color: var(--ink-faint);
}
.picker__backdrop { display: none; }

/* Small screens: the option panel becomes a full-width bottom sheet with big
   tap targets, over a dimmed backdrop, so choosing is comfortable one-handed. */
@media (max-width: 640px) {
  .picker__backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 69;
    background: rgba(74, 56, 34, 0.45);
  }
  .picker__backdrop[hidden] { display: none; }
  .picker__panel {
    position: fixed;
    z-index: 70;
    left: 0;
    right: 0;
    top: auto;
    bottom: 0;
    border: none;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: 0 -12px 40px rgba(74, 56, 34, 0.3);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .picker__list { max-height: min(60vh, 30rem); padding: 10px; }
  .picker__opt { padding: 0.9em 0.85em; }
}

/* Number inputs otherwise keep the native spinner, whose min-height inflates
   the control so it stands taller than the text/date inputs beside it (most
   visible in the admin stock/cost rows). Drop the spinner so a number input is
   the SAME height as every other input; values are still typed normally. */
.input[type="number"] {
  -webkit-appearance: none;
  -moz-appearance: textfield;
  appearance: none;
}
.input[type="number"]::-webkit-outer-spin-button,
.input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'%3E%3Cpath fill='%232c6e72' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9em center;
  padding-right: 2.4em;
}
.field--invalid .input,
.field--invalid .select,
.field--invalid .textarea {
  border-color: var(--clay-500);
  box-shadow: 0 0 0 3px var(--clay-100);
}
/* iOS zooms the viewport whenever a focused form control has a font smaller
   than 16px. On mobile widths we hold every control (including the small
   admin/footer variants and the number steppers) at 16px so focusing a field
   never zooms the page. No maximum-scale/user-scalable hacks are used, so
   pinch-zoom stays available for accessibility. */
@media (max-width: 820px) {
  input,
  select,
  textarea,
  .input,
  .select,
  .textarea,
  .input--sm,
  .select--sm,
  .qty-stepper__input {
    font-size: 16px;
  }
}
.field__error {
  font-size: var(--step--1);
  color: var(--clay-600);
  font-weight: 600;
  min-height: 0;
  margin: 0;
}

/* Honeypot — visually + programmatically hidden from humans */
.hp-field {
  position: absolute !important;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Choice grid (item pre-selection / party helpers) */
.choice-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
.choice {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.choice:hover {
  border-color: var(--nile-500);
  background: var(--nile-100);
}
.choice input {
  width: 20px;
  height: 20px;
  accent-color: var(--nile-700);
  flex-shrink: 0;
}
.choice__label {
  font-weight: 600;
  color: var(--ink);
  font-size: var(--step-0);
}
.choice__price {
  margin-left: auto;
  color: var(--nile-800);
  font-weight: 700;
  font-size: var(--step--1);
}
.choice:has(input:checked) {
  border-color: var(--nile-700);
  background: var(--nile-100);
  box-shadow: 0 0 0 2px var(--nile-200);
}
.choice:has(input:focus-visible) {
  outline: 3px solid var(--color-ring);
  outline-offset: 2px;
}

/* Form-level feedback */
.form-message {
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  font-weight: 600;
  border: 1px solid transparent;
}
.form-message--success {
  background: var(--ok-bg);
  color: var(--ok-text);
  border-color: #b9d8c4;
}
.form-message--error {
  background: var(--full-bg);
  color: var(--full-text);
  border-color: #e2c1b7;
}
/* Amber "attention" variant — a change waiting on payment. Deliberately NOT the
   green success style, so nothing reads as done/paid while money is still owed. */
.form-message--pending {
  background: var(--low-bg);
  color: var(--low-text);
  border-color: #e6d3a6;
}
.form-message[hidden] {
  display: none;
}

/* ------------------------------------------------------------------ *
   13. FAQ ACCORDION (animated details)
 * ------------------------------------------------------------------ */
.faq {
  display: grid;
  gap: var(--space-3);
}
.faq-item {
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
/* Smooth open/close for the FAQ disclosures. Native <details> snaps; modern
   browsers (Chrome, Edge, Safari) can transition its content's block-size to
   auto via interpolate-size + the ::details-content pseudo-element. Firefox
   does not support these yet and simply keeps today's instant toggle. */
@media (prefers-reduced-motion: no-preference) {
  .faq-item {
    interpolate-size: allow-keywords;
  }
  .faq-item::details-content {
    block-size: 0;
    overflow-y: clip;
    transition: block-size 260ms ease, content-visibility 260ms allow-discrete;
  }
  .faq-item[open]::details-content {
    block-size: auto;
  }
}
.faq-item > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  cursor: pointer;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: 0.02em; /* condensed-caps question headers: keep them airy */
  line-height: 1.2;
  color: var(--ink);
  list-style: none;
  transition: background-color var(--dur-fast) var(--ease);
}
.faq-item > summary::-webkit-details-marker {
  display: none;
}
.faq-item > summary:hover {
  background: var(--ecru-100);
}
.faq-item__icon {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  color: var(--nile-700);
  transition: transform var(--dur) var(--ease);
}
.faq-item[open] .faq-item__icon {
  transform: rotate(45deg);
}
.faq-item__body {
  padding: 0 var(--space-5);
  color: var(--ink-soft);
  /* animated open/close via grid-rows */
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur) var(--ease),
    padding var(--dur) var(--ease);
}
.faq-item__body > div {
  overflow: hidden;
  min-height: 0;
}
.faq-item[open] .faq-item__body {
  grid-template-rows: 1fr;
  padding-bottom: var(--space-5);
}
.faq-item__body p:last-child {
  margin-bottom: 0;
}

/* ------------------------------------------------------------------ *
   14. ADMIN
 * ------------------------------------------------------------------ */
/* ADMIN EXCEPTION — the display serif (Oranienbaum) is reserved, inside
   the admin portal, for the big destination title only (#admin-surface-title:
   the h1 that names the active destination — Bookings, Operations, Finance…).
   Every other admin surface is dense working UI — tables, stats, zone
   headers, forms, buttons, docs — where display capitals hurt legibility,
   so it all reads in Poppins. Redefining the --font-display token on the
   admin shell flips every display-font element under it to the body stack
   in one move; the destination title opts back into the display serif below
   with a literal stack so the token override can't reach it. The standalone
   /admin/docs/*.html handbook pages carry the same override on .doc-content
   (they have no .admin-shell wrapper) so their rendered markdown stays
   Poppins too. */
.admin-shell,
.doc-content {
  --font-display: var(--font-sans);
}
/* Every admin jump target (rail chips, quick-find anchors, hash jumps via
   scrollIntoView) lands CLEAR of the sticky chrome: the header plus the
   practice banner when present. scroll-margin only acts while an element is
   being scrolled into view, so this broad rule cannot affect layout. */
.admin-shell [id] {
  scroll-margin-top: calc(80px + var(--dev-banner-h, 0px));
}
/* The global h1-h4 rule uppercases headings for the display serif; admin
   and handbook headings read in Poppins mixed case, so undo it here (the
   destination title below opts back in). */
.admin-shell h1, .admin-shell h2, .admin-shell h3, .admin-shell h4,
.doc-content h1, .doc-content h2, .doc-content h3, .doc-content h4 {
  text-transform: none;
}
#admin-surface-title {
  font-family: "Oranienbaum", "Oranienbaum Fallback", Georgia,
    "Times New Roman", serif;
  letter-spacing: 0.02em; /* display caps: keep the destination title airy */
  text-transform: uppercase;
}
.admin-shell {
  min-height: 60vh;
}
/* Owner amendment: the admin working area gets more width on big screens
   ("more to see" on data-dense surfaces). The header/footer keep the public
   pages' construction and width; only the admin shell's container widens. */
@media (min-width: 1200px) {
  .admin-shell > .container {
    max-width: 1560px;
  }
}
.login-card {
  max-width: 420px;
  margin: var(--space-8) auto;
}
.login-card__logo {
  display: block;
  width: 72px;
  height: auto;
  margin-bottom: var(--space-3);
}
.admin-summary {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  margin-bottom: var(--space-6);
}
.stat {
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  box-shadow: var(--shadow-sm);
}
.stat__value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-3);
  color: var(--nile-800);
  line-height: 1;
}
.stat__label {
  font-size: var(--step--1);
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
}
.admin-session {
  margin-bottom: var(--space-6);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.admin-session__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  background: var(--paper-2);
  border-bottom: 1px solid var(--color-border);
}
.admin-session__head h3 {
  margin: 0;
  font-size: var(--step-1);
}
/* The date heading is a link into Today for that date: a bare button styled as
   the heading text, underlined on hover/focus so it reads as clickable. */
.admin-session__datelink {
  appearance: none;
  border: 0;
  padding: 0;
  margin: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: left;
  border-radius: var(--radius-sm);
}
.admin-session__datelink:hover,
.admin-session__datelink:focus-visible {
  color: var(--clay-600);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.admin-session__stats {
  margin-left: auto;
  display: flex;
  gap: var(--space-4);
  color: var(--ink-soft);
  font-size: var(--step--1);
  font-weight: 600;
  flex-wrap: wrap;
}
.table-wrap {
  overflow-x: auto;
}
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
  min-width: 860px;
}
/* Keep the short row actions on a single tidy line rather than a tall stack.
   The table scrolls inside .table-wrap, so this never affects the page width. */
.admin-table td .row-actions {
  justify-content: flex-start;
  flex-wrap: nowrap;
}
.admin-table td .row-actions button {
  white-space: nowrap;
}
.admin-table th,
.admin-table td {
  text-align: left;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
}
.admin-table th {
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  background: var(--surface);
}
.admin-table tr:last-child td {
  border-bottom: none;
}
.status-badge {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 700;
  padding: 0.2em 0.6em;
  border-radius: var(--radius-pill);
  text-transform: capitalize;
}
.status-confirmed {
  background: var(--nile-200);
  color: var(--nile-800);
}
.status-attended {
  background: var(--ok-bg);
  color: var(--ok-text);
}
.status-no_show {
  background: var(--low-bg);
  color: var(--low-text);
}
.status-cancelled {
  background: var(--full-bg);
  color: var(--full-text);
  text-decoration: line-through;
}
.status-pending_payment {
  background: var(--low-bg);
  color: var(--low-text);
}
/* Payment badges (admin bookings "Payment" column) */
.pay-paid {
  background: var(--ok-bg);
  color: var(--ok-text);
}
.pay-pending {
  background: var(--low-bg);
  color: var(--low-text);
}
.pay-expired,
.pay-failed {
  background: var(--full-bg);
  color: var(--full-text);
}
.pay-none {
  background: var(--ecru-100);
  color: var(--ink-faint);
}
/* The Payment column chip is a compact rounded-RECT (not a pill), so a longer
   "Paid · £69.00 · Stripe" reads on one or two tidy lines instead of a tall
   green blob. Colours come from the .pay-* classes above. */
.pay-chip {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 700;
  padding: 0.28em 0.6em;
  border-radius: var(--radius-sm);
  line-height: 1.3;
  max-width: 100%;
}
.pay-chip-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
/* "Not yet refunded" cue on a paid + cancelled booking, with the refund action
   right beside it (NEW-5). */
.pay-owed {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
  flex-wrap: wrap;
}
.pay-owed__flag {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--full-text);
  background: var(--full-bg);
  border-radius: var(--radius-sm);
  padding: 0.2em 0.5em;
}
.pay-owed__btn { padding-block: 0.2em; }
/* Slot time badge shown under a booking's name in the admin table. */
.slot-badge {
  display: inline-block;
  margin-top: 3px;
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--nile-800);
  background: var(--nile-200);
  border-radius: var(--radius-pill);
  padding: 0.1em 0.55em;
}
/* A CANCELLED booking marker in the (always-visible) Name column, so a cancelled
   row reads as cancelled even when the Status/Payment columns are scrolled off to
   the right inside .table-wrap at 768/1024. */
.bk-cancelled-badge {
  display: inline-block;
  margin-top: 3px;
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--full-text);
  background: var(--full-bg);
  border-radius: var(--radius-pill);
  padding: 0.1em 0.6em;
}
/* Compact + dimmed cancelled rows: they no longer read as tall blank rows (the
   marker above names them) and the dim keeps the eye on live bookings. The name
   cell stays crisp so the marker is legible. */
.admin-table tr.bk-row--cancelled td {
  padding-top: var(--space-2);
  padding-bottom: var(--space-2);
  color: var(--ink-faint);
}
.admin-table tr.bk-row--cancelled td:first-child { color: var(--ink); }
/* Cancelled-bookings disclosure at the foot of a date group. It belongs to the
   date (not a top-level section), so it sits close under the active table rather
   than taking the full inter-zone gap, with a little breathing room under its
   summary bar before the tucked-away rows. Closed by default (the standard zone
   affordance), so each date reads active-only at a glance. */
.admin-session .bk-cancelled-zone { margin-top: var(--space-4); }
.admin-session .bk-cancelled-zone .admin-zone__body { padding-top: var(--space-3); }
/* Per-slot booked/remaining line in a date's admin header. */
.admin-session__slots {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  font-size: var(--step--1);
  color: var(--ink-soft);
  font-weight: 600;
}
.admin-slotstat--full { color: var(--full-text); }
/* Give the bookings table columns room to breathe so name/contact/items/notes
   don't wrap one word per line; the table still scrolls inside .table-wrap. */
.admin-session .admin-table { min-width: 960px; }
.admin-session .admin-table th:nth-child(1),
.admin-session .admin-table td:nth-child(1) { min-width: 9rem; }
.admin-session .admin-table th:nth-child(2),
.admin-session .admin-table td:nth-child(2) { min-width: 12rem; }
.admin-session .admin-table th:nth-child(3),
.admin-session .admin-table td:nth-child(3) { width: 3.5rem; }
.admin-session .admin-table th:nth-child(4),
.admin-session .admin-table td:nth-child(4) { min-width: 11rem; white-space: normal; }
.admin-session .admin-table th:nth-child(5),
.admin-session .admin-table td:nth-child(5) { min-width: 9rem; }
.admin-session .admin-table th:nth-child(7),
.admin-session .admin-table td:nth-child(7) { min-width: 9rem; }

/* Dashboard "Sessions": at laptop/tablet admin widths the 8-column bookings
   table used to overflow its wrapper (the Actions column scrolled off the right
   edge with no cue). Below 1500px each booking row RESTACKS into a labelled card,
   so every field — including the actions — is visible with no left-right scroll.
   Nothing is lost; the wide table returns on very large screens. */
@media (max-width: 1500px) {
  .admin-session .table-wrap { overflow-x: visible; }
  .admin-session .admin-table { min-width: 0; }
  /* Reset the desktop column widths so the stacked rows can flow. */
  .admin-session .admin-table th:nth-child(1), .admin-session .admin-table td:nth-child(1),
  .admin-session .admin-table th:nth-child(2), .admin-session .admin-table td:nth-child(2),
  .admin-session .admin-table th:nth-child(3), .admin-session .admin-table td:nth-child(3),
  .admin-session .admin-table th:nth-child(4), .admin-session .admin-table td:nth-child(4),
  .admin-session .admin-table th:nth-child(5), .admin-session .admin-table td:nth-child(5),
  .admin-session .admin-table th:nth-child(7), .admin-session .admin-table td:nth-child(7) { min-width: 0; width: auto; white-space: normal; }
  /* Hide the header row visually (each cell now carries its own label). */
  .admin-session .admin-table thead {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .admin-session .admin-table,
  .admin-session .admin-table tbody { display: block; }
  .admin-session .admin-table tr {
    display: block;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-2) var(--space-3);
    margin-bottom: var(--space-3);
  }
  .admin-session .admin-table tr:last-child { margin-bottom: 0; }
  .admin-session .admin-table td {
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
    align-items: flex-start;
    text-align: right;
    border: none;
    padding: 5px 0;
  }
  /* The name cell is the card's title: full width, no label, name + badges stack. */
  .admin-session .admin-table td:first-child {
    display: block;
    text-align: left;
    font-weight: 700;
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 7px;
    margin-bottom: 4px;
  }
  .admin-session .admin-table td[data-label]::before {
    content: attr(data-label);
    font-weight: 700;
    color: var(--ink-faint);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: var(--step--2);
    text-align: left;
    flex: 0 0 auto;
    padding-top: 2px;
  }
  .admin-session .admin-table td .row-actions { flex-wrap: wrap; justify-content: flex-end; }
  .admin-session .admin-table td .pay-chip-row { justify-content: flex-end; }
}
/* "Notes for Claude" cards. */
.claude-notes-list { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-3); }
.claude-note { padding: var(--space-3) var(--space-4); }
.claude-note--done { opacity: 0.85; }
.claude-note__head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-3); }
.claude-note__body { white-space: pre-wrap; }

/* Nested per-slot card in the Operations Today view. */
.ops-slotcard { margin-top: var(--space-3); }
.ops-slotcard h4 { margin: 0 0 var(--space-2); font-size: var(--step-0); }
.email-badge {
  font-size: 0.7rem;
  color: var(--ink-faint);
}
.row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.row-actions button {
  font: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  padding: 0.3em 0.7em;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.row-actions button:hover {
  background: var(--ecru-100);
  border-color: var(--ecru-600);
}
.row-actions button.danger:hover {
  background: var(--full-bg);
  border-color: var(--clay-500);
  color: var(--full-text);
}

/* Destructive .btn variant (e.g. the Site tab "Clear banner"), matching the
   red danger palette used by Cancel actions. */
.btn--danger {
  background: var(--full-bg);
  color: var(--full-text);
  border: 1px solid var(--clay-500);
}
.btn--danger:hover {
  background: var(--full-text);
  color: var(--white);
  border-color: var(--full-text);
}

/* Per-slot editor rows in the Site tab sessions table: each slot is on/off +
   its own start/end time + its own capacity, removable, plus "Add a slot". */
.site-slots { display: flex; flex-direction: column; gap: 4px; }
.site-slotline { display: flex; align-items: center; gap: var(--space-2); }
.site-slotline__label { font-size: var(--step--1); min-width: 6.5rem; }
.site-slotline__to { font-size: var(--step--2, 0.72rem); color: var(--ink-soft); }
.site-slottime { width: 6.2rem; }
.site-slotcap { width: 4.5rem; }
.site-slotline__rm { padding-inline: 0.55em; line-height: 1; }
.site-sloterror { color: var(--full-text, #a33); font-weight: 600; }

/* Day-window (start / end / slot length) generator controls in the sessions
   table. The slot list to the right regenerates when any of these change. */
.site-window { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.site-window__field { display: flex; flex-direction: column; gap: 2px; }
.site-window__lab { font-size: var(--step--2, 0.72rem); color: var(--ink-soft); }
.site-window .input { width: 6.5rem; }
.site-window__warn { margin: var(--space-2) 0 0; color: var(--clay-500); max-width: 26rem; }

/* Sessions planner: one compact card per date (no table, no sideways scroll).
   Each card is three wrapping lines — date + open/close, the three numbers,
   location + busyness — with the derived arrival-times summary underneath. */
.site-datecards { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-2); }
.site-datecard {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--paper-2);
  padding: var(--space-3) var(--space-4) var(--space-4);
}
.site-datecard--cancelled { background: var(--surface); }
.site-datecard--cancelled .site-datecard__date { text-decoration: line-through; }
.site-datecard__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  align-items: flex-end;
}
.site-datecard__row + .site-datecard__row { margin-top: var(--space-3); }
.site-datecard__date { width: 10.5rem; }
.site-datecard .input[type="time"] { width: 6.5rem; }
.site-datecard__num { width: 5.5rem; }
/* Remove sits at the far right of the head line, clear of the inputs. */
.site-datecard__rm { margin-left: auto; align-self: flex-end; padding-bottom: 0.4em; }
.site-datecard__cancelled { align-self: flex-end; padding-bottom: 0.45em; margin-left: auto; }
.site-datecard__meta { margin-top: var(--space-3); }
p.site-datecard__meta.site-window__warn { margin-top: var(--space-3); }

/* ------------------------------------------------------------------ *
   14b. CONTACT MODAL
 * ------------------------------------------------------------------ */
.modal-open {
  overflow: hidden;
}
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Respect iOS safe areas; never let the dialog hug a notch or home bar. */
  padding: max(var(--space-4), env(safe-area-inset-top))
    max(var(--space-4), env(safe-area-inset-right))
    max(var(--space-4), env(safe-area-inset-bottom))
    max(var(--space-4), env(safe-area-inset-left));
  background: rgba(46, 38, 32, 0.55);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.modal-overlay.is-open {
  opacity: 1;
}
.modal {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 540px;
  /* Cap to the *visible* viewport (dynamic vh) minus the padded/safe-area
     box so the header and sticky footer stay on-screen even with the
     on-screen keyboard up. The body scrolls internally instead of the
     whole dialog running off the bottom. 100% here resolves against the
     overlay's already safe-area-inset content box. */
  max-height: min(100%, calc(100dvh - 2 * var(--space-4)));
  margin: auto;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  transform: translateY(18px) scale(0.98);
  opacity: 0;
  transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.modal-overlay.is-open .modal {
  transform: none;
  opacity: 1;
}
/* Three regions: pinned head, scrolling body, pinned (sticky) footer. */
.modal__head {
  flex: 0 0 auto;
  padding: var(--space-6) var(--space-6) var(--space-4);
}
.modal__form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  gap: 0;
}
.modal__body {
  display: grid;
  gap: var(--space-5);
  align-content: start;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: var(--space-1) var(--space-6) var(--space-5);
}
.modal__foot {
  flex: 0 0 auto;
  padding: var(--space-4) var(--space-6)
    max(var(--space-6), env(safe-area-inset-bottom));
  border-top: 1px solid var(--color-border);
  background: var(--surface);
}
.modal .eyebrow {
  margin-bottom: var(--space-2);
}
.modal h2 {
  margin-bottom: var(--space-2);
  padding-right: var(--space-7);
}
.modal .lead {
  font-size: var(--step-0);
  margin-bottom: 0;
}
.modal .form-message--success {
  margin: var(--space-2) var(--space-6) var(--space-6);
}
.modal__close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  z-index: 2;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: var(--paper-2);
  color: var(--ink);
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease);
}
.modal__close:hover {
  background: var(--ecru-100);
  transform: rotate(90deg);
}
.cf-turnstile-holder {
  /* Turnstile runs in its most discreet "interaction-only" mode, so it stays
     invisible unless a visitor actually has to solve a challenge. No reserved
     gap is needed; the widget expands in place on the rare occasion it shows. */
  min-height: 0;
}

/* "Find us" (About): map and text share equal height, matched radii and an
   intentional side-by-side proportion so neither card looks stranded. */
.find-us {
  align-items: stretch;
}
.map-frame {
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm);
  background: var(--paper-2);
  min-height: 320px;
  height: 100%;
}
.map-frame iframe {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 320px;
  border: 0;
}
.find-us__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-4);
}
.find-us__text > * {
  margin: 0;
}

/* ------------------------------------------------------------------ *
   15. FOOTER
 * ------------------------------------------------------------------ */
.site-footer {
  background: var(--ink);
  color: var(--ecru-100);
  padding-block: var(--space-7) var(--space-6);
}
.site-footer a {
  color: var(--nile-300);
  text-decoration-color: transparent;
}
.site-footer a:hover {
  color: var(--white);
  text-decoration-color: var(--nile-300);
}
.footer-grid {
  display: grid;
  gap: var(--space-5) var(--space-6);
  grid-template-columns: 1.4fr 1fr 1fr;
  align-items: start;
}
/* Column order on desktop: brand (source-first) / signup / get-in-touch. The
   signup is appended last in the DOM (for the mobile stack), so order lifts it
   into the middle column here. */
.footer-signup {
  order: 1;
}
.footer-contact {
  order: 2;
}
.footer-brand__name {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 700;
  font-size: var(--step-1);
  color: var(--white);
  margin-bottom: var(--space-2);
}
/* The cream wordmark SVG that replaces the plain-text footer brand name.
   Explicit width/height attributes in the HTML prevent layout shift; here we
   just cap it so it never overflows a narrow footer column. */
.footer-brand__logo {
  display: block;
  width: 220px;
  max-width: 100%;
  height: auto;
  margin-bottom: var(--space-3);
}
.footer-col h4 {
  color: var(--ecru-300);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: var(--space-3);
}
.footer-col ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-2);
}
/* Each footer-nav link/button is a flex row: a fixed-width icon then the text in
   its own block, so a wrapped line (e.g. the venue address) aligns under the
   TEXT, not under the icon. */
.footer-col li > a,
.footer-col li > .link-btn {
  display: inline-flex;
  align-items: flex-start;
  gap: 6px;
  text-align: left;
}
.footer-bottom {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--step--1);
  color: var(--ecru-300);
}
.social-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 600;
}
/* In the footer brand column each contact link sits on its own row with a
   consistent gap (the email link is injected at runtime after Instagram). */
.footer-brand .social-link {
  display: flex;
  width: fit-content;
  margin-top: var(--space-2);
}
/* Footer-nav link/button icons (message, map-pin): fixed width and top-aligned
   inside the flex row above, so wrapped label lines align under the text. */
.footer-col li .btn__icon {
  flex: 0 0 auto;
  margin-top: 0.15em;
  opacity: 0.85;
}
.social-link svg {
  width: 22px;
  height: 22px;
}
/* Runtime-assembled contact email, sat just under the Instagram link. */
.footer-brand .footer-email {
  margin-top: var(--space-2);
}
/* Keep the address itself on a single line so it never wraps mid-word (which
   left an orphan "m" in the brand column at tablet widths). It still fits the
   column at 375. */
.footer-email__addr {
  white-space: nowrap;
}
.footer-email .btn__icon {
  width: 20px;
  height: 20px;
}
.footer-email-slim {
  order: -1;
  flex-basis: 100%;
}
.footer-email-slim .footer-email {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 600;
}
@media (max-width: 720px) {
  .footer-grid {
    grid-template-columns: 1fr 1fr;
  }
  .footer-brand {
    grid-column: 1 / -1;
  }
  /* Reset the desktop reorder so the natural DOM order shows: brand, then get in
     touch, then the signup. */
  .footer-signup,
  .footer-contact {
    order: 0;
  }
}
@media (max-width: 460px) {
  .footer-grid {
    grid-template-columns: 1fr;
  }
}

/* ---- About page: Katy photo placeholder ----
   Previously inline style="" attributes, which the strict CSP (style-src
   'self') blocks; moved here so the placeholder actually renders styled. */
/* Katy's story: on mobile the photo sits ABOVE the text, modest and centred;
   from 720px up it floats RIGHT so the paragraphs wrap alongside it and then
   flow underneath at the full column width (no tall blank column). */
/* The About page headline is the opening statement (the page H1), styled as a
   warm display line at a modest scale (no bigger than the lead), under the
   small orange eyebrow — not a shouty banner. */
.about-lead {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(1.3rem, 1.1rem + 1vw, 1.6rem);
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.02em; /* condensed caps: positive tracking over a full caps line */
  color: var(--ink);
  margin: 0 0 var(--space-4);
}
/* The brand wordmark set INLINE in running text (the About headline). Sized in
   em so it scales with the surrounding line, with fixed CSS dimensions (the
   image's own 185x26 ratio) so there is zero layout shift while it loads. */
.wordmark-inline {
  display: inline-block;
  width: 6.05em;
  height: 0.85em;
  vertical-align: -0.08em;
}
.about-photo {
  max-width: 260px; /* mobile: modest, gently centred above the text */
  margin: 0 auto var(--space-4);
  /* Soft shadow on the WRAPPER: box-shadow ignores clip-path, so drop-shadow
     follows the splotch outline instead. */
  filter: drop-shadow(0 12px 16px rgba(74, 56, 34, 0.22));
}
.about-story::after {
  content: "";
  display: block;
  clear: both;
}
@media (min-width: 720px) {
  .about-photo {
    float: right;
    width: 320px;
    max-width: 42%;
    margin: 0.25rem 0 var(--space-4) var(--space-6);
    /* Let the text hug the splotch rather than a hard rectangle. */
    shape-outside: ellipse(48% 48% at 50% 50%);
    shape-margin: 12px;
  }
}
/* Paint-splotch frame: the irregular clip path shapes the photo; the frame is
   0.9 aspect and matches the 720x800 source, so object-fit never crops Katy. */
.about-photo__frame {
  display: block;
  aspect-ratio: 720 / 800;
  max-width: 100%;
  -webkit-clip-path: url(#katy-splotch);
  clip-path: url(#katy-splotch);
  background: linear-gradient(135deg, #f4ede0, #e5d8c0);
}
.about-photo__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
}
/* The clip-path is defined in a 0x0 helper <svg>. Take it fully out of flow so
   it can never reserve a line box / vertical space (some engines, notably iOS
   Safari, otherwise leave a gap above the photo). Defs still resolve by id. */
.about-photo__clip {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}
/* Mobile: the intro TEXT leads (orange eyebrow, then the serif heading), the
   photo follows it, then the story paragraphs — so About opens exactly like
   every other page instead of behind a tall photo. Desktop (>=720px) keeps the
   float composition below, where the figure stays first in source order.
   Every child here has margin-top:0, so flex ordering changes nothing about the
   vertical rhythm. */
@media (max-width: 719px) {
  .about-story {
    display: flex;
    flex-direction: column;
  }
  .about-story > .eyebrow { order: 1; }
  .about-story > .about-lead { order: 2; }
  .about-story > .about-photo { order: 3; }
  .about-story > p { order: 4; }
}

/* ------------------------------------------------------------------ *
   16. MOTION — scroll reveal + page-load ease-in
 * ------------------------------------------------------------------ */
.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity var(--dur-slow) var(--ease),
    transform var(--dur-slow) var(--ease);
  will-change: opacity, transform;
}
.reveal.is-visible {
  opacity: 1;
  transform: none;
}
/* Gentle stagger for grouped children */
.reveal[data-delay="1"] {
  transition-delay: 80ms;
}
.reveal[data-delay="2"] {
  transition-delay: 160ms;
}
.reveal[data-delay="3"] {
  transition-delay: 240ms;
}

/* Page-load ease-in for the hero — pure CSS (no JS trigger, so no flash and
   it works even without JavaScript). Disabled under reduced-motion below. */
.page-intro {
  animation: introUp var(--dur-slow) var(--ease) both;
}
.page-intro[data-delay="1"] {
  animation-delay: 90ms;
}
.page-intro[data-delay="2"] {
  animation-delay: 180ms;
}
.page-intro[data-delay="3"] {
  animation-delay: 270ms;
}
@keyframes introUp {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
/* Navigating within the site: preboot.js marks second-and-later pages of a
   browser session (html.gnc-revisit) and those skip the entrance, landing
   on the settled hero immediately — the entrance is for arrivals, and
   replaying it on every internal page made navigation feel flashy. */
.gnc-revisit .page-intro {
  animation: none;
}
/* Admin-editable copy: while overrides are being fetched (main.js sets
   .content-pending pre-paint, only on pages that carry editable blocks), the
   hero's entrance animation holds at its invisible first frame so any new
   words are swapped in unseen. Lifted the moment the overrides are applied
   (or on the short safety cap), and neutralised by reduced-motion below. */
.js-ready.content-pending .page-intro {
  animation-play-state: paused;
}

/* Spinner for async buttons */
.spinner {
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* No-JS: never leave scroll-reveal content hidden (the observer never runs) */
.no-js .reveal {
  opacity: 1;
  transform: none;
}

/* ------------------------------------------------------------------ *
   17. REDUCED MOTION — respect the user's preference
 * ------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal,
  .page-intro {
    opacity: 1 !important;
    transform: none !important;
  }
  .btn:hover,
  .card--interactive:hover,
  .gallery-card:hover,
  .ig-tile:hover {
    transform: none;
  }
}

/* ------------------------------------------------------------------ *
   18. SMALL UTILITIES
 * ------------------------------------------------------------------ */
.noscript-note {
  background: var(--low-bg);
  color: var(--low-text);
  border: 1px solid #e6d3a6;
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-5);
  font-weight: 600;
}
.chip-note {
  font-size: var(--step--1);
  color: var(--ink-faint);
}
[hidden] {
  display: none !important;
}
.mt-0 {
  margin-top: 0;
}
.mb-0 {
  margin-bottom: 0;
}
.mt-3 {
  margin-top: var(--space-3);
}
.mt-4 {
  margin-top: var(--space-4);
}
.mt-5 {
  margin-top: var(--space-5);
}
.mt-6 {
  margin-top: var(--space-6);
}
.mt-7 {
  margin-top: var(--space-7);
}

/* ------------------------------------------------------------------ *
   19. BOOKING SUMMARY + DISCOUNT CODE
 * ------------------------------------------------------------------ */
.code-row {
  display: flex;
  gap: var(--space-3);
  align-items: stretch;
}
.code-row .input {
  flex: 1 1 auto;
  text-transform: uppercase;
}
.code-row .input::placeholder {
  text-transform: none;
}
.code-row .btn {
  flex: 0 0 auto;
  white-space: nowrap;
}
.code-message {
  min-height: 0;
}
.code-message--success {
  color: var(--ok-text);
  font-weight: 600;
}
.code-message--error {
  color: var(--clay-600);
  font-weight: 600;
}

.booking-summary {
  background: var(--nile-100);
  border: 1px solid var(--nile-200);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  box-shadow: var(--shadow-sm);
}
.summary__title {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 700;
  font-size: var(--step-1);
  color: var(--ink);
  margin: 0 0 var(--space-3);
}
.summary-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-1) 0;
  color: var(--ink-soft);
}
.summary-row__value {
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
}
.summary-row--total {
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--nile-300);
  font-size: var(--step-1);
}
.summary-row--total .summary-row__label {
  font-weight: 700;
  color: var(--ink);
}
.summary-row--total .summary-row__value {
  color: var(--nile-800);
}
.summary__note {
  margin: var(--space-3) 0 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
}
/* Location line in the booking summary (shown only for non-default venues). */
.summary__note--where {
  margin-top: var(--space-1);
  color: var(--nile-800);
  font-weight: 600;
}

/* ------------------------------------------------------------------ *
   20. BEFORE / AFTER GALLERY
 * ------------------------------------------------------------------ */
.gallery {
  display: grid;
  gap: var(--space-5);
  /* Three across on desktop; a single stacked column below 1024. The old
     auto-fit dropped to two columns in the tablet band and left the third card
     as a lop-sided orphan on its own row — this makes every width deliberate:
     3-up when there is room, otherwise a clean stack. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}
@media (max-width: 1023px) {
  .gallery { grid-template-columns: 1fr; }
}
.gallery-card {
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  margin: 0;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.gallery-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow);
}
.gallery-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.gallery-slot {
  position: relative;
  margin: 0;
}
.gallery-slot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  background: var(--ecru-100);
}
.gallery-slot figcaption {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  background: rgba(251, 247, 239, 0.88);
  padding: 0.15em 0.6em;
  border-radius: var(--radius-pill);
}
.gallery-slot--after figcaption {
  color: var(--nile-800);
}
.gallery-card__caption {
  padding: var(--space-3) var(--space-4);
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-soft);
}

/* ------------------------------------------------------------------ *
   21. PRIVATE HIRE — QUOTE WIDGET
 * ------------------------------------------------------------------ */
.quote {
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
}
.quote .form {
  gap: var(--space-4);
}
.quote__result {
  margin-top: var(--space-4);
  padding: var(--space-5);
  background: linear-gradient(180deg, var(--nile-100), var(--nile-200));
  border: 1px solid var(--nile-300);
  border-radius: var(--radius);
  text-align: center;
}
.quote__amount-label {
  display: block;
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--clay-500);
  margin-bottom: var(--space-2);
}
.quote__amount {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 700;
  font-size: var(--step-4);
  color: var(--nile-800);
  line-height: 1;
}
.quote__breakdown {
  margin: var(--space-3) 0 0;
  font-size: var(--step--1);
  color: var(--ink-soft);
}

/* ------------------------------------------------------------------ *
   22. SITE-WIDE ANNOUNCEMENT BANNER (admin-managed, all public pages)
 * ------------------------------------------------------------------ */
.site-banner {
  border-bottom: 1px solid var(--nile-300);
  background: var(--nile-100);
  color: var(--nile-800);
}
.site-banner--success {
  background: var(--ok-bg);
  color: var(--ok-text);
  border-bottom-color: #b9d8c4;
}
.site-banner--alert {
  background: var(--low-bg);
  color: var(--low-text);
  border-bottom-color: #e6d3a6;
}
.site-banner__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-1) var(--space-3);
  padding-block: var(--space-2);
}
.site-banner__text {
  font-weight: 600;
}
.site-banner__link {
  font-weight: 700;
  color: inherit;
  text-decoration-color: currentColor;
  white-space: nowrap;
}

/* ------------------------------------------------------------------ *
   Development-site banner (dev deployment only — never on live).
   Injected by main.js above the sticky header when /api/config carries
   the devSite flag. A permanent, non-dismissible full-width bar in a
   distinct warm amber so the practice copy can never be mistaken for the
   real site. Sits in normal flow at the very top of the page; the header's
   own sticky positioning is unaffected.
 * ------------------------------------------------------------------ */
.dev-banner {
  background: #7a3410; /* deep burnt amber — deliberately not a brand accent */
  color: #fff7ec;
  border-bottom: 3px solid #f2a44e;
  width: 100%;
  /* Always visible: the practice notice sticks to the top of the viewport
     so the owner can never scroll it away and mistake this for the real
     site. main.js publishes the banner's height as --dev-banner-h so the
     (also sticky) site header parks itself directly beneath. */
  position: sticky;
  top: 0;
  z-index: 110;
}
.dev-banner__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2, 8px) var(--space-3, 16px);
  text-align: center;
  max-width: 1120px;
  margin: 0 auto;
  padding: 10px 16px;
}
.dev-banner__tag {
  flex: none;
  display: inline-block;
  background: #f2a44e;
  color: #3a1806;
  font-weight: 800;
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: 999px;
}
.dev-banner__text {
  font-weight: 700;
  font-size: 0.95rem;
  line-height: 1.35;
}
@media (max-width: 480px) {
  .dev-banner__inner {
    padding: 8px 12px;
  }
  .dev-banner__text {
    font-size: 0.86rem;
  }
}
/* First-ever visit only (no cached flag yet): the banner arrives after the
   config fetch, so ease it in rather than jumping the page down. Cached
   navigations build the banner pre-paint and skip this class entirely. */
@media (prefers-reduced-motion: no-preference) {
  .dev-banner--enter {
    animation: dev-banner-drop 240ms ease-out;
  }
  @keyframes dev-banner-drop {
    from {
      transform: translateY(-100%);
      opacity: 0.5;
    }
    to {
      transform: translateY(0);
      opacity: 1;
    }
  }
}

.pill--cancelled {
  background: var(--full-bg);
  color: var(--full-text);
  text-decoration: line-through;
}

/* ------------------------------------------------------------------ *
   23. ADMIN — TABS + PANELS
 * ------------------------------------------------------------------ */
/* Admin header "View site" bridge link (opens the public site in a new tab). The
   admin's own tabs replace the public nav, so this is the only header link. */
.admin-viewsite {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--nile-800);
  text-decoration: none;
  padding: 0.3em 0.7em;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--surface);
}
.admin-viewsite:hover,
.admin-viewsite:focus-visible {
  border-color: var(--nile-500);
  color: var(--nile-900, var(--nile-800));
}
.admin-viewsite .btn__icon {
  width: 1em;
  height: 1em;
}
@media (max-width: 460px) {
  /* Tight header: keep the icon + accessible name, drop the visible label. */
  .admin-viewsite__label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}
.admin-topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.admin-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-5) 0 var(--space-6);
  border-bottom: 2px solid var(--color-border);
}
.admin-tab {
  font: inherit;
  font-weight: 700;
  font-size: var(--step--1);
  padding: var(--space-3) var(--space-5);
  border: 0;
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.admin-tab:hover {
  background: var(--ecru-100);
  color: var(--ink);
}
.admin-tab.is-active,
.admin-tab[aria-selected="true"] {
  color: var(--nile-800);
  border-bottom-color: var(--nile-700);
}

/* ------------------------------------------------------------------ *
   14a. ADMIN COLLAPSIBLE ZONES (one construction everywhere)
   Closed zones read as clean underlined headers; the hint carries a cheap
   count/status so nothing needs opening just to check.
 * ------------------------------------------------------------------ */
.admin-zone {
  margin-top: var(--space-6);
}
/* Collapsible zone header: a clearly INTERACTIVE bar — a tinted, rounded row
   with a chevron in its own chip and a whole-row hover — so it reads
   unmistakably as "tap to open/close", never as a plain heading. Permanent
   primary section headings (plain <h2>/<h3>, no chevron, no bar) are styled
   below and stay visually distinct: no affordance, so nobody wonders why they
   "won't close". */
.admin-zone__summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--paper-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  cursor: pointer;
  list-style: none;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.admin-zone__summary::-webkit-details-marker {
  display: none;
}
.admin-zone__summary::before {
  content: "\25BE"; /* down chevron in a chip; rotates when open */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.6em;
  height: 1.6em;
  border-radius: var(--radius-pill);
  background: var(--nile-100);
  color: var(--nile-800);
  font-size: var(--step--1);
  line-height: 1;
  transition: transform var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.admin-zone[open] > .admin-zone__summary::before {
  transform: rotate(180deg);
}
.admin-zone__summary:hover {
  background: var(--nile-100);
  border-color: var(--nile-300);
}
.admin-zone__summary:hover::before {
  background: var(--nile-200);
}
.admin-zone[open] > .admin-zone__summary {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.admin-zone__title {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--ink);
}
.admin-zone__hint {
  margin-left: auto;
  color: var(--ink-soft);
  font-size: var(--step--1);
  font-weight: 600;
  text-align: right;
}
.admin-zone__body > .card:first-child,
.admin-zone__body > section.card:first-child {
  margin-top: var(--space-4);
}

/* ------------------------------------------------------------------ *
   14b. ADMIN NAVIGATION SHELL (rail on desktop, bottom bar on phone)
   Plain hash links; the active item is Nile teal with a clay accent bar.
 * ------------------------------------------------------------------ */
.admin-layout {
  margin-top: var(--space-5);
}
.admin-nav__item {
  display: block;
  font: inherit;
  font-weight: 700;
  font-size: var(--step--1);
  color: var(--ink-soft);
  text-decoration: none;
  background: transparent;
  border: 0;
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.admin-nav__item:hover {
  background: var(--ecru-100);
  color: var(--ink);
  text-decoration: none;
}
.admin-nav__item.is-active {
  color: var(--nile-800);
}
.admin-nav__label-short {
  display: none;
}

/* Desktop and tablet (>=900px): fixed-width left rail, sticky under the
   header, content beside it. */
@media (min-width: 900px) {
  .admin-layout {
    display: grid;
    grid-template-columns: 200px minmax(0, 1fr);
    gap: var(--space-6);
    align-items: start;
  }
  .admin-nav {
    position: sticky;
    top: calc(var(--space-5) + var(--dev-banner-h, 0px));
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    background: var(--surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--space-3);
    box-shadow: var(--shadow-sm);
    /* Stay put while a long destination scrolls, and if the rail itself is taller
       than the viewport (short screen / many items) give it its OWN scroll so
       every destination link stays reachable rather than being cut off. */
    max-height: calc(100vh - var(--space-5) * 2);
    overflow-y: auto;
  }
  .admin-nav__group {
    display: contents;
  }
  .admin-nav__item {
    padding: var(--space-3) var(--space-4);
    border-left: 3px solid transparent;
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  }
  .admin-nav__item.is-active {
    border-left-color: var(--clay-500);
    background: var(--nile-100);
  }
  /* The More button and the phone-only Guide entry are phone furniture:
     on desktop every rail item shows and Guide lives in the top bar "?". */
  .admin-nav__more,
  .admin-nav__item--phone {
    display: none;
  }
}

/* Phone (<900px): fixed bottom bar with five items; Money, Messages, Website
   and Guide sit in a sheet behind More. Content gets breathing room so the
   bar never covers the page end. */
@media (max-width: 899.98px) {
  .admin-layout {
    padding-bottom: 72px;
  }
  .admin-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 90;
    display: flex;
    align-items: stretch;
    background: var(--surface);
    border-top: 1px solid var(--color-border-strong);
    box-shadow: 0 -2px 10px rgb(46 38 32 / 0.08);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .admin-nav > .admin-nav__item,
  .admin-nav__more {
    flex: 1 1 0;
    min-height: 52px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: var(--space-2) var(--space-1);
    border-radius: 0;
    border-top: 3px solid transparent;
  }
  .admin-nav > .admin-nav__item.is-active {
    border-top-color: var(--clay-500);
    background: var(--nile-100);
  }
  .admin-nav__label-full {
    display: none;
  }
  .admin-nav__label-short {
    display: inline;
  }
  /* The More sheet: a small panel above the bar. */
  .admin-nav__group {
    display: none;
    position: absolute;
    right: var(--space-3);
    bottom: calc(100% + var(--space-2));
    min-width: 180px;
    background: var(--surface);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg, 0 10px 30px rgb(46 38 32 / 0.18));
    padding: var(--space-2);
  }
  .admin-nav__group.is-open {
    display: block;
  }
  .admin-nav__group .admin-nav__item {
    padding: var(--space-3) var(--space-4);
    min-height: 48px;
    display: flex;
    align-items: center;
  }
  .admin-nav__group .admin-nav__item.is-active {
    background: var(--nile-100);
    color: var(--nile-800);
  }
}

/* ------------------------------------------------------------------ *
   24. BUSINESS DASHBOARD
 * ------------------------------------------------------------------ */
.biz-intro {
  margin-bottom: var(--space-4);
}
.biz-savebar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--paper-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  margin-bottom: var(--space-5);
  position: sticky;
  top: calc(68px + var(--dev-banner-h, 0px));
  z-index: 20;
}
.biz-savebar__status {
  font-size: var(--step--1);
  font-weight: 700;
}

/* Shared admin-wide sticky Save/Reset bar (Site + any settings tab). Hidden
   until a change is made; follows the scroll like the Business save bar. */
.admin-savebar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--paper-2);
  border: 1px solid var(--nile-500);
  border-radius: var(--radius);
  margin-bottom: var(--space-5);
  position: sticky;
  top: calc(68px + var(--dev-banner-h, 0px));
  z-index: 20;
  box-shadow: var(--shadow-sm);
}
.admin-savebar[hidden] { display: none; }
.admin-savebar__status {
  font-size: var(--step--1);
  font-weight: 700;
}
.admin-savebar__status.is-dirty { color: var(--low-text); }
.admin-savebar__status.is-saved { color: var(--ok-text); }
.admin-savebar__status.is-error { color: var(--clay-600); }
.biz-savebar__status.is-dirty {
  color: var(--low-text);
}
.biz-savebar__status.is-saved {
  color: var(--ok-text);
}
.biz-savebar__status.is-error {
  color: var(--clay-600);
}

.biz-quick {
  margin-bottom: var(--space-6);
}
.biz-quick__grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  margin-top: var(--space-4);
}
.biz-quick__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.biz-quick__item.choice {
  flex-direction: row;
  align-items: center;
}
.biz-quick__label {
  font-weight: 700;
  font-size: var(--step--1);
  color: var(--ink);
}
.biz-seg {
  display: inline-flex;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  overflow: hidden;
  width: fit-content;
}
.biz-seg__btn {
  font: inherit;
  font-weight: 700;
  font-size: var(--step--1);
  padding: 0.5em 1.1em;
  border: 0;
  background: var(--surface);
  color: var(--ink-soft);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.biz-seg__btn + .biz-seg__btn {
  border-left: 1.5px solid var(--color-border-strong);
}
.biz-seg__btn.is-active {
  background: var(--nile-700);
  color: var(--white);
}
.biz-echo {
  margin: 0;
}
/* Compact segment (busyness chip in the Sessions table) */
.biz-seg--sm .biz-seg__btn {
  padding: 0.35em 0.7em;
  font-size: var(--step--2);
}

/* Money forecast controls hold a comfortable 48px touch target on phones (the
   scenario segments, the item-mix steppers and the preset chips). Scoped to the
   Money panel so the compact Sessions busyness chip stays small. */
@media (max-width: 640px) {
  #panel-money .biz-seg__btn { min-height: 48px; }
  #panel-money .biz-stepper__btn { width: 48px; height: 48px; }
  #panel-money .biz-preset { min-height: 48px; }
}

/* Money surface: stacked sections with a little breathing room between. */
.money-section {
  margin-top: var(--space-7);
  padding-top: var(--space-6);
  border-top: 1px solid var(--color-border);
}

/* Item-mix stepper allocator */
.biz-mixalloc { margin-bottom: var(--space-6); }
.biz-presets {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-3) 0 var(--space-4);
}
.biz-mixgrid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
}
.biz-stepper {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--surface);
}
.biz-stepper__label {
  flex: 1 1 auto;
  font-weight: 700;
  font-size: var(--step--1);
  color: var(--ink);
}
.biz-stepper__btn {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: var(--step-1);
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease);
}
.biz-stepper__btn:hover { background: var(--nile-100); }
.biz-stepper__val {
  flex: 0 0 auto;
  min-width: 1.75em;
  text-align: center;
  font-weight: 700;
  font-size: var(--step-0);
  font-variant-numeric: tabular-nums;
}
.biz-mixsummary { margin: var(--space-4) 0 0; }

/* Percent field with a trailing % suffix */
.biz-pctwrap {
  display: flex;
  align-items: center;
  gap: 0.4em;
}
.biz-pctwrap .input { flex: 1 1 auto; min-width: 0; }
.biz-pctsuffix {
  flex: 0 0 auto;
  font-weight: 700;
  color: var(--ink-soft);
}

/* "Use your real numbers" adoption nudge */
.biz-adopt {
  border-left: 5px solid var(--nile-500);
  margin-bottom: var(--space-6);
}

/* Quick-find (top-bar search to jump anywhere) */
.admin-quickfind {
  position: relative;
  flex: 1 1 220px;
  max-width: 340px;
  min-width: 0;
}
.admin-quickfind__icon {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  color: var(--ink-soft);
  pointer-events: none;
}
/* The compound selector raises specificity above `.input--sm` (whose `padding`
   shorthand appears later in the file and otherwise reset the left padding),
   so the magnifier icon never overlaps the placeholder. Icon is 16px at
   left:10px (ends 26px); 34px leaves a clean gap at every width. */
.admin-quickfind .admin-quickfind__input {
  width: 100%;
  padding-left: 34px;
}
.admin-quickfind__results {
  position: absolute;
  z-index: 40;
  left: 0;
  right: 0;
  top: calc(100% + 4px);
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md, 0 8px 24px rgba(0,0,0,0.12));
  max-height: 60vh;
  overflow-y: auto;
}
.admin-quickfind__opt {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.admin-quickfind__opt.is-active,
.admin-quickfind__opt:hover { background: var(--nile-100); }
.admin-quickfind__opt-label { font-weight: 700; color: var(--ink); }
.admin-quickfind__opt-dest { font-size: var(--step--2); color: var(--ink-soft); white-space: nowrap; }
@media (max-width: 640px) {
  .admin-quickfind { flex-basis: 100%; order: 3; max-width: none; margin-top: var(--space-2); }
}

/* Settings surface: live token preview + read-only lists */
.settings-preview__line {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--step--1);
  background: var(--nile-100);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  margin: var(--space-1) 0 var(--space-3);
  word-break: break-word;
}
.settings-list {
  margin: var(--space-2) 0 0;
  padding-left: 1.2em;
}
.settings-list li { margin-bottom: var(--space-1); }

/* Discount-code "only these sessions" tickable list */
.code-dates {
  display: grid;
  gap: var(--space-1);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  margin-top: var(--space-1);
}
.code-dates__opt {
  align-items: center;
}

/* plain-English summary + traffic lights */
.biz-plain {
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-left: 5px solid var(--clay-500);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-5);
  box-shadow: var(--shadow-sm);
}
.biz-plain__lead {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-2);
  color: var(--ink);
  margin: 0 0 var(--space-2);
  line-height: 1.25;
}
.biz-plain__sub {
  margin: 0;
  color: var(--ink-soft);
}
.biz-lights {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  margin-bottom: var(--space-6);
}
.biz-light {
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-top: 4px solid var(--ecru-300);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  box-shadow: var(--shadow-sm);
}
.biz-light--good {
  border-top-color: var(--ok-text);
}
.biz-light--warn {
  border-top-color: var(--low-text);
}
.biz-light--bad {
  border-top-color: var(--full-text);
}
.biz-light__top {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.biz-light__dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--ecru-400);
}
.biz-light--good .biz-light__dot {
  background: var(--ok-text);
}
.biz-light--warn .biz-light__dot {
  background: var(--low-text);
}
.biz-light--bad .biz-light__dot {
  background: var(--full-text);
}
.biz-light__value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-3);
  color: var(--nile-800);
  line-height: 1;
}
.biz-light__label {
  font-weight: 700;
  margin-top: var(--space-2);
  color: var(--ink);
}
.biz-light__expl {
  font-size: var(--step--1);
  color: var(--ink-faint);
  margin-top: var(--space-1);
}

/* charts */
.biz-charts-grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  margin-bottom: var(--space-6);
}
.biz-chart-card__title {
  font-size: var(--step-1);
  margin-bottom: var(--space-1);
}
.gc-chart-wrap {
  overflow-x: auto;
}
.gc-chart {
  width: 100%;
  height: auto;
  display: block;
}
/* The "Money in and out across the season" timeline card renders TWO full-width
   charts (the cumulative line + the money-in/out bars). At wide admin widths they
   ballooned to ~500px tall. Cap them to a sensible, still-readable size, centred. */
.biz-timeline-card .gc-chart {
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
}
/* The Website "Visitors" zone's visits-per-day chart: same cap (a full-width
   30-bar chart would balloon on wide screens too). */
.site-visitors-chart .gc-chart {
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
}
.gc-axis {
  stroke: var(--color-border-strong);
  stroke-width: 1;
}
.gc-lbl {
  font-family: var(--font-sans);
  font-size: 11px;
  fill: var(--ink-faint);
}
.gc-lbl--val {
  fill: var(--ink-soft);
  font-weight: 700;
}
.gc-bar {
  transform-box: fill-box;
  transform-origin: bottom;
}
.gc-bar.gc-anim {
  animation: gcGrowY var(--dur-slow) var(--ease) both;
}
.gc-hbar {
  transform-box: fill-box;
  transform-origin: left;
}
.gc-hbar.gc-anim {
  animation: gcGrowX var(--dur-slow) var(--ease) both;
}
.gc-line {
  stroke-dasharray: 1;
}
.gc-line.gc-anim {
  animation: gcDraw var(--dur-slow) var(--ease) both;
}
.gc-fade {
  animation: gcFade var(--dur-slow) var(--ease) both;
}
@keyframes gcGrowY {
  from { transform: scaleY(0); }
  to { transform: scaleY(1); }
}
@keyframes gcGrowX {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
@keyframes gcDraw {
  from { stroke-dashoffset: 1; }
  to { stroke-dashoffset: 0; }
}
@keyframes gcFade {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Business sub-tabs (Live / Forecast / Historical) — a lighter version of the
   main admin tab pattern, so the whole Business tab is one URL-hash-aware group. */
.admin-subtabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-5);
  border-bottom: 2px solid var(--color-border);
}
.admin-subtab {
  font: inherit;
  font-weight: 700;
  font-size: var(--step--1);
  padding: var(--space-2) var(--space-4);
  border: 0;
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.admin-subtab:hover {
  background: var(--ecru-100);
  color: var(--ink);
}
.admin-subtab.is-active,
.admin-subtab[aria-selected="true"] {
  color: var(--nile-800);
  border-bottom-color: var(--nile-700);
}
.admin-subpanel[hidden] { display: none; }

/* Cash-flow timeline + inline disclosures + alert lists (Live/Forecast/Historical) */
.biz-timeline-card,
.biz-hist-card {
  margin-top: var(--space-5);
}
.biz-detail--inline {
  margin-top: var(--space-4);
  box-shadow: none;
}
.biz-detail--inline > summary {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  padding: var(--space-3) var(--space-4);
}
.biz-alertlist {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}
.biz-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--step--1);
  color: var(--ink);
  line-height: 1.4;
}
.biz-alert__dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 3px;
  background: var(--ecru-400);
}
.biz-alert--good .biz-alert__dot { background: var(--ok-text); }
.biz-alert--warn .biz-alert__dot { background: var(--low-text); }
.biz-alert--bad .biz-alert__dot { background: var(--full-text); }
.biz-hist-card__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.biz-hist-card__sugg {
  margin-top: var(--space-3);
  font-style: italic;
}

/* progressive-disclosure detail block */
.biz-detail {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  margin-top: var(--space-4);
}
.biz-detail > summary {
  cursor: pointer;
  padding: var(--space-4) var(--space-5);
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--ink);
}
.biz-detail > summary:hover {
  background: var(--ecru-100);
}
.biz-detail__body {
  padding: var(--space-2) var(--space-5) var(--space-5);
}
.biz-editor {
  margin-bottom: var(--space-6);
}
.biz-editor__group {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  min-inline-size: 0;
}
.biz-editor__group legend {
  font-weight: 700;
  padding: 0 var(--space-2);
  color: var(--nile-800);
}
.biz-editor__grid {
  display: grid;
  gap: var(--space-3) var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
}
.biz-field__label {
  font-size: var(--step--1);
  font-weight: 600;
}
.biz-mixcheck {
  font-size: var(--step--1);
  font-weight: 700;
  margin: var(--space-3) 0 0;
}
.biz-mixcheck.is-ok {
  color: var(--ok-text);
}
.biz-mixcheck.is-bad {
  color: var(--clay-600);
}
.biz-tablesec {
  margin-top: var(--space-6);
}
.biz-tablesec h3 {
  font-size: var(--step-1);
  margin-bottom: var(--space-2);
}
.biz-table {
  min-width: 640px;
}
.biz-table td.biz-pos,
.biz-pos {
  color: var(--ok-text);
  font-weight: 700;
}
.biz-table td.biz-neg,
.biz-neg {
  color: var(--full-text);
  font-weight: 700;
}
.biz-heat td {
  text-align: center;
  font-weight: 700;
  color: var(--ink);
}
.biz-heat th[scope="row"] {
  font-weight: 700;
  background: var(--paper-2);
}
.biz-flag {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 700;
  padding: 0.2em 0.6em;
  border-radius: var(--radius-pill);
  text-decoration: none;
}
.biz-flag--now {
  background: var(--full-bg);
  color: var(--full-text);
}
.biz-flag--soon {
  background: var(--low-bg);
  color: var(--low-text);
}
.biz-flag--ok {
  background: var(--ok-bg);
  color: var(--ok-text);
}
.biz-order-note {
  margin-top: var(--space-3);
  font-weight: 600;
  color: var(--ink-soft);
}

/* ------------------------------------------------------------------ *
   25. SITE CONTENT MANAGEMENT
 * ------------------------------------------------------------------ */
.site-sec {
  margin-bottom: var(--space-6);
}
.site-table {
  /* Seven columns (Name, Key, Price, Cost, Description, Public, Remove) need
     real room: at 720px they squeezed until name/key truncated to ambiguity and
     the Remove button clipped mid-word. Widen the table so every field is legible
     and let it scroll inside .table-wrap at narrow widths (never the page). */
  min-width: 940px;
}
.site-table td {
  vertical-align: middle;
}
/* Readable minimums so the text inputs never collapse to a few characters. */
.site-table td .input { min-width: 7rem; }
.site-table td:nth-child(1) .input { min-width: 9.5rem; } /* Name */
.site-table td:nth-child(2) .input { min-width: 7.5rem; } /* Key */
.site-table td:nth-child(5) .input { min-width: 12rem; }  /* Description */
/* Keep the Remove button whole (never clipped mid-word). */
.site-table td .row-actions { flex-wrap: nowrap; }
.site-table td .row-actions .danger { white-space: nowrap; }
.input--sm,
.select--sm {
  padding: 0.4em 0.6em;
  font-size: var(--step--1);
  border-radius: var(--radius-sm);
  /* Fixed height so text, number, select AND date/time controls line up at the
     exact same height in a compact admin row. Native selects/number spinners
     otherwise render a little taller than an appearance:none date input, which
     left the date looking squashed next to its neighbours. */
  height: 2.5rem;
}
/* Centre the date/time value vertically inside the fixed control height. */
.input--sm[type="date"],
.input--sm[type="time"],
.input[type="date"],
.input[type="time"] {
  display: flex;
  align-items: center;
}
/* Full-size date/time inputs (e.g. the discount-codes "Expires" field) have no
   text run of their own, so without a min-height they hug their content and
   render SHORTER than the text inputs beside them. Pin them to the same box a
   standard .input computes: line-height 1.5em + vertical padding 2x0.7em +
   border 2x1.5px. Keeps the codes form visually level. */
.input[type="date"],
.input[type="time"] {
  min-height: calc(1.5em + 1.4em + 3px);
}
.site-subcard {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-4);
  margin-top: var(--space-4);
  background: var(--paper-2);
}
/* A one-line add/remove row (e.g. an Instagram post URL + its Remove button). */
.ops-inline-row {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  flex-wrap: wrap;
}
.ops-inline-row .input {
  flex: 1 1 16rem;
  min-width: 0;
}
.site-grid {
  display: grid;
  gap: var(--space-3) var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
}

/* ---- Pages & words (admin page-copy editor) ---- */
.pages-page { margin-top: var(--space-4); }
.pages-block { margin-top: var(--space-4); margin-bottom: 0; }
.pages-block__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.pages-reset {
  font-size: var(--step--2, 0.72rem);
  color: var(--nile-800);
  white-space: nowrap;
}
.pages-reset:disabled {
  color: var(--ink-faint);
  cursor: default;
  text-decoration: none;
}
.pages-imgrow {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  flex-wrap: wrap;
}
.pages-imgthumb {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: var(--ecru-100);
}
.pages-gallery h5 { font-size: var(--step-0); }
.pages-faqrow__bar {
  display: flex;
  gap: var(--space-2);
  justify-content: flex-end;
  flex-wrap: wrap;
  margin-top: var(--space-2);
}

/* ---- Photos (admin R2 media library) ---- */
.media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-4);
}
.media-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--paper-2);
}
.media-card__thumb {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--ecru-100);
}
.media-card__meta {
  padding: var(--space-2) var(--space-2) 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1);
}
.media-card__name {
  font-size: var(--step--2, 0.72rem);
  font-weight: 600;
  word-break: break-word;
  min-width: 0;
}
.media-inuse {
  background: var(--nile-500);
  color: var(--nile-800);
  font-size: var(--step--2, 0.72rem);
}
.media-card__actions {
  display: flex;
  gap: var(--space-1);
  padding: var(--space-2);
  flex-wrap: wrap;
}

/* ---- Newsletter builder (Operations tab) ---- */
.news-blocks { margin-top: var(--space-3); }
.news-block { margin-top: var(--space-2); }
.news-echo { display: block; }
.news-preview {
  display: block;
  width: 100%;
  height: 540px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: #efe7d7; /* the email's own page colour behind the card */
  margin-top: var(--space-3);
}

/* ==========================================================================
   TIER-1 OPERATIONS — admin ops tab, public collection + manage, footer signup
   ========================================================================== */

/* small utilities */
.ml-2 { margin-left: var(--space-2); }
.choice--sm { font-size: var(--step--1); align-items: flex-start; }

/* ---- Operations tab layout ---- */
.ops-datebar { margin-bottom: var(--space-5); }
.ops-datebar__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin-top: var(--space-3);
}
.ops-card { margin-top: var(--space-5); }
.ops-card h3 { margin-bottom: var(--space-2); }

/* capacity headroom bar */
.ops-capbar {
  height: 14px;
  border-radius: var(--radius-pill);
  background: var(--ecru-100);
  overflow: hidden;
  margin: var(--space-3) 0 var(--space-2);
  border: 1px solid var(--color-border);
}
.ops-capbar__fill {
  height: 100%;
  background: var(--nile-500);
  transition: width var(--dur, .3s) var(--ease, ease);
}
.ops-capbar__fill.is-full { background: var(--full-text); }

/* today roster */
.ops-roster { margin-top: var(--space-4); }
.ops-rosterrow {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--color-border);
}
.ops-rosterrow.is-in { opacity: 0.72; }
.ops-rosterrow__main { flex: 1 1 12rem; min-width: 0; }
.ops-rosterrow__name { font-size: var(--step-0); }
.ops-rosterrow__meta { margin: 2px 0 0; }
.ops-rosterrow__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}
/* Big touch targets on the phone-first check-in view */
.ops-rosterrow__actions .btn { min-height: 44px; }
.ops-pieceform {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

/* ---- Painters panel (booking-as-pool, tap to assign) ---------------- */
.ops-painters {
  flex-basis: 100%;
  margin-top: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius);
  background: var(--nile-100);
}
.ops-painters__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.ops-pool {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  min-height: 52px;
}
.ops-pool__label {
  font-weight: 700;
  font-size: var(--step--1);
  color: var(--ink);
  margin-right: var(--space-1);
}
/* Chips are big touch targets so assigning is quick on a phone at the stall. */
.ops-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 0 var(--space-4);
  border: 1.5px solid var(--nile-500);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  font-size: var(--step--1);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.ops-chip:hover { background: var(--nile-100); }
.ops-chip.is-selected {
  background: var(--nile-700);
  border-color: var(--nile-700);
  color: var(--white);
  transform: translateY(-1px);
}
.ops-chip--assigned {
  cursor: default;
  background: var(--ecru-100);
  border-color: var(--ecru-300);
}
.ops-chip__x {
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font-size: var(--step-1);
  line-height: 1;
  cursor: pointer;
  padding: 0 0 0 var(--space-1);
  min-width: 28px;
  min-height: 28px;
}
.ops-chip__x:hover { color: var(--full-text); }
.ops-painterlist {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-4);
}
.ops-painter {
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--surface);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.ops-painter.is-droppable {
  border-color: var(--nile-500);
  border-style: dashed;
  cursor: pointer;
  box-shadow: 0 0 0 2px var(--nile-200);
}
.ops-painter__top {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.ops-painter__name { flex: 1 1 8rem; min-width: 0; }
.ops-painter__email { flex: 2 1 12rem; min-width: 0; }
.ops-painter__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

/* walk-in + forms */
.ops-walkin {
  margin: var(--space-4) 0;
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  background: var(--nile-100);
}
.ops-walkin > summary {
  cursor: pointer;
  font-weight: 700;
  color: var(--nile-700);
}
.ops-walkin__body { margin-top: var(--space-3); }
.ops-walkin__pay { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.ops-batchform,
.ops-closeform {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: flex-end;
  margin-top: var(--space-3);
}
.ops-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin: var(--space-3) 0;
}
.ops-toolbar .input { max-width: 16rem; }
.ops-statgrid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr));
  margin: var(--space-3) 0;
}

/* piece + batch status badges (reuse .status-badge base) */
.piece-painted { background: var(--nile-200); color: var(--nile-800); }
.piece-at_kiln { background: var(--low-bg); color: var(--low-text); }
.piece-ready { background: var(--ok-bg); color: var(--ok-text); }
.piece-collected { background: var(--ecru-100); color: var(--ink-faint); }
.piece-created { background: var(--nile-100); color: var(--nile-800); }
.piece-lost, .piece-binned { background: var(--full-bg); color: var(--full-text); }

/* ------------------------------------------------------------------ *
   KILN & STOCK — the cross-date board
 * ------------------------------------------------------------------ */
.kiln-jump {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-4) 0 var(--space-5);
  position: sticky;
  top: var(--dev-banner-h, 0px);
  z-index: 10;
  padding: var(--space-2) 0;
  background: var(--paper);
}
.kiln-jump__link {
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--nile-800);
  text-decoration: none;
  background: var(--surface);
}
.kiln-jump__link:hover { background: var(--nile-100); text-decoration: none; }
.kiln-sec-head { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.kiln-emails {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-2) 0 var(--space-3);
  padding: var(--space-3);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius);
  background: var(--nile-100);
}
.kiln-emails__label { font-weight: 700; font-size: var(--step--1); color: var(--nile-800); }
.kiln-dategroup { margin-top: var(--space-4); }
.kiln-dategroup__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
  padding-bottom: var(--space-2);
  border-bottom: 2px solid var(--color-border);
}
.kiln-takings {
  padding: 2px var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--ok-bg);
  color: var(--ok-text);
  font-size: var(--step--1);
  font-weight: 700;
}
.kiln-earlier {
  margin-top: var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  background: var(--paper-2);
}
.kiln-earlier__summary {
  cursor: pointer;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
  font-weight: 700;
  color: var(--ink);
}

/* ------------------------------------------------------------------ *
   TODAY — the stall cockpit (phone-first)
 * ------------------------------------------------------------------ */
/* Day header */
.ops-daycard__ctrl {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin-bottom: var(--space-3);
}
.ops-daycard__date { flex: 1 1 12rem; min-width: 0; }
.ops-daycard__title { font-size: var(--step-1); }
.ops-daycard__loc { margin-top: 2px; }
.ops-enquiry-badge {
  display: inline-block;
  margin-top: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--clay-100);
  color: var(--clay-600);
  font-weight: 700;
  font-size: var(--step--1);
  text-decoration: none;
}
.ops-enquiry-badge:hover { background: var(--clay-300); color: var(--white); text-decoration: none; }

/* Arrival-window chips (filter the roster to one window) */
.ops-slotchips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-4) 0 var(--space-2);
}
.ops-slotchip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 48px;
  padding: 0 var(--space-4);
  border: 1.5px solid var(--nile-500);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-weight: 700;
  font-size: var(--step--1);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.ops-slotchip:hover { background: var(--nile-100); }
.ops-slotchip.is-active {
  background: var(--nile-700);
  border-color: var(--nile-700);
  color: var(--white);
}
.ops-slotchip--past { border-style: dashed; opacity: 0.75; }
.ops-slotchip__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.4em;
  height: 1.4em;
  padding: 0 0.35em;
  border-radius: var(--radius-pill);
  background: var(--ecru-100);
  color: var(--ink-soft);
  font-size: var(--step--1);
  font-weight: 700;
}
.ops-slotchip.is-active .ops-slotchip__count { background: var(--nile-800); color: var(--white); }

/* Roster: fat not-yet-in rows; big tap targets */
.ops-tap { min-height: 48px; }
.ops-rosterrow--todo {
  cursor: pointer;
  min-height: 48px;
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-3);
  margin: 0 calc(-1 * var(--space-3));
  border-bottom: 1px solid var(--color-border);
  transition: background-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.ops-rosterrow--todo:hover { background: var(--nile-100); }
.ops-rosterrow--todo:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--nile-700);
}
/* Checked-in rows collapse to a thin line so the queue floats up above them */
.ops-rosterrow--in {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--step--1);
  color: var(--ink-soft);
}
.ops-rosterrow__tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5em;
  height: 1.5em;
  border-radius: var(--radius-pill);
  background: var(--ok-bg);
  color: var(--ok-text);
  font-weight: 700;
}
.ops-rosterrow__inname { color: var(--ink); }
.ops-rosterrow__inname .chip-note { color: var(--ink-faint); }
.ops-rosterrow__link {
  border: 0;
  background: transparent;
  color: var(--nile-700);
  font: inherit;
  font-weight: 700;
  font-size: var(--step--1);
  cursor: pointer;
  padding: var(--space-2) var(--space-2);
  min-height: 40px;
  border-radius: var(--radius-sm);
}
.ops-rosterrow__link:hover { background: var(--nile-100); }

/* Same-day waitlist nudge */
.ops-waitnudge {
  margin: var(--space-4) 0;
  border: 1px solid var(--low-text);
  border-radius: var(--radius);
  background: var(--low-bg);
  padding: var(--space-3) var(--space-4);
}
.ops-waitnudge__summary {
  cursor: pointer;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  color: var(--low-text);
}
.ops-waitnudge__summary .chip-note { color: var(--low-text); text-decoration: underline; }
.ops-waitnudge__body { margin-top: var(--space-3); display: grid; gap: var(--space-2); }
.ops-waitnudge__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

/* Today's pieces strip */
.ops-piecestrip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--paper-2);
}
.ops-piecestrip__count { font-weight: 700; color: var(--ink); }

/* Sticky bottom action bar (thumb zone). Floats above the phone nav. */
.ops-actionbar {
  position: sticky;
  bottom: var(--space-4);
  z-index: 40;
  margin-top: var(--space-6);
}
.ops-actionbar__inner {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-2);
  background: var(--surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-lg);
}
.ops-actionbar__btn { flex: 1 1 0; min-height: 52px; padding-left: var(--space-3); padding-right: var(--space-3); }
@media (max-width: 899.98px) {
  /* Clear the fixed phone nav (52px + safe area) so both stay tappable. */
  .ops-actionbar { bottom: calc(60px + env(safe-area-inset-bottom, 0)); }
  #panel-ops { padding-bottom: var(--space-6); }
}

/* Branded toasts (replace window.alert) */
.gnc-toasts {
  position: fixed;
  top: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  z-index: 1200;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: min(30rem, calc(100vw - 2 * var(--space-4)));
  pointer-events: none;
}
.gnc-toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--color-border-strong);
  border-left: 4px solid var(--nile-700);
  box-shadow: var(--shadow-lg);
  font-size: var(--step--1);
  animation: gnc-toast-in var(--dur, 0.25s) var(--ease, ease);
}
.gnc-toast--success { border-left-color: var(--ok-text); }
.gnc-toast--error { border-left-color: var(--full-text); }
.gnc-toast--info { border-left-color: var(--nile-700); }
.gnc-toast.is-leaving { opacity: 0; transition: opacity 0.2s var(--ease, ease); }
.gnc-toast__msg { flex: 1 1 auto; color: var(--ink); font-weight: 600; }
.gnc-toast__x {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font-size: var(--step-1);
  line-height: 1;
  cursor: pointer;
  min-width: 28px;
  min-height: 28px;
}
@keyframes gnc-toast-in {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .gnc-toast { animation: none; }
}

/* ---- Public collection status timeline ---- */
.collection-card { margin-bottom: var(--space-5); }
.collection-card__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
}
.timeline {
  list-style: none;
  margin: var(--space-5) 0 0;
  padding: 0;
}
.timeline__step {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: 0 0 var(--space-4) var(--space-1);
  position: relative;
}
.timeline__step:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 10px;
  top: 18px;
  bottom: 0;
  width: 2px;
  background: var(--color-border-strong);
}
.timeline__dot {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--color-border-strong);
  z-index: 1;
}
.timeline__step.is-done .timeline__dot { background: var(--nile-500); border-color: var(--nile-500); }
.timeline__step.is-current .timeline__dot { background: var(--clay-500); border-color: var(--clay-500); }
.timeline__text { display: flex; flex-direction: column; }
.timeline__label { font-weight: 700; color: var(--ink); }
.timeline__step.is-current .timeline__label { color: var(--clay-600); }
.timeline__note { font-size: var(--step--1); color: var(--ink-faint); }
.timeline__note:empty { display: none; }

/* ---- Printable claim tickets ---- */
.print-area { display: none; }
@media print {
  body > *:not(.print-area) { display: none !important; }
  .print-area { display: block !important; }
  /* Branded claim ticket, same visual family as the emails: ink on warm white
     with an Ecru header band and a Nile accent. All still legible in black and
     white (colour is decorative, never load-bearing). */
  .ticket {
    page-break-inside: avoid;
    page-break-after: always;
    border: 2px solid #2e2620;
    border-radius: 12px;
    padding: 0 0 14px;
    margin: 0 0 14px;
    font-family: Georgia, "Times New Roman", serif;
    color: #2e2620;
    background: #fffdf8;
    overflow: hidden;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .ticket__header {
    background: #c2ae93;
    border-bottom: 3px solid #2c6e72;
    padding: 12px 18px;
    margin-bottom: 12px;
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .ticket__logo { width: 36px; height: auto; flex: 0 0 auto; }
  .ticket__brand { font-weight: 700; font-size: 18px; letter-spacing: 0.3px; color: var(--brown-logo); }
  .ticket__tagline { font-family: -apple-system, "Segoe UI", Roboto, Arial, sans-serif; font-size: 11px; margin-top: 2px; }
  .ticket__claim { font-size: 32px; font-weight: 700; margin: 4px 0; padding: 0 18px; color: #2c6e72; }
  .ticket__code { font-size: 12px; letter-spacing: 0.08em; color: #555; margin-bottom: 8px; padding: 0 18px; }
  .ticket__row { font-size: 14px; margin: 2px 0; padding: 0 18px; }
  .ticket__slip {
    margin: 12px 18px 0;
    padding-top: 10px;
    border-top: 1px dashed #927b58;
  }
  .ticket__slip-title { font-weight: 700; font-size: 13px; margin-bottom: 4px; color: #2c6e72; }
  .ticket__slip p { font-size: 12px; margin: 0 0 4px; line-height: 1.4; }
  .ticket__footer {
    margin: 12px 18px 0;
    padding-top: 8px;
    border-top: 1px solid #e4d7c0;
    font-family: -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
    font-size: 10px;
    color: #68604f;
    line-height: 1.4;
  }
}

/* Branded strip on the QR pay screen (matches the email header + claim ticket). */
.qr-brand {
  text-align: center;
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  background: var(--ecru-200);
  border-bottom: 3px solid var(--nile-700);
  border-radius: var(--radius-sm);
}
.qr-brand__logo {
  display: block;
  width: 44px;
  height: auto;
  margin: 0 auto 4px;
}
.qr-brand__mark {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 700;
  font-size: var(--step-1);
  color: var(--brown-logo);
}
.qr-brand__tagline {
  font-size: var(--step--2);
  color: var(--ink-soft);
  margin-top: 2px;
}
.qr-brand__footer {
  margin-top: var(--space-3);
  font-size: var(--step--2);
  color: var(--ink-faint);
  text-align: center;
  line-height: 1.4;
}

/* ---- Footer mailing-list signup ---- */
/* Its own compact footer column ("Stay in the loop"), sitting in the MIDDLE on
   desktop (see the order rules above) so the footer reads brand / signup / get
   in touch, with all three columns close in height. The email input holds a
   usable width with the Sign up button stacked below. */
.footer-signup {
  text-align: left;
}
.footer-signup__form { max-width: 20rem; margin-inline: 0; }
.footer-signup__note { font-size: var(--step--1); margin: 0 0 var(--space-3); }
.footer-signup__row {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  justify-content: flex-start;
  margin-bottom: var(--space-2);
}
/* Input fills the column (~260-320px), the Sign up button sits below it. */
.footer-signup__row .input { flex: 1 1 100%; min-width: 0; }
.footer-signup__row .btn { flex: 0 0 auto; }
/* iOS zooms the viewport when a focused control's font is under 16px. The
   earlier mobile rule (max-width:820px) sets 16px, but the .input--sm base
   font-size is declared LATER in this file and would otherwise win inside the
   breakpoint, re-introducing the focus-zoom on the JS-injected footer signup
   input and the compact admin controls. Re-assert 16px here, after that base. */
@media (max-width: 820px) {
  input, select, textarea,
  .input, .select, .textarea,
  .input--sm, .select--sm, .qty-stepper__input,
  .footer-signup__row .input { font-size: 16px; }
}
.footer-signup__msg { font-size: var(--step--1); margin: var(--space-2) 0 0; }
.footer-signup__msg.is-ok { color: var(--ok-text); }
.footer-signup__msg.is-err { color: var(--full-text); }
.footer-signup__msg[hidden] { display: none; }

/* Progressive disclosure: the GDPR consent box stays collapsed until the
   visitor focuses or types in the email field, then glides open using the same
   grid-rows technique as the nav/FAQ (snap-free; respects reduced motion via
   the global reduced-motion rule that zeroes transition-duration). */
.footer-signup__consent {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.footer-signup__consent > div {
  overflow: hidden;
  min-height: 0;
  padding-top: var(--space-1);
}
.footer-signup__consent.is-open {
  grid-template-rows: 1fr;
  opacity: 1;
}

/* ==========================================================================
   BOOKING ITEM QUANTITY PICKER (one piece per painter, required at booking)
   ========================================================================== */
.qty-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
.qty-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  transition: border-color var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.qty-item.is-chosen {
  border-color: var(--nile-700);
  background: var(--nile-100);
  box-shadow: 0 0 0 2px var(--nile-200);
}
.qty-item__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.qty-item__label {
  font-weight: 600;
  color: var(--ink);
  font-size: var(--step-0);
}
.qty-item__price {
  color: var(--nile-800);
  font-weight: 700;
  font-size: var(--step--1);
}
.qty-stepper {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  flex-shrink: 0;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--surface);
}
.qty-stepper__btn {
  font: inherit;
  font-weight: 700;
  font-size: var(--step-1);
  line-height: 1;
  width: 42px;
  height: 42px;
  min-height: 42px;
  border: 0;
  background: var(--surface);
  color: var(--nile-800);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.qty-stepper__btn:hover { background: var(--nile-100); }
.qty-stepper__btn:disabled { opacity: 0.4; cursor: not-allowed; background: var(--surface); }
.qty-stepper__input {
  width: 2.8rem;
  height: 42px;
  border: 0;
  border-inline: 1.5px solid var(--color-border-strong);
  text-align: center;
  font: inherit;
  font-weight: 700;
  color: var(--ink);
  background: var(--surface);
  -moz-appearance: textfield;
  appearance: textfield;
}
.qty-stepper__input::-webkit-outer-spin-button,
.qty-stepper__input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.qty-stepper__input:focus-visible {
  outline: 3px solid var(--color-ring);
  outline-offset: -3px;
}
/* Live "X of N pieces chosen" tally under the picker */
.items-tally {
  font-size: var(--step--1);
  font-weight: 700;
  margin: 0;
}
.items-tally.is-ok { color: var(--ok-text); }
.items-tally.is-under { color: var(--ink-faint); }
.items-tally.is-over { color: var(--clay-600); }

/* ==========================================================================
   GIFT VOUCHER AMOUNT CHIPS (Gift a session page)
   ========================================================================== */
.gift-amounts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.gift-chip {
  font: inherit;
  font-weight: 700;
  font-size: var(--step-1);
  line-height: 1;
  min-width: 84px;
  padding: var(--space-3) var(--space-4);
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--nile-800);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.gift-chip:hover { background: var(--nile-100); }
.gift-chip.is-chosen {
  border-color: var(--nile-700);
  background: var(--nile-100);
  color: var(--nile-800);
  box-shadow: 0 0 0 2px var(--nile-200);
}
.gift-chip:focus-visible {
  outline: 3px solid var(--color-ring);
  outline-offset: 2px;
}
.gift-steps {
  margin: 0;
  padding-left: var(--space-5);
  color: var(--ink);
}
.gift-steps li { margin: 0 0 var(--space-3); line-height: 1.6; }
/* Part-used gift-voucher balance in the admin Vouchers table */
.voucher-partbal { color: var(--clay-600); font-weight: 700; }

/* ==========================================================================
   HOMEPAGE "FROM OUR INSTAGRAM" SHOWCASE
   3 tiles on desktop; a swipeable, scroll-snapping row on small screens.
   ========================================================================== */
/* Instagram showcase: a centred row when the posts fit, a swipeable, scroll-snap
   carousel (with prev/next arrows) when there are more than fit. */
.ig-carousel {
  position: relative;
  margin-top: var(--space-6);
}
.ig-grid {
  display: flex;
  gap: var(--space-4);
  list-style: none;
  padding: 4px; /* breathing room for the card shadow */
  margin: 0;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; /* the arrows drive it; hide the bar */
}
.ig-grid::-webkit-scrollbar {
  display: none;
}
/* One post, or a couple that fit, sit centred rather than hard left. */
.ig-grid--centred {
  justify-content: center;
}
.ig-cell {
  flex: 0 0 360px;
  max-width: 85vw;
  scroll-snap-align: center;
  margin: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm);
}
.ig-cell iframe {
  display: block;
  width: 100%;
  /* Sized close to a single Instagram photo-post embed so there is no tall
     empty band of iframe left under the post. */
  height: 400px;
  border: 0;
  background: var(--surface);
}
.ig-carousel__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
}
.ig-carousel__arrow svg {
  width: 22px;
  height: 22px;
}
.ig-carousel__arrow--prev {
  left: -6px;
}
.ig-carousel__arrow--next {
  right: -6px;
}
.ig-carousel__arrow:hover {
  background: var(--ecru-100);
}
.ig-carousel__arrow:disabled {
  opacity: 0.4;
  cursor: default;
}
.ig-carousel__arrow[hidden] {
  display: none;
}
.ig-tile {
  display: flex;
  flex-direction: column;
  height: 100%;
  text-decoration: none;
  color: var(--ink);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.ig-tile:hover {
  color: var(--ink);
  transform: translateY(-4px);
  box-shadow: var(--shadow);
}
.ig-tile__art { display: block; }
.ig-tile__art svg {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  background: var(--ecru-100);
}
.ig-tile__cap {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--ink-soft);
}
.ig-tile__cap svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--clay-500);
}
@media (max-width: 640px) {
  .ig-grid {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 82%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    padding-bottom: var(--space-2);
  }
  .ig-cell { scroll-snap-align: center; }
}

/* ==========================================================================
   Admin upgrade: demo badges, payment chip menu, stock module, QR
   ========================================================================== */
.mr-2 { margin-right: var(--space-2); }
.mt-2 { margin-top: var(--space-2); }

/* Demo/test data markers */
.demo-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: var(--full-bg);
  color: var(--full-text);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  vertical-align: middle;
}
.is-demo-row { background: repeating-linear-gradient(45deg, transparent, transparent 8px, rgba(154, 61, 36, 0.04) 8px, rgba(154, 61, 36, 0.04) 16px); }

/* "includes demo data" indicator — a quiet pill on any card/total whose figure
   currently folds in demo rows, so nothing masquerades as real. */
.demo-note {
  display: inline-block;
  padding: 1px 8px;
  border-radius: var(--radius-pill);
  background: var(--full-bg);
  color: var(--full-text);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.demo-note--block {
  display: block;
  width: 100%;
  margin-top: var(--space-3);
}

/* Payment chip menu button */
.chip-menu-btn {
  border: 1px solid var(--color-border-strong);
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 0 8px;
  line-height: 1.6;
  cursor: pointer;
  color: var(--ink-soft);
  font-weight: 700;
}
.chip-menu-btn:hover { background: var(--nile-200); color: var(--nile-800); }

/* Stock module */
.stock-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-5);
  margin-top: var(--space-5);
}
/* Column layout with a consistent internal rhythm so the on-hand number, the
   detail lines, the traffic light and the adjust button are not crammed
   together. The light + button sit at the foot with a clear gap above. */
.stock-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-5);
}
.stock-card__head { display: flex; align-items: center; gap: var(--space-2); }
.stock-card__dot { width: 10px; height: 10px; border-radius: 50%; background: var(--ok-text); flex: 0 0 auto; }
.stock-card--low .stock-card__dot { background: var(--low-text); }
.stock-card--short .stock-card__dot { background: var(--full-text); }
.stock-card__hand { font-size: var(--step-3); font-weight: 700; line-height: 1.1; color: var(--ink); }
/* The stacked detail lines carry their own tight rhythm (the flex gap adds the
   rest) instead of clumping on a zero margin. */
.stock-card .chip-note { margin: 0; }
.stock-card__light {
  align-self: flex-start;
  margin-top: var(--space-1);
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-size: 0.72rem;
  font-weight: 700;
}
.stock-card .btn { align-self: flex-start; margin-top: var(--space-2); }
.stock-light--ok { background: var(--ok-bg); color: var(--ok-text); }
.stock-light--low { background: var(--low-bg); color: var(--low-text); }
.stock-light--short { background: var(--full-bg); color: var(--full-text); }
/* Negative on-hand: a data problem (counts don't add up), flagged plainly. */
.stock-card__hand--warn { color: var(--full-text); }
.stock-card__warn {
  align-self: flex-start;
  margin-top: var(--space-1);
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-size: 0.72rem;
  font-weight: 700;
  background: var(--full-bg);
  color: var(--full-text);
}

.stock-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-4);
  margin-bottom: var(--space-4);
  align-items: end;
}
/* Pin each control to the bottom of its cell so a label that wraps to two lines
   (e.g. "Delivery share (£)") does not push its input out of line with the rest
   of the row. */
.stock-form .field {
  grid-template-rows: 1fr auto;
  height: 100%;
}
.stock-needs { margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--color-border); }
/* Collapsible per-date needs (U-1): each date is a <details> so all-covered
   dates fold away and only the ones with an order to place stay open. */
.stock-needs__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  cursor: pointer;
  list-style: none;
  padding: var(--space-2) 0;
  font-weight: 600;
}
.stock-needs__summary::-webkit-details-marker { display: none; }
.stock-needs__summary::before {
  content: "\25B8"; /* right-pointing triangle */
  display: inline-block;
  margin-right: var(--space-2);
  transition: transform var(--dur-fast, 150ms) var(--ease, ease);
  color: var(--color-ink-faint, #888);
}
.stock-needs[open] > .stock-needs__summary::before { transform: rotate(90deg); }
.stock-needs__date { flex: 1 1 auto; }
.stock-needs__tag {
  flex: 0 0 auto;
  font-size: 0.8rem;
  font-weight: 600;
  padding: 2px 10px;
  border-radius: 999px;
}
.stock-needs__tag.is-ok { background: var(--color-success-soft, #e6f4ea); color: var(--color-success, #2e7d4f); }
.stock-needs__tag.is-action { background: var(--color-warn-soft, #fdeede); color: var(--color-warn, #b25b00); }

/* Consistent air between the last field of a form and its submit button, so a
   primary action is never pressed tight against the input above it. Carried by
   any button appended straight after a form/grid block (e.g. the stock "Log
   delivery" button, which sits outside the grid). */
.form-submit { margin-top: var(--space-4); }
.stock-need {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 6px 0;
}
.stock-need__light {
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 0.68rem;
  font-weight: 700;
}
.stock-need__msg { flex: 1 1 auto; min-width: 200px; color: var(--ink); }

/* QR payment holder */
.qr-holder { text-align: center; }
.qr-holder svg { max-width: 300px; width: 100%; height: auto; }
/* The Stripe URL under a QR: wrap long URLs, small print. (Moved off inline
   style attributes so the admin CSP style-src 'self' isn't violated — L-1.) */
.qr-url { word-break: break-all; font-size: 0.8rem; }
/* A wrapping button row (e.g. the Testing panel action grid). */
.btn-row--wrap { flex-wrap: wrap; gap: 8px; }

/* ------------------------------------------------------------------ *
   ADMIN — neutral brand loading / auth-check state
   Shown before the sign-in check resolves, so an authenticated owner
   never sees a flash of the password form.
 * ------------------------------------------------------------------ */
.admin-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: 55vh;
  text-align: center;
}
.admin-loading__mark {
  width: 64px;
  height: auto;
  filter: drop-shadow(0 4px 10px rgba(74, 56, 34, 0.12));
}
.admin-loading__word {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-2);
  color: var(--brown-logo); /* echoes the lockup: mark above, brown lettering */
  letter-spacing: 0.01em;
}
.admin-loading__word .brand__amp { color: var(--clay-500); }
/* Official wordmark image (brown on light), matching the header's brand
   lockup: mark above, wordmark below. */
.admin-loading__wordmark {
  width: min(222px, 70vw);
  height: auto;
}
.admin-loading__spinner {
  width: 26px;
  height: 26px;
  border: 3px solid var(--ecru-200);
  border-right-color: var(--nile-700);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  margin-top: var(--space-2);
}
.admin-loading__note { margin: 0; }
#auth-error-view.admin-loading { min-height: 40vh; }
#auth-error-view .login-card { margin: 0 auto; }

/* ------------------------------------------------------------------ *
   ADMIN — GUIDE tab (handbook + tasks checklist)
 * ------------------------------------------------------------------ */
.guide-intro { margin-bottom: var(--space-5); }
.guide-intro h2 { margin-bottom: var(--space-2); }
.guide-intro__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.guide-intro__head h2 { margin-bottom: 0; }
.guide-intro__logo { width: 44px; height: auto; flex: 0 0 auto; }

/* Two-column layout: sticky side-nav + content on desktop; stacked on mobile. */
.guide-layout {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}
.guide-nav {
  position: sticky;
  top: calc(68px + var(--dev-banner-h, 0px));
  align-self: start;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--space-3);
  box-shadow: var(--shadow-sm);
}
.guide-nav__title {
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--ink-soft);
  margin: var(--space-2) var(--space-2) var(--space-3);
}
.guide-nav__list { list-style: none; margin: 0; padding: 0; }
.guide-nav__list li { margin: 0; }
.guide-nav__link {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--ink);
  text-decoration: none;
  font-weight: 600;
  font-size: var(--step--1);
  border-left: 3px solid transparent;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.guide-nav__link:hover { background: var(--ecru-100); color: var(--nile-800); }
.guide-nav__link.is-active {
  background: var(--nile-100);
  color: var(--nile-800);
  border-left-color: var(--nile-700);
}

.guide-content { min-width: 0; }
.guide-section {
  scroll-margin-top: calc(80px + var(--dev-banner-h, 0px));
  padding-bottom: var(--space-5);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--color-border);
}
.guide-section:last-child { border-bottom: 0; }
.guide-section > h3 {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--nile-800);
  margin-bottom: var(--space-2);
}
.guide-section p { margin: 0 0 var(--space-3); }
.guide-section ul { margin: 0 0 var(--space-3) var(--space-5); }
.guide-section li { margin-bottom: var(--space-1); }
.guide-lead {
  color: var(--ink-soft);
  font-size: var(--step-0);
}
/* Inline "Open <tab>" cross-links rendered as small buttons. */
.guide-jump {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 10px;
  margin: 0 2px;
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--nile-800);
  background: var(--nile-100);
  border: 1px solid var(--nile-300);
  border-radius: var(--radius-pill);
  cursor: pointer;
}
.guide-jump:hover { background: var(--nile-200); }
.guide-callout {
  background: var(--paper-2);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--clay-500);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  margin: 0 0 var(--space-3);
}
.guide-callout p:last-child { margin-bottom: 0; }
/* The one-time "How this admin works" preamble. */
.guide-preamble {
  border-left-color: var(--nile-500);
  margin-bottom: var(--space-6);
}
.guide-preamble h3 { margin-bottom: var(--space-2); }
/* Scannable sub-headings inside a section (What it's for / Everyday / …). */
.guide-block__label {
  margin: var(--space-4) 0 var(--space-1);
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--nile-700);
}
.guide-section .guide-block__label:first-of-type { margin-top: var(--space-2); }

/* Tasks checklist */
.guide-tasks { margin-bottom: var(--space-6); }
.guide-tasks__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.guide-tasks__count { font-size: var(--step--1); color: var(--ink-soft); font-weight: 700; }
.guide-task-list { list-style: none; margin: 0 0 var(--space-4); padding: 0; }
.guide-task {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--surface);
  margin-bottom: var(--space-2);
}
.guide-task__check {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  cursor: pointer;
}
.guide-task__text {
  flex: 1 1 auto;
  min-width: 0;
  line-height: 1.35;
  color: var(--ink);
  word-break: break-word;
}
.guide-task.is-done { background: var(--ok-bg); border-color: #bcd8c4; }
.guide-task.is-done .guide-task__text {
  text-decoration: line-through;
  color: var(--ink-faint);
}
.guide-task__del {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  font-size: 1.2rem;
  line-height: 1;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
}
.guide-task__del:hover { background: var(--full-bg); color: var(--full-text); }
.guide-taskadd {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.guide-taskadd .input { flex: 1 1 260px; }

@media (max-width: 820px) {
  /* minmax(0,1fr) lets the single column shrink below the nowrap jump-list's
     min-content width, so the horizontal chip row scrolls inside the nav
     instead of forcing the page wide. */
  .guide-layout { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
  .guide-nav { position: static; min-width: 0; max-width: 100%; }
  /* Mobile jump-list: horizontal, scrollable chip row. */
  .guide-nav__list {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--space-2);
    overflow-x: auto;
    padding-bottom: var(--space-2);
    -webkit-overflow-scrolling: touch;
  }
  .guide-nav__list li { flex: 0 0 auto; }
  .guide-nav__link {
    border: 1px solid var(--color-border);
    border-left: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    white-space: nowrap;
  }
  .guide-nav__link.is-active { border-color: var(--nile-500); border-left-color: var(--nile-500); }
}

/* ------------------------------------------------------------------ *
   DOCUMENTS — the admin "Documents" section (a library index of the
   business docs) and the standalone readable pages under /admin/docs/.
   Both live behind the /admin* Cloudflare Access policy.
 * ------------------------------------------------------------------ */

/* The index list shown inside the admin Documents zones. */
.doc-index { list-style: none; margin: 0; padding: 0; }
.doc-index > li + li { margin-top: var(--space-4); }
.doc-index__link {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  text-decoration: none;
  color: var(--nile-800);
}
.doc-index__link:hover { text-decoration: underline; }
.doc-index__desc { margin: var(--space-1) 0 0; color: var(--ink-soft); }

/* The standalone reading page. Comfortable measure, clean toolbar. */
.doc-page { padding-block: var(--space-6); }
.doc-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-6);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-border);
}
.doc-back, .doc-download {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-weight: 700;
  text-decoration: none;
  color: var(--nile-800);
}
.doc-back:hover, .doc-download:hover { text-decoration: underline; }
.doc-loading, .doc-error { color: var(--ink-soft); }

/* Rendered Markdown. Elements are plain semantic tags from the renderer. */
.doc-content { color: var(--ink); line-height: 1.65; }
.doc-content h1 { font-size: 1.9rem; line-height: 1.2; margin: 0 0 var(--space-4); }
.doc-content h2 {
  font-size: 1.4rem;
  margin: var(--space-7) 0 var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}
.doc-content h3 { font-size: 1.15rem; margin: var(--space-6) 0 var(--space-2); }
.doc-content h4, .doc-content h5, .doc-content h6 { font-size: 1rem; margin: var(--space-5) 0 var(--space-2); }
.doc-content p { margin: 0 0 var(--space-4); }
.doc-content ul, .doc-content ol { margin: 0 0 var(--space-4); padding-left: var(--space-6); }
.doc-content li { margin-bottom: var(--space-2); }
.doc-content li > ul, .doc-content li > ol { margin: var(--space-2) 0 0; }
.doc-content a { color: var(--nile-800); }
.doc-content code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--paper-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0.1em 0.35em;
}
.doc-pre {
  background: var(--paper-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-4);
  overflow-x: auto;
  margin: 0 0 var(--space-4);
}
.doc-pre code { background: none; border: 0; padding: 0; font-size: 0.85rem; line-height: 1.5; }
.doc-quote {
  margin: 0 0 var(--space-4);
  padding: var(--space-2) var(--space-4);
  border-left: 4px solid var(--nile-300);
  background: var(--paper-2);
  color: var(--ink-soft);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.doc-quote > :last-child { margin-bottom: 0; }
.doc-tablewrap { overflow-x: auto; margin: 0 0 var(--space-4); -webkit-overflow-scrolling: touch; }
.doc-table { border-collapse: collapse; width: 100%; font-size: 0.9rem; }
.doc-table th, .doc-table td {
  border: 1px solid var(--color-border);
  padding: var(--space-2) var(--space-3);
  text-align: left;
  vertical-align: top;
}
.doc-table th { background: var(--paper-2); font-family: var(--font-display); font-weight: 700; }
.doc-content hr { margin: var(--space-6) 0; }

@media print {
  .doc-toolbar { display: none; }
  .doc-page { padding: 0; }
  .doc-content { line-height: 1.5; }
  .doc-tablewrap { overflow: visible; }
  .doc-pre, .doc-quote, .doc-table th { background: transparent; }
}
