/* ==========================================================================
   Summit Golf Academy — funnel page
   Mobile-first from 320px. Every value comes from tokens.css.
   ========================================================================== */

/* --------------------------------------------------------------- base ---- */

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

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

body {
  margin: 0;
  background: var(--ink-900);
  color: var(--bone);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; }

/* Selection is green, the way it is on the main site. Ink on
   --summit-green-bright is 16.2:1, so it reads on any ground the page has.
   The display faces are painted through background-clip:text, so they get the
   colour named explicitly or the glyphs stay transparent under the highlight. */
::selection { background: var(--summit-green-bright); color: var(--ink-900); }
::-moz-selection { background: var(--summit-green-bright); color: var(--ink-900); }
.h1::selection, .h1 span::selection,
.h2::selection { background: var(--summit-green-bright); color: var(--ink-900); }

:focus-visible {
  outline: 3px solid var(--summit-green-bright);
  outline-offset: 3px;
  border-radius: var(--radius-xs);
}
.proof :focus-visible,
.close :focus-visible,
.tile--light :focus-visible { outline-color: var(--ink-900); }

.skip {
  position: absolute;
  left: var(--space-4);
  top: calc(var(--space-4) * -6);
  z-index: 100;
  padding: var(--space-3) var(--space-4);
  background: var(--summit-green-bright);
  color: var(--ink-900);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  text-decoration: none;
  transition: top var(--dur-base) var(--ease-out);
}
.skip:focus { top: var(--space-4); }

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

/* ------------------------------------------------------------- header ---- */

.site-header {
  position: fixed;
  top: var(--space-3);
  left: 50%;
  transform: translateX(-50%);
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8);
  width: auto;
  max-width: calc(100% - (var(--gutter) * 2));
  height: var(--header-h);
  padding: 0 var(--space-2) 0 var(--space-4);

  border: 1px solid var(--glass-border);
  border-radius: var(--radius-xl);
  /* Solid by default so the CTA is readable even without backdrop-filter */
  background: var(--glass-bg-solid);
}

/* Liquid glass only where the browser can actually blur what is behind it */
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .site-header {
    background: var(--glass-bg);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.6);
    backdrop-filter: blur(var(--glass-blur)) saturate(1.6);
  }
}



/* Bone, not green — so the header reads as a different layer to the page */
.site-header .cta { background: var(--summit-green); color: var(--ink-900); }

/* flex: none is load-bearing. The header is a shrink-to-fit fixed element, and
   without it the mark was allowed to shrink — the logo has been rendering at
   21px wide instead of 50px, horizontally squashed, since the header was
   built. It has nothing to do with the link; it was there before. */
.site-header__mark { position: relative; display: flex; align-items: center; flex: none; }
.site-header__mark img { height: 26px; width: auto; }
/* Now that the mark is the link back to the top it needs a real tap target.
   The hit area is extended with a pseudo-element rather than by sizing the
   link, because any width or min-width on the link feeds back through the
   image's aspect ratio and squashes the artwork. Nothing here moves a pixel
   of layout; the gap to the CTA is 32px, so 8px each side stays clear. */
.site-header__mark::after { content: ""; position: absolute; inset: -16px -8px; }

/* ----------------------------------------------------------- structure --- */

.wrap {
  width: 100%;
  /* 60rem on a phone and a tablet; tokens.css widens it from 1024px up */
  max-width: var(--wrap-max, 60rem);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-y); }
.section--tight { padding-block: var(--space-12); }

/* A centred pill sitting between the hero and the price, reading as a divider */
.rated-band { display: flex; justify-content: center; margin-bottom: var(--space-8); }
/* On a phone the generous side padding left 271px for roughly 291px of
   content, so "5.0 on Google" and "50+ reviews" each broke across two lines
   and the whole thing read as damaged. Tighter padding here, the wide version
   from 560px up, and nowrap on the parts so that if it ever does run out of
   room the pieces wrap as whole phrases instead of mid-word. */
.rated-pill {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--ink-900);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}
.rated-pill .stars { color: var(--gold); letter-spacing: 0.06em; }
.rated-pill .stars,
.rated-pill b,
.rated-pill em { white-space: nowrap; }
.rated-pill b { font-weight: 600; }
.rated-pill span.sep { color: var(--ink-700); }
.rated-pill em { font-style: normal; color: var(--ink-500); }
.section--dim { background: var(--ink-800); }

/* ---------------------------------------------------------------- type --- */

/* A soft top-to-bottom sheen on the display face. Falls back to a flat
   colour where background-clip:text is unsupported. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .h1, .h2 {
    background: linear-gradient(180deg, var(--white) 0%, var(--bone) 42%, #B9B8B1 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
  .proof .h2, .close .h2 {
    background: linear-gradient(180deg, #2B2E26 0%, var(--ink-900) 55%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

.display {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
}

/* Measured at the live font: each line must stay under 280px at 320px wide.
   "Home of indoor golf" is 249px, "in Southampton" is 195px. The nowrap on
   .h1 span makes a too-long line overflow visibly rather than silently
   wrapping to three, so it cannot regress unnoticed. */
.h1 { font-size: var(--text-h1); letter-spacing: var(--tracking-tighter); }
.h1 { line-height: var(--leading-h1); }
.h1 span { display: block; white-space: nowrap; }
.h2 { font-size: var(--text-display-sm); }
.h3 {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
}

.eyebrow {
  margin: 0 0 var(--space-3);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--summit-green);
}
.proof .eyebrow { color: var(--summit-green-deep); }

.lede {
  margin: var(--space-3) 0 0;
  max-width: var(--measure);
  font-size: var(--text-sm);
  color: var(--ink-500);
}
.proof .lede { color: var(--ink-300); }

.section-head { margin-bottom: var(--space-4); }

/* A slow radar pulse beside the scarcity line. The dot is the steady state and
   the ring is the pulse, so if the animation never runs there is still a
   green dot rather than nothing.

   Two keyframes that move, plus a still third one to hold the rest between
   pulses — a timing function is applied between EACH pair of stops, so the
   last segment is deliberately identical to the one before it and the second
   pass of the easing has nothing to act on. */
.lede--live {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  color: var(--bone);
  font-weight: 500;
}
.pulse {
  position: relative;
  flex: none;
  /* centred on the FIRST line, not on the paragraph. Half a line box less
     half the dot, in em so it holds if the type size changes. */
  margin-top: 0.42em;
  width: 10px;
  height: 10px;
  background: var(--summit-green-bright);
  border-radius: var(--radius-pill);
}
/* Two rings rather than one, half a cycle apart, so a new one leaves as the
   last is fading and the sweep never stops. Slower than it first was: the dot
   and the rings are bigger now, and a larger shape covering more ground reads
   as faster at the same duration. Two keyframes each — a timing function runs
   between every PAIR of stops, so anything with a middle stop eases twice and
   stutters. */
.pulse::before,
.pulse::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--summit-green-bright);
  border-radius: inherit;
  /* --ease-ripple, not --ease-out. Away quickly then settling, which is what
     a ripple does — but roughly half as aggressive as --ease-out, which puts
     half its travel inside the first tenth of the time and reads as a pop
     rather than a spread. */
  animation: pulse-ring 3000ms var(--ease-ripple) infinite;
}
.pulse::after { animation-delay: 1500ms; }
/* The fade finishes at 45%, not at the end. Both properties shared one timing
   function before, and --ease-out has a very long tail — so the opacity
   crawled the last of the way to zero while the ring sat at its widest, which
   is the lingering. The ring now reaches roughly the same size at roughly the
   same rate and is simply gone by the time it gets there.

   The third stop is deliberately identical to the second. A timing function
   runs between every PAIR of stops, so the easing does get a second pass —
   but across a segment where nothing changes at all, which is the one way a
   middle stop cannot stutter. Do not "tidy" it into two keyframes. */
@keyframes pulse-ring {
  0%   { transform: scale(1);   opacity: 0.5; }
  45%  { transform: scale(3.2); opacity: 0; }
  100% { transform: scale(3.2); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .pulse::before,
  .pulse::after { animation: none; }
}

/* --------------------------------------------------------- proof chip ---- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-500);
}
.chip__stars { color: var(--gold); letter-spacing: 0.06em; }
.proof .chip { color: var(--ink-300); }
.proof .chip__stars { color: var(--gold-deep); }
.close .chip { color: var(--ink-300); }

/* ---------------------------------------------------------------- cta ---- */

.cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  padding: var(--space-3) var(--space-6);
  background: var(--summit-green);
  color: var(--ink-900);
  border: 0;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  will-change: transform;
  transition: transform var(--dur-base) var(--ease-out);
}
.cta:hover { transform: scale(1.03); }
.cta:active { transform: scale(0.975); transition-duration: var(--dur-fast); }

.cta--lg { padding: var(--space-4) var(--space-8); font-size: var(--text-lg); }
.cta--sm { padding: var(--space-2) var(--space-4); font-size: var(--text-sm); }
/* Same height, more shoulder — used on the hero button so it carries more
   weight against the headline above it. */
.cta--wide { padding-inline: var(--space-12); }

/* Leaving for Trackman. The label swaps to "Redirecting" the moment the
   button is used, so arriving on a different domain is not a surprise.

   The pending label sits on top of the real one rather than replacing it:
   ui.js locks the button to the width it already had, so nothing on the page
   moves. It is set a step or two smaller than the label it covers, because it
   is a status rather than a name for the button. The label underneath stays
   in the accessibility tree and keeps its name. */
.cta { position: relative; }

.cta__label,
.cta__pending { transition: opacity var(--dur-fast) var(--ease-standard); }

.cta__pending {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-2xs);
  white-space: nowrap;
  opacity: 0;
}
/* The largest button carries a slightly larger status, or 12px looks lost
   inside it. */
.cta--lg .cta__pending { font-size: var(--text-sm); }

/* The dots sit on their own row underneath rather than trailing the word, so
   the word itself lands dead centre — the same spot the eye just left the
   button's own label. */
.cta__dots {
  display: flex;
  gap: var(--space-1);
}
.cta__dot {
  width: var(--space-1);
  height: var(--space-1);
  border-radius: var(--radius-pill);
  background: currentColor;
  opacity: 0.3;
}

.cta.is-going { pointer-events: none; }
.cta.is-going .cta__label { opacity: 0; }
.cta.is-going .cta__pending { opacity: 1; }
.cta.is-going .cta__dot { animation: cta-dot 1000ms linear infinite; }
.cta.is-going .cta__dot:nth-child(2) { animation-delay: 160ms; }
.cta.is-going .cta__dot:nth-child(3) { animation-delay: 320ms; }

@keyframes cta-dot {
  0%, 60%, 100% { opacity: 0.3; }
  30%           { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .cta__label,
  .cta__pending { transition: none; }
  .cta.is-going .cta__dot { animation: none; opacity: 1; }
}

.cta-note {
  margin: var(--space-3) 0 0;
  font-size: var(--text-xs);
  color: var(--ink-500);
}
.cta-note--under {
  text-align: center;
  font-size: var(--text-xs);
  color: var(--ink-600);
}
.close .cta-note--under { color: var(--ink-300); }
.close .cta-note { color: var(--ink-300); }

/* --------------------------------------------------------------- hero ---- */

.hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 400px;
  min-height: min(74svh, 590px);
  overflow: hidden;
  isolation: isolate;
}

.hero__media { position: absolute; inset: 0; z-index: -2; }
/* Biased right. The left third of the hero photograph is empty wall, so a
   centred crop on a phone — where the box is portrait and only a narrow column
   of a landscape photo survives — would have framed the dark nothing and cut
   the player out. */
.hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: 62% 50%; }

.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to top,
    var(--ink-900) 0%,
    var(--scrim-strong) 30%,
    var(--scrim-mid) 52%,
    var(--scrim-soft) 70%,
    var(--scrim-0) 100%
  );
}

/* The hero is bottom-aligned, so the bottom padding is what lifts the copy
   clear of the section underneath it. */
.hero__inner { padding-block: var(--space-16) var(--space-12); }

.hero__sub {
  margin: var(--space-3) 0 0;
  max-width: 24rem;
  font-size: var(--text-sm);
  color: var(--bone);
}
.hero__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin-top: var(--space-5, var(--space-4)); margin-top: var(--space-4); }
.hero__actions .cta-note { margin: 0; }
.hero__kit { margin: var(--space-8) 0 0; display: flex; align-items: center; gap: var(--space-3); }
.hero__kit span {
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-500);
}
.hero__kit img { width: 108px; }
.hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-4);
  margin-top: var(--space-8);
}
.hero__meta .chip { margin: 0; }
.hero__kit { margin: var(--space-6) 0 0; }

.picker__controls { min-width: 0; }

/* ----------------------------------------------------- price selector ---- */

.price { background: var(--ink-800); }
.price .section { padding-block: var(--space-12); }

/* The whole decision sits in one panel, so it reads as a single object
   instead of a stack of loose controls on the section background. */
.picker {
  display: grid;
  gap: var(--space-6);
  margin-top: var(--space-4);
  padding: var(--space-4);
  background: var(--ink-900);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-lg);
}

.picker__legend {
  display: block;
  padding: 0;
  margin: 0 0 var(--space-2);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-500);
}

.picker__group + .picker__group { margin-top: var(--space-4); }
.picker__group { border: 0; padding: 0; margin: 0; }

/* The label and the two marks share one line. The marks are optical trim —
   they take the height the label already occupies, so nothing about the
   panel's padding or rhythm changes. */
.picker__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-2); }
/* the gap moves to the row, so the marks can centre against the label */
.picker__head .picker__legend { margin-bottom: 0; }
.picker__marks { display: flex; align-items: center; gap: var(--space-3); }
.picker__mark { width: auto; opacity: 0.85; }
.picker__mark--summit { height: 15px; }
/* Trackman's mark is square and reads heavier than the Summit lockup at the
   same height, so it is set a shade smaller to match by eye. Never recoloured
   or reproportioned — the file is the supplied artwork. */
.picker__mark--tm { height: 13px; }

.picker__row { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-2); }

.opt { position: relative; }
.opt input {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer;
}
.opt span {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  height: var(--tap-min);
  padding: var(--space-2) var(--space-1);
  background: var(--ink-800);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  transition:
    background-color var(--dur-base) var(--ease-standard),
    color var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard);
}
.opt input:checked + span {
  background: var(--summit-green);
  border-color: var(--summit-green);
  color: var(--ink-900);
}
.opt input:focus-visible + span { outline: 3px solid var(--summit-green-bright); outline-offset: 3px; }

/* duration slider */

.slider-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
.slider-value {
  font-size: var(--text-lg);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--summit-green);
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
}

.slider-wrap {
  position: relative;
  height: var(--tap-min);
  margin-top: 0;
  touch-action: pan-y;
}

/* The native input sits invisible on top: it keeps keyboard support,
   screen-reader semantics and touch dragging. Everything you can see is
   drawn from the same value, so the fill and the knob cannot disagree. */
.slider-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: grab;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  border: 0;
  outline: none;
  -webkit-tap-highlight-color: transparent;
}
.slider-input:active { cursor: grabbing; }
.slider-input::-webkit-slider-thumb { -webkit-appearance: none; width: 30px; height: 30px; }
.slider-input::-moz-range-thumb { width: 30px; height: 30px; border: 0; opacity: 0; }

.slider-rail {
  position: absolute;
  top: 50%;
  left: 13px;
  right: 13px;
  height: 6px;
  margin-top: -3px;
  background: var(--ink-700);
  border-radius: var(--radius-pill);
  pointer-events: none;
}
.slider-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: var(--summit-green);
  border-radius: var(--radius-pill);
  pointer-events: none;
  /* a tap jumps the value, so ease it; dragging turns this off below */
  transition: width var(--dur-base) var(--ease-out);
}
.slider-knob {
  position: absolute;
  top: 50%;
  left: 13px;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  background: var(--summit-green);
  border: 3px solid var(--ink-900);
  border-radius: var(--radius-pill);
  pointer-events: none;
  box-shadow: 0 2px 8px var(--scrim-strong);
  transition: left var(--dur-base) var(--ease-out);
}

/* No easing while dragging (the knob must track the finger), and none on
   first paint either — otherwise the bar animates up to its default value
   as the section scrolls into view. */
.slider-wrap.is-dragging .slider-fill,
.slider-wrap.is-dragging .slider-knob,
.slider-wrap.is-idle .slider-fill,
.slider-wrap.is-idle .slider-knob { transition: none; }
.slider-input:focus-visible ~ .slider-knob {
  outline: 3px solid var(--summit-green-bright);
  outline-offset: 3px;
}

/* Ticks line up with the knob centres, which stop 13px short of each end */
.slider-ticks {
  position: relative;
  height: 1.4em;
  margin-top: var(--space-1);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-500);
  font-variant-numeric: tabular-nums;
}
.slider-ticks span {
  position: absolute;
  top: 0;
  left: calc(13px + (var(--p) * (100% - 26px)));
  transform: translateX(-50%);
  white-space: nowrap;
}
.slider-ticks span:nth-child(1) { --p: 0; }
.slider-ticks span:nth-child(2) { --p: 0.3333; }
.slider-ticks span:nth-child(3) { --p: 0.6667; }
.slider-ticks span:nth-child(4) { --p: 1; }

/* the price panel */

.total {
  padding-top: var(--space-3);
  border-top: 1px solid var(--ink-700);
}
.total__label { display: none; }
.total__row {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
}
/* Inter, NOT Anton. Anton has no tabular figures: measured at the live font,
   "£14" is 47.06px and "£25" is 53.27px, so the total jumped 6px sideways as
   the slider moved and dragged the line next to it with it. Inter holds every
   value in the matrix at exactly the same width. */
.total__figure {
  display: block;
  font-family: var(--font-body);
  font-size: var(--text-3xl);
  font-weight: 800;
  line-height: 0.9;
  letter-spacing: var(--tracking-tighter);
  font-variant-numeric: tabular-nums;
  color: var(--bone);
}
.total__meta {
  margin: 0;
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--ink-500);
}
.total__meta b { color: var(--summit-green); font-weight: 600; }
.total__actions { margin-top: var(--space-3); }
.total__actions .cta { width: 100%; font-size: var(--text-base); }

/* ------------------------------------------------------- reassurance ----- */

.reassure {
  position: relative;
  isolation: isolate;
  background: var(--ink-900);
  color: var(--bone);
  overflow: hidden;
  padding-block: var(--space-12);
}
.reassure .eyebrow { color: var(--summit-green); }
.reassure > .wrap, .reassure > .marquee { position: relative; z-index: 1; }

/* The footage itself, knocked back hard. It is texture, not a thing to watch:
   at full brightness it fought the copy and pulled the eye off the page. */
.reassure__bg { position: absolute; inset: 0; z-index: -2; background: var(--ink-900); }
.reassure__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: brightness(var(--video-dim)) saturate(0.85);
}
/* A second, even pass over the top, so the darkest frames and the brightest
   ones both sit far enough under --bone for the text to hold. */
.reassure__scrim { position: absolute; inset: 0; z-index: -1; background: var(--scrim-soft); }

.marquee {
  margin-top: var(--space-6);
  overflow: hidden;
  overflow: hidden;
  cursor: grab;
  touch-action: pan-y;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 2.5%, #000 97.5%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 2.5%, #000 97.5%, transparent);
}

.marquee__track {
  display: flex;
  gap: var(--space-3);
  width: max-content;
  will-change: transform;
}
.marquee.is-dragging { cursor: grabbing; }
.marquee__set { display: flex; gap: var(--space-3); }
.mchip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--ink-900);
  color: var(--bone);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
}
.mchip svg { flex: 0 0 auto; width: 16px; height: 16px; fill: var(--summit-green); }
/* On the bone ground these read as solid pills. Over the footage they need a
   hairline, or they dissolve into the darker frames. */
.reassure .mchip { border: 1px solid var(--ink-700); }
.reassure .display { max-width: 18ch; }
/* Named, rather than a bare element selector on this section. That also
   matched the eyebrow, which is a <p>, and handed it a 12px top margin —
   which collapsed out through .wrap and left the section sitting 60px off
   the top against 48px at the bottom. It was also why this eyebrow needed a
   different gap under it from every other one on the page. */
.reassure__body { margin: 0; max-width: var(--measure); font-size: var(--text-sm); color: var(--bone); }

/* -------------------------------------------------------------- bento ---- */

/* One swipeable row rather than a stack. Six tiles stacked were most of the
   page's scroll; the height saved goes to the photographs underneath. Same
   mechanics as the reviews carousel, so the two behave identically. */
.bento {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: var(--space-2);
  /* tiles run to the screen edge, but a snapped tile stops at the gutter */
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  scroll-padding-inline: var(--gutter);
}
.bento::-webkit-scrollbar { display: none; }

.tile {
  position: relative;
  flex: 0 0 82%;
  max-width: 22rem;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  background: var(--ink-800);
  border: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  /* a lit top edge and a soft drop, so the card sits off the page slightly */
  box-shadow:
    inset 0 1px 0 rgba(242, 241, 236, 0.14),
    inset 0 -18px 40px -24px rgba(0, 0, 0, 0.9),
    0 6px 18px -8px var(--scrim-strong);
}
/* Named rather than positional, so reordering the carousel cannot shuffle the
   tints out from under the subjects they were chosen for. */
.tile--t1 { background: var(--tile-1); --edge: var(--tile-1-edge); --bar: var(--bar-1); }
.tile--t2 { background: var(--tile-2); --edge: var(--tile-2-edge); --bar: var(--bar-2); }
.tile--t3 { background: var(--tile-3); --edge: var(--tile-3-edge); --bar: var(--bar-3); }
.tile--t4 { background: var(--tile-4); --edge: var(--tile-4-edge); --bar: var(--bar-4); }
.tile--t5 { background: var(--tile-5); --edge: var(--tile-5-edge); --bar: var(--bar-5); }
.tile--t6 { background: var(--tile-6); --edge: var(--tile-6-edge); --bar: var(--bar-6); }
.tile .h3 { color: var(--edge); }

/* Coloured edge around the text, fading out as it rises past the image */
.tile::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(
    to top,
    var(--edge) 0%,
    var(--edge) 34%,
    rgba(242, 241, 236, 0.16) 58%,
    rgba(242, 241, 236, 0.03) 100%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}

/* highlight catching the top-left corner */
.tile::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--sheen);
  pointer-events: none;
}
.tile__media { aspect-ratio: 21 / 9; }
.tile__media img { width: 100%; height: 100%; object-fit: cover; }
/* The Practice tile is a screenshot, not a photograph: its numbers sit hard
   against the left edge, and a centred crop cuts them in half at the widths
   where the tile trims its sides. Anchored left so the readings survive. */
.tile__media--start img { object-position: left center; }
.tile__body { position: relative; display: flex; flex-direction: column; flex: 1; overflow: hidden; padding: var(--space-4); }
.tile__body p { margin: var(--space-2) 0 0; font-size: var(--text-sm); color: var(--ink-500); }
.tile__body > * { position: relative; z-index: 1; }

/* Corporate is an enquiry, not a booking. A ghost button, never a green fill:
   green is reserved for the one action that ends on Trackman, and a second
   filled CTA on this page would compete with it. */
.tile__ask {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: transparent;
  color: var(--bone);
  border: 1px solid var(--edge);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  will-change: transform;
  transition: transform var(--dur-base) var(--ease-out);
}
.tile__ask:hover { transform: scale(1.03); }
.tile__ask:active { transform: scale(0.975); transition-duration: var(--dur-fast); }

/* Scroll position for the tile row. The fill's colour is set from the tint of
   whichever tile you are on and blended across the two either side of the
   join, so the bar carries the same palette the tiles do — see js/ui.js. */
.bento-bar {
  position: relative;
  height: var(--bar-h);
  margin-top: var(--space-3);
  background: var(--ink-700);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
/* Fills from the left as the row moves, rather than sliding along as a thumb:
   it reads as "how much of this have I seen", which is what the row is
   actually telling you. Colour comes from js/ui.js. */
.bento-bar__fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 20%;
  background: var(--bar-1);
  border-radius: var(--radius-pill);
  /* Short enough to be imperceptible as lag, long enough to smooth over a
     scroll event that arrives in a big jump. */
  transition: background-color 120ms linear;
}

.facts { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); margin-top: var(--space-3); }
.fact {
  position: relative;
  display: block;
  padding: var(--space-4);
  border: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow:
    inset 0 1px 0 rgba(242, 241, 236, 0.12),
    0 6px 18px -8px var(--scrim-strong);
}
/* The facts are deliberately NOT part of the tile rotation. Every tint is
   spoken for by a tile above them, so borrowing two would have put the repeat
   straight back; and they are a different kind of thing anyway — supporting
   facts rather than something you can book. Neutral says that. */
.facts .fact { background: var(--ink-800); --edge: var(--ink-500); }
.fact::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(to top, var(--edge) 0%, rgba(242, 241, 236, 0.10) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.fact > * { position: relative; z-index: 1; }
.fact svg { width: 26px; height: 26px; margin-bottom: var(--space-3); fill: var(--edge); }
.fact b { display: block; font-size: var(--text-base); font-weight: 700; line-height: var(--leading-tight); }
.fact span { display: block; margin-top: var(--space-1); font-size: var(--text-sm); color: var(--ink-500); }

/* ------------------------------------------------------------ gallery ---- */

/* Two up on a phone. The tiles above became one swipeable row, and the height
   that bought is spent here — these are the only real photographs of the
   place on the page, and at four across they were thumbnails. */
.gallery {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-6);
}

/* Sits over the join of the four tiles as a social prompt */
.gallery__tag {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--glass-sheen), var(--scrim-strong);
  border: 1px solid var(--glass-rim);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--bone);
  white-space: nowrap;
  pointer-events: none;
  /* specular top edge, faint underside, and a cast shadow onto the photos */
  box-shadow:
    inset 0 1px 0 var(--glass-lip),
    inset 0 -1px 0 var(--glass-underlip),
    0 8px 22px var(--glass-cast);
}
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  /* Refracting rather than just blurring: saturation and a touch of lift */
  .gallery__tag {
    /* Was nearly clear, because it used to sit over four small photos and
       hid too much of them. The strip is twice the size now, so it can carry
       its own weight rather than borrowing all of it from the picture. */
    background: var(--glass-sheen), rgba(18, 19, 17, 0.62);
    -webkit-backdrop-filter: blur(16px) saturate(1.4);
    backdrop-filter: blur(16px) saturate(1.4);
  }
}

.gframe {
  position: relative;
  /* Square, not 3:4. Two up at portrait was a quarter taller than it needed
     to be and pushed the reviews off the screen. */
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--ink-800);
}
.gframe img {
  position: absolute;
  inset: 0;
  width: 100%;
  /* height:auto alone is not enough here — the width/height attributes set an
     explicit height that beats aspect-ratio, so the frame owns the box. */
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--dur-cross) var(--ease-standard);
}
.gframe img.is-on { opacity: 1; }

/* the wordmark, signing off the photo row */
/* Small and tucked close to the photos — at 150px with generous space above
   it read as a footer rather than a sign-off on the row. */
/* The mark sits exactly halfway between the last photo and the end of the
   section, at half the section's usual rhythm on both sides. Written off
   --section-y rather than as a fixed number, so the two stay matched if the
   rhythm ever changes. */
.section--signoff { padding-bottom: calc(var(--section-y) / 2); }
.signoff {
  width: 96px;
  height: auto;
  margin: calc(var(--section-y) / 2) auto 0;
  opacity: 0.42;
}

/* -------------------------------------------------------------- proof ---- */

.proof { background: var(--bone-dim); color: var(--ink-900); padding-block: var(--space-8) var(--space-6); }

.rating {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-4);
}
/* --gold-deep is 3.60:1 on bone — fine for a graphic, and the words next to
   it carry the meaning. Bright gold is 1.5:1 here and would vanish. */
.rating__stars { color: var(--gold-mid); font-size: var(--text-lg); letter-spacing: 0.06em; }
.rating__text { font-size: var(--text-sm); font-weight: 600; }
.rating__note { font-size: var(--text-sm); color: var(--ink-300); }

.reviews {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: var(--space-2);
  /* cards run toward the screen edge, but a snapped card always stops at the
     gutter so its left edge stays visible */
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  scroll-padding-inline: var(--space-2);
}
.reviews::-webkit-scrollbar { display: none; }

.review {
  flex: 0 0 82%;
  max-width: 22rem;
  scroll-snap-align: start;
  margin: 0;
  padding: var(--space-3) var(--space-4) var(--space-4);
  background: var(--white);
  border: 1px solid var(--card-edge);
  border-radius: var(--radius-lg);
}
.review__head { display: flex; align-items: flex-start; gap: var(--space-3); }
.review__head .review__thumb:first-of-type { margin-left: auto; z-index: 1; }
/* Where a reviewer left two photos they sit as a small overlapping stack.
   Side by side they took 100px out of the head and pushed the name onto a
   second line; the stack costs 28px and reads as "there is more than one".
   The FIRST photo sits on top — it is the one that was chosen to lead, and
   underneath it was getting its subject clipped by the one behind. */
.review__thumb + .review__thumb { margin-left: calc((var(--space-3) + 16px) * -1); }
.avatar {
  flex: 0 0 auto;
  width: 34px; height: 34px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  color: var(--white);
  font-size: var(--text-base);
  font-weight: 600;
  line-height: 1;
}
.avatar--1 { background: var(--avatar-1); }
.avatar--2 { background: var(--avatar-2); }
.avatar--3 { background: var(--avatar-3); }
.avatar--4 { background: var(--avatar-4); }
.avatar--5 { background: var(--avatar-5); }
.avatar--6 { background: var(--avatar-6); }

.review__who { min-width: 0; }
.review__name { display: block; font-size: var(--text-sm); font-weight: 600; }
.review__when { display: block; font-size: var(--text-xs); color: var(--ink-300); }
.review__stars { margin: var(--space-2) 0 var(--space-1); color: var(--gold-mid); letter-spacing: 0.06em; }
.review blockquote { margin: 0; font-size: var(--text-sm); color: var(--ink-900); }

/* photo sits in the space to the right of shorter reviews */
.review__thumb {
  position: relative;
  flex: 0 0 auto;
  /* Sized to the name-and-date block so it aligns top AND bottom — at 50px it
     hung below and read as sitting low, even though its top was already
     8px above the name's cap height. */
  width: 44px;
  height: 44px;
  margin-top: -2px;
  padding: 0;
  border: 2px solid var(--bone);
  border-radius: var(--radius-sm);
  box-shadow: 0 1px 4px var(--scrim-soft);
  overflow: hidden;
  background: var(--bone-dim);
  cursor: zoom-in;
  will-change: transform;
  transition: transform var(--dur-base) var(--ease-out);
}
.review__thumb:hover { transform: scale(1.06); }
.review__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* a magnifier badge, so it reads as something you can open */
.review__thumb::after {
  content: "";
  position: absolute;
  right: 2px;
  bottom: 2px;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-pill);
  background:
    var(--ink-900) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23F2F1EC'%3E%3Cpath d='M10 3a7 7 0 1 0 4.2 12.6l4.1 4.1 1.4-1.4-4.1-4.1A7 7 0 0 0 10 3zm0 2a5 5 0 1 1 0 10 5 5 0 0 1 0-10zm-1 1.6v2.4H6.6v2H9v2.4h2V11h2.4V9H11V6.6H9z'/%3E%3C/svg%3E")
    center / 12px 12px no-repeat;
}

.reviews-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-2);
}
.slider-hint {
  margin: 0;
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-300);
}
.review-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-4);
  background: var(--white);
  color: var(--ink-900);
  border: 1px solid var(--card-edge);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  will-change: transform;
  transition: transform var(--dur-base) var(--ease-out);
}
.review-cta:hover { transform: scale(1.03); }
.review-cta:active { transform: scale(0.975); transition-duration: var(--dur-fast); }
.review-cta svg { width: 16px; height: 16px; fill: var(--gold-deep); }

/* ------------------------------------------------------------ lightbox --- */

.lightbox[hidden] { display: none; }
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: var(--gutter);
  background: var(--scrim-strong);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--dur-slow) var(--ease-out),
    visibility 0s linear var(--dur-slow);
}
.lightbox.is-open {
  opacity: 1;
  visibility: visible;
  transition:
    opacity var(--dur-slow) var(--ease-out),
    visibility 0s linear 0s;
}
.lightbox__inner {
  position: relative;
  max-width: min(92vw, 34rem);
  transform: scale(0.94);
  transition: transform var(--dur-slow) var(--ease-out);
}
.lightbox.is-open .lightbox__inner { transform: scale(1); }
.lightbox img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 60px var(--scrim-strong);
}
.lightbox__close {
  position: absolute;
  top: calc(var(--space-3) * -1);
  right: calc(var(--space-3) * -1);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  background: var(--ink-800);
  color: var(--bone);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-pill);
  cursor: pointer;
  will-change: transform;
  transition: transform var(--dur-base) var(--ease-out);
}
.lightbox__close:hover { transform: scale(1.08); }
.lightbox__close { padding: 0; line-height: 0; }
.lightbox__close svg { display: block; width: 18px; height: 18px; fill: currentColor; }
.lightbox__cap {
  margin: var(--space-3) 0 0;
  text-align: center;
  font-size: var(--text-sm);
  color: var(--ink-500);
}

/* ------------------------------------------------------------- findus ---- */

/* Warning, address, tabs and directions all sit in one panel so the
   directions read as belonging to the tab above them. */
/* Transparent on a phone: the wrapper exists only so the left-hand column can
   be one grid cell on desktop. With display:contents its children stay direct
   children of .findus, in the same order, laid out exactly as before. */
.findus__info { display: contents; }

.findus {
  padding: var(--space-4);
  background: var(--ink-900);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-lg);
}
.warn {
  padding-left: var(--space-4);
  border-left: 4px solid var(--summit-green);
  margin-bottom: var(--space-6);
}
.warn p { margin: 0; font-size: var(--text-sm); }
.warn b { color: var(--summit-green); }

/* Lazy so it costs nothing until somebody scrolls this far down — the map
   is the single heaviest thing on the page and it sits in section eight. */
.findus__map {
  margin: 0 0 var(--space-4);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--ink-800);
}
.findus__map iframe {
  display: block;
  width: 100%;
  height: 190px;
  border: 0;
  filter: grayscale(0.18) contrast(1.05);
}

.findus__address {
  margin: 0 0 var(--space-4);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--ink-700);
  font-style: normal;
  font-size: var(--text-sm);
  color: var(--ink-500);
}
.findus__address b { color: var(--bone); }

.tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }
.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-4);
  background: var(--ink-900);
  color: var(--bone);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition:
    background-color var(--dur-base) var(--ease-standard),
    color var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard);
}
.tab svg { width: 18px; height: 18px; fill: currentColor; }
.tab[aria-selected="true"] {
  background: var(--summit-green);
  border-color: var(--summit-green);
  color: var(--ink-900);
}

/* Same 0fr -> 1fr height animation as the FAQ, so switching tabs grows and
   shrinks smoothly instead of snapping. Without JS both panels stay open. */
.tabpanel { display: grid; grid-template-rows: 1fr; }
.tabpanel > div { overflow: hidden; }

.js .tabpanel {
  grid-template-rows: 0fr;
  opacity: 0;
  visibility: hidden;
  transition:
    grid-template-rows var(--dur-faq) var(--ease-out),
    opacity var(--dur-base) var(--ease-out),
    visibility 0s linear var(--dur-faq);
}
.js .tabpanel.is-active {
  grid-template-rows: 1fr;
  opacity: 1;
  visibility: visible;
  transition:
    grid-template-rows var(--dur-faq) var(--ease-out),
    opacity var(--dur-base) var(--ease-out) 100ms,
    visibility 0s linear 0s;
}
.tabpanel p { margin: 0; color: var(--ink-500); font-size: var(--text-sm); }
/* Deliberately not a filled pill — that reads as a button and gets tapped */
.tabpanel .highlight {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: 0;
  background: none;
  color: var(--summit-green);
  font-size: var(--text-sm);
  font-weight: 600;
}
.tabpanel .highlight svg { width: 16px; height: 16px; fill: var(--summit-green); }
.tabpanel p + p { margin-top: var(--space-2); }

.hours {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2) var(--space-4);
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--ink-900);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-pill);
  text-align: center;
}
.hours__label {
  margin: 0;
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--summit-green);
}
.hours span {
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--bone);
}
.hours span b { color: var(--ink-500); font-weight: 500; }


/* ------------------------------------------------------------- video ---- */
/* A still with a play button standing in for the real player. Vimeo's embed
   pulls roughly 600KB of player before anyone has asked to watch anything,
   which on cold paid mobile traffic is a real cost for something most people
   never press. This is about 27KB, and the iframe only arrives on the tap —
   see the video block in js/ui.js. */

.vfacade {
  position: relative;
  display: block;
  width: 100%;
  /* Equal above and below. The block before it in "how to find us" carries a
     24px bottom margin which collapses with this one, so both ends land on
     24px rather than one end landing on nothing. */
  margin-block: var(--space-6);
  padding: 0;
  aspect-ratio: 16 / 9;
  background: var(--ink-800);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: pointer;
  will-change: transform;
  transition: transform var(--dur-base) var(--ease-out);
}
.vfacade img { width: 100%; height: 100%; object-fit: cover; }
/* The poster is Summit's own title card, which is green — the colour that
   means "action" everywhere else on this page. The scrim knocks it back so it
   reads as a still rather than competing with the booking buttons. */
.vfacade::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--scrim-mid);
  transition: background var(--dur-base) var(--ease-standard);
}
.vfacade:hover { transform: scale(1.01); }
.vfacade:hover::after { background: var(--scrim-soft); }
.vfacade:active { transform: scale(0.995); transition-duration: var(--dur-fast); }

.vfacade__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  background: var(--summit-green);
  color: var(--ink-900);
  border-radius: var(--radius-pill);
  box-shadow: 0 8px 24px var(--scrim-strong);
  transition: transform var(--dur-base) var(--ease-out);
}
/* the triangle's optical centre sits left of its box */
.vfacade__play svg { width: 26px; height: 26px; margin-left: 3px; fill: currentColor; }
.vfacade:hover .vfacade__play { transform: translate(-50%, -50%) scale(1.08); }

.vfacade__meta {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-3);
  z-index: 1;
  padding: var(--space-1) var(--space-2);
  background: var(--scrim-strong);
  color: var(--bone);
  border-radius: var(--radius-xs);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}

/* what replaces the button once it is pressed */
.vframe {
  display: block;
  width: 100%;
  margin-block: var(--space-6);
  aspect-ratio: 16 / 9;
  background: var(--ink-900);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-md);
}

/* Inside an answer it lines up with the answer's own measure. The top margin
   comes off because the answer above it uses PADDING, which does not collapse
   the way a margin would — leaving it on made the gap above 48px against 24px
   below. */
.faq__body .vfacade,
.faq__body .vframe { max-width: var(--measure); margin-top: 0; margin-bottom: var(--space-6); }


/* ---------------------------------------------------------------- faq --- */

.faq__list { margin-top: var(--space-4); border-top: 1px solid var(--ink-700); }
.faq__item { border-bottom: 1px solid var(--ink-700); }
.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--space-4) 0;
  background: none;
  border: 0;
  color: inherit;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: color var(--dur-base) var(--ease-standard);
}
.faq__q:hover { color: var(--summit-green); }
.faq__item h3 { margin: 0; font: inherit; }

/* 0fr -> 1fr is the only height change CSS can actually animate */
.faq__body { display: grid; grid-template-rows: 1fr; transition: grid-template-rows var(--dur-faq) var(--ease-out); }
.js .faq__body { grid-template-rows: 0fr; }
.js .faq__body.is-open { grid-template-rows: 1fr; }
.faq__body > div { overflow: hidden; }

.faq__mark { flex: 0 0 auto; width: 18px; height: 18px; position: relative; transition: transform var(--dur-base) var(--ease-standard); }
.faq__mark::before, .faq__mark::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto 0;
  height: 2px;
  background: var(--summit-green);
}
.faq__mark::after { transform: rotate(90deg); transition: opacity var(--dur-base) var(--ease-standard); }
.faq__q[aria-expanded="true"] .faq__mark::after { opacity: 0; }

.faq__answer {
  padding: 0 0 var(--space-6);
  margin: 0;
  max-width: var(--measure);
  font-size: var(--text-sm);
  color: var(--ink-500);
}
/* Green as an underline, not as type: the greens are light tints and fail
   contrast as text. The word itself brightens to bone so it reads as a link. */
.faq__link {
  color: var(--bone);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: var(--summit-green);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}


/* ----------------------------------------------------------- last cta ---- */

.close { background: var(--summit-green); color: var(--ink-900); text-align: center; }
.close .display { margin-inline: auto; max-width: 14ch; }
.close p { margin: var(--space-3) auto 0; max-width: var(--measure); font-size: var(--text-sm); }
.close .cta { margin-top: var(--space-4); background: var(--ink-900); color: var(--bone); }

/* White on --summit-green is 1.4:1, so the rating sits on a dark pill
   rather than directly on the green. */
.close .rated-band { margin: var(--space-4) 0 0; }
.close .rated-pill { background: var(--ink-900); border-color: var(--ink-800); color: var(--bone); }
.close .rated-pill .stars { color: var(--gold); }
.close .rated-pill em { color: var(--ink-500); }

/* -------------------------------------------------------------- footer --- */

.foot { padding-block: var(--space-8); font-size: var(--text-xs); color: var(--ink-500); }
.foot p { margin: 0 0 var(--space-2); }
.socials { display: flex; gap: var(--space-3); margin-bottom: var(--space-6); }
.socials a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  background: var(--ink-800);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-pill);
  will-change: transform;
  transition: transform var(--dur-base) var(--ease-out);
}
.socials a:hover { transform: scale(1.08); }
.socials svg { width: 20px; height: 20px; fill: var(--bone); }


/* ------------------------------------------------------------- reveal ---- */

/* Content is visible by default and never depends on JavaScript to appear.
   .is-in only plays an entrance animation over the top of it. */
[data-reveal].is-armed { opacity: 0; }
[data-reveal].is-armed.is-in {
  animation: reveal-in var(--dur-reveal) var(--ease-out) both;
  will-change: opacity, transform, filter;
}

@keyframes reveal-in {
  from {
    opacity: 0;
    transform: translateY(var(--reveal-shift));
    filter: blur(var(--reveal-blur));
  }
  to { opacity: 1; transform: none; filter: blur(0); }
}

/* ---------------------------------------------------------- 560px up ----- */

@media (min-width: 35rem) {
  .rated-pill { padding-inline: var(--space-8); }
  .tile { flex-basis: 46%; }
  .gallery { grid-template-columns: repeat(4, 1fr); gap: var(--space-3); }
  .review { flex-basis: 46%; }
  .total__actions .cta { width: auto; }
}

/* ---------------------------------------------------------- 704px up ----- */

@media (min-width: 44rem) {
  /* Flatten the picker: controls on the left, price and button on the right,
     so the whole decision fits in one screen without scrolling. */
  .picker {
    grid-template-columns: 1.25fr 1fr;
    gap: var(--space-12);
    align-items: center;
  }
  .total {
    padding-top: 0;
    padding-left: var(--space-12);
    border-top: 0;
    border-left: 1px solid var(--ink-700);
  }
}

/* ---------------------------------------------------------- 900px up ----- */

@media (min-width: 56rem) {
  .review { flex-basis: 31%; }
  .tile { flex-basis: 31%; }
  .hero__inner { padding-block: var(--space-24) var(--space-12); }
}

/* ========================================================== 1024px up ====
   DESKTOP. Everything below this line is inside a media query, so none of it
   can reach a phone. The page was a 960px column with a token-sized header
   floating over it and two carousels that stopped dead short of the screen
   edges; this widens the column, lets the carousels run the full width, and
   gives the panels padding that suits their new size.
   ======================================================================== */

@media (min-width: 64rem) {

  /* --- header: a bar, with real distance between the mark and the CTA --- */
  .site-header {
    gap: var(--space-24);
    padding: 0 var(--space-3) 0 var(--space-8);
    border-radius: var(--radius-pill);
  }
  .site-header__mark img { height: 30px; }
  .site-header .cta { padding: var(--space-3) var(--space-6); font-size: var(--text-base); }

  /* --- hero: fills a landscape screen instead of sitting in a band ------ */
  .hero { min-height: min(86svh, 780px); }
  .hero__sub { max-width: 32rem; font-size: var(--text-md); }
  .hero__actions { gap: var(--space-4); margin-top: var(--space-6); }
  .hero__kit img { width: 132px; }

  /* --- the rating sits on the heading line, not on a line of its own ----
     Centred above the heading it needed a band of its own, and between that
     band and the panel the section was mostly empty space. On the same line,
     ranged right, it reads as a credential against the heading and gives back
     about a hundred pixels of height. */
  .price .wrap {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    column-gap: var(--space-8);
  }
  .price .section-head { grid-column: 1; grid-row: 1; margin-bottom: 0; }
  .price .rated-band { grid-column: 2; grid-row: 1; justify-content: flex-end; margin-bottom: 0; }
  .price .picker { grid-column: 1 / -1; grid-row: 2; margin-top: var(--space-8); }
  .price { padding-block: var(--space-16); }

  /* --- the decision panel ----------------------------------------------
     Everything in here is scaled up together. At desktop sizes the controls
     were phone-sized inside a panel four times as wide, which left the whole
     thing looking like a small card that had been stretched. */
  .picker {
    grid-template-columns: 1.35fr 1fr;
    padding: var(--space-12);
    gap: var(--space-16);
    align-items: center;
  }
  .total { padding-left: var(--space-16); }
  .picker__head { margin-bottom: var(--space-4); }
  .picker__legend { font-size: var(--text-sm); }
  .picker__row { gap: var(--space-3); }
  .opt span { min-height: 68px; height: 68px; font-size: var(--text-2xl); border-radius: var(--radius-lg); }
  .picker__group + .picker__group { margin-top: var(--space-8); }
  .slider-wrap { height: 68px; }
  .slider-rail { height: 8px; margin-top: -4px; }
  .slider-knob { width: 32px; height: 32px; margin: -16px 0 0 -16px; }
  .slider-value { font-size: var(--text-2xl); }
  .slider-ticks { font-size: var(--text-sm); }
  .total__figure { font-size: var(--text-5xl); }
  .total__meta { font-size: var(--text-base); }
  .total__actions { margin-top: var(--space-6); }
  /* Full width of its column. As a text-width pill it left a band of empty
     panel beside it, and it is the button the whole section exists for. */
  .total__actions .cta { width: 100%; padding: var(--space-4) var(--space-12); font-size: var(--text-lg); }
  /* Its label grows here, so the redirecting status grows with it */
  .total__actions .cta__pending { font-size: var(--text-sm); }

  /* The two marks earn a little more presence at this size */
  .picker__mark--summit { height: 22px; }
  .picker__mark--tm { height: 19px; }

  /* --- carousels run to the screen edges -------------------------------
     --bleed is the distance from the page column out to the screen edge, so
     a negative margin of it reaches the edge and the same figure back as
     padding returns the first card to the heading above it. main is clipped
     so a classic scrollbar cannot turn the viewport maths into a horizontal
     scroll of the whole page. */
  main { overflow-x: clip; }
  .reviews {
    margin-inline: calc(var(--bleed) * -1);
    padding-inline: var(--bleed);
    scroll-padding-inline: var(--bleed);
  }
  .review { flex-basis: 20rem; max-width: 20rem; }

  /* --- the tiles stop scrolling and become a grid ----------------------
     Sideways scrolling bought height back on a phone. On a desktop there is
     no height to buy, so all six are laid out at once: two wide tiles on
     opposite corners of a four-column grid, which keeps it from reading as
     six identical boxes. The media is a fixed height rather than a ratio, or
     the wide tiles would have set a taller row than the narrow ones and left
     holes under their text. */
  .bento {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-4);
    overflow: visible;
    margin-inline: 0;
    padding-inline: 0;
    padding-bottom: 0;
    scroll-snap-type: none;
  }
  .tile { flex: none; max-width: none; scroll-snap-align: none; }
  .tile:nth-child(1),
  .tile:nth-child(6) { grid-column: span 2; }
  .tile__media { aspect-ratio: auto; height: 13rem; }
  .tile__body { padding: var(--space-6); }
  .tile__body p { font-size: var(--text-base); }
  .bento-bar { display: none; }

  /* --- the two fact cards, and the photo strip ------------------------- */
  .facts { gap: var(--space-4); margin-top: var(--space-4); }
  .fact { padding: var(--space-6); }
  .fact svg { width: 30px; height: 30px; }
  .fact b { font-size: var(--text-lg); }
  .gallery { gap: var(--space-4); margin-top: var(--space-8); }
  .gallery__tag { padding: var(--space-3) var(--space-6); font-size: var(--text-sm); }

  /* --- the video band needs height, or the footage is a letterbox ------
     Centred here rather than ranged left: against a full-width video, copy
     down the left leaves a large empty half that reads as a mistake. */
  .reassure { min-height: 34rem; display: grid; align-content: center; }
  .reassure .wrap { text-align: center; }
  .reassure .display { max-width: none; }
  .reassure__body { margin-inline: auto; font-size: var(--text-md); }
  /* The block is vertically centred, so opening this gap lifts the copy as
     well as pushing the chips down — they were sitting right under the last
     line of the paragraph. */
  .reassure .marquee { margin-top: var(--space-16); }

  /* --- how to find us: the map takes the right-hand column ------------- */
  /* Two columns, two rows, three items — the address and the block under it
     on the left, the map spanning both on the right. The wrapper matters: with
     the left content as five separate items the map had to span five rows,
     and a spanning item's height is shared out across the rows it covers,
     which forced holes between the address and the note below it. One cell
     per column and there is nothing to distribute. */
  .findus {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    grid-template-rows: min-content 1fr;
    align-items: start;
    gap: 0 var(--space-8);
    padding: var(--space-6);
  }
  .findus__info { display: block; grid-column: 1; grid-row: 2; }
  .findus__address { grid-column: 1; grid-row: 1; font-size: var(--text-md); }
  .findus__map { grid-column: 2; grid-row: 1 / -1; margin: 0; height: 100%; }
  .findus__map iframe { height: 100%; min-height: 20rem; }

  /* --- questions read as headings at this size ------------------------- */
  .faq__q { font-size: var(--text-lg); padding-block: var(--space-6); }
  .faq__answer { font-size: var(--text-base); }

  /* --- the closing call to action ------------------------------------- */
  .close p { font-size: var(--text-md); }
}


/* ========================================================== 1440px up ==== */

@media (min-width: 90rem) {
  /* NOTE: no .tile width here. The tiles are a grid from 1024px up, and a
     max-width left over from the carousel was capping the double-width ones
     at a single column, which tore a hole in the middle of the first row. */
  .review { flex-basis: 21rem; max-width: 21rem; }
  .hero { min-height: min(86svh, 860px); }
}


/* ------------------------------------------------- reduced motion -------- */

@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;
  }
  [data-reveal].is-armed { opacity: 1; }
  [data-reveal].is-armed.is-in { animation: none; }
}

/* ==========================================================================
   PAGE LOADER — novelty, and easy to remove
   --------------------------------------------------------------------------
   To delete: this block, the .loader markup at the top of <body>, and the
   js/loader.js script tag. Nothing else references it.

   The exit is driven by CSS, not JavaScript. Even with every script blocked
   the overlay fades and stops taking pointer events on its own, so it can
   never strand somebody on a blank screen.
   ========================================================================== */

.loader {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  background: var(--ink-900);
  animation: loader-out var(--loader-total) var(--ease-standard) forwards;
}

@keyframes loader-out {
  0%, 76%   { opacity: 1; visibility: visible; pointer-events: auto; }
  99%, 100% { opacity: 0; visibility: hidden; pointer-events: none; }
}

.loader__scene { width: min(48vw, 230px); }

/* --- the green, seen in perspective -------------------------------------- */

.l-rim   { fill: var(--loader-green-rim); }
.l-green { fill: url(#lGreen); }
.l-contour { fill: #86AB4C; opacity: 0.30; }
.l-dip     { fill: #3F5A22; opacity: 0.38; }
.l-hole  { fill: #0B0C0A; }
.l-lip   { fill: none; stroke: var(--loader-green-rim); stroke-width: 1.5; opacity: 0.9; }

/* a flat ring on the green, so the drop never reads as the ball coming back */
.l-ripple {
  fill: none;
  stroke: var(--bone);
  stroke-width: 2;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: l-ripple 520ms var(--ease-out) calc(var(--loader-roll) + 150ms) both;
}
@keyframes l-ripple {
  0%   { opacity: 0.85; transform: scale(0.35); }
  100% { opacity: 0;    transform: scale(1.9); }
}

/* --- flag: nods only AFTER the ball has gone, and only slightly ---------- */

.l-flag {
  transform-box: fill-box;
  transform-origin: bottom center;
  animation: l-flag 780ms var(--ease-out) calc(var(--loader-roll) + 260ms) both;
}
@keyframes l-flag {
  0%   { transform: rotate(0deg); }
  30%  { transform: rotate(-4.5deg); }
  60%  { transform: rotate(2.2deg); }
  100% { transform: rotate(0deg); }
}
.l-pole  { stroke: var(--bone); stroke-width: 2.5; stroke-linecap: round; }
.l-cloth { fill: var(--loader-flag); }

/* --- the ball: a putt. Rolls, decelerates, shrinks as it recedes --------- */

.l-ball {
  transform-box: fill-box;
  transform-origin: center;
  animation:
    l-roll var(--loader-roll) cubic-bezier(0.17, 0.72, 0.36, 1) forwards,
    l-sink 300ms cubic-bezier(0.55, 0, 0.85, 0.5) var(--loader-roll) forwards;
}
/* Exactly two keyframes on purpose. A timing function is applied between
   each PAIR of keyframes — with five stops the ease-out ran four times over
   and the ball visibly slowed and picked up again at each one. */
@keyframes l-roll {
  from { translate: 0 0;        scale: 1; }
  to   { translate: 80px -40px; scale: 0.83; }
}
/* straight down into the cup — no outward motion at all */
@keyframes l-sink {
  0%   { translate: 80px -40px; scale: 0.83; opacity: 1; }
  60%  { translate: 80px -35px; scale: 0.52; opacity: 1; }
  100% { translate: 80px -32px; scale: 0.18; opacity: 0; }
}

.l-ballshape { fill: url(#lBall); }
.l-balldot   { fill: #C9C8C2; }
.l-ballshadow { fill: #0E1A08; opacity: 0.45; }

.l-spin {
  transform-box: fill-box;
  transform-origin: center;
  animation: l-spin var(--loader-roll) cubic-bezier(0.17, 0.72, 0.36, 1) forwards;
}
@keyframes l-spin { to { transform: rotate(660deg); } }

/* Nobody who has asked for less motion should sit through this */
@media (prefers-reduced-motion: reduce) {
  .loader { animation: loader-out 1ms linear forwards; }
  .loader__scene { display: none; }
}

/* --- alternative: the mark assembling itself ----------------------------- */
/* Peaks rise, then the ball comes up from behind them like a sunrise, then
   the name fades in. Swap which markup block is commented in index.html. */

.s-peak {
  transform-box: fill-box;
  transform-origin: bottom center;
}
.s-peak--back  { fill: var(--loader-peak-back); animation: s-rise 700ms var(--ease-out) 60ms both; }
.s-peak--front { fill: var(--loader-peak-front); animation: s-rise 700ms var(--ease-out) 0ms both; }
@keyframes s-rise {
  from { translate: 0 74px; opacity: 0; }
  to   { translate: 0 0;    opacity: 1; }
}

/* rises from behind the peaks and settles */
.s-ball { animation: s-sun 900ms cubic-bezier(0.22, 1.12, 0.36, 1) 240ms both; }
@keyframes s-sun {
  from { translate: 0 78px; }
  to   { translate: 0 0; }
}
.s-ballshape { fill: url(#sBall); }
.s-balldot   { fill: #C9C8C2; }

.s-name {
  font-family: var(--font-display);
  font-size: 19px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  fill: var(--bone);
  text-anchor: middle;
  animation: s-name 520ms var(--ease-out) 860ms both;
}
@keyframes s-name {
  from { opacity: 0; translate: 0 8px; }
  to   { opacity: 1; translate: 0 0; }
}

/* ==========================================================================
   CHAT — placeholder. Send hands off to WhatsApp with the message prefilled.
   To remove: this block, the .chat markup, and the chat part of js/ui.js.
   ========================================================================== */

.chat {
  position: fixed;
  right: var(--gutter);
  bottom: var(--gutter);
  z-index: 150;                 /* over the header, under the lightbox */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-3);
}

.chat__toggle {
  position: relative;
  /* Above the panel at every frame of the fade. The panel builds a stacking
     context while its opacity is below 1 and drops it the instant opacity
     hits 1 — which flipped the paint order mid-animation and made the button
     jump out from behind the panel's shadow. */
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 54px;
  height: 54px;
  background: var(--summit-green);
  color: var(--ink-900);
  border-radius: var(--radius-pill);
  /* Black, not green. A green halo around a green button read as a second
     glowing ring and softened the bubble's edge; black cuts it out cleanly
     against both the dark page and the photography. */
  box-shadow:
    0 0 0 2px var(--chat-halo),
    0 0 26px var(--chat-glow),
    0 10px 26px var(--glass-cast);
  will-change: transform;
  transition:
    transform var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}

/* Hidden until the hero has gone by — see the chat block in js/ui.js.
   Opacity only, no travel: the bubble is fixed furniture, and sliding it
   about drew the eye to the movement rather than to the button. */
.chat.is-away { opacity: 0; visibility: hidden; pointer-events: none; }

/* A light wash over the page while the panel is open, so it reads as the
   thing in front. Click-through, so tapping the page still closes it. */
.chat::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--chat-dim);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-panel) var(--ease-out);
}
.chat.is-open::before { opacity: 1; }
.chat {
  transition:
    opacity var(--dur-slow) var(--ease-standard),
    visibility 0s linear var(--dur-slow);
}
.chat:not(.is-away) { transition-delay: 0s; }

.chat__toggle:hover { transform: scale(1.06); }
.chat__toggle:active { transform: scale(0.96); transition-duration: var(--dur-fast); }

/* Open state: the bubble grows and its bloom deepens, so the close X reads as
   the front-most thing on screen instead of sitting under the panel's shadow. */
.chat.is-open .chat__toggle {
  transform: scale(1.08);
  box-shadow:
    0 0 0 3px var(--chat-halo),
    0 0 34px var(--chat-glow-open),
    0 14px 32px var(--glass-cast);
}
.chat.is-open .chat__toggle:active { transform: scale(1); }

/* Both icons are stacked on the same spot and cross-fade with a quarter turn,
   rather than one being switched off and the other on. */
.chat__icon {
  position: absolute;
  width: 26px;
  height: 26px;
  fill: currentColor;
  transition:
    opacity var(--dur-base) var(--ease-out),
    transform var(--dur-base) var(--ease-out);
}
.chat__icon--close { width: 28px; height: 28px; opacity: 0; transform: rotate(-90deg) scale(0.55); }
.chat.is-open .chat__icon--open { opacity: 0; transform: rotate(90deg) scale(0.55); }
.chat.is-open .chat__icon--close { opacity: 1; transform: none; }

.chat__panel {
  order: -1;                    /* sits above the button */
  width: min(19rem, calc(100vw - (var(--gutter) * 2)));
  padding: var(--space-4);
  background: var(--ink-800);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px var(--scrim-strong);
  /* A transition rather than an entry animation, so it plays on the way out
     as well — it used to vanish the instant the panel was hidden. Growing
     from the bubble's corner, and a shade quicker leaving than arriving. */
  transform-origin: bottom right;
  opacity: 0;
  transform: translateY(10px) scale(0.96);
  transition:
    opacity var(--dur-base) var(--ease-standard),
    transform var(--dur-base) var(--ease-standard);
}
.chat.is-open .chat__panel {
  opacity: 1;
  transform: none;
  transition-duration: var(--dur-panel);
  transition-timing-function: var(--ease-out);
}
.chat__panel[hidden] { display: none; }

.chat__intro { margin: 0 0 var(--space-3); }
.chat__intro b { display: block; font-size: var(--text-base); font-weight: 700; }
.chat__intro span { display: block; margin-top: var(--space-1); font-size: var(--text-xs); color: var(--ink-500); }
.chat__speed { display: flex; align-items: center; gap: var(--space-2); color: var(--summit-green) !important; font-weight: 600; }
.chat__speed i { width: 7px; height: 7px; border-radius: var(--radius-pill); background: var(--summit-green); }

.chat__chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-3); }
.chat__chips button {
  padding: var(--space-2) var(--space-3);
  background: var(--ink-900);
  color: var(--bone);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  transition: background-color var(--dur-base) var(--ease-standard);
}
.chat__chips button:hover { background: var(--ink-700); }

.chat__panel textarea {
  width: 100%;
  padding: var(--space-3);
  background: var(--ink-900);
  color: var(--bone);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-md);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  resize: vertical;
}
.chat__panel textarea::placeholder { color: var(--ink-600); }
.chat__panel textarea:focus-visible { outline: 3px solid var(--summit-green-bright); outline-offset: 2px; }

.chat__send { width: 100%; margin-top: var(--space-3); font-size: var(--text-sm); }

@media (prefers-reduced-motion: reduce) {
  .chat__panel { transform: none; }
}

/* --- answer sheet: a chip resolves the question before it becomes a message */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 180;
  display: grid;
  place-items: end center;
  padding: var(--gutter);
  background: var(--scrim-strong);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--dur-slow) var(--ease-out),
    visibility 0s linear var(--dur-slow);
}
.sheet[hidden] { display: none; }
.sheet.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-slow) var(--ease-out), visibility 0s linear 0s;
}

.sheet__card {
  position: relative;
  width: min(24rem, 100%);
  margin-bottom: calc(var(--gutter) * 2);
  padding: var(--space-6) var(--space-4) var(--space-4);
  background: var(--ink-800);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 50px var(--scrim-strong);
  transform: translateY(14px);
  transition: transform var(--dur-slow) var(--ease-out);
}
.sheet.is-open .sheet__card { transform: none; }

.sheet__close {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 0;
  background: none;
  border: 0;
  color: var(--ink-500);
  cursor: pointer;
}
.sheet__close svg { width: 18px; height: 18px; fill: currentColor; }

.sheet__title {
  margin: 0 0 var(--space-3);
  font-size: var(--text-xl);
  text-transform: uppercase;
  letter-spacing: var(--tracking-display);
  color: var(--bone);
}
.sheet__body p { margin: 0 0 var(--space-3); font-size: var(--text-sm); color: var(--ink-500); }
.sheet__body p:last-child { margin-bottom: 0; }
.sheet__body b { color: var(--bone); font-weight: 600; }
.sheet__body dl { margin: 0 0 var(--space-3); display: grid; grid-template-columns: auto 1fr; gap: var(--space-1) var(--space-4); font-size: var(--text-sm); }
.sheet__body dt { color: var(--ink-500); }
.sheet__body dd { margin: 0; color: var(--bone); font-variant-numeric: tabular-nums; }

/* The corporate line, lifted out of the paragraph flow so an enquiry does not
   have to be read for. Green as a wash and a rule, never as the type itself. */
.sheet__body .sheet__flag {
  padding: var(--space-3);
  background: var(--green-wash);
  border-left: 3px solid var(--green-wash-edge);
  border-radius: var(--radius-xs);
  color: var(--bone);
}
.sheet__flag b { color: var(--summit-green); }

.sheet__ask {
  width: 100%;
  margin-top: var(--space-4);
  background: var(--ink-900);
  color: var(--bone);
  font-size: var(--text-sm);
}

@media (prefers-reduced-motion: reduce) {
  .sheet, .sheet__card { transition: none; }
}
