/*! wizparties.co.uk | Design and code (c) 2026 PageLaunch | https://pagelaunch.co.uk/
    Licensed to Wiz Parties for use on this domain only. Build PL-wizparties-co-uk-20260819-ad79.

    build.mjs inlines each @import below into a single stylesheet so there is no
    request waterfall. Anything in `utilities` beats anything in `components`
    regardless of specificity. */
@layer reset, tokens, base, layout, components, utilities;

@layer reset {
*, *::before, *::after { box-sizing: border-box; }

/* Any `display` declaration beats the [hidden] attribute's UA `display:none`,
   which silently breaks every JS-toggled element that also happens to be a
   grid or flex container. */
[hidden] { display: none !important; }

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

body, h1, h2, h3, h4, h5, p, figure, blockquote, dl, dd, ul, ol {
  margin: 0;
}

ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }

body { min-height: 100svh; text-rendering: optimizeSpeed; }

h1, h2, h3, h4, button, input, label { line-height: 1.2; }

img, picture, svg, video {
  max-width: 100%;
  display: block;
}

img { height: auto; }

input, button, textarea, select {
  font: inherit;
  color: inherit;
}

button { background: none; border: 0; }

a { color: inherit; }

table { border-collapse: collapse; }

:target { scroll-margin-block-start: calc(var(--header-h) + var(--space-5)); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
}

@layer tokens {
/* ============================================================================
   The only file in this project that may contain a colour, spacing, radius,
   font-size or duration literal. Everything else references these names.

   Seeds come from docs/direction.md: purple hue 290 read off the client's logo,
   magenta 345 from the capes in their photographs, gold 88 from the brief,
   neutral 50 from the warm candlelight in the party photos.
   ========================================================================= */

:root {
  color-scheme: light;

  /* --- seeds ------------------------------------------------------------ */
  --brand-h: 290;      /* purple, from the logo wordmark */
  --brand-c: 0.19;
  --brand2-h: 345;     /* magenta, from the capes. Carries the action */
  --brand2-c: 0.21;
  --brand3-h: 88;      /* gold. Detail only */
  --brand3-c: 0.13;
  --neutral-h: 50;     /* warm, from the photography's neutral field */
  --neutral-c: 0.008;

  /* --- purple ramp ------------------------------------------------------ */
  --purple-50:  oklch(0.971 calc(var(--brand-c) * 0.06) var(--brand-h));
  --purple-100: oklch(0.936 calc(var(--brand-c) * 0.13) var(--brand-h));
  --purple-200: oklch(0.885 calc(var(--brand-c) * 0.25) var(--brand-h));
  --purple-300: oklch(0.809 calc(var(--brand-c) * 0.43) var(--brand-h));
  --purple-400: oklch(0.707 calc(var(--brand-c) * 0.67) var(--brand-h));
  --purple-500: oklch(0.623 calc(var(--brand-c) * 0.87) var(--brand-h));
  --purple-600: oklch(0.546 calc(var(--brand-c) * 1.00) var(--brand-h));
  --purple-700: oklch(0.488 calc(var(--brand-c) * 0.99) var(--brand-h));
  --purple-800: oklch(0.424 calc(var(--brand-c) * 0.85) var(--brand-h));
  --purple-900: oklch(0.379 calc(var(--brand-c) * 0.60) var(--brand-h));
  --purple-950: oklch(0.282 calc(var(--brand-c) * 0.37) var(--brand-h));

  /* --- magenta ramp ----------------------------------------------------- */
  --pink-50:  oklch(0.971 calc(var(--brand2-c) * 0.06) var(--brand2-h));
  --pink-100: oklch(0.936 calc(var(--brand2-c) * 0.13) var(--brand2-h));
  --pink-200: oklch(0.885 calc(var(--brand2-c) * 0.25) var(--brand2-h));
  --pink-300: oklch(0.809 calc(var(--brand2-c) * 0.43) var(--brand2-h));
  --pink-400: oklch(0.707 calc(var(--brand2-c) * 0.67) var(--brand2-h));
  --pink-500: oklch(0.623 calc(var(--brand2-c) * 0.87) var(--brand2-h));
  --pink-600: oklch(0.546 calc(var(--brand2-c) * 1.00) var(--brand2-h));
  --pink-700: oklch(0.488 calc(var(--brand2-c) * 0.99) var(--brand2-h));
  --pink-800: oklch(0.424 calc(var(--brand2-c) * 0.85) var(--brand2-h));
  --pink-900: oklch(0.379 calc(var(--brand2-c) * 0.60) var(--brand2-h));

  /* --- gold ramp -------------------------------------------------------- */
  --gold-100: oklch(0.936 calc(var(--brand3-c) * 0.13) var(--brand3-h));
  --gold-200: oklch(0.885 calc(var(--brand3-c) * 0.25) var(--brand3-h));
  --gold-300: oklch(0.809 calc(var(--brand3-c) * 0.43) var(--brand3-h));
  --gold-400: oklch(0.707 calc(var(--brand3-c) * 0.67) var(--brand3-h));
  --gold-700: oklch(0.488 calc(var(--brand3-c) * 0.99) var(--brand3-h));
  --gold-800: oklch(0.424 calc(var(--brand3-c) * 0.85) var(--brand3-h));

  /* --- warm neutral ramp ------------------------------------------------ */
  --n-25:  oklch(0.995 calc(var(--neutral-c) * 0.5) var(--neutral-h));
  --n-50:  oklch(0.985 var(--neutral-c) var(--neutral-h));
  --n-100: oklch(0.968 var(--neutral-c) var(--neutral-h));
  --n-200: oklch(0.938 var(--neutral-c) var(--neutral-h));
  --n-300: oklch(0.890 var(--neutral-c) var(--neutral-h));
  --n-400: oklch(0.760 var(--neutral-c) var(--neutral-h));
  --n-500: oklch(0.620 var(--neutral-c) var(--neutral-h));
  --n-600: oklch(0.480 var(--neutral-c) var(--neutral-h));
  --n-700: oklch(0.395 var(--neutral-c) var(--neutral-h));
  --n-800: oklch(0.305 var(--neutral-c) var(--neutral-h));
  --n-900: oklch(0.235 calc(var(--neutral-c) * 1.5) var(--neutral-h));

  /* --- semantics. Components use only these ----------------------------- */
  --canvas:        var(--n-50);
  --sunken:        var(--n-100);
  --surface:       var(--n-25);
  --text:          var(--n-900);
  --text-muted:    var(--n-600);
  --border:        var(--n-200);
  --border-strong: var(--n-300);
  --border-control: var(--n-500);

  /* action: magenta 700. The only element wearing this shade is an action. */
  --accent:          var(--pink-700);
  --accent-hover:    var(--pink-800);
  --accent-active:   var(--pink-900);
  --accent-contrast: var(--n-25);
  --accent-wash:     var(--pink-50);

  /* grounds: this is the layer that puts colour on the page */
  --ground-deep:    var(--purple-950);
  --on-ground-deep: var(--n-50);
  --ground-mid:     var(--purple-900);
  --ground-tint:    var(--purple-50);
  --ground-tint-2:  var(--pink-50);
  --ground-gold:    var(--gold-100);
  --panel:          color-mix(in oklch, var(--purple-500) 7%, var(--surface));
  --panel-border:   color-mix(in oklch, var(--purple-500) 18%, var(--surface));

  /* detail colour on the deep ground */
  --detail:         var(--gold-300);
  --detail-ink:     var(--gold-800);
  --rule-on-deep:   oklch(0.809 calc(var(--brand3-c) * 0.43) var(--brand3-h) / 0.28);

  --focus:   var(--pink-700);
  --danger:  oklch(0.478 0.180 27);
  --danger-wash: oklch(0.968 0.017 27);
  --success: oklch(0.480 0.130 150);
  --success-wash: oklch(0.968 0.021 150);

  /* --- type ------------------------------------------------------------- */
  --font-display: "Familjen Grotesk", "Familjen Fallback", system-ui, sans-serif;
  --font-text: "Figtree", "Figtree Fallback", system-ui, -apple-system, "Segoe UI", sans-serif;

  --step--2: clamp(0.75rem,   0.7255rem + 0.1087vw, 0.8125rem);
  --step--1: clamp(0.875rem,  0.8505rem + 0.1087vw, 0.9375rem);
  --step-0:  clamp(1.0625rem, 1.0136rem + 0.2174vw, 1.1875rem);
  --step-1:  clamp(1.1875rem, 1.1141rem + 0.3261vw, 1.375rem);
  --step-2:  clamp(1.3125rem, 1.1413rem + 0.7609vw, 1.75rem);
  --step-3:  clamp(1.625rem,  1.2826rem + 1.5217vw, 2.5rem);
  --step-4:  clamp(2rem,      1.2174rem + 3.4783vw, 4rem);
  --step-5:  clamp(2.375rem,  1.4076rem + 4.2989vw, 4.75rem);

  /* --- space ------------------------------------------------------------ */
  --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;
  --space-10: 8rem;

  /* --- shape, depth, motion --------------------------------------------- */
  --radius-sm: 6px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --radius-full: 999px;

  --shadow-sm: 0 1px 2px oklch(0.30 0.05 var(--brand-h) / 0.07),
               0 2px 6px  oklch(0.30 0.05 var(--brand-h) / 0.06);
  --shadow-md: 0 2px 4px  oklch(0.30 0.05 var(--brand-h) / 0.07),
               0 10px 26px oklch(0.30 0.05 var(--brand-h) / 0.10);
  --shadow-lg: 0 4px 8px  oklch(0.30 0.05 var(--brand-h) / 0.08),
               0 22px 52px oklch(0.30 0.05 var(--brand-h) / 0.14);

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 130ms;
  --dur-base: 220ms;
  --dur-slow: 420ms;
  --dur-entrance: 620ms;

  /* --- layout ----------------------------------------------------------- */
  --measure: 66ch;
  --container: 74rem;
  --container-narrow: 46rem;
  --gutter: clamp(1.125rem, 4.5vw, 2.5rem);
  --header-h: 5.75rem;
  --callbar-h: 3.75rem;

  /* --- print only -------------------------------------------------------
     Pure black on pure white is correct for ink and is the single deliberate
     exception to the off-black / off-white rule. Used by print.css alone. */
  --print-ink: #000;
  --print-paper: #fff;
  --print-rule: #999;

  /* --- z scale ---------------------------------------------------------- */
  --z-sticky: 100;
  --z-header: 200;
  --z-menu: 300;
  --z-dialog: 400;
}

@media (width >= 48rem) {
  :root { --header-h: 6.5rem; --callbar-h: 0rem; }
}

/* Richer brand colour on wide-gamut displays. Hue is preserved. */
@media (color-gamut: p3) {
  :root { --brand-c: 0.24; --brand2-c: 0.26; }
}
}

@layer base {
/* --- self-hosted faces. No third-party font origin is ever contacted. ----- */
@font-face {
  font-family: "Familjen Grotesk";
  src: url("/assets/fonts/familjengrotesk-var.woff2") format("woff2-variations");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Figtree";
  src: url("/assets/fonts/figtree-var.woff2") format("woff2-variations");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}

/* Metric-matched fallbacks so the swap does not shift layout. */
@font-face {
  font-family: "Familjen Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Roboto");
  size-adjust: 105.3%;
  ascent-override: 97.3%;
  descent-override: 21.4%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Figtree Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Roboto");
  size-adjust: 103.5%;
  ascent-override: 91.8%;
  descent-override: 24.2%;
  line-gap-override: 0%;
}

body {
  background: var(--canvas);
  color: var(--text);
  font-family: var(--font-text);
  font-size: 1.0625rem;
  font-weight: 400;
  line-height: 1.62;
  font-optical-sizing: auto;
  font-variant-ligatures: common-ligatures contextual;
  font-kerning: normal;
  overflow-x: hidden;
}

@media (width >= 48rem) {
  body { font-size: 1.125rem; }
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  text-wrap: balance;
  color: var(--text);
}

h1 { font-size: var(--step-4); line-height: 1.06; letter-spacing: -0.026em; max-width: 17ch; }
h2 { font-size: var(--step-3); line-height: 1.14; letter-spacing: -0.02em; max-width: 21ch; }
h3 { font-size: var(--step-2); line-height: 1.24; letter-spacing: -0.012em; max-width: 28ch; }
h4 { font-size: var(--step-1); line-height: 1.3;  letter-spacing: -0.008em; max-width: 34ch; }

p, li, dd { text-wrap: pretty; }

/* Long unbroken strings (emails, URLs) must not widen the page on a phone. */
body { overflow-wrap: break-word; }

/* <picture> is inline by default, so it shrinks to its content and refuses to
   fill an absolutely-positioned media box. Every responsive image on this site
   is wrapped in one, so this rule is what makes object-fit work at all. */
picture { display: block; }
.hero__media picture,
.pagehead__media picture,
.media picture,
.party__media picture,
.strip picture { display: block; width: 100%; height: 100%; }
.hero__media picture img,
.pagehead__media picture img,
.media picture img,
.party__media picture img { width: 100%; height: 100%; object-fit: cover; }

a { color: var(--accent); text-underline-offset: 0.18em; text-decoration-thickness: from-font; }
a:hover { color: var(--accent-hover); }

strong, b { font-weight: 650; }

/* Prices, phone numbers, dates and counts must align and must not jitter. */
.num, .price, time, .tel {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1;
}

/* Inputs must compute to at least 16px or iOS Safari zooms and never zooms back. */
input, select, textarea, button {
  font-family: inherit;
  font-size: max(16px, 1rem);
}

input, textarea, select { accent-color: var(--accent); }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

::selection { background: var(--pink-200); color: var(--n-900); }

.skip-link {
  position: absolute;
  inset-inline-start: var(--space-4);
  inset-block-start: -100%;
  z-index: var(--z-dialog);
  padding: var(--space-3) var(--space-5);
  background: var(--accent);
  color: var(--accent-contrast);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { inset-block-start: 0; color: var(--accent-contrast); }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

hr {
  border: 0;
  border-block-start: 1px solid var(--border);
  margin-block: var(--space-7);
}
}

@layer layout {
/* Breakpoints used throughout, kept in sync by hand because @custom-media
   is not implemented in any browser: sm 30rem / md 48rem / lg 64rem / xl 80rem. */

.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-width: var(--container-narrow); }

/* Colour goes on the full-width band, never on the inner container. */
.section {
  padding-block: clamp(3.5rem, 8vw, 6.5rem);
  position: relative;
}
.section--tight { padding-block: clamp(2.5rem, 5vw, 4rem); }
.section--loose { padding-block: clamp(4.5rem, 10vw, 8rem); }

.section--deep {
  background: var(--ground-deep);
  color: var(--on-ground-deep);
  --canvas: var(--ground-deep);
  --text: var(--on-ground-deep);
  --text-muted: oklch(0.800 0.020 var(--brand-h));
  --border: var(--rule-on-deep);
  --surface: oklch(0.330 calc(var(--brand-c) * 0.34) var(--brand-h));
  --panel: oklch(0.330 calc(var(--brand-c) * 0.34) var(--brand-h));
  --panel-border: var(--rule-on-deep);
  --accent-wash: oklch(0.340 calc(var(--brand2-c) * 0.30) var(--brand2-h));
}
.section--deep :is(h1, h2, h3, h4, p, li, dt, dd, figcaption) { color: inherit; }
.section--deep a:not(.btn) { color: var(--pink-300); }
.section--deep a:not(.btn):hover { color: var(--n-50); }

.section--tint   { background: var(--ground-tint); }
.section--tint-2 { background: var(--ground-tint-2); }
.section--sunken { background: var(--sunken); }

/* Rhythm helpers ---------------------------------------------------------- */
.stack > * + * { margin-block-start: var(--flow, var(--space-5)); }
.stack--tight { --flow: var(--space-3); }
.stack--loose { --flow: var(--space-6); }

.prose { max-width: var(--measure); }
.prose > * + * { margin-block-start: 1.25em; }
.prose > * + :is(h2) { margin-block-start: 2.25em; }
.prose > * + :is(h3) { margin-block-start: 1.75em; }
.prose > :is(h2, h3) + * { margin-block-start: 0.55em; }
.prose ul, .prose ol { padding-inline-start: 1.35em; }
.prose li + li { margin-block-start: 0.4em; }
.prose li::marker { color: var(--accent); }

.lead {
  font-size: var(--step-1);
  line-height: 1.48;
  max-width: 54ch;
  color: var(--text);
}

.muted { color: var(--text-muted); }

/* Default vertical rhythm inside a section. Without this, a plain block of
   h2 + p + p in a container renders with the headline welded to the first
   paragraph, which reads as a broken section rather than a quiet one. */
.section .container > * + *,
.section .container--narrow > * + * { margin-block-start: var(--space-4); }
.section .container > * + :is(h2, h3) { margin-block-start: var(--space-7); }
.section .container > :is(h2, h3) + * { margin-block-start: var(--space-4); }
.section .container > .grid,
.section .container > .split,
.section .container > .steps,
.section .container > .faq,
.section .container > .strip,
.section .container > ul.two-col-list,
.section .container > ul.arealinks { margin-block-start: var(--space-6); }

/* Section headers: headline stacked over body, never a floating corner note. */
.section-head { max-width: 40rem; margin-block-end: var(--space-7); }
.section-head > p { margin-block-start: var(--space-4); }

/* Grid and flex children default to min-width:auto, so they refuse to shrink
   below their content's minimum. A date input or a select then pushes the
   whole page sideways on a 320px screen. This is the fix. */
.grid > *,
.split > *,
.form > *,
.field > *,
.steps > *,
.offerings > *,
.booking__states > *,
.site-footer__top > * { min-width: 0; }

/* Generic responsive grid ------------------------------------------------- */
.grid { display: grid; gap: var(--space-5); }
.grid--2 { grid-template-columns: 1fr; }
.grid--3 { grid-template-columns: 1fr; }
.grid--4 { grid-template-columns: 1fr; }

@media (width >= 40rem) {
  .grid--2, .grid--3, .grid--4 { grid-template-columns: repeat(2, 1fr); }
}
@media (width >= 64rem) {
  .grid { gap: var(--space-6); }
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
  .grid--4 { grid-template-columns: repeat(4, 1fr); }
}

/* Split: text beside a picture. Capped at two consecutive uses by the
   layout-repetition rule in the direction contract. */
.split {
  display: grid;
  gap: var(--space-6);
  align-items: center;
}
@media (width >= 56rem) {
  .split { grid-template-columns: 1fr 1fr; gap: var(--space-8); }
  .split--wide-media { grid-template-columns: 1fr 1.35fr; }
  .split--wide-text  { grid-template-columns: 1.35fr 1fr; }
  .split--flip > :first-child { order: 2; }
}

.media {
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--panel);
}
.media img { width: 100%; height: 100%; object-fit: cover; }

.media--tall { aspect-ratio: 4 / 5; }
.media--card { aspect-ratio: 4 / 3; }
.media--wide { aspect-ratio: 16 / 9; }

/* Full-bleed band that escapes the container */
.bleed {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
}

.center { text-align: center; }
/* .flow-end lives in utilities.css: components that reset their own margin
   (.ticks, .contact-list) sit in a later layer and were silently killing it. */
}

@layer components {
/* --- buttons -------------------------------------------------------------
   One action shade for the whole site: --accent (magenta 700). Nothing that
   is not an action wears it. On the deep purple ground the button inverts
   rather than reusing the accent on its own family.
   ------------------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 3rem;
  padding: 0.8rem 1.6rem;
  border-radius: var(--radius-full);
  font-family: var(--font-text);
  font-size: 1.0625rem;
  font-weight: 650;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  border: 2px solid transparent;
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}

.btn--primary {
  background: var(--accent);
  color: var(--accent-contrast);
  box-shadow: var(--shadow-sm);
}
.btn--primary:hover { background: var(--accent-hover); color: var(--accent-contrast); box-shadow: var(--shadow-md); }
.btn--primary:active { background: var(--accent-active); transform: translateY(1px); box-shadow: var(--shadow-sm); }

.btn--ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--border-control);
}
.btn--ghost:hover { border-color: var(--text); color: var(--text); background: color-mix(in oklch, var(--text) 5%, transparent); }
.btn--ghost:active { transform: translateY(1px); }

/* On a purple ground the primary inverts to cream, per 06-colour.md 7.4, which
   is about not putting an accent on its own hue family. The hero keeps the
   magenta button: different family, 6.8:1 on cream text, and it is the single
   strongest thing on the page. */
.section--deep .btn--primary,
.site-footer .btn--primary {
  background: var(--n-50);
  color: var(--purple-900);
  box-shadow: none;
}
.section--deep .btn--primary:hover,
.site-footer .btn--primary:hover { background: var(--n-200); color: var(--purple-950); }

/* Ghost buttons sit on four different dark grounds. Without this they inherit
   --text and become near-invisible dark-on-dark. */
.section--deep .btn--ghost,
.hero .btn--ghost,
.pagehead .btn--ghost,
.site-footer .btn--ghost {
  color: var(--n-50);
  border-color: oklch(0.985 0.008 var(--neutral-h) / 0.55);
}
.section--deep .btn--ghost:hover,
.hero .btn--ghost:hover,
.pagehead .btn--ghost:hover,
.site-footer .btn--ghost:hover {
  color: var(--n-50);
  border-color: var(--n-50);
  background: oklch(0.985 0.008 var(--neutral-h) / 0.12);
}

.btn--block { width: 100%; }
.btn--lg { min-height: 3.75rem; padding: 1.05rem 2.25rem; font-size: 1.125rem; }
.btn--sm { min-height: 2.75rem; padding: 0.55rem 1.25rem; font-size: 1rem; }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

.btn__icon { width: 1.15em; height: 1.15em; flex: none; }

/* Buttons need air. A row that sits straight under a paragraph reads as
   cramped, so it always carries its own clearance unless something has
   deliberately overridden it. */
.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-block-start: var(--space-6);
}
:is(h1, h2, h3, h4) + .btn-row { margin-block-start: var(--space-5); }
.btn-row:first-child { margin-block-start: 0; }
.btn-row + .btn-row { margin-block-start: var(--space-4); }
@media (width < 30rem) {
  /* Full width in the thumb zone. Labels may wrap here rather than push the
     page sideways; the no-wrap rule that matters is the desktop one. */
  .btn-row .btn { width: 100%; white-space: normal; }
  .btn--lg { padding-inline: var(--space-4); }
}

/* --- small pieces of trust and metadata ---------------------------------- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.3rem 0.8rem;
  border-radius: var(--radius-full);
  background: var(--accent-wash);
  color: var(--accent-hover);
  font-size: var(--step--1);
  font-weight: 650;
  line-height: 1.4;
}
.section--deep .pill {
  background: oklch(0.985 0.008 var(--neutral-h) / 0.12);
  color: var(--detail);
}

.factline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-muted);
  list-style: none;
  padding: 0;
  margin: 0;
}
.factline li { display: flex; align-items: center; gap: var(--space-2); }
.factline svg { width: 1.1em; height: 1.1em; flex: none; color: var(--accent); }
.section--deep .factline svg { color: var(--detail); }

/* Price sits in the open, on the same line as the thing it prices. */
.price-line {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.price-line .price {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--accent);
}
.section--deep .price-line .price { color: var(--detail); }
.price-line .price-note { font-size: var(--step--1); color: var(--text-muted); }
}

@layer components {
/* ===========================================================================
   HEADER
   Sits transparent over the hero at the top of the page and settles into a
   solid bar with a gold hairline once you scroll. The logo is the largest
   thing in it, because on a site with no brand recognition yet the wordmark
   is doing the introducing.
   ======================================================================== */

.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  isolation: isolate;
  color: var(--n-50);
  background: transparent;
}

/* The settled state. Set by main.js once the page has scrolled, and used as
   the default on any page that does not open with a dark hero. */
/* The blur lives on a pseudo-element, NOT on .site-header itself. A
   backdrop-filter makes its element the containing block for any fixed
   descendant, which silently re-parented the mobile menu panel to the tiny
   <details> and let its content overflow the document to 492px. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0;
  background: color-mix(in oklch, var(--ground-deep) 92%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  box-shadow: 0 1px 0 var(--rule-on-deep), 0 10px 30px oklch(0.20 0.05 var(--brand-h) / 0.28);
  transition: opacity var(--dur-base) var(--ease-out);
  pointer-events: none;
}
.site-header[data-solid="true"]::before,
body:not(.has-hero) .site-header::before { opacity: 1; }
@supports not (backdrop-filter: blur(1px)) {
  .site-header::before { background: var(--ground-deep); }
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  min-height: var(--header-h);
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
  /* Breathing room above and below the mark, so it is not welded to the
     edges of the bar. */
  padding-block: var(--space-3);
}
@media (width >= 48rem) { .site-header__inner { padding-block: var(--space-4); } }

/* --- logo ----------------------------------------------------------------
   Large on purpose. 00-vitality.md sets a 40/56px floor; this is well above
   it because the mark carries its own wordmark and needs to be read, not
   just recognised.
   ------------------------------------------------------------------------ */
.brand {
  display: flex;
  align-items: center;
  flex: none;
  text-decoration: none;
  transition: transform var(--dur-base) var(--ease-out);
}
.brand:hover { transform: scale(1.03); }
.brand:active { transform: scale(0.99); }
.brand__mark { display: block; }
.brand img {
  height: 4.5rem;
  width: auto;
  filter: drop-shadow(0 2px 10px oklch(0.20 0.05 var(--brand-h) / 0.55));
}
@media (width >= 48rem) { .brand img { height: 5rem; } }
@media (width >= 64rem) { .brand img { height: 5.5rem; } }

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

@media (width >= 64rem) {
  .nav { display: block; }
  .nav__list {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .nav__item { position: relative; }

  .nav__link {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.6rem 0.2rem;
    color: var(--n-50);
    font-weight: 550;
    font-size: 1.0625rem;
    letter-spacing: -0.005em;
    text-decoration: none;
    white-space: nowrap;
  }
  /* A gold rule that grows from the centre. Cheaper and calmer than a
     background pill, and it reads as considered rather than templated. */
  .nav__link::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0.15rem;
    height: 2px;
    background: var(--gold-400);
    border-radius: 2px;
    scale: 0 1;
    transition: scale var(--dur-base) var(--ease-out);
  }
  .nav__link:hover::after,
  .nav__item:focus-within > .nav__link::after { scale: 1 1; }
  .nav__link[aria-current="page"],
  .nav__link[aria-current="true"] { color: var(--detail); }
  .nav__link[aria-current="page"]::after,
  .nav__link[aria-current="true"]::after { scale: 1 1; }

  .nav__item + .nav__item { margin-inline-start: var(--space-4); }

  .nav__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* WCAG 2.2 AA 2.5.8 wants 24x24 CSS px. Padding alone left this 22.4px
       wide, and the 4.5px gap to the Parties link is too small to claim the
       spacing exception. */
    min-width: 24px;
    min-height: 24px;
    padding: 0.6rem 0.3rem;
    color: var(--n-50);
    cursor: pointer;
    border-radius: var(--radius-sm);
  }
  .nav__toggle svg {
    width: 0.8rem; height: 0.8rem;
    transition: transform var(--dur-base) var(--ease-out);
  }

  .nav__menu {
    position: absolute;
    inset-block-start: calc(100% + 0.9rem);
    inset-inline-start: -1rem;
    min-width: 25rem;
    padding: var(--space-3);
    background: var(--surface);
    color: var(--n-900);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    opacity: 0;
    visibility: hidden;
    translate: 0 -8px;
    transition: opacity var(--dur-base) var(--ease-out),
                translate var(--dur-base) var(--ease-out),
                visibility var(--dur-base);
  }
  /* A pointer bridge, so the menu does not close in the gap under the link. */
  .nav__menu::before {
    content: "";
    position: absolute;
    inset-block-start: -0.9rem;
    inset-inline: 0;
    height: 0.9rem;
  }
  .nav__item:hover > .nav__menu,
  .nav__item:focus-within > .nav__menu,
  .nav__item[data-open="true"] > .nav__menu {
    opacity: 1;
    visibility: visible;
    translate: 0 0;
  }
  .nav__item:hover > .nav__toggle svg,
  .nav__item[data-open="true"] > .nav__toggle svg { transform: rotate(180deg); }

  .nav__menu ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
  .nav__menu a {
    display: grid;
    gap: 0.1rem;
    padding: 0.7rem 0.85rem;
    border-radius: var(--radius-md);
    color: var(--n-900);
    font-weight: 600;
    text-decoration: none;
    transition: background-color var(--dur-fast) var(--ease-out);
  }
  .nav__menu a:hover { background: var(--ground-tint); color: var(--purple-800); }
  .nav__menu a[aria-current="page"] { background: var(--accent-wash); color: var(--accent-hover); }
  .nav__menu .nav__menu-note {
    font-weight: 400;
    font-size: var(--step--1);
    color: var(--text-muted);
  }
  .nav__menu a:hover .nav__menu-note { color: var(--purple-700); }
  .nav__menu-foot {
    margin-block-start: var(--space-2);
    padding-block-start: var(--space-3);
    border-block-start: 1px solid var(--border);
  }
  .nav__menu-foot a { font-weight: 700; color: var(--accent); }
}

/* --- right-hand actions --------------------------------------------------- */
.header-actions { display: flex; align-items: center; gap: var(--space-4); }

.header-tel {
  display: none;
  align-items: center;
  gap: var(--space-3);
  color: var(--n-50);
  text-decoration: none;
}
.header-tel svg {
  width: 2.4rem; height: 2.4rem;
  padding: 0.6rem;
  flex: none;
  border-radius: var(--radius-full);
  border: 1px solid var(--rule-on-deep);
  color: var(--detail);
  transition: background-color var(--dur-fast) var(--ease-out);
}
.header-tel:hover svg { background: oklch(0.985 0.008 var(--neutral-h) / 0.12); }
.header-tel__label {
  display: block;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: oklch(0.800 0.020 var(--brand-h));
  line-height: 1.3;
}
.header-tel__num { display: block; font-weight: 700; font-size: 1.0625rem; line-height: 1.25; }
@media (width >= 48rem) { .header-tel { display: inline-flex; } }
@media (width >= 64rem) { .header-tel { margin-inline-end: var(--space-2); } }

.header-actions .btn--header { display: none; }
@media (width >= 64rem) { .header-actions .btn--header { display: inline-flex; } }

/* --- mobile menu ---------------------------------------------------------
   A <details>, so it works with JavaScript disabled.
   ------------------------------------------------------------------------ */
.menu { position: relative; }
@media (width >= 64rem) { .menu { display: none; } }

.menu > summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 3rem;
  padding: 0.55rem 1.1rem;
  border: 1px solid oklch(0.985 0.008 var(--neutral-h) / 0.45);
  border-radius: var(--radius-full);
  background: oklch(0.20 0.05 var(--brand-h) / 0.35);
  backdrop-filter: blur(8px);
  color: var(--n-50);
  font-weight: 650;
  font-size: 1rem;
  cursor: pointer;
  list-style: none;
}
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary::marker { content: ""; }
.menu__bars { width: 1.05rem; height: 0.85rem; position: relative; flex: none; }
.menu__bars span {
  position: absolute;
  inset-inline: 0;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.menu__bars span:nth-child(1) { inset-block-start: 0; }
.menu__bars span:nth-child(2) { inset-block-start: 50%; translate: 0 -50%; }
.menu__bars span:nth-child(3) { inset-block-end: 0; }
.menu[open] .menu__bars span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.menu[open] .menu__bars span:nth-child(2) { opacity: 0; }
.menu[open] .menu__bars span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* A closed <details> still lays its content out (content-visibility: hidden
   keeps the box), so the panel's rows inflate document scrollWidth. Taking it
   out of flow entirely when closed keeps the measurement honest. */
.menu:not([open]) .menu__panel { display: none; }

.menu__panel {
  position: fixed;
  inset-block-start: var(--header-h);
  inset-inline: 0;
  z-index: var(--z-menu);
  max-height: calc(100svh - var(--header-h));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-5) var(--gutter) calc(var(--space-8) + env(safe-area-inset-bottom));
  background: var(--ground-deep);
  border-block-start: 1px solid var(--rule-on-deep);
  box-shadow: 0 24px 60px oklch(0.15 0.04 var(--brand-h) / 0.5);
}

.menu__panel h2 {
  font-size: var(--step--1);
  font-family: var(--font-text);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--detail);
  margin-block: var(--space-6) var(--space-3);
  max-width: none;
}
.menu__panel h2:first-child { margin-block-start: 0; }

.menu__panel ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.menu__panel li { min-width: 0; }
.menu__panel a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  min-width: 0;
  padding: 0.9rem 0.85rem;
  border-radius: var(--radius-md);
  color: var(--n-50);
  font-weight: 600;
  font-size: 1.0625rem;
  text-decoration: none;
}
.menu__panel a:hover { background: oklch(0.985 0.008 var(--neutral-h) / 0.1); color: var(--n-50); }
.menu__panel a[aria-current="page"] { color: var(--detail); }
.menu__panel a > :first-child { min-width: 0; }
.menu__panel .menu__note {
  flex: none;
  white-space: nowrap;
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--detail);
}
.menu__panel .btn-row { margin-block-start: var(--space-6); }

/* --- sticky mobile call bar ---------------------------------------------- */
.callbar {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: var(--z-sticky);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--rule-on-deep);
  border-block-start: 1px solid var(--rule-on-deep);
  padding-block-end: env(safe-area-inset-bottom);
  box-shadow: 0 -8px 28px oklch(0.15 0.04 var(--brand-h) / 0.35);
}
.callbar a {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: var(--callbar-h);
  font-weight: 650;
  font-size: 1rem;
  text-decoration: none;
}
.callbar svg { width: 1.15rem; height: 1.15rem; }
.callbar__call { background: var(--ground-deep); color: var(--n-50); }
.callbar__book { background: var(--accent); color: var(--accent-contrast); }
.callbar__call:hover { color: var(--n-50); }
.callbar__book:hover { color: var(--accent-contrast); background: var(--accent-hover); }

/* Reserve the bar's height so it never covers the end of the footer. Pages
   that opt out (the booking page) render no bar, so they reserve nothing. */
body { padding-block-end: calc(var(--callbar-h) + env(safe-area-inset-bottom)); }
body.no-callbar { padding-block-end: 0; }

@media (width >= 48rem) {
  .callbar { display: none; }
  body { padding-block-end: 0; }
}
}

@layer components {
/* ===========================================================================
   HERO
   Same full-bleed treatment as every page header, on a generated 3168px
   photograph. The scrim is built from the brand aubergine rather than flat
   black so the room keeps its warmth, and it is heaviest on the side the
   headline sits on.
   ======================================================================== */
.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ground-deep);
  color: var(--n-50);
  --text: var(--n-50);
  --text-muted: oklch(0.855 0.020 var(--brand-h));
  --border: var(--rule-on-deep);
  --canvas: var(--ground-deep);
}

.hero__media { position: absolute; inset: 0; z-index: -2; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to right,
      oklch(0.19 0.055 var(--brand-h) / 0.90) 0%,
      oklch(0.19 0.055 var(--brand-h) / 0.80) 44%,
      oklch(0.20 0.060 var(--brand-h) / 0.52) 100%),
    linear-gradient(to top,
      oklch(0.17 0.050 var(--brand-h) / 0.66) 0%,
      oklch(0.19 0.055 var(--brand-h) / 0.24) 55%,
      oklch(0.20 0.060 var(--brand-h) / 0.44) 100%);
}
@media (width < 48rem) {
  .hero::after {
    background: linear-gradient(to top,
      oklch(0.17 0.050 var(--brand-h) / 0.92) 0%,
      oklch(0.19 0.055 var(--brand-h) / 0.84) 55%,
      oklch(0.20 0.060 var(--brand-h) / 0.76) 100%);
  }
}

.hero__inner {
  position: relative;
  max-width: var(--container);
  margin-inline: auto;
  padding: var(--space-7) var(--gutter) var(--space-8);
  min-height: 30rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
@media (width >= 48rem) { .hero__inner { min-height: 34rem; padding-block: var(--space-9); } }
@media (width >= 62rem) { .hero__inner { min-height: 40rem; } }

.hero__body { max-width: 36rem; }

.hero h1 {
  color: var(--n-50);
  font-size: var(--step-5);
  line-height: 1.0;
  letter-spacing: -0.032em;
  max-width: 13ch;
  margin-block-start: var(--space-4);
}
.hero h1 em { font-style: normal; color: var(--detail); }

.hero__lede {
  font-size: var(--step-0);
  line-height: 1.55;
  color: oklch(0.935 0.014 var(--neutral-h));
  max-width: 38ch;
  margin-block-start: var(--space-4);
}
@media (width >= 62rem) { .hero__lede { font-size: var(--step-1); margin-block-start: var(--space-5); } }

.hero .btn-row { margin-block-start: var(--space-5); }
@media (width >= 62rem) { .hero .btn-row { margin-block-start: var(--space-6); } }

/* Three stacked two-line facts eat most of a phone screen, so below 30rem
   they run inline and wrap like a sentence instead. */
.hero__facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin-block-start: var(--space-6);
  padding-block-start: var(--space-5);
  border-block-start: 1px solid var(--rule-on-deep);
  list-style: none;
  padding-inline: 0;
  font-size: var(--step--1);
  font-weight: 600;
  color: oklch(0.900 0.018 var(--brand-h));
}
.hero__facts li { display: flex; align-items: center; gap: var(--space-2); }
.hero__facts svg { width: 1.15em; height: 1.15em; color: var(--detail); flex: none; }
.hero__facts strong {
  font-weight: 700;
  font-family: var(--font-display);
  letter-spacing: -0.015em;
  color: var(--n-50);
}
@media (width >= 30rem) {
  .hero__facts { gap: var(--space-6); align-items: flex-start; }
  .hero__facts strong { display: block; font-size: var(--step-1); letter-spacing: -0.02em; }
}
.hero__facts strong .num { font: inherit; color: inherit; }

/* ===========================================================================
   PAGE HEADER
   Full-bleed generated photograph behind every inner page. The generated
   heroes are 3168px wide with a deliberately quiet left third, so unlike the
   client's ~1320px phone shots they hold up at full width without going soft.

   The scrim is two layers: a vertical gradient that anchors the bottom, and a
   horizontal one that darkens the side the text sits on. Both are built from
   the brand aubergine rather than flat black, so the photograph keeps its
   warmth instead of going grey.
   ======================================================================== */
.pagehead {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ground-deep);
  color: var(--n-50);
  --text: var(--n-50);
  --text-muted: oklch(0.855 0.020 var(--brand-h));
  --border: var(--rule-on-deep);
  --canvas: var(--ground-deep);
  border-block-end: 1px solid var(--rule-on-deep);
}

.pagehead__media { position: absolute; inset: 0; z-index: -2; }
.pagehead__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }

.pagehead::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to right,
      oklch(0.19 0.055 var(--brand-h) / 0.90) 0%,
      oklch(0.19 0.055 var(--brand-h) / 0.80) 44%,
      oklch(0.20 0.060 var(--brand-h) / 0.52) 100%),
    linear-gradient(to top,
      oklch(0.17 0.050 var(--brand-h) / 0.66) 0%,
      oklch(0.19 0.055 var(--brand-h) / 0.24) 55%,
      oklch(0.20 0.060 var(--brand-h) / 0.44) 100%);
}
/* On a phone the text sits over the whole width, so the scrim is even. */
@media (width < 48rem) {
  .pagehead::after {
    background: linear-gradient(to top,
      oklch(0.17 0.050 var(--brand-h) / 0.93) 0%,
      oklch(0.19 0.055 var(--brand-h) / 0.86) 55%,
      oklch(0.20 0.060 var(--brand-h) / 0.78) 100%);
  }
}

/* Pages with nothing worth photographing keep the plain aubergine ground. */
.pagehead--plain::after { display: none; }

.pagehead__inner {
  position: relative;
  max-width: var(--container);
  margin-inline: auto;
  padding: var(--space-7) var(--gutter);
  min-height: 18rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
@media (width >= 48rem) {
  .pagehead__inner { padding-block: var(--space-9); min-height: 24rem; }
}
@media (width >= 62rem) {
  .pagehead__inner > * { max-width: 46rem; }
}

.pagehead h1 { color: var(--n-50); margin-block-start: var(--space-4); }
.pagehead__lede {
  font-size: var(--step-1);
  line-height: 1.5;
  color: oklch(0.935 0.014 var(--neutral-h));
  max-width: 46ch;
  margin-block-start: var(--space-4);
}
.pagehead .factline { color: oklch(0.900 0.018 var(--brand-h)); margin-block-start: var(--space-6); }
.pagehead .factline svg { color: var(--detail); }
.pagehead .btn-row { margin-block-start: var(--space-7); }

/* Links on the aubergine ground must not inherit the magenta action colour,
   which is dark on dark and fails contrast. */
.pagehead a:not(.btn),
.hero a:not(.btn) { color: var(--pink-300); }
.pagehead a:not(.btn):hover,
.hero a:not(.btn):hover { color: var(--n-50); }

/* --- breadcrumb ----------------------------------------------------------- */
.crumbs { font-size: var(--step--1); color: oklch(0.850 0.020 var(--brand-h)); }
.crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.crumbs li::after { content: "/"; margin-inline-start: var(--space-2); opacity: 0.45; }
.crumbs li:last-child::after { content: ""; }
.crumbs a { color: inherit; text-decoration: underline; text-underline-offset: 0.2em; }
.crumbs a:hover { color: var(--n-50); }
.crumbs [aria-current="page"] { color: var(--detail); font-weight: 600; }
}

@layer components {
/* --- party card ----------------------------------------------------------
   The price sits in the open on the same line as the name, borrowed from the
   painted seaside attraction board (candidate 6 in docs/direction.md).
   ------------------------------------------------------------------------ */
.party {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.party:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--panel-border);
}
.party:has(a:focus-visible) { box-shadow: var(--shadow-md); }

.party__media { aspect-ratio: 4 / 3; background: var(--ground-tint); }
.party__media img { width: 100%; height: 100%; object-fit: cover; }

.party__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: var(--space-3);
  padding: var(--space-5);
}

.party__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}
.party__title { font-size: var(--step-2); margin: 0; max-width: none; }
.party__title a {
  color: var(--text);
  text-decoration: none;
}
/* Stretched link so the whole card is clickable without wrecking the
   accessible name of the heading. */
.party__title a::after { content: ""; position: absolute; inset: 0; }
.party:hover .party__title a { color: var(--accent-hover); }

.party__price {
  flex: none;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--accent);
  letter-spacing: -0.015em;
}

.party__desc { color: var(--text-muted); font-size: var(--step-0); margin: 0; }
.party__meta {
  margin-block-start: auto;
  padding-block-start: var(--space-3);
  border-block-start: 1px solid var(--border);
}

/* Alternate card grounds so a grid is never white on white. */
.grid .party:nth-child(3n+2) { background: var(--ground-tint); }
.grid .party:nth-child(3n+3) { background: var(--ground-tint-2); }

.section--deep .party {
  background: var(--panel);
  border-color: var(--rule-on-deep);
  color: var(--n-50);
}
.section--deep .grid .party:nth-child(3n+2),
.section--deep .grid .party:nth-child(3n+3) { background: var(--panel); }
.section--deep .party__title a { color: var(--n-50); }
.section--deep .party:hover .party__title a { color: var(--detail); }
.section--deep .party__price { color: var(--detail); }
.section--deep .party__desc { color: oklch(0.820 0.020 var(--brand-h)); }

/* --- jump bar ------------------------------------------------------------
   A long index page needs to tell you it is long. This sits under the page
   header, sticks below the site header as you scroll, and is a plain list of
   anchors so it works with JavaScript off.
   ------------------------------------------------------------------------ */
.jumpbar {
  position: sticky;
  inset-block-start: var(--header-h);
  z-index: var(--z-sticky);
  isolation: isolate;
  border-block-end: 1px solid var(--border);
}
/* Blur on a pseudo-element, for the same containing-block reason as the header. */
.jumpbar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: color-mix(in oklch, var(--canvas) 94%, transparent);
  backdrop-filter: blur(12px);
}
@supports not (backdrop-filter: blur(1px)) { .jumpbar::before { background: var(--canvas); } }

.jumpbar__inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  max-width: var(--container);
  margin-inline: auto;
  padding: var(--space-3) var(--gutter);
  overflow-x: auto;
  scrollbar-width: none;
}
.jumpbar__inner::-webkit-scrollbar { display: none; }

/* A fade on the trailing edge, so a phone shows there is more to swipe to. */
.jumpbar { position: sticky; }
.jumpbar::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  width: 2.5rem;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--canvas));
}
@media (width >= 56rem) { .jumpbar::after { display: none; } }

.jumpbar__label {
  flex: none;
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.jumpbar ul {
  display: flex;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.jumpbar a {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.55rem 1rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font-size: var(--step--1);
  font-weight: 650;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.jumpbar a span { font-weight: 500; color: var(--text-muted); }
.jumpbar a:hover {
  background: var(--accent-wash);
  border-color: var(--accent);
  color: var(--accent-hover);
}
.jumpbar a:hover span { color: var(--accent-hover); }

/* Anchor targets must clear both sticky bars. */
#children, #grown-ups, #groups, #included { scroll-margin-block-start: calc(var(--header-h) + 4.25rem); }

/* --- group heading, so the age split reads as a real division ------------ */
.group-eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-block-end: var(--space-4);
}
.group-eyebrow span {
  flex: none;
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--detail-ink);
}
.section--deep .group-eyebrow span { color: var(--detail); }
.group-eyebrow::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(to right, var(--gold-400), transparent);
}

/* --- spec list -----------------------------------------------------------
   Label left, value right, one row each, hairline between. Its own component
   rather than borrowing the booking widget's summary styles: that rule targets
   a dl INSIDE .booking__summary, so on a dl carrying the class itself it never
   applied at all, which is what broke this in the first place.

   Exactly one <div> per dt/dd pair. A div inside a dl is valid HTML and it is
   what makes the hairline run unbroken across both columns.
   ------------------------------------------------------------------------ */
.speclist { margin: 0; display: grid; }

.speclist > div {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: var(--space-2) var(--space-4);
  padding-block: var(--space-3);
  border-block-start: 1px solid var(--border);
}
.speclist > div:first-child { border-block-start: 0; padding-block-start: 0; }
.speclist > div:last-child { padding-block-end: 0; }

.speclist dt {
  margin: 0;
  min-width: 0;
  color: var(--text-muted);
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.4;
}
/* Every value gets the same treatment, whether the number is the whole value
   or sits inside a phrase. Mixing display-face numerals with body-face ones
   down the same column looked like a bug. */
.speclist dd,
.speclist dd .num {
  margin: 0;
  min-width: 0;
  text-align: end;
  font-family: var(--font-text);
  font-size: var(--step-0);
  font-weight: 700;
  line-height: 1.4;
  color: var(--text);
  font-variant-numeric: tabular-nums lining-nums;
}
.speclist + p { margin-block-start: var(--space-5); }

/* --- stat grid -----------------------------------------------------------
   Visual relief for a section whose argument is genuinely numeric, so it does
   not sit on the page as an unbroken wall of prose.
   ------------------------------------------------------------------------ */
.statgrid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}
.statgrid li {
  display: grid;
  align-content: start;
  gap: var(--space-2);
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--panel-border);
}
.statgrid b {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--accent);
}
.statgrid span { font-size: var(--step--1); color: var(--text-muted); }
.section--deep .statgrid li { background: var(--panel); border-color: var(--rule-on-deep); }
.section--deep .statgrid b { color: var(--detail); }

/* --- numbered steps, from the spell card the children follow ------------- */
.steps { counter-reset: step; display: grid; gap: var(--space-5); list-style: none; padding: 0; margin: 0; }
@media (width >= 56rem) { .steps { grid-template-columns: repeat(4, 1fr); gap: var(--space-6); } }

.step { counter-increment: step; position: relative; padding-block-start: var(--space-6); }
.step::before {
  content: counter(step);
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-full);
  background: var(--accent);
  color: var(--accent-contrast);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
  font-variant-numeric: lining-nums;
}
.section--deep .step::before { background: var(--detail); color: var(--purple-950); }
.step h3 { font-size: var(--step-1); margin-block-end: var(--space-2); }
.step p { color: var(--text-muted); margin: 0; }
.section--deep .step p { color: oklch(0.820 0.020 var(--brand-h)); }

/* --- panel: a tinted box for practicals, never white on white ------------ */
.panel {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
@media (width >= 48rem) { .panel { padding: var(--space-6); } }
.panel h3 { margin-block-end: var(--space-3); }

.panel--gold {
  background: var(--ground-gold);
  border-color: color-mix(in oklch, var(--gold-400) 40%, var(--ground-gold));
}

/* --- what's included list ------------------------------------------------ */
.ticks { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-3); }
.ticks li { display: grid; grid-template-columns: 1.4rem 1fr; gap: var(--space-3); align-items: start; }
.ticks svg { width: 1.4rem; height: 1.4rem; color: var(--accent); margin-block-start: 0.12em; }
.section--deep .ticks svg { color: var(--detail); }

/* --- gallery strip ------------------------------------------------------- */
.strip {
  min-width: 0;
  max-width: 100%;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: min(72%, 22rem);
  gap: var(--space-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-block-end: var(--space-3);
  scrollbar-width: thin;
}
.strip > * { scroll-snap-align: start; }
.strip figure { margin: 0; }
.strip img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius-md);
}
.strip figcaption {
  margin-block-start: var(--space-2);
  font-size: var(--step--1);
  color: var(--text-muted);
}
@media (width >= 64rem) {
  .strip {
    grid-auto-flow: row;
    grid-template-columns: repeat(3, 1fr);
    overflow: visible;
  }
}

/* --- quote --------------------------------------------------------------- */
.quote {
  margin: 0;
  padding: var(--space-6);
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-lg);
}
.quote p {
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1.3;
  letter-spacing: -0.015em;
  margin: 0;
}
.quote figcaption {
  margin-block-start: var(--space-4);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-muted);
}
}

@layer components {
.form { display: grid; gap: var(--space-5); }

.field { display: grid; gap: var(--space-2); }

.field label {
  font-weight: 650;
  font-size: var(--step-0);
  color: var(--text);
}
.field .hint {
  font-weight: 400;
  font-size: var(--step--1);
  color: var(--text-muted);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 3rem;
  padding: 0.7rem 0.9rem;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-control);
  border-radius: var(--radius-md);
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.field textarea { min-height: 8rem; resize: vertical; line-height: 1.55; }
.field select { appearance: base-select, auto; }

.field input::placeholder,
.field textarea::placeholder { color: var(--n-500); }

.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--n-600); }

.field :is(input, select, textarea):focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 1px;
  border-color: var(--focus);
}

.field:has(:user-invalid) label { color: var(--danger); }
.field :is(input, select, textarea):user-invalid {
  border-color: var(--danger);
  background: var(--danger-wash);
}
.field .error {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--danger);
  display: none;
}
.field:has(:user-invalid) .error { display: block; }

.field--half { grid-column: span 1; }
@media (width >= 40rem) {
  .form--two { grid-template-columns: 1fr 1fr; }
  .form--two .field--full { grid-column: 1 / -1; }
}

.form__footer {
  display: grid;
  gap: var(--space-4);
  grid-column: 1 / -1;
}
.form__note { font-size: var(--step--1); color: var(--text-muted); margin: 0; }

.form-status {
  margin: 0;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  font-weight: 600;
  display: none;
}
.form-status[data-state="error"] {
  display: block;
  background: var(--danger-wash);
  color: var(--danger);
  border: 1px solid color-mix(in oklch, var(--danger) 30%, var(--danger-wash));
}
.form-status[data-state="ok"] {
  display: block;
  background: var(--success-wash);
  color: var(--success);
  border: 1px solid color-mix(in oklch, var(--success) 30%, var(--success-wash));
}
.form-status[data-state="busy"] { display: block; background: var(--sunken); color: var(--text-muted); }

/* Honeypot. Off-screen, hidden from assistive tech, never focusable. */
.hp {
  position: absolute !important;
  inset-inline-start: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.cf-turnstile { min-height: 65px; }

/* --- contact cards -------------------------------------------------------- */
.contact-list { display: grid; gap: var(--space-4); list-style: none; padding: 0; margin: 0; }
.contact-list li {
  display: grid;
  grid-template-columns: 2.75rem 1fr;
  gap: var(--space-4);
  align-items: start;
}
.contact-list li > * { min-width: 0; }
/* An email address has no break opportunity, so on a 320px screen it will
   push the page sideways unless it is allowed to break anywhere. */
.contact-list .value { overflow-wrap: anywhere; }
.contact-list svg { width: 2.75rem; height: 2.75rem; padding: 0.6rem; border-radius: var(--radius-full); background: var(--accent-wash); color: var(--accent-hover); }
.section--deep .contact-list svg { background: oklch(0.985 0.008 var(--neutral-h) / 0.12); color: var(--detail); }
/* The label and the value are both spans, so without an explicit block they
   run straight into each other: "PHONE OR TEXT07835 501419". */
.contact-list li > div { display: grid; gap: 0.15rem; }
.contact-list dt, .contact-list .label {
  display: block;
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-muted);
  line-height: 1.4;
}
.contact-list .value { display: block; font-size: var(--step-1); font-weight: 650; line-height: 1.35; }
.contact-list .value a { text-decoration: none; }
.contact-list .value a:hover { text-decoration: underline; }
}

@layer components {
/* --- booking widget ------------------------------------------------------
   Progressive enhancement. The markup ships with the enquiry route visible
   and working; JavaScript replaces it with live availability only once the
   booking API answers. If the API is unreachable the enquiry route stays.
   ------------------------------------------------------------------------ */
.booking { display: grid; gap: var(--space-5); }

.booking__states > [hidden] { display: none !important; }

.booking__loading,
.booking__offline {
  padding: var(--space-5);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-lg);
  background: var(--panel);
}

.booking__skeleton { display: grid; gap: var(--space-3); }
.booking__skeleton span {
  display: block;
  height: 3rem;
  border-radius: var(--radius-md);
  background: linear-gradient(
    100deg,
    color-mix(in oklch, var(--purple-500) 10%, var(--surface)) 20%,
    color-mix(in oklch, var(--purple-500) 18%, var(--surface)) 40%,
    color-mix(in oklch, var(--purple-500) 10%, var(--surface)) 60%
  );
  background-size: 220% 100%;
}
@media (prefers-reduced-motion: no-preference) {
  .booking__skeleton span { animation: shimmer 1.6s var(--ease-out) infinite; }
  @keyframes shimmer { from { background-position: 180% 0; } to { background-position: -40% 0; } }
}

/* offerings ---------------------------------------------------------------- */
.offerings { display: grid; gap: var(--space-3); }
@media (width >= 48rem) { .offerings { grid-template-columns: repeat(2, 1fr); } }

.offering {
  position: relative;
  display: grid;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border: 2px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.offering:not(:has(input:checked)):hover { border-color: var(--panel-border); background: var(--ground-tint); }
.offering:has(input:checked) { border-color: var(--accent); background: var(--accent-wash); }
.offering:has(input:focus-visible) { outline: 3px solid var(--focus); outline-offset: 2px; }
.offering input { position: absolute; opacity: 0; pointer-events: none; }

.offering__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}
.offering__name { font-family: var(--font-display); font-weight: 700; font-size: var(--step-1); letter-spacing: -0.015em; }
.offering__price { font-weight: 700; color: var(--accent); flex: none; }
.offering__desc { font-size: var(--step--1); color: var(--text-muted); margin: 0; }

/* --- calendar -------------------------------------------------------------
   A month grid rather than a long list of dates. A parent thinks in "the
   Saturday after next", not in a scrolling column of every free slot.
   ------------------------------------------------------------------------ */
.cal { display: grid; gap: var(--space-6); align-items: start; }
/* Month on the left, times for the chosen day on the right, so the times are
   in view the moment you pick a date rather than below the fold. Stacks on a
   phone, where under the calendar is the only sensible place for them. */
@media (width >= 56rem) {
  .cal { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); gap: var(--space-8); }
}
.cal__main, .cal__times { min-width: 0; }
.cal__grid, .cal__nav, .cal__key { max-width: 34rem; }

.cal__prompt {
  margin: 0;
  padding: var(--space-5);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  color: var(--text-muted);
  font-size: var(--step--1);
}
.cal__prompt:empty { display: none; }
@media (width < 56rem) { .cal__prompt { display: none; } }

.cal__nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-block-end: var(--space-4);
}
.cal__month {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  letter-spacing: -0.02em;
}
.cal__arrow {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.cal__arrow svg { width: 1.15rem; height: 1.15rem; }
.cal__arrow:hover:not([disabled]) { background: var(--accent-wash); border-color: var(--accent); color: var(--accent-hover); }
.cal__arrow[disabled] { opacity: 0.35; cursor: not-allowed; }

.cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 0.4rem;
}
@media (width >= 40rem) { .cal__grid { gap: 0.6rem; } }

.cal__wd {
  display: grid;
  place-items: center;
  padding-block: var(--space-2);
  font-size: var(--step--2);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.cal__pad { aspect-ratio: 1; max-height: 4rem; }

.cal__day {
  position: relative;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 0.15rem;
  aspect-ratio: 1;
  min-height: 2.75rem;
  max-height: 4rem;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  cursor: default;
}
.cal__num { font-size: var(--step-0); font-weight: 600; line-height: 1; }

/* A day you can actually book. Everything else stays quiet so the free ones
   are the only thing your eye lands on. */
.cal__day.is-free {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text);
  cursor: pointer;
  font-weight: 700;
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.cal__day.is-free:not([aria-pressed="true"]):hover { background: var(--accent-wash); border-color: var(--accent); transform: translateY(-1px); }
.cal__day[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}
.cal__day[disabled] { opacity: 0.4; }
.cal__day.is-past { opacity: 0.22; }

.cal__dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}
.cal__day[aria-pressed="true"] .cal__dot { background: var(--accent-contrast); }

.cal__key {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-block-start: var(--space-4);
  font-size: var(--step--1);
  color: var(--text-muted);
}

.cal__times:empty { display: none; }
@media (width >= 56rem) {
  .cal__times:not(:empty) {
    position: sticky;
    inset-block-start: calc(var(--header-h) + var(--space-5));
  }
}
.cal__times-h {
  font-size: var(--step-2);
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  margin-block-end: var(--space-1);
  max-width: none;
}
.cal__times-sub {
  margin: 0 0 var(--space-4);
  font-size: var(--step--1);
  color: var(--text-muted);
}
.cal__timegrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
  gap: var(--space-3);
  max-width: 26rem;
}

.slot {
  position: relative;
  display: grid;
  gap: 0.15rem;
  place-items: center;
  padding: var(--space-3);
  min-height: 3.5rem;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.slot:not([disabled]):not([aria-pressed="true"]):hover { border-color: var(--accent); background: var(--accent-wash); }
.slot[aria-pressed="true"] { border-color: var(--accent); background: var(--accent); color: var(--accent-contrast); }
.slot[disabled] { opacity: 0.45; cursor: not-allowed; }
.slot__day { font-weight: 700; font-size: var(--step-0); font-variant-numeric: tabular-nums; }
.slot__left { font-size: var(--step--2); font-weight: 650; color: var(--accent); }
.slot[aria-pressed="true"] .slot__left { color: var(--accent-contrast); }

.booking__empty {
  padding: var(--space-6);
  text-align: center;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  color: var(--text-muted);
}
.booking__empty p { margin: 0 0 var(--space-4); }

.booking__summary {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-5);
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-lg);
}
.booking__summary dl { display: grid; grid-template-columns: auto 1fr; gap: var(--space-2) var(--space-4); margin: 0; }
.booking__summary dt { font-weight: 650; color: var(--text-muted); }
.booking__summary dd { margin: 0; text-align: end; font-weight: 650; }
.booking__total { border-block-start: 1px solid var(--panel-border); padding-block-start: var(--space-3); margin-block-start: var(--space-2); }
.booking__total dd { color: var(--accent); font-size: var(--step-1); font-family: var(--font-display); }
}

@layer components {
.site-footer {
  background: var(--ground-deep);
  color: var(--n-50);
  border-block-start: 4px solid var(--gold-400);
  --canvas: var(--ground-deep);
  --text-muted: oklch(0.815 0.020 var(--brand-h));
}

.site-footer__top {
  display: grid;
  gap: var(--space-7);
  padding-block: clamp(3rem, 7vw, 4.5rem);
}
@media (width >= 48rem) {
  .site-footer__top { grid-template-columns: 1.4fr 1fr 1fr; gap: var(--space-6); }
}
@media (width >= 64rem) {
  .site-footer__top { grid-template-columns: 1.5fr 1fr 1fr 1.1fr; }
}

.site-footer h2 {
  font-family: var(--font-text);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--detail);
  margin-block-end: var(--space-4);
  max-width: none;
}

.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.site-footer a { color: var(--n-50); text-decoration: none; }
.site-footer a:hover { color: var(--detail); text-decoration: underline; text-underline-offset: 0.2em; }

.site-footer__brand .brand__mark { margin-block-end: var(--space-4); }
.site-footer__brand img { height: 4.5rem; width: auto; }
.site-footer__brand p { color: var(--text-muted); max-width: 34ch; }

.site-footer address {
  font-style: normal;
  display: grid;
  gap: var(--space-3);
}
.site-footer address a { font-weight: 650; }

.site-footer__social {
  display: flex;
  gap: var(--space-3);
  margin-block-start: var(--space-5);
}
.site-footer__social a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--rule-on-deep);
  color: var(--n-50);
}
.site-footer__social a:hover { background: oklch(0.985 0.008 var(--neutral-h) / 0.12); color: var(--detail); }
.site-footer__social svg { width: 1.25rem; height: 1.25rem; }

.site-footer__bottom {
  border-block-start: 1px solid var(--rule-on-deep);
  padding-block: var(--space-5) calc(var(--space-5) + env(safe-area-inset-bottom));
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  align-items: center;
  justify-content: space-between;
  font-size: var(--step--1);
  color: var(--text-muted);
}
.site-footer__bottom ul { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-5); }

/* The PageLaunch credit. Visible, normal footer text, dofollow. */
.site-credit { margin: 0; color: var(--text-muted); }
.site-credit a { color: var(--text-muted); text-decoration: underline; text-underline-offset: 0.2em; }
.site-credit a:hover { color: var(--detail); }

/* --- area link cloud ------------------------------------------------------ */
.arealinks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  padding: 0;
  margin: 0;
}
.arealinks a {
  display: inline-flex;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: var(--step--1);
  font-weight: 600;
  text-decoration: none;
}
.arealinks a:hover { background: var(--accent-wash); border-color: var(--accent); color: var(--accent-hover); }
.section--deep .arealinks a {
  background: transparent;
  border-color: var(--rule-on-deep);
  color: var(--n-50);
}
.section--deep .arealinks a:hover { background: oklch(0.985 0.008 var(--neutral-h) / 0.12); color: var(--detail); }
}

@layer utilities {
/* --- FAQ accordion -------------------------------------------------------
   <details> so it is keyboard accessible, findable by browser find-in-page,
   and indexed. No FAQPage JSON-LD: Google removed that rich result in 2026.
   ------------------------------------------------------------------------ */
.faq { display: grid; gap: var(--space-3); }

.faq details {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.faq details[open] { border-color: var(--panel-border); background: var(--panel); }

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  min-height: 3.25rem;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 650;
  letter-spacing: -0.01em;
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::marker { content: ""; }
.faq summary::after {
  content: "";
  flex: none;
  width: 0.7rem;
  height: 0.7rem;
  border-inline-end: 2.5px solid var(--accent);
  border-block-end: 2.5px solid var(--accent);
  rotate: 45deg;
  translate: 0 -0.18rem;
  transition: rotate var(--dur-base) var(--ease-out), translate var(--dur-base) var(--ease-out);
}
.faq details[open] summary::after { rotate: 225deg; translate: 0 0.12rem; }
.faq summary:hover { color: var(--accent-hover); }

.faq__body { padding: 0 var(--space-5) var(--space-5); }
.faq__body > * + * { margin-block-start: var(--space-4); }
.faq__body p { color: var(--text-muted); max-width: var(--measure); }

.section--deep .faq details { background: var(--panel); border-color: var(--rule-on-deep); }
.section--deep .faq summary::after { border-color: var(--detail); }

/* --- scroll reveal -------------------------------------------------------
   Content is visible at rest. The animation only ever runs where both
   scroll-driven animation and motion preference allow it, so the no-support
   state is simply the finished page. Never opacity:0 without a guarantee.
   ------------------------------------------------------------------------ */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .reveal {
      animation: reveal-up linear both;
      animation-timeline: view();
      animation-range: entry 2% entry 70%;
    }
    /* Transform only, deliberately. Ramping opacity on a scroll timeline can
       park an element at partial opacity if the reader never scrolls past it,
       and that is a contrast failure rather than a style choice. Sliding up
       reads the same and cannot make text unreadable. */
    @keyframes reveal-up {
      from { transform: translateY(1.5rem); }
      to   { transform: translateY(0); }
    }
  }
}

/* --- utilities ------------------------------------------------------------ */
.flow-none { margin-block-start: 0 !important; }
.text-center { text-align: center; }
.mx-auto { margin-inline: auto; }
.max-measure { max-width: var(--measure); }
.no-wrap { white-space: nowrap; }

.two-col-list {
  display: grid;
  gap: var(--space-2) var(--space-6);
  list-style: none;
  padding: 0;
  margin: 0;
}
@media (width >= 30rem) { .two-col-list { grid-template-columns: repeat(2, 1fr); } }
@media (width >= 64rem) { .two-col-list { grid-template-columns: repeat(3, 1fr); } }
.two-col-list li {
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
  padding-block: 0.25rem;
}
.two-col-list li::before { content: ""; flex: none; width: 5px; height: 5px; border-radius: 50%; background: var(--gold-400); translate: 0 -0.2em; }

/* Space above an element that follows a heading or a block of prose.
   This must live in the utilities layer. It started life in `layout`, where
   any component setting `margin: 0` on itself beat it, so `.ticks.flow-end`
   rendered with a 0px gap under its own heading on nine pages. */
.flow-end { margin-block-start: var(--space-6); }
}

@layer utilities {
@media print {
  .site-header, .callbar, .menu, .site-footer__social, .booking, .skip-link { display: none !important; }

  body {
    background: var(--print-paper);
    color: var(--print-ink);
    font-size: 11pt;
    padding-block-end: 0;
  }

  .section, .hero, .pagehead, .site-footer {
    background: var(--print-paper) !important;
    color: var(--print-ink) !important;
    padding-block: 1rem;
  }
  .hero::after, .pagehead::after { display: none; }
  .hero__media, .pagehead__media { position: static; }

  .section--deep :is(h1, h2, h3, h4, p, li, a) { color: var(--print-ink) !important; }

  a { text-decoration: underline; }
  /* Print the destination so a paper copy is still usable. */
  main a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; }
  main a[href^="/"]::after { content: " (wizparties.co.uk" attr(href) ")"; font-size: 9pt; }
  a[href^="tel:"]::after, a[href^="mailto:"]::after { content: ""; }

  .party, .panel, .quote, .faq details { border: 1px solid var(--print-rule); break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }
  img { max-width: 40% !important; }
}
}
