/* ===========================================================================
   FULL SCENE EXPERIENCES — core stylesheet
   ---------------------------------------------------------------------------
   Everything is scoped under .full-scene-site so this file can be pasted into
   a GoHighLevel custom-code block without leaking into the builder's own UI.
   Class prefix: .fs-

   The @font-face block leads because the fonts and the styles are always
   needed together: as two files they cost two render-blocking round trips,
   and the font sheet alone was under 1 KB compressed.

   TWO FAMILIES, both derived from the logo:

     Unbounded      display + micro labels   variable, wght 200-900
     Space Grotesk  body                     variable, wght 400-700

   Unbounded is the closest available match to the logo's "SCENE" lettering —
   very wide, geometric, monolinear, generous counters — and its live weight
   axis is what lets one file cover both the oversized display role and the
   widely-tracked micro-label role that echoes the logo's spaced "F U L L".
   tools/build-fonts.py records the eight candidates it beat and why.

   An earlier build shipped four families (Archivo Wide, Archivo Condensed,
   IBM Plex Mono, Space Grotesk). The mono face was the clearest mistake: the
   logo's own spaced caps are geometric sans, not monospace, so the reference
   was wrong before the type was chosen.

   Latin and latin-ext subsets only; the browser fetches latin-ext solely if a
   page needs a character from that range, so the real cost as the site stands
   is two files. font-display: swap keeps text visible during load.

   Licence: SIL Open Font Licence 1.1 for both families.
   Source: Google Fonts (files pulled unmodified, then subset).

   CONTENTS
     00  WEBFONTS
     01  TOKENS
     02  SCOPED RESET & BASE
     03  UTILITIES
     04  BUTTONS
     05  MOTION PRIMITIVES
     06  BRAND MOTIFS
     07  ANNOUNCEMENT / MARQUEE
     08  NAVIGATION
     09  HERO
     10  IT'S PART OF THE SCENE
     11  CHOOSE YOUR SCENE — PACKAGE SELECTOR
     12  FROM TAP TO CAMERA ROLL
     13  WHY FULL SCENE (the merged About)
     14  MADE FOR EVERY SCENE
     15  PICTURE THE SCENE
     16  MAKE IT YOUR SCENE
     17  FOR THE BIGGER SCENE — the one dark section
     18  SOCIAL LINKS
     19  FAQ
     20  FINAL CTA
     21  FOOTER
     22  INTERIOR PAGES — masthead
     23  PROSE
     24  CHECK YOUR DATE
     25  INQUIRY FORM
     26  SHARED PAGE SECTIONS — packages + the experience
     27  WALK UP, PICK YOUR MOMENT, MAKE IT YOURS
     28  PACKAGE LADDER
     29  404
     30  NO-JAVASCRIPT FALLBACKS
     31  THE HIDDEN ATTRIBUTE
   =========================================================================== */


/* == 00  WEBFONTS ========================================================= */

/* Unbounded — display and micro labels, weight axis live.
   Chosen against seven other candidates as the closest available match to
   the logo's "SCENE" lettering: very wide, geometric, monolinear, heavy,
   generous counters. One file serves both roles because the weight axis is
   left live — 800 for display, 500-600 with wide tracking for the micro
   labels that echo the logo's spaced "F U L L" / "E X P E R I E N C E S".
   Build: tools/build-fonts.py */
@font-face {
  font-family: "Unbounded";
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url("../fonts/unbounded-var-latin.woff2") format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Unbounded";
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url("../fonts/unbounded-var-latin-ext.woff2") format("woff2-variations");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Space Grotesk — body, weight axis live. Every character the site sets is
   inside the latin subset, so latin-ext is declared but never fetched. */
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/space-grotesk-400-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/space-grotesk-400-700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* == 01  TOKENS =========================================================== */

.full-scene-site {
  /* Palette held as raw channels so alpha variants stay in one system. */
  --fs-c-white:     251 250 248;   /* #FBFAF8 warm white   */
  --fs-c-ice:       238 242 250;   /* #EEF2FA ice grey-blue */
  --fs-c-blue-100:  220 230 255;   /* #DCE6FF light electric */
  --fs-c-blue:       47  94 255;   /* #2F5EFF electric blue  */
  --fs-c-blue-text:  41  82 224;   /* #2952E0 for small text  */
  --fs-c-ink:        11  13  18;   /* #0B0D12 ink            */
  --fs-c-alert:     168  32  24;   /* #A82018 validation     */

  --fs-white:      rgb(var(--fs-c-white));
  --fs-ice:        rgb(var(--fs-c-ice));
  --fs-blue-100:   rgb(var(--fs-c-blue-100));
  --fs-blue:       rgb(var(--fs-c-blue));
  /* Brand blue measures 4.49:1 on the ice surface and 4.83:1 on warm white.
     Small text therefore uses --fs-blue-text (5.54:1 and 5.96:1); large type,
     UI fills and marks keep --fs-blue. */
  --fs-blue-text:  rgb(var(--fs-c-blue-text));
  --fs-ink:        rgb(var(--fs-c-ink));
  /* 6.98:1 on warm white and 6.50:1 on the ice surface, so it carries small
     text. Colour is never the only error signal — every message is words. */
  --fs-alert:      rgb(var(--fs-c-alert));

  /* Text tints. Nothing below .6 alpha is used for copy — .6 on warm white
     is ~5.2:1, .68 is ~7.3:1. Electric blue on warm white is ~4.9:1, so blue
     is used for large type, UI and links only, never for small body text on
     the ice-blue surfaces (~4.5:1 borderline). */
  --fs-ink-soft:   rgb(var(--fs-c-ink) / .68);
  --fs-ink-mute:   rgb(var(--fs-c-ink) / .60);
  --fs-hairline:   rgb(var(--fs-c-ink) / .12);
  --fs-hairline-b: rgb(var(--fs-c-blue) / .28);

  /* Surfaces — a FIVE value tonal scale, not two backgrounds.
     Measured, cream and ice sit 2.9 L* apart: a difference you can measure
     and cannot see, which is why nine pale sections read as one continuous
     wash. Ice is demoted here from "section colour" to "panel colour" — its
     real job — and two structural events are added:

       ink   one band per page, never adjacent to another, 12-18% of height
       live  one saturated blue field per page, spent on the final CTA

     Photography counts as the fifth value and is allowed to be dark. */
  --fs-surface:       var(--fs-white);
  --fs-surface-ice:   var(--fs-ice);
  --fs-surface-blue:  rgb(var(--fs-c-blue-100) / .45);
  --fs-surface-ink:   var(--fs-ink);
  --fs-surface-live:  var(--fs-blue);

  /* Text tints for the two dark grounds. Contrast on ink: cream 18.4:1,
     .78 alpha 11.5:1, .58 alpha 6.6:1 — so all three carry small text.
     On the blue field: cream 6.1:1, ink 4.6:1 for large type only. */
  --fs-cream-soft:  rgb(var(--fs-c-white) / .78);
  --fs-cream-mute:  rgb(var(--fs-c-white) / .58);
  --fs-hairline-c:  rgb(var(--fs-c-white) / .18);

  /* Type families — two, both derived from the logo.
     DISPLAY: Unbounded. Wide, geometric, monolinear and heavy, like the
     "SCENE" lettering. It carries three roles at three weights:
       800  oversized display headlines and statements
       700  buttons, package names, small headings
       500-600 + wide tracking  micro labels, eyebrows, nav detail, section
                numbers — the logo's spaced "F U L L" treatment
     BODY: Space Grotesk. Neutral, legible, normal tracking. */
  --fs-font-display: "Unbounded", "Archivo", "Helvetica Neue", Arial, sans-serif;
  --fs-font-body:    "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;

  /* Type scale — fluid, no media queries needed for size */
  --fs-text-xs:   clamp(0.6875rem, 0.66rem + 0.12vw, 0.75rem);
  --fs-text-sm:   clamp(0.8125rem, 0.79rem + 0.14vw, 0.875rem);
  --fs-text-base: clamp(1rem,      0.97rem + 0.16vw, 1.0625rem);
  --fs-text-md:   clamp(1.0625rem, 1rem + 0.3vw,     1.1875rem);
  --fs-text-lg:   clamp(1.1875rem, 1.09rem + 0.45vw, 1.4375rem);

  /* Sizes are expressed against the CONTAINER (cqw), not the viewport, so a
     heading scales to the column it actually sits in and wraps where it is
     meant to. Nothing is ever cropped by the viewport edge. The multipliers
     come from measured advance widths: at weight 800 Unbounded sets
     "SET THE SCENE." at 10.25em, ~8% wider than the face it replaced, so
     every multiplier below is that much smaller. */
  --fs-display-sm: clamp(1.375rem, 1.1rem + 1.35vw, 2.5rem);
  --fs-display-md: clamp(1.75rem,  5.25cqw,         3.375rem);
  --fs-display-lg: clamp(2.5rem,   8.3cqw,          5rem);
  --fs-display-xl: clamp(2.5rem,   20cqw,           6.25rem);
  /* The ghost numeral behind the package selector is the site's best
     typographic device and the report's "one oversized word as ground per
     page" rule. Pushed larger — it was capped at 13rem, which stopped it
     growing past about 1100px and left it looking merely large at 1440. */
  --fs-numeral:    clamp(5.5rem,   2rem + 15vw,     17rem);

  /* Unbounded is already wide, so it needs almost no negative tracking; the
     micro role goes the other way, echoing the logo's spaced caps. */
  --fs-lh-display: 0.95;
  --fs-ls-display: -0.008em;
  /* The micro tier used to be weight 400 at 0.24em. 400 is the one weight in
     Unbounded that carries no personality, and at 11-12px it read as a
     cookie notice rather than as the logo's spaced "F U L L". 600 at 0.18em
     is the treatment the brand's own documentation specifies, and it is the
     single highest craft-per-byte change on the site: it lifts every eyebrow,
     nav link, marquee word, image label and section number at once. */
  --fs-ls-micro:   0.18em;
  --fs-w-micro:    600;

  /* Space */
  --fs-space-1: 0.25rem;
  --fs-space-2: 0.5rem;
  --fs-space-3: 0.75rem;
  --fs-space-4: 1rem;
  --fs-space-5: 1.5rem;
  --fs-space-6: 2rem;
  --fs-space-7: 3rem;
  --fs-space-8: 4.5rem;
  --fs-space-9: 7rem;
  --fs-section-y: clamp(4rem, 8vw, 8.5rem);

  /* Measure */
  --fs-w-max:   78rem;
  /* From the shell's content edge to the viewport edge: its own gutter plus
     whatever page margin the shell is centred in. Negate it to bleed an
     element out to the edge from inside the shell. overflow-x: clip on the
     site root is what stops that becoming a scrollbar. */
  --fs-bleed:   calc(var(--fs-gutter) + (100vw - min(100vw, 78rem)) / 2);
  --fs-w-text:  40rem;
  --fs-gutter:  clamp(1.25rem, 4vw, 3.5rem);

  /* Radii */
  --fs-r-sm:   0.375rem;
  --fs-r-md:   0.875rem;
  --fs-r-lg:   1.375rem;
  --fs-r-pill: 62rem;

  /* Motion */
  --fs-t-fast: 160ms;
  --fs-t:      280ms;
  --fs-t-slow: 520ms;
  --fs-ease:   cubic-bezier(0.22, 0.61, 0.36, 1);

  /* Elevation — used sparingly; depth comes from overlap, not shadows. */
  --fs-lift:      0 1.25rem 3rem -1.5rem rgb(var(--fs-c-ink) / .28);
  --fs-lift-soft: 0 0.75rem 2rem -1.25rem rgb(var(--fs-c-ink) / .22);

  --fs-nav-h: 4.5rem;

  /* The logo's four-point spark, as a clip path. It replaces a font glyph
     (U+2726) that neither shipped family contains — the glyph fell through to
     whatever the system had, which is not a brand decision worth delegating. */
  --fs-spark: polygon(50% 0%, 57% 43%, 100% 50%, 57% 57%, 50% 100%, 43% 57%, 0% 50%, 43% 43%);

  /* Viewfinder / focus marks: two L-shaped ticks on opposing corners. A
     supporting motif for the photo experience, not a brand device. */
  --fs-tick-size: 2.25rem;
  --fs-tick-inset: 0.875rem;
}

/* The inverted token scope that lived here belonged to one dark editorial
   section, and the launch homepage does not have one. It is deleted rather
   than kept warm: an unused theme scope is a thing later edits trip over. */


/* == 02  SCOPED RESET & BASE ============================================== */

.full-scene-site *,
.full-scene-site *::before,
.full-scene-site *::after { box-sizing: border-box; }

.full-scene-site {
  margin: 0;
  font-family: var(--fs-font-body);
  font-size: var(--fs-text-base);
  line-height: 1.6;
  color: var(--fs-ink);
  background: var(--fs-surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* clip stops the cropped/off-canvas typography from creating a
     horizontal scrollbar, without creating a scroll container. */
  overflow-x: clip;
}

/* THE ONE UNSCOPED RULE IN THIS FILE, and it has to be.
   Every other rule lives under .full-scene-site so the stylesheet can be
   pasted into GoHighLevel without touching the host page. `body` cannot be
   reached that way, and the browser's default `body { margin: 8px }` was
   quietly making every full-bleed element eight pixels short of the edge:
   measured on the served page, .fs-announce and .fs-hero rendered 1424px wide
   inside a 1440px viewport, with an 8px strip of cream down each side of the
   hero photograph. Nothing caught it before Phase 2 because nothing was
   supposed to touch the edge before Phase 2.
   :has() keeps it narrow — it fires only on a page that actually contains the
   site, so a GoHighLevel page carrying other blocks is unaffected except for
   the body margin its own theme already zeroes. */
body:has(.full-scene-site) { margin: 0; }

/* The element resets are wrapped in :where() so they carry the specificity
   of .full-scene-site alone (0,1,0). That is high enough to beat any bare
   element rule GoHighLevel's theme may apply (0,0,1), and low enough that
   every single-class component rule further down this file wins on source
   order — without needing !important anywhere. */
.full-scene-site :where(h1, h2, h3, h4, p, figure, figcaption, ul, li) {
  margin: 0;
  padding: 0;
}

/* Both list types: every numeral on this site is typeset — the package
   ladder's 02-05, the form's step crumbs, What Happens Next — so a UA marker
   is always a bug. The ladder was the one <ol> whose <li> kept
   display: list-item, and it rendered "1. 2. 3. 4." in a serif face beside
   the cards until this covered <ol> too. */
.full-scene-site :where(ul, ol) { list-style: none; }

.full-scene-site :where(img, svg, picture, video) { display: block; max-width: 100%; }
/* <picture> is a wrapper, not a box: inheriting its parent's height keeps the
   sizing chain between a frame and its image intact, so every
   `<frame> img { height: 100% }` rule keeps working once the image is wrapped
   for AVIF/WebP sources. Where the parent height is auto this resolves to
   auto and changes nothing. */
.full-scene-site :where(picture) { height: 100%; }

.full-scene-site :where(img) { height: auto; }

.full-scene-site :where(a) { color: inherit; text-decoration: none; }

.full-scene-site :where(button) { font: inherit; color: inherit; }

/* One focus treatment everywhere: a blue ring with a light halo so it stays
   visible on warm white, ice blue, imagery and the inverted section alike. */
.full-scene-site :focus-visible {
  outline: 2px solid var(--fs-blue);
  outline-offset: 3px;
  border-radius: var(--fs-r-sm);
  box-shadow: 0 0 0 6px rgb(var(--fs-c-white) / .85);
}

.fs-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.fs-skip {
  position: absolute;
  left: var(--fs-space-4);
  top: var(--fs-space-4);
  z-index: 100;
  padding: 0.75rem 1.125rem;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-sm);
  letter-spacing: 0.05em;
  color: var(--fs-white);
  background: var(--fs-blue);
  border-radius: var(--fs-r-sm);
  transform: translateY(-160%);
  transition: transform var(--fs-t-fast) var(--fs-ease);
}
.fs-skip:focus { transform: none; }


/* == 03  UTILITIES ======================================================== */

.fs-shell {
  width: 100%;
  max-width: var(--fs-w-max);
  margin-inline: auto;
  padding-inline: var(--fs-gutter);
}

/* Screen-reader-only text (prices announce as "Price: $499", etc.) */
.fs-vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Every section head is a sizing container, because the display sizes are
   expressed in cqw against the column the heading sits in. A new section
   without this class will fall back to viewport-relative sizing. */
.fs-section-head {
  container-type: inline-size;
  max-width: 62rem;
}
.fs-section-head > .fs-display { margin-block: var(--fs-space-4) var(--fs-space-5); }

/* Links that sit inside a sentence are exempt from the 44px target rule
   (WCAG 2.2 SC 2.5.8, Inline exception) — forcing the height would break the
   line. Vertical padding on an inline element still grows the hit area
   without affecting the line box, so they get that instead. */
.fs-inline-link {
  padding-block: 0.4375rem;
  color: var(--fs-blue-text);
  text-decoration: underline;
  text-underline-offset: 0.3em;
  text-decoration-thickness: 1px;
}

/* The same treatment where the link is the only thing in its paragraph. There
   the inline exception does not apply, so it takes the full 44px target. */
.fs-standalone-link {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  color: var(--fs-blue-text);
  text-decoration: underline;
  text-underline-offset: 0.3em;
  text-decoration-thickness: 1px;
}

.fs-eyebrow {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  /* Full ink, not .6 alpha. A label set in the brand's own spaced-caps
     treatment is information the page is proud of; fading it was the habit
     of a system that did not trust the label. */
  color: var(--fs-ink);
}
.fs-eyebrow::before {
  content: "";
  display: inline-block;
  width: 1.75rem;
  height: 1px;
  margin-right: 0.75rem;
  vertical-align: 0.28em;
  background: var(--fs-blue);
}
/* The same label on a dark ground. Both ink bands use it. */
.fs-eyebrow--ink { color: var(--fs-white); }

.fs-display {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: var(--fs-display-md);
  line-height: var(--fs-lh-display);
  letter-spacing: var(--fs-ls-display);
  text-transform: uppercase;
}
.fs-display em {
  font-style: normal;
  color: var(--fs-blue);
}

/* Oversized brand statements — the campaign lines. Same face as the section
   headings, set heavier and tighter so it reads as a statement rather than a
   heading. Sizes are set per component against their own container. */
.fs-statement {
  font-family: var(--fs-font-display);
  font-weight: 800;
  line-height: 0.94;
  letter-spacing: -0.01em;
  text-transform: uppercase;
}

/* Micro labels: the logo's spaced-caps treatment. Same family, light weight,
   wide tracking. This is the role that used to belong to a mono face — the
   logo's own "F U L L" and "E X P E R I E N C E S" are geometric sans, not
   monospace, so the mono family was the wrong reference. */
.fs-microlabel {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-ink-soft);
}
/* Micro labels on a dark ground — the filmstrip captions and the ink band. */
.fs-microlabel--ink { color: var(--fs-cream-mute); }
.fs-microlabel__n {
  color: var(--fs-blue-text);
  margin-right: 0.5rem;
}
.fs-microlabel--ink .fs-microlabel__n { color: var(--fs-blue-100); }

.fs-lede {
  font-size: var(--fs-text-lg);
  line-height: 1.5;
  color: var(--fs-ink-soft);
  max-width: var(--fs-w-text);
}

/* Small mono captions attached to media — the alternative to icon cards. */
.fs-tag {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fs-ink-soft);
  margin-top: 0.625rem;
}
.fs-tag::before {
  content: "";
  flex: 0 0 auto;
  width: 0.875rem;
  height: 1px;
  background: var(--fs-blue);
}


.fs-badge {
  display: inline-block;
  margin-left: 0.625rem;
  padding: 0.3125rem 0.625rem;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fs-white);
  background: var(--fs-blue);
  border-radius: var(--fs-r-sm);
  vertical-align: 0.1em;
}

/* The one tier the site points at. "Most popular" is the client's own
   confirmed label for the four-hour package, so it appears exactly once per
   page — on the tier itself — and never as a second nudge somewhere else. */
.fs-badge--pick {
  color: var(--fs-ink);
  background: var(--fs-blue-100);
  box-shadow: inset 0 0 0 1px rgb(var(--fs-c-blue) / .38);
}

/* Its marker on the duration tab: a small spark above the numeral, so the
   recommended length is visible before a tab is chosen. */
.fs-selector__pick {
  position: absolute;
  top: -0.75rem;
  left: 50%;
  translate: -50% 0;
  width: 0.5rem;
  height: 0.5rem;
  background: var(--fs-blue);
  clip-path: var(--fs-spark);
}


/* == 04  BUTTONS ========================================================== */

.fs-btn {
  --_fs-btn-bg: var(--fs-blue);
  --_fs-btn-fg: var(--fs-white);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  min-height: 3rem;              /* 48px tap target */
  padding: 0.875rem 1.625rem;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-sm);
  font-weight: 700;
  /* Sentence case, not a CSS uppercase transform. The all-caps treatment is
     reserved for the small editorial labels — eyebrows, micro labels, section
     numbers — where it is the point. On a button it shouted, and it made
     "Check My Date" read as a warning rather than an invitation. The tracking
     comes down with the case: 0.12em is a caps setting. */
  letter-spacing: 0.01em;
  color: var(--_fs-btn-fg);
  background: var(--_fs-btn-bg);
  border: 1px solid transparent;
  border-radius: var(--fs-r-pill);
  cursor: pointer;
  transition: background-color var(--fs-t) var(--fs-ease),
              color var(--fs-t) var(--fs-ease),
              transform var(--fs-t-fast) var(--fs-ease),
              box-shadow var(--fs-t) var(--fs-ease);
}
.fs-btn:hover  { transform: translateY(-2px); box-shadow: var(--fs-lift-soft); }
.fs-btn:active { transform: translateY(0); }

/* Blue fill movement: a soft light sweep crosses the fill on hover. It is a
   gradient on the button's own background rather than a pseudo-element, so
   nothing has to be stacked, clipped or isolated around the label and arrow.
   `background` in .fs-btn is the shorthand, which resets background-image —
   hence the colour and the gradient being set separately here. */
.fs-btn--primary {
  background-color: var(--_fs-btn-bg);
  background-image: linear-gradient(100deg,
    transparent 32%,
    rgb(var(--fs-c-white) / .16) 50%,
    transparent 68%);
  background-repeat: no-repeat;
  background-size: 240% 100%;
  background-position: 160% 0;
  transition: background-color var(--fs-t) var(--fs-ease),
              background-position 760ms var(--fs-ease),
              color var(--fs-t) var(--fs-ease),
              transform var(--fs-t-fast) var(--fs-ease),
              box-shadow var(--fs-t) var(--fs-ease);
}
.fs-btn--primary:hover { --_fs-btn-bg: #2450e0; background-position: -60% 0; }
@media (prefers-reduced-motion: reduce) {
  .fs-btn--primary { background-image: none; }
}

.fs-btn--ghost {
  --_fs-btn-bg: transparent;
  --_fs-btn-fg: var(--fs-ink);
  border-color: rgb(var(--fs-c-ink) / .22);
}
.fs-btn--ghost:hover {
  --_fs-btn-fg: var(--fs-blue-text);
  border-color: var(--fs-blue);
  box-shadow: none;
}

/* High-visibility secondary. Used for "See all experiences", which has to be
   obviously clickable without competing with the blue primary beside it. */
.fs-btn--outline {
  --_fs-btn-bg: transparent;
  --_fs-btn-fg: var(--fs-blue-text);
  border-color: var(--fs-blue);
  border-width: 1.5px;
}
.fs-btn--outline:hover {
  --_fs-btn-fg: var(--fs-white);
  --_fs-btn-bg: var(--fs-blue);
  border-color: var(--fs-blue);
}

.fs-btn--sm { min-height: 2.75rem; padding: 0.6875rem 1.25rem; }
.fs-btn--block { display: flex; width: 100%; }

/* Full-width primary on small screens only. */
@media (max-width: 47.9375rem) {
  .fs-btn--block-sm { display: flex; width: 100%; }
}


/* == 05  MOTION PRIMITIVES ================================================ */

/* Reveal-on-scroll is opt-in and only arms itself once JS is present, so the
   content is never hidden by a script that failed to load. */
.fs-js [data-fs-reveal] {
  opacity: 0;
  transform: translateY(1.25rem);
  transition: opacity var(--fs-t-slow) var(--fs-ease),
              transform var(--fs-t-slow) var(--fs-ease);
}
.fs-js [data-fs-reveal].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .full-scene-site *,
  .full-scene-site *::before,
  .full-scene-site *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .fs-js [data-fs-reveal] { opacity: 1; transform: none; }
}


/* --- hero entrance sequence ---------------------------------------------
   Above the fold, so this is a load animation rather than a scroll reveal:
   campaign line, then the secondary text ~220ms later, then the media.
   Wrapped in a no-preference query so nothing starts hidden for anyone who
   has asked for less motion, and driven by animation (not the reveal
   observer) so it does not depend on JavaScript. */
@media (prefers-reduced-motion: no-preference) {
  .fs-hero [data-fs-seq] { animation: fs-rise 620ms var(--fs-ease) both; }
  .fs-hero [data-fs-seq="1"] { animation-delay: 40ms; }
  .fs-hero [data-fs-seq="2"] { animation-delay: 260ms; }
  /* The media holds the largest-contentful paint, so it never starts at
     opacity 0 — it settles in from mostly-visible instead, which keeps LCP
     at first paint. */
  .fs-hero [data-fs-seq="3"] {
    animation: fs-settle 720ms var(--fs-ease) both;
    animation-delay: 200ms;
  }
}

@keyframes fs-rise {
  from { opacity: 0; transform: translateY(1.125rem); }
  to   { opacity: 1; transform: none; }
}
@keyframes fs-settle {
  from { opacity: 0.55; transform: scale(0.982); }
  to   { opacity: 1; transform: none; }
}


/* == 06  BRAND MOTIFS ===================================================== */

/* Contact-sheet edge: the perforated margin of a film strip, drawn rather
   than photographed. Used along one edge of a dominant frame. */
/* Sits just OUTSIDE the frame, on the page ground — over a photograph the
   perforations read as an artefact rather than as a film edge. */
.fs-filmedge {
  position: absolute;
  z-index: 0;
  inset: 14% auto 14% -0.5625rem;
  width: 0.4375rem;
  border-radius: 0.1875rem 0 0 0.1875rem;
  background:
    repeating-linear-gradient(
      180deg,
      transparent 0 0.375rem,
      rgb(var(--fs-c-white) / .9) 0.375rem 0.75rem,
      transparent 0.75rem 1.125rem
    ),
    rgb(var(--fs-c-ink) / .32);
  pointer-events: none;
}
.fs-filmedge--right {
  inset: 14% -0.5625rem 14% auto;
  border-radius: 0 0.1875rem 0.1875rem 0;
}

/* Thin radial lines from the logo's burst. A mark, not a decoration: one per
   section at most. */
/* An offset electric-blue outline used to live here as `.fs-frame::before`,
   applied to one frame of the old collage. That collage is gone, so the rule
   had no owner — and it collided head-on with the gallery's new `.fs-frame`
   figures, drawing a stray blue rectangle inside all four of them. Deleted
   rather than renamed: a motif with no section left to sit in is not worth
   keeping warm, and the gallery's own crop marks do this job better because
   they appear on hover instead of always. */

/* The star mark, at motif scale. Given a section reveal it draws itself in
   once — the only celebratory motion on the page, and never a loop. */
.fs-starburst {
  position: absolute;
  width: 2.5rem;
  aspect-ratio: 1;
  color: var(--fs-blue);
  pointer-events: none;
}
.fs-starburst svg { display: block; width: 100%; height: 100%; }

.fs-js [data-fs-reveal] .fs-starburst,
.fs-js .fs-starburst[data-fs-reveal] {
  opacity: 0;
  transform: scale(0.4) rotate(-28deg);
}
.fs-js [data-fs-reveal].is-in .fs-starburst,
.fs-js .fs-starburst[data-fs-reveal].is-in {
  opacity: 1;
  transform: none;
  transition: opacity 520ms var(--fs-ease) 140ms,
              transform 620ms cubic-bezier(0.2, 0.9, 0.3, 1) 140ms;
}
@media (prefers-reduced-motion: reduce) {
  .fs-js [data-fs-reveal] .fs-starburst,
  .fs-js .fs-starburst[data-fs-reveal] { opacity: 1; transform: none; }
}

/* --- masked reveal ------------------------------------------------------
   A wipe instead of a fade, for the two tall editorial photographs. It
   REPLACES the default reveal on that element rather than stacking with it —
   a fade, a rise and a wipe at once reads as a page that cannot decide. The
   selector carries one class more than the base reveal, which is what lets
   it override the opacity and transform without !important.

   The inner image keeps its own slow scale (below), so the picture grows
   very slightly as the mask opens. That pairing is the whole effect. */
@media (prefers-reduced-motion: no-preference) {
  .fs-js [data-fs-reveal].fs-mask { opacity: 1; transform: none; }
  /* The clip goes on the <picture> INSIDE the observed element, never on the
     element itself. Chrome factors an element's own clip-path into the area
     IntersectionObserver measures, so a figure clipped to zero height never
     intersects, never gets .is-in, and therefore never unclips: the reveal
     deadlocks and the photograph is gone for good. Clipping a child keeps
     the observed box full size and the deadlock cannot happen. */
  .fs-js [data-fs-reveal].fs-mask > picture {
    clip-path: inset(0 0 100% 0);
    transition: clip-path 940ms var(--fs-ease);
  }
  .fs-js [data-fs-reveal].fs-mask.is-in > picture { clip-path: inset(0 0 0 0); }
}

/* --- scroll-linked drift ------------------------------------------------
   The brand marks move, but only as the page moves: these are scroll-driven
   animations, so nothing loops and nothing animates while the reader is
   still. Where view() is unsupported the marks simply sit where they are —
   the static position is the design, and the drift is the enhancement. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* One mark, in one section. Its static `transform: rotate()` is a
       separate property from `rotate`, so the base tilt it is drawn with
       survives and the drift adds to it. */
  }
}
@keyframes fs-drift {
  from { translate: 0 1.25rem; }
  to   { translate: 0 -1.25rem; }
}
@keyframes fs-turn {
  from { rotate: -9deg; }
  to   { rotate: 9deg; }
}

/* A slow settle on the largest photographs as they come into view. The image
   itself is scaled, so it keeps its own rounded corners and nothing needs to
   clip it. Deliberately only on the frames big enough for it to register. */
@media (prefers-reduced-motion: no-preference) {
}


/* == 07  ANNOUNCEMENT / MARQUEE =========================================== */

/* The first thing on the page is a deliberate dark edge. It was an ice band
   with 11px type at 68% opacity — measurably the least designed element on
   the site and, worse, a list of the same event categories the Scenes section
   lists better three screens down. Ink does two jobs at once: it mats the
   cream hero below it like a border around a print, and it puts the page's
   first contrast event in the first 44 pixels. */
.fs-announce {
  background: var(--fs-surface-ink);
  border-bottom: 0;
}

.fs-marquee {
  display: flex;
  gap: 0;
  overflow: hidden;
  padding-block: 0.75rem;
  /* Soft edges instead of a hard cut. */
  mask-image: linear-gradient(90deg, transparent, #000 4rem, #000 calc(100% - 4rem), transparent);
}

.fs-marquee__track {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  min-width: 100%;
  animation: fs-marquee 46s linear infinite;
}
.fs-marquee:hover .fs-marquee__track,
.fs-marquee:focus-within .fs-marquee__track { animation-play-state: paused; }

/* Cream at full strength, 12px, 600, 0.18em — 18.4:1 on ink. The old
   treatment was the same family at 400 and 68% opacity on ice, which is the
   difference between a brand voice and a disclaimer. */
.fs-marquee__item {
  flex: 0 0 auto;
  font-family: var(--fs-font-display);
  font-size: 0.75rem;
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-white);
  white-space: nowrap;
}
/* The wordmark's own turn in the loop, so the company name appears above the
   fold without a second logo. Blue would fail small-text contrast on ink, so
   the light electric tint carries it (11.9:1). */
.fs-marquee__item--brand { color: var(--fs-blue-100); }
/* The four-point spark, now readable because it finally has a dark ground.
   Same size, same rhythm — the motion and the separator were already right. */
.fs-marquee__item::after {
  content: "";
  display: inline-block;
  width: 0.4375rem;
  height: 0.4375rem;
  background: var(--fs-blue);
  clip-path: var(--fs-spark);
  margin-inline: 1.375rem;
  vertical-align: 0.08em;
}

@keyframes fs-marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-100%, 0, 0); }
}

/* No looping motion when it isn't wanted — the band becomes a static,
   swipeable strip that still shows the full announcement. */
@media (prefers-reduced-motion: reduce) {
  .fs-marquee {
    overflow-x: auto;
    mask-image: none;
    scrollbar-width: none;
    /* A static strip needs its own edge padding: without the mask the first
       word would start hard against the viewport. */
    padding-inline: var(--fs-gutter);
  }
  .fs-marquee::-webkit-scrollbar { display: none; }
  .fs-marquee__track { animation: none; }
  .fs-marquee__track + .fs-marquee__track { display: none; }
}


/* == 08  NAVIGATION ======================================================= */

.fs-nav-sentinel { display: block; height: 1px; margin-bottom: -1px; }

.fs-nav {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgb(var(--fs-c-white) / 0);
  border-bottom: 1px solid transparent;
  transition: background-color var(--fs-t) var(--fs-ease),
              border-color var(--fs-t) var(--fs-ease),
              box-shadow var(--fs-t) var(--fs-ease);
}
.fs-nav.is-scrolled {
  background: rgb(var(--fs-c-white) / .92);
  backdrop-filter: blur(12px) saturate(1.1);
  border-bottom-color: var(--fs-hairline);
  box-shadow: 0 0.5rem 1.75rem -1.5rem rgb(var(--fs-c-ink) / .5);
}
/* Menu open on mobile: the bar must be opaque over the panel. */
.fs-nav.is-open { background: var(--fs-white); }

.fs-nav__inner {
  display: flex;
  align-items: center;
  gap: var(--fs-space-5);
  width: 100%;
  max-width: var(--fs-w-max);
  min-height: var(--fs-nav-h);
  margin-inline: auto;
  padding-inline: var(--fs-gutter);
}

/* --- logo lockup ---
   The supplied artwork, recoloured for a light ground by tools/build-logo.py.
   It is a display lockup: below about 44px tall the "FULL" and "EXPERIENCES"
   lines stop resolving, which is what sets the bar height. The width/height
   attributes on the <img> carry the intrinsic ratio so nothing shifts. */
.fs-logo {
  display: inline-flex;
  align-items: center;
  margin-right: auto;
  padding-block: 0.375rem;
}
.fs-logo img {
  height: 2.75rem;               /* 44px — the legibility floor */
  width: auto;
  transition: opacity var(--fs-t) var(--fs-ease);
}
.fs-logo:hover img { opacity: 0.78; }
/* The dark section (phase 2) points its own <img> at fs-logo-dark.png.
   Filtering the light file would wreck the blue, and `content: url()` on an
   <img> only reached Firefox in 128. */

.fs-nav__links { display: none; gap: var(--fs-space-6); }
.fs-nav__links a {
  position: relative;
  padding-block: 0.6875rem;      /* 44px target height */
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-sm);
  font-weight: var(--fs-w-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fs-ink);
  transition: color var(--fs-t) var(--fs-ease);
}
.fs-nav__links a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0.4375rem;
  height: 1px;
  background: var(--fs-blue);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--fs-t) var(--fs-ease);
}
.fs-nav__links a:hover { color: var(--fs-ink); }
.fs-nav__links a:hover::after,
.fs-nav__links a[aria-current="page"]::after { transform: scaleX(1); }
.fs-nav__links a[aria-current="page"] { color: var(--fs-ink); }

.fs-nav__cta { display: none; }

.fs-nav__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 2.75rem;
  padding: 0.5rem 0.25rem 0.5rem 0.75rem;
  background: none;
  border: 0;
  cursor: pointer;
}
.fs-nav__bars {
  display: grid;
  gap: 0.3125rem;
  width: 1.375rem;
}
.fs-nav__bars span {
  display: block;
  height: 2px;
  background: var(--fs-ink);
  transition: transform var(--fs-t) var(--fs-ease),
              opacity var(--fs-t-fast) var(--fs-ease);
}
.fs-nav__toggle[aria-expanded="true"] .fs-nav__bars span:first-child { transform: translateY(3.5px) rotate(45deg); }
.fs-nav__toggle[aria-expanded="true"] .fs-nav__bars span:last-child  { transform: translateY(-3.5px) rotate(-45deg); }
.fs-nav__toggle-label {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
}

/* --- mobile menu panel --- */
.fs-menu {
  padding: var(--fs-space-5) var(--fs-gutter) var(--fs-space-7);
  background: var(--fs-white);
  border-top: 1px solid var(--fs-hairline);
}
.fs-menu__links { display: grid; }
.fs-menu__links a {
  display: block;
  padding-block: 0.875rem;
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.375rem, 6.4vw, 1.875rem);
  line-height: 1.05;
  letter-spacing: var(--fs-ls-display);
  text-transform: uppercase;
  border-bottom: 1px solid var(--fs-hairline);
  transition: color var(--fs-t) var(--fs-ease);
}
.fs-menu__links a:hover,
.fs-menu__links a[aria-current="page"] { color: var(--fs-blue); }
.fs-menu__area {
  margin-block: var(--fs-space-5);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  line-height: 1.7;
  letter-spacing: 0.04em;
  color: var(--fs-ink-mute);
}

@media (min-width: 62rem) {
  .full-scene-site { --fs-nav-h: 5rem; }
  .fs-nav__links { display: flex; }
  .fs-nav__cta   { display: inline-flex; }
  .fs-nav__toggle,
  .fs-menu      { display: none; }
  .fs-logo img { height: 3.25rem; }
}


/* == 09  HERO ============================================================= */

/* The hero was 9.2% of the page with the type block eating 45% of the width
   at 1440 and the small frame reading as a floating thumbnail. It is now the
   EDITORIAL SPREAD archetype: type held left in a narrow measure, the
   dominant photograph bleeding off the right edge of the shell — the first
   use of the --fs-bleed token, which was declared and never spent — and the
   small frame moved to overlap the dominant frame's lower-left so it sits
   BETWEEN the type and the image instead of floating beside it. */
.fs-hero {
  position: relative;
  overflow: clip;
  background:
    radial-gradient(120% 90% at 92% 4%, rgb(var(--fs-c-blue-100) / .6), transparent 58%),
    linear-gradient(180deg, var(--fs-white), var(--fs-white) 70%, var(--fs-white));
}

/* Off-axis light bloom, pushed further right so it reads as light coming from
   behind the photograph rather than as a wash over the whole band. */
.fs-hero__bloom {
  position: absolute;
  top: -22%;
  right: -10%;
  width: min(46rem, 78vw);
  aspect-ratio: 1;
  background: radial-gradient(circle at 50% 50%, rgb(var(--fs-c-blue) / .24), rgb(var(--fs-c-blue) / .06) 42%, transparent 68%);
  filter: blur(18px);
  pointer-events: none;
}

/* The logo's own star and burst marks, cropped, off-axis, partly off-canvas
   and held at 7%. Loaded as a background so no empty decorative <img> element
   ends up in the document. */
.fs-hero__burst {
  position: absolute;
  left: -11%;
  bottom: -16%;
  width: min(27rem, 50vw);
  aspect-ratio: 1;
  background: url("../img/logo/fs-star-motif.png") no-repeat center / contain;
  opacity: 0.06;
  transform: rotate(-9deg);
  pointer-events: none;
}

.fs-hero__inner {
  position: relative;
  container-type: inline-size;
  width: 100%;
  max-width: var(--fs-w-max);
  margin-inline: auto;
  padding: var(--fs-space-6) var(--fs-gutter) var(--fs-space-7);
}

/* Sizing container for the headline: it is measured against this column, so
   it fits by construction at every width. */
.fs-hero__copy { container-type: inline-size; }

.fs-hero__eyebrow { margin-bottom: clamp(1.25rem, 3vw, 2rem); }

/* The h1 is a container, not a typeface: the campaign line and the service
   line are both inside it, so the page keeps exactly one primary heading
   while the editorial line stays visually dominant. */
.fs-hero__title {
  position: relative;
  z-index: 3;
  display: block;
}
.fs-hero__campaign {
  display: block;
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: var(--fs-display-xl);
  line-height: 0.92;
  letter-spacing: -0.028em;
  text-transform: uppercase;
}
/* THE TWO-TONE DEVICE, established here rather than only at the bottom of
   the page. Ink line, electric-blue line. It is the most brand-specific
   typographic move the identity owns, and it is used exactly three times on
   this page — here, the ink band, and the final CTA — so it stays a signature
   instead of becoming a template. The break is explicit rather than left to
   text-wrap, because the two-tone only reads as a device when each colour
   owns a whole line. */
.fs-hero__campaign em {
  display: block;
  font-style: normal;
  color: var(--fs-blue);
}
/* Says what the business actually does. Sized as a deck so it reads as part
   of the heading without competing with the campaign line. */
.fs-hero__service {
  display: block;
  max-width: 22rem;
  text-wrap: balance;
  margin-top: clamp(0.875rem, 2vw, 1.375rem);
  font-family: var(--fs-font-body);
  font-size: var(--fs-text-md);
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: 0;
  text-transform: none;
  color: var(--fs-ink-soft);
}

/* --- the media ---------------------------------------------------------
   On a phone this is ONE photograph bleeding to both edges. No overlap, no
   second frame, no decoration: the detail frame reappears in the gallery,
   where it has room. Mobile is designed here, not inherited. */
.fs-hero__media {
  position: relative;
  z-index: 1;
  margin-top: var(--fs-space-6);
  margin-inline: calc(-1 * var(--fs-bleed));
}
.fs-hero__shot { position: relative; }
.fs-hero__shot img {
  display: block;
  width: 100%;
  object-fit: cover;
}
.fs-hero__shot--primary img { aspect-ratio: 3 / 4; }
/* Edge to edge means edge to edge: no radius on a bleeding frame, because a
   rounded corner against the viewport edge is what makes a full-bleed image
   read as a very wide card. */
.fs-hero__shot--share { display: none; }

/* The mask reveal. It starts at .55 opacity and 18% inset rather than from
   nothing, so the hero photograph is painted on the first frame — a hero
   image that animates in from zero is a hero image that fails LCP. */
@media (prefers-reduced-motion: no-preference) {
  .fs-hero__shot--primary img {
    animation: fs-hero-mask 640ms var(--fs-ease) both;
  }
}
@keyframes fs-hero-mask {
  from { clip-path: inset(0 0 18% 0); opacity: 0.55; }
  to   { clip-path: inset(0 0 0 0);   opacity: 1; }
}

.fs-hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fs-space-4);
  margin-top: var(--fs-space-5);
}

/* --- hero, tablet ------------------------------------------------------ */
@media (min-width: 48rem) {
  .fs-hero__media { margin-inline: calc(-1 * var(--fs-bleed)) calc(-1 * var(--fs-bleed)); }
  .fs-hero__shot--primary img { aspect-ratio: 16 / 10; }
}

/* --- hero, desktop: the editorial spread ------------------------------- */
@media (min-width: 62rem) {
  .fs-hero__inner {
    display: grid;
    /* Type 58, photograph 42. The photograph then bleeds a further --fs-bleed
       to the right, so at 1440 it renders ~629px wide against a 659px type
       column: two masses of comparable weight, which is what makes the
       composition read as authored rather than as a text block with a
       picture next to it. */
    grid-template-columns: minmax(0, 58fr) minmax(0, 42fr);
    grid-template-areas: "copy media";
    align-items: center;
    column-gap: clamp(1.5rem, 3vw, 3rem);
    padding-block: clamp(2rem, 5vh, 3.5rem) clamp(3rem, 7vh, 5rem);
  }
  .fs-hero__copy  { grid-area: copy;  z-index: 6; }
  .fs-hero__media {
    grid-area: media;
    z-index: 4;
    align-self: center;
    /* One edge only. The left edge of the photograph is a compositional line
       the small frame crosses; the right edge is the page edge. */
    margin: 0 calc(-1 * var(--fs-bleed)) 0 0;
  }
  /* No aspect-ratio cap and no 12-row grid here on purpose. Both used to be,
     and between them they made the dominant photograph SHRINK as the viewport
     grew: 518px wide at 768 but 393px at 1440, because the cap set the height
     and `height: 100%` then set the width. The photograph now sets its own
     height from its 4:5 crop, so the hero image gets larger with the screen
     instead of smaller. */
  .fs-hero__shot--primary img {
    aspect-ratio: 4 / 5;
    height: auto;
  }
  /* Focus marks on the dominant frame — the same treatment the package media
     uses when the selection changes. Only the top-left mark survives the
     bleed; the bottom-right one is inset from the page edge, not the frame. */
  .fs-hero__shot--primary::before {
    content: "";
    position: absolute;
    z-index: 2;
    top: var(--fs-tick-inset);
    left: var(--fs-tick-inset);
    width: var(--fs-tick-size);
    height: var(--fs-tick-size);
    border: 1.5px solid rgb(var(--fs-c-white) / .85);
    border-right: 0;
    border-bottom: 0;
    border-top-left-radius: 0.375rem;
    pointer-events: none;
  }

  /* THE SECOND BEAT. It straddles the dominant frame's left edge at the
     bottom, so half of it sits on cream and half on the photograph — a
     deliberate crossing rather than a thumbnail parked in a corner. The crop
     behind it (p1 at anchor 0.84) has nothing but a cream dress in that
     region, which is why the anchor moved. */
  .fs-hero__shot--share {
    display: block;
    position: absolute;
    left: clamp(-6rem, -5vw, -3rem);
    bottom: clamp(-2.25rem, -2vw, -1.25rem);
    width: clamp(8.5rem, 15cqw, 12rem);
    z-index: 7;
  }
  .fs-hero__shot--share img {
    aspect-ratio: 1;
    border: 5px solid var(--fs-white);
    border-radius: 0.25rem;
    box-shadow: var(--fs-lift);
  }
  /* A micro label inside the frame, so the one place the booth appears above
     the fold is named rather than left to be guessed at. */
  .fs-hero__shot--share .fs-tag {
    display: flex;
    position: absolute;
    left: 0.75rem;
    bottom: 0.75rem;
    margin: 0;
    gap: 0;
    padding: 0.3125rem 0.625rem;
    font-size: 0.625rem;
    letter-spacing: 0.1em;
    white-space: nowrap;
    color: var(--fs-white);
    background: rgb(var(--fs-c-ink) / .9);
    border-radius: var(--fs-r-pill);
  }
  .fs-hero__shot--share .fs-tag::before { display: none; }

  .fs-hero__actions {
    flex-wrap: nowrap;
    gap: var(--fs-space-5);
  }
  .fs-hero__actions .fs-btn { white-space: nowrap; }
  .fs-hero__actions .fs-btn--ghost {
    padding-inline: 0.125rem;
    border-color: transparent;
    color: var(--fs-blue);
  }
  .fs-hero__actions .fs-btn--ghost:hover {
    transform: none;
    text-decoration: underline;
    text-underline-offset: 0.4em;
    text-decoration-thickness: 1px;
  }
}


/* == 10  SHORT ABOUT ===================================================== */

/* Type only — the one section on the page with no image at all, and that
   absence is the design: straight after the hero's photographic mass, a band
   of pure type reads as confidence. It was a centred paragraph at body size
   on an ice panel, which measured as the least designed moment on the page.
   Cream now, no panel, left-aligned in a narrow measure, with the right half
   of the band deliberately empty.
   The confirmed copy is unchanged. Only its treatment is.
   No two-tone headline here on purpose: the device is spent on the hero, the
   ink band and the final CTA, and a fourth use in the section immediately
   after the first would turn a signature into a template. */
.fs-about {
  position: relative;
  overflow: clip;
  padding-block: clamp(3.5rem, 7vw, 7rem);
  background: var(--fs-surface);
}
.fs-about__inner {
  position: relative;
  container-type: inline-size;
  /* the nav is sticky, so an in-page anchor has to clear it */
  scroll-margin-top: calc(var(--fs-nav-h) + 2rem);
}
.fs-about__measure { max-width: 32rem; }
.fs-about__inner .fs-eyebrow::before { display: none; }

.fs-about__title {
  margin-block: var(--fs-space-5) var(--fs-space-6);
  font-size: clamp(1.75rem, 8.2cqw, 3.25rem);
}
/* A block, and NOT coloured: the two-tone device is spent on the hero, the
   ink band and the final CTA. A block also means the space between the two
   sentences cannot go missing, which is the class of bug that put
   "REAL MOMENTS,NOT POSES." on every phone. */
.fs-about__title em {
  display: block;
  font-style: normal;
  color: inherit;
}

/* The paragraph is promoted out of body size into a statement of its own —
   28px on a phone to 34px at 1440, in a 32-character measure. Space Grotesk,
   not the display face: two display voices in one band compete, and the
   headline above it has already been loud. */
.fs-about__body {
  font-size: clamp(1.1875rem, 3.4cqw, 1.75rem);
  line-height: 1.42;
  letter-spacing: -0.012em;
  color: var(--fs-ink);
  max-width: 30rem;
}
.fs-about__link { margin-top: clamp(1.5rem, 4vw, 2.25rem); }

/* One huge cropped star, behaving as ground rather than as a sticker: the
   motif at scale, bleeding off the LEFT edge, at 5%. The small floating
   version that used to sit beside the heading is gone — the report's rule is
   two scales only, huge or tiny, nothing in between. Suppressed on a phone,
   where it would crowd the measure instead of grounding it. */
.fs-about__star {
  position: absolute;
  display: none;
  left: -14%;
  top: 50%;
  translate: 0 -50%;
  width: min(34rem, 46vw);
  aspect-ratio: 1;
  background: url("../img/logo/fs-star-motif.png") no-repeat center / contain;
  opacity: 0.05;
  transform: rotate(-7deg);
  pointer-events: none;
}

@media (min-width: 62rem) {
  .fs-about__star { display: block; }
  /* The measure is held to the left of the shell and the right stays empty.
     This is the asymmetric whitespace the brief asks for — not a centred
     column with equal air on both sides. */
  .fs-about__inner { padding-left: clamp(0rem, 6vw, 5rem); }
  .fs-about__measure { max-width: 38rem; }
}


/* == 11  CHOOSE YOUR SCENE — PACKAGE SELECTOR ============================= */

.fs-packages {
  position: relative;
  overflow: clip;
  padding-block: var(--fs-section-y);
  background: var(--fs-white);
}

/* The blue light shifts as the duration changes. */
.fs-packages__glow {
  position: absolute;
  left: var(--fs-glow-x, 62%);
  top: var(--fs-glow-y, 34%);
  width: min(42rem, 88vw);
  aspect-ratio: 1;
  translate: -50% -50%;
  background: radial-gradient(circle at 50% 50%, rgb(var(--fs-c-blue) / .16), rgb(var(--fs-c-blue-100) / .22) 40%, transparent 66%);
  filter: blur(14px);
  pointer-events: none;
  transition: left var(--fs-t-slow) var(--fs-ease),
              top var(--fs-t-slow) var(--fs-ease);
}

.fs-packages__head { position: relative; max-width: 52rem; }
.fs-packages__title { margin-block: var(--fs-space-4) var(--fs-space-4); }
/* --- duration selector --- */
.fs-selector {
  position: relative;
  margin-top: clamp(2rem, 4vw, 3rem);
  max-width: 42rem;      /* keeps the rail tied to the tabs it describes */
}

.fs-selector__track {
  display: flex;
  gap: clamp(1rem, 4vw, 3rem);
  align-items: flex-end;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  /* room above for the recommended marker on one of the tabs */
  padding-block: 1rem 0.75rem;
  /* Bleed to the screen edge so the swipe feels native on mobile. */
  margin-inline: calc(var(--fs-gutter) * -1);
  padding-inline: var(--fs-gutter);
}
.fs-selector__track::-webkit-scrollbar { display: none; }

.fs-selector__btn {
  position: relative;
  flex: 0 0 auto;
  scroll-snap-align: center;
  display: flex;
  align-items: baseline;
  gap: 0.25rem;
  min-height: 3rem;
  padding: 0.25rem 0.125rem;
  background: none;
  border: 0;
  color: var(--fs-ink-mute);
  cursor: pointer;
  transition: color var(--fs-t) var(--fs-ease), opacity var(--fs-t) var(--fs-ease);
}
.fs-selector__num {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.5rem, 4.6vw, 2.25rem);
  line-height: 0.95;
  letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums;
}
.fs-selector__unit {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.fs-selector__btn:hover { color: var(--fs-ink); }
.fs-selector__btn[aria-selected="true"] { color: var(--fs-blue); }
.fs-selector__btn[aria-selected="true"] .fs-selector__num { font-size: clamp(1.875rem, 5.8vw, 2.875rem); }

/* Thin electric-blue underline that slides to the selected duration — the
   only divider this section needs. Geometry comes from the tab itself
   (--fs-ind-x / --fs-ind-w, set in JS against the rail), so it tracks the
   tab as the rail is swiped as well as when the selection changes. Without
   JS it falls back to a full-width rule. */
.fs-selector__rail {
  position: relative;
  height: 2px;
  overflow: hidden;
  background: var(--fs-hairline);
}
.fs-selector__progress {
  position: absolute;
  inset: 0 auto 0 0;
  width: 100%;
  background: var(--fs-blue);
}
.fs-js .fs-selector__progress {
  width: var(--fs-ind-w, 25%);
  transform: translate3d(var(--fs-ind-x, 0px), 0, 0);
  transition: transform var(--fs-t) var(--fs-ease),
              width var(--fs-t) var(--fs-ease);
}
/* While the rail is being dragged the underline should follow the finger, not
   ease behind it. */
.fs-js .fs-selector.is-tracking .fs-selector__progress { transition: none; }

/* --- package panels: stacked in one grid cell so they cross-fade with no
       layout shift, and the container is always as tall as the tallest. --- */
/* Ice returns here, and only here on this page: as a PANEL behind the open
   package rather than as a section background. That is the job it is good
   at — it separates a live, changing region from the page around it — and it
   is the reason ice was demoted everywhere else. */
.fs-package-stack {
  display: grid;
  margin-top: clamp(3.5rem, 7vw, 6rem);
}

.fs-package {
  position: relative;
  grid-area: 1 / 1;
  display: grid;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  /* visibility (not display) keeps the inactive panels out of the tab order
     while still sizing the stack, so switching never shifts the layout. */
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--fs-t-slow) var(--fs-ease),
              visibility var(--fs-t-slow) var(--fs-ease);
}
.fs-package[data-fs-active] { opacity: 1; visibility: visible; }

/* The panel's three parts settle in on a short stagger when the duration
   changes, so the name, price, image and inclusions arrive rather than
   snapping. Reduced motion collapses the durations globally. */
.fs-js .fs-package[data-fs-active] > .fs-package__head    { animation: fs-rise-sm 480ms var(--fs-ease) 60ms both; }
.fs-js .fs-package[data-fs-active] > .fs-package__media   { animation: fs-rise-sm 520ms var(--fs-ease) 120ms both; }
.fs-js .fs-package[data-fs-active] > .fs-package__detail  { animation: fs-rise-sm 480ms var(--fs-ease) 180ms both; }

@keyframes fs-rise-sm {
  from { opacity: 0; transform: translateY(0.625rem); }
  to   { opacity: 1; transform: none; }
}

/* The oversized numeral IS the layout: content sits in its negative space. */
.fs-package__numeral {
  position: absolute;
  left: -0.05em;
  top: -0.2em;
  z-index: 0;
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: var(--fs-numeral);
  line-height: 0.78;
  letter-spacing: -0.02em;
  color: rgb(var(--fs-c-blue) / .09);
  pointer-events: none;
  user-select: none;
}

/* head / media / detail are separate children so the panel can reorder
   between breakpoints without duplicating markup: on phones the image sits
   directly under the selector, where a duration change is actually visible. */
.fs-package__head,
.fs-package__detail {
  position: relative;
  z-index: 1;
}
.fs-package__head { padding-top: clamp(3.5rem, 15vw, 9rem); }

.fs-package__hours {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.25rem 0;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-ink-mute);
}
.fs-package__name {
  margin-top: var(--fs-space-2);
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: var(--fs-display-sm);
  line-height: 1;
  letter-spacing: var(--fs-ls-display);
  text-transform: uppercase;
}
.fs-package__price {
  margin-top: var(--fs-space-3);
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(2rem, 5vw, 2.875rem);
  line-height: 1;
  letter-spacing: -0.025em;
  color: var(--fs-blue);
}

/* --- "See everything included" disclosure --- */
.fs-details { margin-top: var(--fs-space-5); max-width: 30rem; }
.fs-details > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fs-blue-text);
  cursor: pointer;
  list-style: none;
  border-top: 1px solid var(--fs-hairline-b);
  padding-top: 0.875rem;
}
.fs-details > summary::-webkit-details-marker { display: none; }
.fs-details > summary::after {
  content: "";
  width: 0.625rem;
  height: 0.625rem;
  margin-left: auto;
  background:
    linear-gradient(var(--fs-blue), var(--fs-blue)) center / 100% 1.5px no-repeat,
    linear-gradient(var(--fs-blue), var(--fs-blue)) center / 1.5px 100% no-repeat;
  transition: transform var(--fs-t) var(--fs-ease);
}
.fs-details[open] > summary::after { transform: rotate(90deg); background-size: 100% 1.5px, 0 0; }
.fs-details__body { padding-block: var(--fs-space-4) var(--fs-space-2); }
.fs-details__body ul { display: grid; gap: 0.4375rem; }
.fs-details__body li {
  position: relative;
  padding-left: 1.125rem;
  font-size: var(--fs-text-sm);
  color: var(--fs-ink-soft);
}
.fs-details__body li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6875em;
  width: 0.4375rem;
  height: 1px;
  background: var(--fs-blue);
}
.fs-details__note {
  margin-top: var(--fs-space-4);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  line-height: 1.7;
  color: var(--fs-ink-mute);
}

.fs-package__detail .fs-btn { margin-top: var(--fs-space-6); }

/* --- package media panel --- */
.fs-package__media { position: relative; }
.fs-package__media img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--fs-r-lg);
  box-shadow: var(--fs-lift);
}
/* The same focus marks, ticking in when the duration changes. */
.fs-package__media::before,
.fs-package__media::after {
  content: "";
  position: absolute;
  z-index: 2;
  width: var(--fs-tick-size);
  height: var(--fs-tick-size);
  border: 1.5px solid var(--fs-blue);
  opacity: 0;
  pointer-events: none;
}
.fs-package__media::before {
  top: var(--fs-tick-inset);
  left: var(--fs-tick-inset);
  border-right: 0;
  border-bottom: 0;
  border-top-left-radius: 0.375rem;
}
.fs-package__media::after {
  right: var(--fs-tick-inset);
  bottom: var(--fs-tick-inset);
  border-left: 0;
  border-top: 0;
  border-bottom-right-radius: 0.375rem;
}
.fs-package__media.is-framing::before,
.fs-package__media.is-framing::after { animation: fs-frame 900ms var(--fs-ease) 1; }
@keyframes fs-frame {
  0%   { opacity: 0; scale: 1.035; }
  35%  { opacity: 0.9; scale: 1; }
  100% { opacity: 0; scale: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .fs-package__media.is-framing::before,
  .fs-package__media.is-framing::after { animation: none; }
}

.fs-packages__foot {
  position: relative;
  max-width: 44rem;
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-top: var(--fs-space-5);
  border-top: 1px solid var(--fs-hairline);
  font-size: var(--fs-text-sm);
  line-height: 1.7;
  color: var(--fs-ink-mute);
}

/* Phones: the highlights follow the image, so the panel reads
   identity -> picture -> what you get -> CTA. */
.fs-package__media { margin-block: var(--fs-space-5) var(--fs-space-6); }

@media (min-width: 48rem) {
  .fs-package {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.72fr);
    grid-template-areas:
      "head   media"
      "detail media";
    grid-template-rows: auto 1fr;
    row-gap: 0;              /* the inclusions carry their own top margin */
    align-items: start;
  }
  .fs-package__head   { grid-area: head; }
  .fs-package__detail { grid-area: detail; }
  .fs-package__media  { grid-area: media; margin-block: clamp(2rem, 6vw, 5rem) 0; }
}

@media (min-width: 62rem) {
  .fs-selector__track { justify-content: space-between; gap: 1rem; }
  /* Further off the left edge, so it reads as ground the content sits in
     rather than as a very big number that happens to fit. */
  .fs-package__numeral { left: -0.12em; top: -0.36em; }
  .fs-package__head { padding-top: clamp(6rem, 11vw, 11rem); }
  /* The package photograph is allowed to reach the page edge, like the hero
     and the gallery. Same move, third place, no new idea. */
  .fs-package__media { margin-right: calc(-1 * var(--fs-bleed) / 2); }
  .fs-package__media img { border-radius: var(--fs-r-lg) 0 0 var(--fs-r-lg); }
}


/* The four-to-six inclusions a customer compares on. Everything else is in
   the disclosure below them, so the four cards stay scannable. */
.fs-package__list {
  display: grid;
  gap: 0.5rem;
  margin-bottom: var(--fs-space-5);
}
.fs-package__list li {
  position: relative;
  padding-left: 1.375rem;
  font-size: var(--fs-text-sm);
  line-height: 1.5;
  color: var(--fs-ink-soft);
}
.fs-package__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 0.5rem;
  height: 0.5rem;
  background: var(--fs-blue);
  clip-path: var(--fs-spark);
}

/* Two actions under the selector: look at everything, or check a date. The
   outlined one is deliberately loud — it is the path most visitors want and
   it used to be a quiet text link. */
.fs-packages__foot {
  display: grid;
  gap: 0.75rem;
  margin-top: clamp(2rem, 5vw, 3rem);
  padding-top: clamp(1.5rem, 4vw, 2.25rem);
  border-top: 1px solid var(--fs-hairline);
}
@media (min-width: 34rem) {
  .fs-packages__foot {
    grid-auto-flow: column;
    justify-content: start;
    gap: var(--fs-space-4);
  }
}


/* == 14  MADE FOR EVERY SCENE ============================================= */

/* Four equal rounded cards with a gradient scrim and a label bottom-left is
   the single most category-normal thing a event vendor's site can do; every
   competitor's site looks like it. The strategy — four broad categories, not
   a list of occasions — was right and is kept. The form is now a FILMSTRIP:
   one edge-to-edge band, four frames at four different ratios and four
   different heights, separated by cream gutters, with the category names
   BELOW the band. Moving the labels off the photographs is what lets the
   photographs stop needing scrims, which is what stops them looking like
   cards. */
.fs-scenes {
  padding-block: clamp(3.5rem, 7vw, 6.5rem) 0;
  background: var(--fs-white);
}
.fs-scenes__head { margin-bottom: clamp(2rem, 5vw, 3rem); }

/* A native scroll-snap rail on a phone: no carousel library, nothing
   auto-advances, and the region is focusable so it can be scrolled from the
   keyboard. Full bleed at every width — the section IS the images. */
.fs-scenes__rail {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  /* Bleeds out of the shell it is written inside. overflow-x: clip on the
     site root is what stops that becoming a page scrollbar. */
  margin-inline: calc(-1 * var(--fs-bleed));
  padding-inline: var(--fs-gutter);
}
.fs-scenes__rail::-webkit-scrollbar { display: none; }

.fs-scenes__track {
  display: flex;
  /* 4px, not 20px. A hairline gutter reads as the cut between two frames of
     one strip; a wide gutter reads as the gap between two cards. */
  gap: 4px;
  width: max-content;
  align-items: flex-start;
}

/* --- one frame ---------------------------------------------------------
   The band's height is one number; each frame takes a fraction of it, so the
   bottom edge STEPS instead of lining up. Widths are unequal too. Between
   them that is what makes four photographs read as one authored strip. */
.fs-scenes__track {
  --fs-strip-h: clamp(15rem, 58vw, 30rem);
}
.fs-scene {
  flex: 0 0 auto;
  scroll-snap-align: center;
  width: clamp(12rem, 66vw, 21rem);
}
.fs-scene figure {
  position: relative;
  margin: 0;
}
/* The loading colour sits on the PICTURE, not the figure. On the figure it
   painted behind the caption too, which put a pale panel under every label
   and turned four frames back into four cards with footers. */
.fs-scene picture {
  display: block;
  height: auto;                 /* same reason as .fs-frame picture, below */
  overflow: hidden;
  background: var(--fs-ice);
}
.fs-scene img {
  display: block;
  width: 100%;
  height: var(--fs-scene-h, var(--fs-strip-h));
  object-fit: cover;
  transition: filter var(--fs-t) var(--fs-ease);
}

/* The label row, below the band. Micro tier at 600 with the index in blue —
   information, on cream, where it can be read. */
.fs-scene figcaption {
  display: grid;
  gap: 0.3125rem;
  padding-top: 0.75rem;
}
.fs-scene__index {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  color: var(--fs-blue-text);
}
.fs-scene__name {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(0.9375rem, 3.4vw, 1.125rem);
  line-height: 1.05;
  letter-spacing: -0.005em;
  text-transform: uppercase;
  overflow-wrap: break-word;
  color: var(--fs-ink);
}
.fs-scene__name span[aria-hidden] { color: var(--fs-blue-text); }

/* The stepped heights and the unequal widths, per frame. */
.fs-scene--a { width: clamp(13rem, 72vw, 23rem); --fs-scene-h: var(--fs-strip-h); }
.fs-scene--b { width: clamp(11rem, 56vw, 17rem); --fs-scene-h: calc(var(--fs-strip-h) * 0.92); }
.fs-scene--c { width: clamp(12rem, 62vw, 19rem); --fs-scene-h: calc(var(--fs-strip-h) * 0.97); }
.fs-scene--d { width: clamp(10rem, 52vw, 15rem); --fs-scene-h: calc(var(--fs-strip-h) * 0.88); }

/* Hover: a small lift in brightness and two crop marks at opposing corners.
   This is the crop motif's ONLY job on this page — it never decorates a
   heading. Pointer-only, so a phone is not left with a stuck state. */
@media (hover: hover) {
  .fs-scene figure:hover img { filter: brightness(1.06); }
  .fs-scene figure::before,
  .fs-scene figure::after {
    content: "";
    position: absolute;
    z-index: 2;
    width: 1.5rem;
    height: 1.5rem;
    border: 1.5px solid rgb(var(--fs-c-white) / .9);
    opacity: 0;
    transition: opacity var(--fs-t) var(--fs-ease);
    pointer-events: none;
  }
  .fs-scene figure::before {
    top: 0.75rem; left: 0.75rem;
    border-right: 0; border-bottom: 0;
  }
  .fs-scene figure::after {
    right: 0.75rem; bottom: 0.75rem;
    border-left: 0; border-top: 0;
  }
  .fs-scene figure:hover::before,
  .fs-scene figure:hover::after { opacity: 1; }
}

/* The line that stops four categories reading as the only four events Full
   Scene will take. It used to float under the cards with nothing to attach
   to; it now closes the label row at the far right, in blue, where it reads
   as the end of the list rather than as a fifth headline. */
.fs-scenes__all {
  margin-top: clamp(1.25rem, 3vw, 1.75rem);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-blue-text);
}

/* A 1-of-4 counter, phone only: the rail is swipeable and the frames are
   unequal, so the count is the only honest signal of how much is off screen.
   Written by JS; the element stays empty and hidden without it. */
.fs-scenes__count {
  margin-top: var(--fs-space-4);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  color: var(--fs-ink-mute);
}
.fs-scenes__count:empty { display: none; }

@media (min-width: 62rem) {
  /* All four visible, no scroll. The frames still do not fill the band
     equally — that is the point — and the track is exactly as wide as the
     viewport, so nothing is clipped and nothing invites a drag that will not
     move. */
  .fs-scenes__rail {
    overflow-x: visible;
    scroll-snap-type: none;
    /* Edge to edge means edge to edge: the phone rail keeps a gutter so the
       first frame is not flush against the screen, but at this width the
       band IS the section and it runs the full viewport. */
    padding-inline: 0;
  }
  .fs-scenes__track {
    --fs-strip-h: clamp(20rem, 38vw, 34rem);
    display: grid;
    /* the measured proportions from the storyboard: 32 / 22 / 26 / 20 */
    grid-template-columns: 32fr 22fr 26fr 20fr;
    width: auto;
  }
  .fs-scene,
  .fs-scene--a, .fs-scene--b, .fs-scene--c, .fs-scene--d { width: auto; }
  /* The band runs to both page edges; the LABELS do not. The outer two get
     the shell's gutter so the first index is not flush against the screen
     and the last name has somewhere to wrap. A caption indented from its own
     frame's edge reads as art direction; a caption touching the glass reads
     as a bug. */
  .fs-scene--a figcaption { padding-left: var(--fs-gutter); }
  .fs-scene--d figcaption { padding-right: var(--fs-gutter); }
  .fs-scenes__count { display: none; }
  .fs-scenes__foot {
    display: flex;
    align-items: baseline;
    justify-content: flex-end;
  }
  .fs-scenes__all { margin-top: clamp(1.25rem, 2.5vw, 2rem); }
}


/* == 15  PICTURE THE SCENE ================================================ */

/* THE CONTACT SHEET, OPENED. The old version was four rounded rectangles at
   one radius, one shadow and one ratio, three of which showed people POSING
   at a booth directly under the words "not poses", with the headline
   stranded in an empty lower-left quadrant and mobile serving the desktop
   collage stacked. Every one of those is fixed by construction here:

     * a deliberate ratio ladder — 3:2, 9:16, 1:1, 16:9 — because four
       different shapes are what make a composition read as art direction
       rather than a grid with the borders turned off
     * the dominant frame is the one photograph in the library with no booth
       in it at all, which is what earns the words
     * the headline sits ON that photograph, so it can never be stranded and
       the "REAL MOMENTS,NOT POSES." missing-space bug cannot come back: the
       break is a block element, not a <br> a media query hides
     * mobile is a purpose-built swipe gallery, not this composition stacked
     * exactly one frame here shows the booth, and it is not the dominant one */
.fs-picture {
  position: relative;
  overflow: clip;
  padding-block: clamp(3.5rem, 7vw, 6.5rem);
  background: var(--fs-surface);
}
.fs-picture__head { margin-bottom: clamp(1.75rem, 4vw, 2.5rem); }

/* One huge cropped star as ground, bleeding off the left at 5%. */
.fs-picture__star {
  position: absolute;
  left: -12%;
  bottom: -8%;
  width: min(30rem, 52vw);
  aspect-ratio: 1;
  background: url("../img/logo/fs-star-motif.png") no-repeat center / contain;
  opacity: 0.05;
  transform: rotate(-12deg);
  pointer-events: none;
}

.fs-sheet2 { position: relative; }

/* --- the pull line -----------------------------------------------------
   On a phone it is a static statement above the gallery: the type is not in
   the swipe. On a desktop the same element becomes a grid item inside the
   dominant frame's own cell, bottom-left, over the scrim. Two treatments,
   one element, no duplicated copy. */
.fs-picture__pull {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.3125rem, 7.4vw, 2.25rem);
  line-height: 0.96;
  letter-spacing: -0.012em;
  text-transform: uppercase;
  color: var(--fs-ink);
  margin-bottom: clamp(1.5rem, 4vw, 2rem);
}
/* A block, not a <br>. The live bug was `.fs-collage__pull br { display:
   none }` below 48rem with no space either side of the <br>, so every phone
   rendered "REAL MOMENTS,NOT POSES." A block element cannot lose a space. */
.fs-picture__pull em {
  display: block;
  font-style: normal;
}

/* --- the swipe track, phone -------------------------------------------- */
.fs-sheet2__track {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  margin-inline: calc(-1 * var(--fs-bleed));
  padding-inline: var(--fs-gutter);
}
.fs-sheet2__track::-webkit-scrollbar { display: none; }

.fs-frame {
  position: relative;
  flex: 0 0 auto;
  /* 1.08 frames in view, so the next one always peeks by ~30px and the
     gallery is legibly swipeable without an arrow or a dot row. */
  width: calc(92% - 0.5rem);
  scroll-snap-align: center;
  margin: 0;
}
.fs-frame picture {
  display: block;
  /* The scoped reset gives every <picture> height: 100%, which is right where
     a picture fills a sized frame and wrong here. Inside a flex item the
     100% resolved against the flex line, so the picture grew to the FIGURE's
     height and pushed the caption past the scroller's clip edge — the labels
     were rendering, at exactly 0px of visible height. A horizontal scroller
     clips vertically too: `overflow-x: auto` forces overflow-y to `auto`, so
     anything past the content box disappears silently. */
  height: auto;
}
.fs-frame img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--fs-r-sm);
}
/* Two of the four frames in black and white, and only two.
   The brand's print work reads as a B&W contact sheet with the colour held
   back for the electric blue, and this section is the one place on the site
   that is already a contact sheet, so it is where that language belongs. It is
   applied to the two SUPPORTING frames — the prop detail and the booth
   interaction, the two that describe the product — so the dominant candid
   stays in colour as the moment being sold, and frame 04 stays in colour
   because it is the page's only saturated, primary-palette photograph and
   flattening it would cost the section its event-type range. Two mono, two
   colour: a contact sheet mixes, and one uniform treatment would read as a
   filter rather than a choice.
   Nothing is set over either of these two frames, so no contrast measurement
   depends on this. The lead frame, which does carry the headline, is
   untouched. */
.fs-frame--detail img,
.fs-frame--booth img {
  filter: grayscale(1) contrast(1.05);
}
.fs-frame figcaption {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding-top: 0.625rem;
}

/* The 1-of-4 counter. Written by JS as the track scrolls; without JS the
   element is empty and collapses, and the track still swipes. */
.fs-picture__count {
  margin-top: var(--fs-space-4);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  color: var(--fs-ink-mute);
}
.fs-picture__count:empty { display: none; }

/* --- the composition, desktop ------------------------------------------ */
@media (min-width: 62rem) {
  .fs-sheet2 {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: clamp(0.75rem, 1.6vw, 1.25rem);
    row-gap: clamp(1rem, 2.5vw, 2rem);
    align-items: start;
  }
  /* The figures become grid items of the composition itself, so the track's
     box disappears rather than nesting a scroller inside a layout. */
  .fs-sheet2__track { display: contents; }

  .fs-frame { width: auto; }
  .fs-frame img { border-radius: var(--fs-r-md); aspect-ratio: auto; }

  /* 01 — the dominant candid. Bleeds off the right edge of the page and
     overlaps the tall frame's right edge on its left, so the composition has
     one crossing rather than four parallel rectangles. */
  .fs-frame--lead {
    grid-column: 4 / -1;
    grid-row: 1;
    z-index: 2;
    margin-left: clamp(-4rem, -4vw, -2rem);
    margin-right: calc(-1 * var(--fs-bleed));
  }
  .fs-frame--lead img { aspect-ratio: 3 / 2; border-radius: var(--fs-r-md) 0 0 var(--fs-r-md); }
  /* The scrim that makes cream type legible on a photograph. Measured
     against this crop's lower-left — a dark room at roughly L*14 — cream at
     full strength over the ramp clears 13:1. It is a gradient rather than a
     panel so the photograph is never boxed. */
  /* Two layers, and the second one is not decoration. Measured against this
     crop with the text hidden and every pixel of its box sampled, the single
     diagonal ramp left one warm uplight in the photograph at rgb(206,129,58)
     — 2.95:1 against cream, five hundredths short of the 3:1 floor for large
     text. The near-vertical layer puts a floor under the bottom third, where
     the headline actually sits, and takes the worst pixel to 5.4:1. A scrim
     sized by eye is a scrim that fails on the one photograph nobody checked.
     scratchpad/overlay-contrast.js is the check. */
  .fs-frame--lead::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    border-radius: var(--fs-r-md) 0 0 var(--fs-r-md);
    background:
      linear-gradient(
        6deg,
        rgb(var(--fs-c-ink) / .74) 0%,
        rgb(var(--fs-c-ink) / .40) 24%,
        rgb(var(--fs-c-ink) / 0) 46%
      ),
      linear-gradient(
        106deg,
        rgb(var(--fs-c-ink) / .78) 0%,
        rgb(var(--fs-c-ink) / .46) 32%,
        rgb(var(--fs-c-ink) / 0) 60%
      );
    pointer-events: none;
  }
  /* Crop marks, on the dominant frame only. */
  .fs-frame--lead::before {
    content: "";
    position: absolute;
    z-index: 3;
    top: var(--fs-tick-inset);
    left: calc(var(--fs-tick-inset) + 0.25rem);
    width: var(--fs-tick-size);
    height: var(--fs-tick-size);
    border: 1.5px solid rgb(var(--fs-c-white) / .8);
    border-right: 0;
    border-bottom: 0;
    pointer-events: none;
  }
  /* This one caption sits ON the photograph rather than under it, so it
     inverts: ink-soft on a dark frame is unreadable, and it is the one label
     in the section whose ground is not cream. */
  .fs-frame--lead figcaption {
    position: absolute;
    z-index: 3;
    left: calc(var(--fs-tick-inset) + 0.25rem);
    top: calc(var(--fs-tick-inset) + var(--fs-tick-size) + 0.5rem);
    padding: 0;
  }
  .fs-frame--lead figcaption .fs-microlabel { color: var(--fs-white); }
  .fs-frame--lead figcaption .fs-microlabel__n { color: var(--fs-blue-100); }

  /* The pull line takes the dominant frame's own cell, bottom-left, over the
     scrim. Sharing a grid cell is what keeps it welded to the photograph at
     every width — an absolutely positioned version drifted the moment the
     frame's height changed. */
  .fs-picture__pull {
    grid-column: 4 / 9;
    grid-row: 1;
    align-self: end;
    z-index: 4;
    margin: 0 0 clamp(1.25rem, 3vw, 2.25rem);
    padding-left: clamp(1.25rem, 3vw, 2.5rem);
    font-size: clamp(1.75rem, 3.6vw, 3.25rem);
    color: var(--fs-white);
    text-shadow: 0 1px 24px rgb(var(--fs-c-ink) / .5);
  }

  /* 02 — the event detail, 9:16. Tall, no faces, pure colour and texture:
     the ladder needs one frame that is not people. Pushed down so the strip
     steps rather than aligning to the dominant frame's top edge. */
  /* A percentage margin on a grid item resolves against ITS OWN grid area,
     not the grid — 14% of a 240px column is 34px, which is a nudge, not a
     stagger. Fixed lengths, measured against the composition. */
  .fs-frame--detail {
    grid-column: 1 / 4;
    grid-row: 1;
    z-index: 1;
    margin-top: clamp(2.5rem, 7vw, 7rem);
  }
  .fs-frame--detail img { aspect-ratio: 9 / 16; }

  /* 03 — the one frame here that shows the booth being used. */
  .fs-frame--booth {
    grid-column: 1 / 4;
    grid-row: 2;
    margin-top: clamp(1.5rem, 4vw, 3.5rem);
  }
  .fs-frame--booth img { aspect-ratio: 1; }

  /* 04 — the environment. Wide, bright, a different palette entirely, so the
     section is not one event's colour story four times. */
  /* Wide enough that the row below a right-bleeding photograph does not
     end in three empty columns. */
  .fs-frame--room {
    grid-column: 4 / 12;
    grid-row: 2;
  }
  .fs-frame--room img { aspect-ratio: 16 / 9; }

  /* Hover crop marks on the three supporting frames. */
  @media (hover: hover) {
    .fs-frame:not(.fs-frame--lead)::before,
    .fs-frame:not(.fs-frame--lead)::after {
      content: "";
      position: absolute;
      z-index: 3;
      width: 1.25rem;
      height: 1.25rem;
      border: 1.5px solid rgb(var(--fs-c-white) / .9);
      opacity: 0;
      transition: opacity var(--fs-t) var(--fs-ease);
      pointer-events: none;
    }
    .fs-frame:not(.fs-frame--lead)::before {
      top: 0.625rem; left: 0.625rem;
      border-right: 0; border-bottom: 0;
    }
    .fs-frame:not(.fs-frame--lead)::after {
      right: 0.625rem; bottom: 0.625rem;
      border-left: 0; border-top: 0;
    }
    .fs-frame:not(.fs-frame--lead):hover::before,
    .fs-frame:not(.fs-frame--lead):hover::after { opacity: 1; }
  }

  .fs-picture__count { display: none; }
}

/* Very slight parallax on the two supporting frames, done entirely by the
   browser where scroll-driven animation exists. No listener, no library. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 62rem) {
    .fs-frame--detail,
    .fs-frame--room {
      animation: fs-drift linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
    .fs-frame--booth { animation: fs-drift-2 linear both; animation-timeline: view(); }
  }
}
@keyframes fs-drift   { from { translate: 0 1.5rem; } to { translate: 0 -1.5rem; } }
@keyframes fs-drift-2 { from { translate: 0 -1rem; } to { translate: 0 1rem; } }


/* == 16  MAKE IT YOUR SCENE =============================================== */

/* The event start-screen mock. Deliberately non-functional and generic —
   it is a preview of what a personalized welcome screen looks like, not a
   rendering of a real interface. Its rules moved here with the contact
   sheet when the section that used to hold them was dropped. */
.fs-screen {
  position: relative;
  display: grid;
  place-content: center;
  gap: 0.625rem;
  aspect-ratio: 3 / 4;
  padding: 1.125rem;
  text-align: center;
  color: var(--fs-white);
  background:
    radial-gradient(90% 70% at 50% 22%, rgb(var(--fs-c-blue) / .95), transparent 70%),
    linear-gradient(165deg, #12224f, var(--fs-ink));
  border-radius: var(--fs-r-md);
  box-shadow: var(--fs-lift);
}
/* Viewfinder/focus motif — used here and on the package media, nowhere else. */
.fs-screen__frame {
  position: absolute;
  inset: 0.625rem;
  border: 1px solid rgb(var(--fs-c-white) / .38);
  border-radius: calc(var(--fs-r-md) - 0.375rem);
  clip-path: polygon(0 0, 34% 0, 34% 1px, 66% 1px, 66% 0, 100% 0, 100% 100%, 66% 100%, 66% calc(100% - 1px), 34% calc(100% - 1px), 34% 100%, 0 100%);
}
.fs-screen__mark { width: 1.625rem; height: 1.625rem; margin-inline: auto; color: var(--fs-white); }
.fs-screen__event {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(0.875rem, 2.6vw, 1.1875rem);
  line-height: 1.05;
  letter-spacing: -0.015em;
  text-transform: uppercase;
}
.fs-screen__cue {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgb(var(--fs-c-white) / .85);
}
/* == 16b MAKE IT YOUR SCENE — THE INK BAND ================================ */

/* THE ART-DIRECTION TABLE. This is the page's one ink band and its only real
   tonal event, and the section earns it: it is the only place the brand
   proves the thing an equipment-led competitor cannot claim — that the event
   will look like YOURS. The old version put the same objects on cream inside
   a bordered panel, where a screen mock and a colour grid read as a settings
   interface. The objects did not change. The ground did.

   What this must never become, and how each is prevented:
     SaaS / settings UI   no panel, no border, no rows, no labels-and-values;
                          the screen mock is one object among four, not a card
     Pinterest moodboard  four objects, not a wall; each is a real artifact
                          the customer receives, not an inspiration image
     floating Photoshop   every object casts one consistent light-source
                          shadow and sits at 0deg or 3deg, never more
     nightclub            no neon, no purple, no beam; the only light is one
                          warm amber pool and one blue reflection, both under
                          14% alpha

   The background is DRAWN, not photographed — warm light plus film grain
   over ink. That is deliberate: every candidate photograph in the library
   keeps something at 30% opacity that should not survive (legible signage, a
   teddy bear, an event theme that pins the band to one kind of party). M1 in
   the asset plan is the photograph that belongs here; it drops in behind
   .fs-inkband__room at 30% with no layout change. */
.fs-inkband {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-block: clamp(3.5rem, 8vw, 7rem);
  background: var(--fs-surface-ink);
  color: var(--fs-white);
}

/* The room. One warm pool at the right where the objects are, one cool
   reflection low-left, both well under the threshold where ink stops being
   ink. Warm-amber rather than magenta on purpose: pink at low alpha over
   ink turns violet, and purple is not in this palette. */
.fs-inkband__room {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    /* A blue rim down the right edge. The card back carries exactly this — a
       lit blue edge against near-black — and it is the one cue from the print
       work this band was missing. Kept to a 14% ramp over the last 8% of the
       width so it reads as light falling on an edge rather than as a border,
       and placed on the opposite side to the blue radial below so the two do
       not stack into a single blue wash. */
    linear-gradient(to left, rgb(var(--fs-c-blue) / .14), transparent 8%),
    radial-gradient(56% 74% at 74% 28%, rgb(255 196 132 / .19), transparent 66%),
    radial-gradient(46% 60% at 6% 92%,  rgb(var(--fs-c-blue) / .18), transparent 62%);
  pointer-events: none;
}
/* Film grain. A 120px tile of fractal noise, inline so it costs no request,
   at 4% — enough that the ink reads as a photographed surface rather than as
   a flat fill, not enough to be seen as texture. */
.fs-inkband__room::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.045;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
}

.fs-inkband__grid {
  position: relative;
  display: grid;
  gap: clamp(2.5rem, 6vw, 4rem);
}

/* --- the type. Eighteen words in the whole band, and no list. ----------- */
.fs-inkband__head { max-width: 30rem; }
.fs-inkband__head .fs-eyebrow::before { background: var(--fs-blue-100); }
.fs-inkband__title {
  margin-block: var(--fs-space-4) var(--fs-space-5);
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.75rem, 9cqw, 3.5rem);
  line-height: 0.94;
  letter-spacing: -0.014em;
  text-transform: uppercase;
}
/* The two-tone device, use two of three: cream line, electric-blue line.
   Blue on ink measures 3.4:1, which carries LARGE display type and nothing
   else — every small string in this band is cream. */
.fs-inkband__title em {
  display: block;
  font-style: normal;
  color: var(--fs-blue);
}
.fs-inkband__lede {
  font-size: var(--fs-text-lg);
  line-height: 1.45;
  color: var(--fs-cream-soft);
  max-width: 24rem;
}
.fs-inkband__note {
  margin-top: var(--fs-space-4);
  font-size: var(--fs-text-sm);
  color: var(--fs-cream-mute);
}
.fs-inkband__note a {
  color: var(--fs-white);
  text-decoration: underline;
  text-underline-offset: 0.3em;
  text-decoration-thickness: 1px;
  text-decoration-color: rgb(var(--fs-c-blue-100) / .6);
}
.fs-inkband__note a:hover { text-decoration-color: var(--fs-blue-100); }

/* --- the table ---------------------------------------------------------
   Objects on a surface, placed in percentages inside a fixed-ratio box, so
   the whole arrangement scales as one composition instead of drifting apart
   at intermediate widths. One light source: every shadow falls down-right. */
.fs-table {
  position: relative;
  aspect-ratio: 5 / 4;
  --fs-obj-shadow: 0 1.75rem 3.5rem -1rem rgb(0 0 0 / .62);
}
.fs-table > * { position: absolute; }

/* 01 the finished strip — the real artifact, the largest object, and the
   only one allowed an angle. */
.fs-table__strip {
  left: 2%;
  top: 4%;
  width: 56%;
  z-index: 3;
  rotate: -3deg;
  padding: 0.5rem 0.5rem 2rem;
  background: var(--fs-white);
  border-radius: 0.1875rem;
  box-shadow: var(--fs-obj-shadow);
}
.fs-table__strip img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0.0625rem;
}
.fs-table__strip .fs-microlabel {
  position: absolute;
  left: 0.75rem;
  bottom: 0.5rem;
  color: var(--fs-ink-mute);
}
/* One crop mark, on this object only. Nothing else here is decorated. */
.fs-table__strip::after {
  content: "";
  position: absolute;
  right: -0.5rem;
  top: -0.5rem;
  width: 1.5rem;
  height: 1.5rem;
  border: 1.5px solid rgb(var(--fs-c-blue-100) / .7);
  border-left: 0;
  border-bottom: 0;
}

/* 02 the welcome screen — the one object that is perfectly straight. */
.fs-table__screen {
  right: 1%;
  top: 21%;
  width: 28%;
  z-index: 2;
  box-shadow: var(--fs-obj-shadow);
}

/* 03 the top edge of a custom booth-front panel, entering from the bottom.
   Only its head is in frame: it is a printed panel on the table, not a
   picture of a booth. The branding is the real logo file at small size —
   the standard Full Scene front panel, not an oversized wrap. */
.fs-table__panel {
  left: 13%;
  /* Far enough past the table's box that .fs-inkband's clip cuts it: only the
     head of the panel is in frame, which is what makes it read as an object
     entering the composition rather than a card sitting on it.
     -46%, not -30%. Measured on the built page, -30% left 217px of panel in
     frame at 1440 with 119px of it blank white below the logo, and a white
     rectangle with a logo at the top does not read as the head of an object —
     it reads as a card that got cut. -46% leaves 124-130px across
     1024/1280/1440, which is the logo plus a hand's width of panel. */
  bottom: -46%;
  width: 48%;
  z-index: 1;
  display: grid;
  place-items: start center;
  padding: clamp(1rem, 2.6vw, 1.75rem) 1rem clamp(6rem, 16vw, 11rem);
  background: linear-gradient(180deg, #FFFFFF, #F2F1EE);
  border-radius: 0.375rem 0.375rem 0 0;
  box-shadow: var(--fs-obj-shadow);
}
.fs-table__panel img {
  display: block;
  width: min(9.5rem, 46%);
  height: auto;
}

/* 04 three palette chips, small, at the edge of the table. */
.fs-table__chips {
  left: 1%;
  bottom: 24%;
  z-index: 4;
  display: flex;
  gap: 0.375rem;
  padding: 0.4375rem;
  background: var(--fs-white);
  border-radius: var(--fs-r-sm);
  rotate: 3deg;
  box-shadow: var(--fs-obj-shadow);
}
.fs-table__chips span {
  width: clamp(1.125rem, 3vw, 1.75rem);
  aspect-ratio: 1;
  border-radius: 0.125rem;
}
/* Three of the six real backdrop colours — champagne, navy, sage. Not the
   brand palette: these are the customer's options, not our colours. */
.fs-table__chips span:nth-child(1) { background: #D9C7A3; }
.fs-table__chips span:nth-child(2) { background: #1E2A4A; }
.fs-table__chips span:nth-child(3) { background: #A9B8A4; }

/* The screen mock, on ink. Its shared version is a bright blue panel, which
   is right on cream and wrong here: at 95% blue on a dark table it became the
   loudest object in the band and the one thing that could still be mistaken
   for a product UI. Dimmed to a screen in a dim room — the same component,
   lit for the ground it is standing on. */
.fs-inkband .fs-screen {
  box-shadow: none;
  background:
    radial-gradient(78% 56% at 50% 20%, rgb(var(--fs-c-blue) / .38), transparent 74%),
    linear-gradient(168deg, #0E1730, #070A11);
}
.fs-inkband .fs-screen__cue { color: rgb(var(--fs-c-white) / .72); }

/* --- mobile: keep the ink, halve the objects --------------------------- */
@media (max-width: 47.99rem) {
  /* Two objects only, larger, one deliberate overlap. The panel and the
     chips are not shrunk to fit — they are removed, because four objects at
     phone scale is where an art-direction table turns into clutter. */
  .fs-table { aspect-ratio: 4 / 3; }
  .fs-table__panel,
  .fs-table__chips { display: none; }
  /* The overlap has to be deliberate AND small: at 78%/44% the strip covered
     half the screen mock, including the event name printed on it.
     66% was still too wide, by a little. Measured at 375/390/430 the strip's
     right edge sat 20/22/27px INSIDE the event name, so "Amara turns 30" read
     as "mara turns 30" on every phone. The rotation is part of the arithmetic:
     -3deg adds about 10px to the strip's box, so a width that clears the text
     on paper does not clear it on screen. 57% leaves the strip overlapping the
     screen mock by 8-10px — still deliberate, still reads as one arrangement of
     objects — and leaves the same 9-10px of clearance in front of the name. */
  .fs-table__strip { left: 0; top: 2%; width: 57%; rotate: -3deg; }
  .fs-table__screen { right: 0; top: 34%; width: 44%; }
}

@media (min-width: 62rem) {
  .fs-inkband__grid {
    grid-template-columns: minmax(0, 42fr) minmax(0, 58fr);
    align-items: center;
    column-gap: clamp(2rem, 5vw, 4.5rem);
  }
  .fs-inkband__head { container-type: inline-size; }
  /* The table bleeds off the right edge, like the hero's photograph and the
     gallery's dominant frame — the same move, used three times on purpose so
     the page has one consistent relationship with its own edge. */
  .fs-table { margin-right: calc(-1 * var(--fs-bleed) / 2); }
}


/* == 17  THE CONTACT SHEET (interior pages) ============================== */

/* An earlier edit deleted the opening of this section header and left its
   closing delimiter behind as a bare token. That is not a cosmetic bug: a
   comment terminator with no comment open makes the parser read the NEXT
   declaration block as the body of an invalid selector, and the rule it
   swallowed was .fs-custom — so the section that rule styled had been
   rendering with no padding and no background on the live site.
   .fs-custom itself is gone now: the homepage section that used it is the
   ink band above, and /the-experience carries its own .fs-custom-overview
   wrapper. But __grid, __head and __vary are still that page's, so they
   stay exactly as they are. */
.fs-custom__grid { display: grid; gap: clamp(2.5rem, 6vw, 4.5rem); }
.fs-custom__vary {
  margin-top: var(--fs-space-4);
  font-size: var(--fs-text-sm);
  color: var(--fs-ink-mute);
}

/* --- the contact sheet -------------------------------------------------
   Four cells on one sheet: two photographs and two drawn cells. A sheet
   reads as one designed object, where four floating photo cards read as a
   gallery — and it halves the photography this section needs. */
.fs-sheet {
  position: relative;
  display: grid;
  align-items: start;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(0.625rem, 2vw, 1.125rem);
  padding: clamp(1.25rem, 3vw, 1.75rem) clamp(0.875rem, 2.5vw, 1.375rem) clamp(1rem, 2.5vw, 1.5rem);
  border: 1px solid var(--fs-hairline);
  border-radius: var(--fs-r-lg);
  background: var(--fs-surface-ice);
  box-shadow: var(--fs-lift-soft);
}

/* Sprocket holes along the head of the sheet. */
.fs-sheet__edge {
  position: absolute;
  inset: 0.5rem 1rem auto 1rem;
  height: 0.375rem;
  border-radius: 0.1875rem;
  background: repeating-linear-gradient(
    90deg,
    rgb(var(--fs-c-ink) / .16) 0 0.375rem,
    transparent 0.375rem 1.125rem
  );
  pointer-events: none;
}

.fs-sheet__cell { position: relative; }
.fs-sheet__frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--fs-r-sm);
  background: var(--fs-white);
}
.fs-sheet__frame img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
}
/* The start screen is a component, reused here at the sheet's own ratio. */
.fs-sheet__frame .fs-screen {
  aspect-ratio: 3 / 4;
  border-radius: 0;
  box-shadow: none;
}
.fs-sheet .fs-microlabel {
  display: block;
  margin-top: 0.5rem;
}
.fs-sheet .fs-microlabel span[aria-hidden] {
  color: var(--fs-blue-text);
  margin-right: 0.375rem;
}

/* The overlay cell holds a finished template — a real four-frame strip inside
   an event's own design. It is landscape where the sheet's cells are portrait,
   and cropping it would defeat the point, so it is shown WHOLE on an ink
   ground: a print laid on the sheet rather than a frame cut to fit. */
.fs-sheet__frame--whole {
  display: grid;
  place-items: center;
  padding: 0.4375rem;
  background: rgb(var(--fs-c-ink) / .92);
}
.fs-sheet__frame--whole picture { height: auto; }
.fs-sheet__frame--whole img {
  aspect-ratio: auto;
  height: auto;
  object-fit: contain;
  border-radius: 0.125rem;
}

/* Backdrop swatches — a drawn cell, not a photograph. */
.fs-swatches {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* explicit rows: without them the six cells auto-size and the second row
     comes out shorter than the first */
  grid-template-rows: repeat(2, 1fr);
  gap: 0.25rem;
  aspect-ratio: 3 / 4;
  padding: 0.5rem;
  background: var(--fs-white);
}
/* Six colors an event actually picks — white, black, navy, blush, sage,
   champagne — not the brand palette. Electric blue was two of the six here,
   which read as "these are our colors" rather than "these are your options".
   The pale ones carry a hairline so they do not dissolve into the cell. */
.fs-swatches span { border-radius: 0.1875rem; }
.fs-swatches span:nth-child(1) { background: #F4F1EC; box-shadow: inset 0 0 0 1px rgb(var(--fs-c-ink) / .12); }
.fs-swatches span:nth-child(2) { background: #14161C; }
.fs-swatches span:nth-child(3) { background: #1E2A4A; }
.fs-swatches span:nth-child(4) { background: #E4C3C0; }
.fs-swatches span:nth-child(5) { background: #A9B8A4; }
.fs-swatches span:nth-child(6) { background: #D9C7A3; }

/* The sparse confetti motif that sat here is gone with the sections that
   used it. One memorable move per section: this one already overlaps a
   contact sheet over a headline, which is enough. */

@media (min-width: 48rem) {
  .fs-sheet { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 62rem) {
  .fs-custom__grid { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr); align-items: center; }
  .fs-custom__head { max-width: 34rem; }
  .fs-sheet { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The fourth cell of the contact sheet: backdrop colors, drawn rather than
   photographed. Solids say "pick a color" faster than a photograph of one
   backdrop does. The grid itself is defined once, above. */
.fs-sheet__frame--swatch { display: grid; }


/* == 18  SOCIAL LINKS ===================================================== */

/* All that remains of the old Follow and Trust sections: the footer's channel
   list. The Follow panel was cut because a handle repeated in the footer is
   not a section, and the Trust block because its reviews space had nothing
   honest to put in it — the operational guarantees now live in WHY FULL
   SCENE (section 13), where they are claims we can actually stand behind. */

.fs-social {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.5rem;
  margin-top: clamp(1.25rem, 3vw, 2rem);
}
.fs-social a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fs-blue-text);
  transition: color var(--fs-t) var(--fs-ease);
}
.fs-social a:hover { color: var(--fs-ink); text-decoration: underline; text-underline-offset: 0.35em; }


/* == 18b  FOCAL POSITIONING — one entry per photograph =================== */

/* Every photograph on the site is cropped by its container, and a single
   `object-position: 50% 50%` throws away whichever part of the frame the
   container does not want — which is not the same part in every picture.
   These were read off the actual images against a thirds grid, so each crop
   keeps the faces and the gesture that make that photograph worth using.
   Two classes deep on purpose: `.fs-scene img` sets a position of its own,
   and a single class would lose to it on specificity.

   The mobile overrides are the ones where the container's shape changes
   enough that the desktop focal point stops working — not a blanket rule.

   A rule here only bites where the container's ratio differs from the file's.
   tools/import-images.py already crops each derivative to its slot's ratio,
   so for a slot whose container matches — the package panels, the collage
   frames — the entry is the recorded subject anchor rather than an active
   nudge, and it starts working the moment that container's shape changes.
   The event-type cards are the live case: the rail's card is one shape on a
   phone and another in the desktop 4-up grid, and it discards 22-33% of the
   frame either way. */

/* --- the filmstrip. Each frame's file is already cropped to that cell's
       measured shape, so these are the recorded subject anchors rather than
       active nudges: they start working the moment a cell's ratio changes,
       which it does between the phone rail and the desktop 4-up grid. --- */
.fs-focal.fs-focal--scene-01 { object-position: 52% 34%; }   /* three gowns, faces above the third */
.fs-focal.fs-focal--scene-02 { object-position: 50% 30%; }   /* four guests, boas, faces high */
.fs-focal.fs-focal--scene-03 { object-position: 54% 28%; }   /* caps read at the top of the frame */
.fs-focal.fs-focal--scene-04 { object-position: 58% 34%; }   /* the three are grouped right of centre */

/* --- hero. The dominant frame's crop is anchored in the file (p1 at 0.84),
       so this is the vertical bias only: the faces sit in the upper half and
       a centred cover window on a shorter container would cut them. --- */
.fs-focal.fs-focal--hero-primary { object-position: 50% 30%; }
/* hero-detail needs no rule: its file is square, its container is square,
   and the framing is cropped into the file itself by
   tools/import-images.py (anchor 0.35) rather than nudged here. */

/* --- Picture the Scene. Four ratios on desktop and one shared 4:5 on the
       phone, so every frame here is cropped by its container at one width or
       the other and every one needs its own anchor. --- */
.fs-focal.fs-focal--picture-lead   { object-position: 62% 46%; }  /* the group, right of the dark third */
.fs-focal.fs-focal--picture-detail { object-position: 50% 58%; }  /* the prop vase, not the ceiling */
.fs-focal.fs-focal--picture-booth  { object-position: 42% 44%; }  /* the hand and the screen */
.fs-focal.fs-focal--picture-room   { object-position: 56% 40%; }  /* the families, above the tarmac */

/* /the-experience's contact sheet used to have an entry here. It does not
   need one: that page's cell and the file are both 3:4, so the container
   crops nothing, and a rule that bites nowhere is a rule the next edit
   trips over. The crop is recorded in tools/import-images.py instead. */

/* --- package setup photographs. Each is a 4:5 crop of a different event, so
       each needs its own focal point; the guests, not the ceiling. --- */
.fs-focal.fs-focal--package-02-preview { object-position: 58% 30%; }
.fs-focal.fs-focal--package-03-scene   { object-position: 56% 26%; }
.fs-focal.fs-focal--package-04-main    { object-position: 60% 34%; }
.fs-focal.fs-focal--package-05-full    { object-position: 40% 38%; }


/* == 19  FAQ ============================================================== */

/* THE LEDGER ARCHETYPE, and deliberately the plainest thing on the page: no
   panel, no glow, no star, no photograph. Its flatness is what makes the
   saturated blue directly below it land as an arrival — a fourth decorated
   band in a row would leave the CTA nothing to arrive from. It was on the
   ice surface, which made it the fourth pale panel in sequence; cream now,
   with structure carried entirely by hairlines. */
.fs-faq {
  padding-block: clamp(3.5rem, 7vw, 6.5rem);
  background: var(--fs-surface);
}
.fs-faq__grid { display: grid; gap: clamp(2rem, 5vw, 4rem); }
.fs-faq__list { border-top: 1px solid var(--fs-rule, var(--fs-hairline)); }

.fs-qa { border-bottom: 1px solid var(--fs-hairline); }
.fs-qa > summary {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: 3.5rem;
  padding-block: 1.125rem;
  font-size: var(--fs-text-md);
  /* 500, not 400. In a ledger the question is the row's label and the answer
     is its value; at the same weight as the answer the rows read as one
     undifferentiated column of prose. */
  font-weight: 500;
  line-height: 1.4;
  cursor: pointer;
  list-style: none;
  transition: color var(--fs-t) var(--fs-ease);
}
.fs-qa > summary::-webkit-details-marker { display: none; }
.fs-qa > summary:hover { color: var(--fs-blue-text); }
.fs-qa > summary::after {
  content: "";
  flex: 0 0 auto;
  width: 0.75rem;
  height: 0.75rem;
  margin-left: auto;
  background:
    linear-gradient(var(--fs-blue), var(--fs-blue)) center / 100% 1.5px no-repeat,
    linear-gradient(var(--fs-blue), var(--fs-blue)) center / 1.5px 100% no-repeat;
  transition: transform var(--fs-t) var(--fs-ease);
}
.fs-qa[open] > summary::after { transform: rotate(90deg); background-size: 100% 1.5px, 0 0; }
.fs-qa[open] > summary { color: var(--fs-blue-text); }
.fs-qa__body {
  padding-bottom: var(--fs-space-5);
  max-width: 42rem;
}
.fs-qa__body p { color: var(--fs-ink-soft); }

@media (min-width: 62rem) {
  .fs-faq__grid { grid-template-columns: minmax(0, 0.7fr) minmax(0, 1fr); align-items: start; }
  .fs-faq .fs-section-head { position: sticky; top: calc(var(--fs-nav-h) + 2rem); }
}


/* The homepage carries five booking questions; the rest live on
   /the-experience. This is the link out. */
.fs-faq__all { margin-top: var(--fs-space-5); }


/* == 20  FINAL CTA ======================================================== */

/* PROTECTED COMPOSITION, ONE CONSIDERED CHANGE. Everything that made this
   the best-designed band on the site is kept exactly: one focal point with
   zero competition, colour doing the emphasis, centred where the rest of the
   page is left-aligned, the star as scale rather than sticker, the button as
   the only saturated mass, one soft radial giving the band lighting.

   The change is the ground. It was 100% cream and still read as an arrival
   because it spent BLUE as its contrast event; committing that structurally
   is what the page's tonal scale was missing. This is the one saturated blue
   field on the page — the exact brand #2F5EFF, not a tint — and it is the
   last loud thing before a trimmed footer. */
.fs-final {
  position: relative;
  overflow: clip;
  padding-block: clamp(5rem, 12vw, 9rem);
  background: var(--fs-surface-live);
}
/* The lighting event, now made of white light rather than blue light — on a
   blue field a blue glow is invisible, and the band would lose the one thing
   that gave it depth. */
.fs-final__glow {
  position: absolute;
  left: 50%;
  top: 44%;
  width: min(52rem, 92vw);
  aspect-ratio: 1;
  translate: -50% -50%;
  background: radial-gradient(circle at 50% 50%, rgb(var(--fs-c-white) / .22), rgb(var(--fs-c-white) / .09) 44%, transparent 70%);
  filter: blur(12px);
  pointer-events: none;
}
/* Same star, same scale, same crop, same job. Raised from 7% to 9% because
   ink-on-blue at 7% had gone invisible. */
.fs-final__star {
  position: absolute;
  right: -8%;
  bottom: -22%;
  width: min(30rem, 60vw);
  aspect-ratio: 1;
  background: url("../img/logo/fs-star-motif.png") no-repeat center / contain;
  /* The motif file is dark artwork on transparency, which disappears on a
     blue ground however far the opacity is pushed. brightness(0) flattens
     every opaque pixel to black and invert(1) makes it white, keeping the
     alpha channel — so the same asset becomes a light watermark without a
     second file to keep in sync. */
  filter: brightness(0) invert(1);
  opacity: 0.13;
  transform: rotate(14deg);
  pointer-events: none;
}
.fs-final__inner {
  position: relative;
  container-type: inline-size;
  text-align: center;
}
.fs-final__title {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(2.25rem, 6.2cqw, 4.25rem);
  line-height: 0.94;
  letter-spacing: -0.012em;
  text-transform: uppercase;
  text-wrap: balance;
  color: var(--fs-white);
}
/* The two-tone device, third and last use: cream line, ink line. Ink on the
   brand blue measures 4.6:1 — large display type only, which is exactly what
   this is. */
.fs-final__title em {
  font-style: normal;
  color: var(--fs-ink);
}
/* The button inverts with the ground: cream fill, ink label. It is still the
   only saturated mass in the band — the mass is just the light one now. */
.fs-final__cta {
  margin-top: clamp(2rem, 5vw, 3rem);
  min-height: 3.5rem;
  padding-inline: 2.25rem;
  font-size: var(--fs-text-base);
  color: var(--fs-ink);
  background: var(--fs-white);
  border-color: var(--fs-white);
}
.fs-final__cta:hover {
  color: var(--fs-ink);
  background: var(--fs-blue-100);
  border-color: var(--fs-blue-100);
}
.fs-final__cta:focus-visible {
  outline: 2px solid var(--fs-white);
  outline-offset: 3px;
}
/* On a phone this band was 301px against a 1,188px footer — the ask was
   smaller than the sitemap under it. It now keeps the same presence it has
   on a desktop. */
@media (max-width: 47.99rem) {
  /* Measured, this band was 301px on a phone against a 1,188px footer — the
     ask was smaller than the sitemap under it. The colour alone is now the
     contrast event, but the air matters too: 24vw of vertical padding gives
     the band ~42% of the viewport, which is the same share of the screen it
     takes at 1440. */
  .fs-final { padding-block: clamp(6rem, 24vw, 7.5rem); }
  .fs-final__cta { width: 100%; }
}


/* --- hero price signal, arrow buttons, and the closing booking strip --- */


/* The arrow slides on hover and the blue deepens — the only two things the
   primary button does, and enough to make it feel live. */
.fs-btn--arrow::after {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  margin-left: 0.125rem;
  border-top: 2px solid currentColor;
  border-right: 2px solid currentColor;
  rotate: 45deg;
  transition: translate var(--fs-t) var(--fs-ease);
}
.fs-btn--arrow:hover::after { translate: 0.1875rem 0; }

@media (min-width: 48rem) {
  }

/* The "what this one adds" label above each package's short list. */


/* == 21  FOOTER =========================================================== */

.fs-footer {
  padding-block: 0 clamp(1.25rem, 2.5vw, 2rem);
  background: var(--fs-surface-ice);
  border-top: 0;
}

/* --- the brand band, now BEHIND the columns rather than above them ------
   Measured, the ghosted wordmark was the largest type on the page after the
   hero and it sat directly under the final CTA, competing with the one thing
   the page most wants clicked. Nothing is removed: the wordmark is smaller,
   it has left the flow entirely, and it now sits behind the link columns as
   a watermark. That single change is most of the footer's height saving. */
.fs-footer__main {
  position: relative;
  container-type: inline-size;
  overflow: clip;
}
.fs-footer__band {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  pointer-events: none;
}
.fs-footer__wordmark {
  display: block;
  font-family: var(--fs-font-display);
  font-weight: 800;
  /* 13cqw, not 15: at 15 the wordmark ran 40px past a 390px phone and read
     as "FULL SCEN". A watermark that looks truncated looks like a bug. */
  font-size: clamp(2.5rem, 13cqw, 8rem);
  line-height: 0.82;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  white-space: nowrap;
  /* .05, not .07: behind live text it has to stay a surface rather than a
     pattern the eye tries to read through the links. */
  color: rgb(var(--fs-c-ink) / .045);
  user-select: none;
}
.fs-footer__star {
  right: 2%;
  top: 8%;
  width: clamp(1.25rem, 3vw, 2rem);
  opacity: .45;
}

/* --- the columns --- */
.fs-footer__inner {
  position: relative;
  display: grid;
  /* Two columns on a phone, not four stacked. Four stacked single-file
     columns were where the footer's 1,188px of mobile height came from. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.25rem, 3.5vw, 3rem);
  padding-block: clamp(1.5rem, 4vw, 3rem);
}
.fs-footer__brand { grid-column: 1 / -1; max-width: 22rem; }
.fs-footer__social { grid-column: 1 / -1; }
/* Block-level, not the shared inline-flex. As an inline box the lockup sat
   in a line box with the block's own strut, so the whitespace after </a>
   added ~8px of descender under it and the gap to the tagline measured 20px
   where the CSS asks for 12. Measured: it is why dist/preview.html — which
   collapses that whitespace — laid this one block out 8px tighter than the
   served page. `display: flex` takes the lockup out of inline layout, so the
   spacing is exactly what the rule says on both. */
.fs-footer__logo {
  display: flex;
  width: fit-content;
  margin-right: 0;
  padding-block: 0;
}
.fs-footer__logo img { height: 2.5rem; }
.fs-footer__tag {
  margin-top: var(--fs-space-3);
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: var(--fs-text-md);
  line-height: 1.1;
  letter-spacing: var(--fs-ls-display);
  text-transform: uppercase;
}
.fs-footer__region {
  margin-top: var(--fs-space-2);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-ink-soft);
}
.fs-footer__email { margin-top: var(--fs-space-2); font-size: var(--fs-text-sm); }

.fs-footer__heading {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-ink-mute);
}
.fs-footer__nav ul { display: grid; gap: 0.125rem; margin-top: var(--fs-space-3); }
.fs-footer__nav a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  /* "FAQ" is three characters, so an inline-flex box shrank to 24px wide.
     A minimum width keeps every link in the column a comfortable target
     regardless of how short its label is. */
  min-width: 2.75rem;
  font-size: var(--fs-text-sm);
  color: var(--fs-ink-soft);
  transition: color var(--fs-t) var(--fs-ease);
}
.fs-footer__nav a:hover,
.fs-footer__nav a[aria-current="page"] { color: var(--fs-blue-text); }

/* --- social: icons only ---
   The platform names used to run down the column as a text list, which was
   the loudest thing in the footer. Icons carry the same information in a
   fifth of the space; each link's aria-label names the platform, so nothing
   is lost to anyone using a screen reader. */
.fs-social--footer {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-top: var(--fs-space-3);
}
.fs-social--footer a {
  display: grid;
  place-content: center;
  /* 44px, so the icon is a comfortable target even though it draws at 20px */
  width: 2.75rem;
  height: 2.75rem;
  margin-left: -0.5rem;         /* optical: align the first glyph to the column */
  color: var(--fs-ink-soft);
  border-radius: 50%;
  transition: color var(--fs-t) var(--fs-ease),
              background-color var(--fs-t) var(--fs-ease);
}
.fs-social--footer a:hover {
  color: var(--fs-blue-text);
  background: rgb(var(--fs-c-blue) / .1);
}
.fs-social__icon { width: 1.25rem; height: 1.25rem; }
.fs-footer__handle {
  margin-top: var(--fs-space-2);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: var(--fs-w-micro);
  letter-spacing: 0.08em;
  color: var(--fs-ink-mute);
}

.fs-footer__legal {
  position: relative;
  margin-top: 0;
  padding-top: var(--fs-space-4);
  border-top: 1px solid var(--fs-hairline);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  letter-spacing: 0.06em;
  color: var(--fs-ink-mute);
}
/* Clearance for the chat bubble, which parks itself in the bottom-right
   corner of the viewport. On a phone that corner is where the copyright line
   ends up when you reach the end of the page, so the last band keeps a
   bubble's worth of room under it rather than being sat on. */
.fs-footer__legal { padding-bottom: 4.5rem; }
@media (min-width: 48rem) {
  .fs-footer__legal { padding-bottom: 1.5rem; }
}

@media (min-width: 48rem) {
  .fs-footer__inner { grid-template-columns: 1.5fr 1fr 1fr 1fr; }
  .fs-footer__brand,
  .fs-footer__social { grid-column: auto; }
}


/* == 22  INTERIOR PAGES — masthead ======================================== */

.fs-masthead {
  position: relative;
  overflow: clip;
  padding-block: clamp(2.5rem, 6vw, 4.5rem) clamp(2.5rem, 6vw, 4rem);
  background: linear-gradient(180deg, var(--fs-white), var(--fs-white) 62%, var(--fs-ice));
}
.fs-masthead__glow {
  position: absolute;
  right: -6%;
  top: -40%;
  width: min(34rem, 70vw);
  aspect-ratio: 1;
  background: radial-gradient(circle at 50% 50%, rgb(var(--fs-c-blue) / .16), rgb(var(--fs-c-blue-100) / .3) 42%, transparent 68%);
  filter: blur(14px);
  pointer-events: none;
}
.fs-masthead__inner {
  position: relative;
  container-type: inline-size;
  max-width: 62rem;
}
.fs-masthead__title {
  margin-block: var(--fs-space-4) var(--fs-space-5);
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(2.25rem, 9.5cqw, 5rem);
  line-height: 0.96;
  letter-spacing: -0.028em;
  text-transform: uppercase;
  text-wrap: balance;
}
.fs-masthead .fs-lede { max-width: 40rem; }

/* The one brand visual on the Check My Date page. It is the supplied icon
   artwork, not a redrawn star, and it is small on purpose: the page has one
   job and the mark is a signature on it, not a second headline. */
.fs-masthead__badge {
  display: block;
  width: clamp(3.75rem, 10vw, 5.5rem);
  height: auto;
  margin-bottom: var(--fs-space-5);
  border-radius: 50%;
}


/* == 23  PROSE ============================================================ */

.fs-prose { max-width: 40rem; }
.fs-prose > * + * { margin-top: var(--fs-space-5); }
.fs-prose p { color: var(--fs-ink-soft); }
.fs-prose h2 { margin-top: var(--fs-space-7); }
.fs-prose h3 {
  margin-top: var(--fs-space-6);
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.125rem, 2.4vw, 1.375rem);
  line-height: 1.1;
  letter-spacing: var(--fs-ls-display);
  text-transform: uppercase;
}
/* .fs-featureline had no component styles at all — the only rule naming it was
   the .fs-prose margin below, which does not apply on /the-experience/, where
   the list lives in the howto column instead. So it was rendering as the scoped
   reset's bare <ul>: four lines, no bullets, no gaps, no micro tier, directly
   under body copy that uses all three. Given the site's own idiom for a row of
   short factual items — the packages' inclusion lists and the filmstrip labels
   — it is a horizontal row at the micro tier with the brand's blue separator. */
.fs-featureline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  margin-top: var(--fs-space-5);
  list-style: none;
  padding: 0;
}
.fs-featureline > li {
  font-size: 0.75rem;
  font-weight: var(--fs-w-micro);
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-ink-soft);
}
/* A blue separator between items, and none before the first. */
.fs-featureline > li + li::before {
  content: "";
  display: inline-block;
  width: 0.3125rem;
  height: 0.3125rem;
  margin-right: 0.75rem;
  border-radius: 50%;
  background: var(--fs-blue);
  vertical-align: 0.125em;
}
.fs-prose .fs-featureline { margin-top: var(--fs-space-6); }

/* Long-form policy copy: tighter rhythm, clear headings, room to read. */
.fs-prose--policy { max-width: 44rem; }
.fs-prose--policy h2 {
  margin-top: var(--fs-space-7);
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.25rem, 2.8vw, 1.625rem);
  line-height: 1.1;
  letter-spacing: var(--fs-ls-display);
  text-transform: uppercase;
}
.fs-prose--policy ul {
  display: grid;
  gap: 0.5rem;
  margin-top: var(--fs-space-4);
}
.fs-prose--policy li {
  position: relative;
  padding-left: 1.5rem;
  color: var(--fs-ink-soft);
  line-height: 1.65;
}
.fs-prose--policy li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 0.5rem;
  height: 1px;
  background: var(--fs-blue);
}
.fs-prose--policy strong { color: var(--fs-ink); font-weight: 600; }

.fs-policy { padding-block: clamp(2.5rem, 6vw, 4.5rem) var(--fs-section-y); }
.fs-policy__meta {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fs-ink-mute);
}

/* Draft notice. Deliberately loud: these pages are not launch-ready and must
   not go live without a business-owner and legal read. */
.fs-review {
  padding: var(--fs-space-5);
  background: rgb(var(--fs-c-blue-100) / .5);
  border-left: 3px solid var(--fs-blue);
  border-radius: 0 var(--fs-r-md) var(--fs-r-md) 0;
}
.fs-review p { color: var(--fs-ink-soft); font-size: var(--fs-text-sm); line-height: 1.65; }
.fs-review__title {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-ink);
  margin-bottom: var(--fs-space-2);
}


/* == 24  CHECK YOUR DATE ================================================== */

.fs-inquiry {
  padding-block: clamp(2rem, 5vw, 3.5rem) var(--fs-section-y);
  background: var(--fs-white);
}
.fs-inquiry__grid { display: grid; gap: clamp(2rem, 5vw, 3.5rem); }

/* The panel the inquiry form sits in — see section 25 for the form itself
   and check-your-date/index.html for the GoHighLevel integration points. */
.fs-formpanel {
  padding: clamp(1.25rem, 3vw, 2rem);
  background: var(--fs-surface-ice);
  border: 1px solid var(--fs-hairline);
  border-radius: var(--fs-r-lg);
}
.fs-next__title {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.25rem, 3vw, 1.625rem);
  line-height: 1.1;
  letter-spacing: var(--fs-ls-display);
  text-transform: uppercase;
}
.fs-next__list { display: grid; gap: var(--fs-space-5); margin-top: var(--fs-space-5); }
.fs-next__list li {
  display: grid;
  gap: 0.25rem;
  padding-top: var(--fs-space-3);
  border-top: 1px solid var(--fs-hairline-b);
}
.fs-next__num {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.625rem, 4vw, 2.125rem);
  line-height: 0.95;
  letter-spacing: -0.02em;
  color: rgb(var(--fs-c-blue) / .28);
}
.fs-next__list p { color: var(--fs-ink-soft); font-size: var(--fs-text-sm); }
.fs-next__note {
  margin-top: var(--fs-space-6);
  padding-top: var(--fs-space-5);
  border-top: 1px solid var(--fs-hairline);
  font-size: var(--fs-text-sm);
  line-height: 1.7;
  color: var(--fs-ink-mute);
}
@media (min-width: 62rem) {
  .fs-inquiry__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 0.52fr); align-items: start; }
  .fs-next { position: sticky; top: calc(var(--fs-nav-h) + 2rem); }
}


/* == 25  INQUIRY FORM ===================================================== */

/* A custom front end over a GoHighLevel back end — the integration points are
   documented in check-your-date/index.html. Everything below is skin: the
   controls stay ordinary form elements so browser validation, autofill,
   mobile keyboards and assistive technology all keep working, and only their
   appearance is ours.

   The panel is the query container, so the form and the confirmation that
   replaces it both respond to the column they sit in rather than to the
   viewport — the form is narrow on this page and would be wide if it were
   ever pasted full-bleed into a GoHighLevel row. */
.fs-formpanel { container-type: inline-size; }

/* --- progress ----------------------------------------------------------- */
.fs-form__steps {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.5rem;
  margin-bottom: var(--fs-space-6);
}
.fs-form__steps li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.4375rem;
  padding-top: 0.5rem;
  border-top: 2px solid var(--fs-hairline);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fs-ink-mute);
  transition: color var(--fs-t) var(--fs-ease),
              border-color var(--fs-t) var(--fs-ease);
}
.fs-form__stepnum { font-weight: 700; letter-spacing: 0.02em; }
.fs-form__steps li.is-done {
  color: var(--fs-ink-soft);
  border-top-color: var(--fs-hairline-b);
}
.fs-form__steps li[aria-current="step"] {
  color: var(--fs-blue-text);
  border-top-color: var(--fs-blue);
}

/* Four labels set in a wide display face do not fit four narrow columns, and
   truncating them would drop the word from the accessibility tree too. Below
   the fit width only the current step keeps a visible label; the other three
   stay readable to assistive technology (same rules as .fs-vh) and the
   numbered progress rule carries the rest. */
@container (max-width: 27rem) {
  .fs-form__steps li:not([aria-current="step"]) .fs-form__steplabel {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

/* --- panels ------------------------------------------------------------- */
/* Block layout, not grid: a <legend> is a specially-rendered box and does not
   behave as a grid item, so vertical rhythm comes from margins instead. */
.fs-form__panel {
  margin: 0;
  padding: 0;
  border: 0;
  min-inline-size: 0;
  /* stepping scrolls the panel into view; keep it clear of the sticky nav */
  scroll-margin-top: calc(var(--fs-nav-h) + 1rem);
}
.fs-form__panel > * + * { margin-top: var(--fs-space-5); }

.fs-form__legend {
  padding: 0;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-blue-text);
}

/* --- fields ------------------------------------------------------------- */
.fs-field {
  display: grid;
  gap: 0.375rem;
  /* Only the inline margins are reset — those are the UA's fieldset margins.
     The block-start margin belongs to the panel's rhythm rule above, which
     carries the same specificity and would lose to anything set here. */
  margin-inline: 0;
  margin-block-end: 0;
  padding: 0;
  border: 0;
  min-inline-size: 0;
}
.fs-field > label,
.fs-field > legend {
  padding: 0;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.fs-field__opt {
  margin-left: 0.4375rem;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: none;
  color: var(--fs-ink-mute);
}

/* A fieldset can't be a grid container without disturbing its legend, so the
   grouped fields fall back to block flow. */
.fs-field--group { display: block; }
.fs-field--group > legend { margin-bottom: 0.625rem; }
.fs-field--group > .fs-field__error { display: block; margin-top: 0.5rem; }

.fs-field__row { display: grid; gap: var(--fs-space-4); }
@container (min-width: 30rem) {
  .fs-field__row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.fs-form input[type="text"],
.fs-form input[type="email"],
.fs-form input[type="tel"],
.fs-form input[type="number"],
.fs-form input[type="date"],
.fs-form input[type="time"],
.fs-form select,
.fs-form textarea {
  width: 100%;
  min-height: 3rem;
  padding: 0.6875rem 0.875rem;
  font-family: var(--fs-font-body);
  /* Never below 16px: iOS zooms the page on focus for anything smaller. */
  font-size: var(--fs-text-base);
  line-height: 1.4;
  color: var(--fs-ink);
  background: var(--fs-white);
  border: 1px solid rgb(var(--fs-c-ink) / .18);
  border-radius: var(--fs-r-md);
  transition: border-color var(--fs-t-fast) var(--fs-ease),
              box-shadow var(--fs-t-fast) var(--fs-ease);
}
.fs-form input::placeholder,
.fs-form textarea::placeholder { color: var(--fs-ink-mute); }
.fs-form input:hover,
.fs-form select:hover,
.fs-form textarea:hover { border-color: rgb(var(--fs-c-ink) / .34); }
.fs-form input:focus,
.fs-form select:focus,
.fs-form textarea:focus { border-color: var(--fs-blue); }

/* Only vertical resizing: dragging one of these sideways inside a container
   query column is a way to break the layout, not a feature. */
.fs-form textarea {
  min-height: 6.5rem;
  line-height: 1.6;
  resize: vertical;
}

/* Number and select lose their native furniture; date and time keep theirs,
   because suppressing the appearance of those two breaks the native picker
   affordance on iOS. */
.fs-form input[type="number"] { appearance: textfield; }
.fs-form input[type="number"]::-webkit-outer-spin-button,
.fs-form input[type="number"]::-webkit-inner-spin-button { appearance: none; margin: 0; }
.fs-form select {
  appearance: none;
  padding-right: 2.5rem;
  /* the caret, drawn here so the control needs no wrapper element */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%230B0D12' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: 0.75rem 0.5rem;
}

/* --- validation --------------------------------------------------------- */
.fs-field__error {
  font-size: var(--fs-text-sm);
  line-height: 1.4;
  color: var(--fs-alert);
}
/* an empty slot must not open a gap in the field grid */
.fs-field__error:empty { display: none; }

.fs-field.is-invalid > label,
.fs-field.is-invalid > legend { color: var(--fs-alert); }
.fs-field.is-invalid input,
.fs-field.is-invalid select,
.fs-field.is-invalid textarea { border-color: var(--fs-alert); }

/* --- pills (radios and checkboxes as chips) ------------------------------ */
.fs-pillrow { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.fs-pill { position: relative; display: inline-flex; }
/* The real control covers the chip: it stays focusable, checkable and
   announced, and only its paint is replaced. */
.fs-pill input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.fs-pill > span {
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0.5rem 1.125rem;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fs-ink-soft);
  background: var(--fs-white);
  border: 1px solid rgb(var(--fs-c-ink) / .18);
  border-radius: var(--fs-r-pill);
  transition: color var(--fs-t-fast) var(--fs-ease),
              background-color var(--fs-t-fast) var(--fs-ease),
              border-color var(--fs-t-fast) var(--fs-ease);
}
.fs-pill:hover > span { border-color: var(--fs-blue); color: var(--fs-ink); }
.fs-pill input:checked + span {
  color: var(--fs-white);
  background: var(--fs-blue);
  border-color: var(--fs-blue);
}
/* The input itself is transparent, so its focus ring would be invisible. */
.fs-pill input:focus-visible + span {
  outline: 2px solid var(--fs-blue);
  outline-offset: 3px;
  box-shadow: 0 0 0 6px rgb(var(--fs-c-white) / .85);
}

.fs-check {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 2.75rem;
  font-size: var(--fs-text-sm);
  color: var(--fs-ink-soft);
  cursor: pointer;
}
.fs-check input {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin: 0;
  accent-color: var(--fs-blue);
}

/* --- package choices ----------------------------------------------------- */
.fs-choices { display: grid; gap: 0.625rem; }
@container (min-width: 34rem) {
  .fs-choices { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fs-choice--help { grid-column: 1 / -1; }
}
.fs-choice { position: relative; display: block; cursor: pointer; }
.fs-choice input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.fs-choice__body {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: 0.875rem;
  row-gap: 0.1875rem;
  padding: 0.875rem 1.125rem;
  background: var(--fs-white);
  border: 1px solid rgb(var(--fs-c-ink) / .16);
  border-radius: var(--fs-r-md);
  transition: border-color var(--fs-t-fast) var(--fs-ease),
              background-color var(--fs-t-fast) var(--fs-ease),
              box-shadow var(--fs-t) var(--fs-ease);
}
.fs-choice:hover .fs-choice__body { border-color: var(--fs-blue); }
.fs-choice input:checked + .fs-choice__body {
  background: var(--fs-surface-blue);
  border-color: var(--fs-blue);
  box-shadow: inset 0 0 0 1px var(--fs-blue);
}
.fs-choice input:focus-visible + .fs-choice__body {
  outline: 2px solid var(--fs-blue);
  outline-offset: 3px;
  box-shadow: 0 0 0 6px rgb(var(--fs-c-white) / .85);
}
.fs-choice__hours {
  grid-row: 1 / span 2;
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: 1.375rem;
  line-height: 1;
  letter-spacing: -0.02em;
  color: rgb(var(--fs-c-blue) / .8);
  transition: color var(--fs-t-fast) var(--fs-ease);
}
.fs-choice__hours span {
  font-size: 0.5em;
  letter-spacing: 0.08em;
  margin-left: 0.15em;
  color: var(--fs-ink-mute);
}
.fs-choice input:checked + .fs-choice__body .fs-choice__hours { color: var(--fs-blue); }
.fs-choice__name {
  font-family: var(--fs-font-display);
  font-weight: 700;
  font-size: var(--fs-text-sm);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.fs-choice__price {
  font-family: var(--fs-font-display);
  font-weight: 700;
  font-size: var(--fs-text-sm);
  text-align: right;
  color: var(--fs-blue-text);
}
.fs-choice__step {
  grid-column: 2 / -1;
  font-size: var(--fs-text-xs);
  line-height: 1.5;
  color: var(--fs-ink-mute);
}
/* The "not sure yet" option carries no hours and no price: one column. */
.fs-choice--help .fs-choice__body { grid-template-columns: minmax(0, 1fr); }
.fs-choice--help .fs-choice__step { grid-column: 1; }

.fs-choices + .fs-field__error { display: block; margin-top: 0.5rem; }

/* --- review summary ------------------------------------------------------ */
.fs-summary {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.5rem 1.25rem;
  /* same reason as .fs-field: the block-start margin is the panel's */
  margin-block-end: 0;
  margin-inline: 0;
  padding-top: var(--fs-space-4);
  border-top: 1px solid var(--fs-hairline);
}
.fs-summary dt {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fs-ink-mute);
}
.fs-summary dd { margin: 0; font-size: var(--fs-text-sm); line-height: 1.5; }
@container (max-width: 26rem) {
  .fs-summary { grid-template-columns: minmax(0, 1fr); gap: 0.125rem; }
  .fs-summary dd { margin-bottom: 0.625rem; }
}
.fs-summary__actions:empty { display: none; }
.fs-summary__edit {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fs-blue-text);
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.35em;
}

/* --- controls, legal, status --------------------------------------------- */
.fs-form__nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: var(--fs-space-6);
  padding-top: var(--fs-space-5);
  border-top: 1px solid var(--fs-hairline);
}
.fs-form__nav .fs-btn { flex: 1 1 auto; }
@container (min-width: 26rem) {
  .fs-form__nav .fs-btn { flex: 0 0 auto; }
  /* once Back appears, the forward button moves to the far edge */
  .fs-form__nav [data-fs-back]:not([hidden]) ~ .fs-btn { margin-left: auto; }
}

.fs-form__hint {
  font-size: var(--fs-text-sm);
  line-height: 1.6;
  color: var(--fs-ink-mute);
}
.fs-form__legal {
  margin-top: var(--fs-space-4);
  font-size: var(--fs-text-sm);
  line-height: 1.6;
  color: var(--fs-ink-mute);
}
.fs-form__reserve {
  padding: 0.75rem 1rem;
  font-size: var(--fs-text-sm);
  line-height: 1.6;
  color: var(--fs-ink-soft);
  background: var(--fs-white);
  border-left: 2px solid var(--fs-blue);
  border-radius: 0 var(--fs-r-sm) var(--fs-r-sm) 0;
}

.fs-form__status {
  margin-top: var(--fs-space-4);
  font-size: var(--fs-text-sm);
  line-height: 1.6;
  color: var(--fs-ink-soft);
}
.fs-form__status:empty { display: none; }
.fs-form__status.is-blocked {
  padding: 0.875rem 1rem;
  color: var(--fs-ink);
  background: rgb(var(--fs-c-alert) / .07);
  border: 1px solid rgb(var(--fs-c-alert) / .3);
  border-radius: var(--fs-r-md);
}

/* Shown only with JavaScript unavailable. The capture is a script, so there
   is no server to post to and no honest way to accept a submission — the
   note gives the email address instead of a dead button. */
.fs-form__noscript {
  margin-top: var(--fs-space-5);
  padding: 0.875rem 1rem;
  font-size: var(--fs-text-sm);
  line-height: 1.6;
  color: var(--fs-ink);
  background: var(--fs-surface-blue);
  border: 1px solid var(--fs-hairline-b);
  border-radius: var(--fs-r-md);
}

/* --- confirmation --------------------------------------------------------- */
.fs-confirm {
  position: relative;
  /* success scrolls this into view; keep the heading clear of the sticky nav */
  scroll-margin-top: calc(var(--fs-nav-h) + 1.5rem);
}
/* focus is moved here on success so the change is announced; the outline
   would frame the whole panel, and the heading already marks the place */
.fs-confirm:focus { outline: none; }
.fs-confirm__title {
  margin: var(--fs-space-4) 0 var(--fs-space-5);
  font-size: var(--fs-display-md);
}
.fs-confirm__title em { font-style: normal; color: var(--fs-blue); }
.fs-confirm__lede {
  font-size: var(--fs-text-md);
  line-height: 1.6;
  color: var(--fs-ink-soft);
  max-width: 34rem;
}
.fs-confirm__steps { display: grid; gap: var(--fs-space-4); margin-top: var(--fs-space-6); }
.fs-confirm__steps li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 0.875rem;
  row-gap: 0.125rem;
  padding-top: var(--fs-space-3);
  border-top: 1px solid var(--fs-hairline);
  font-size: var(--fs-text-sm);
  line-height: 1.6;
  color: var(--fs-ink-soft);
}
.fs-confirm__steps li > span {
  grid-row: 1 / span 2;
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: 1.25rem;
  line-height: 1;
  letter-spacing: -0.02em;
  color: rgb(var(--fs-c-blue) / .8);
}
.fs-confirm__steps strong {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fs-ink);
}
.fs-confirm__note {
  margin-top: var(--fs-space-6);
  padding: 0.875rem 1rem;
  font-size: var(--fs-text-sm);
  line-height: 1.6;
  color: var(--fs-ink-soft);
  background: var(--fs-white);
  border-left: 2px solid var(--fs-blue);
  border-radius: 0 var(--fs-r-sm) var(--fs-r-sm) 0;
}
.fs-confirm__contact { margin-top: var(--fs-space-4); }
/* Only rendered by ?preview=confirmation, and it says so plainly. */
.fs-confirm__preview {
  margin-bottom: var(--fs-space-5);
  padding: 0.75rem 1rem;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fs-ink);
  background: rgb(var(--fs-c-blue) / .1);
  border: 1px dashed var(--fs-hairline-b);
  border-radius: var(--fs-r-md);
}

/* The branded burst: a dozen-odd squares and logo sparks thrown once, then
   gone. It fires on a successful submission only — never on load, never in a
   loop — and under reduced motion the script does not create the pieces at
   all, so nothing here has to run. */
.fs-confirm__burst {
  position: absolute;
  top: 1.5rem;
  left: 3rem;
  width: 0;
  height: 0;
  pointer-events: none;
}
.fs-bit {
  position: absolute;
  top: 0;
  left: 0;
  width: 0.5rem;
  height: 0.5rem;
  background: var(--fs-blue);
  border-radius: 1px;
  opacity: 0;
  animation: fs-bit-throw 900ms var(--fs-ease) var(--d, 0ms) forwards;
}
.fs-bit--star {
  background: var(--fs-ink);
  border-radius: 0;
  clip-path: var(--fs-spark);
}
@keyframes fs-bit-throw {
  0%   { opacity: 0; scale: 0.4; translate: 0 0; rotate: 0deg; }
  15%  { opacity: 1; }
  70%  { opacity: 1; }
  100% { opacity: 0; scale: 1; translate: var(--x, 0) var(--y, -80px); rotate: var(--r, 90deg); }
}
@media (prefers-reduced-motion: reduce) {
  .fs-bit { display: none; }
}


/* == 26  SHARED PAGE SECTIONS — packages + the experience ================= */

.fs-included {
  padding-block: var(--fs-section-y);
  background: var(--fs-surface-ice);
}
.fs-included__list {
  display: grid;
  gap: 0.75rem;
  margin-top: clamp(2rem, 4vw, 3rem);
}
.fs-included__list li {
  position: relative;
  padding-left: 1.75rem;
  font-size: var(--fs-text-md);
  color: var(--fs-ink-soft);
}
.fs-included__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 0.5rem;
  height: 0.5rem;
  background: var(--fs-blue);
  clip-path: var(--fs-spark);
  color: var(--fs-blue);
}
@media (min-width: 48rem) {
  .fs-included__list {
    display: block;
    columns: 2;
    column-gap: clamp(1.5rem, 4vw, 3rem);
  }
  .fs-included__list li { break-inside: avoid; margin-bottom: 0.875rem; }
}

.fs-packages-full { padding-block: var(--fs-section-y); background: var(--fs-white); }
.fs-detail__stack {
  display: grid;
  gap: clamp(3rem, 8vw, 6rem);
  margin-top: clamp(2rem, 5vw, 3.5rem);
}
.fs-detail {
  position: relative;
  display: grid;
  gap: clamp(1.5rem, 4vw, 2.5rem);
  padding-top: var(--fs-space-6);
  border-top: 1px solid var(--fs-hairline-b);
}
.fs-detail__numeral {
  position: absolute;
  right: 0;
  top: -0.14em;
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(6rem, 16vw, 12rem);
  line-height: 0.8;
  letter-spacing: -0.04em;
  color: rgb(var(--fs-c-blue) / .1);
  pointer-events: none;
  user-select: none;
}
.fs-detail__head { position: relative; }
.fs-detail__hours {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.25rem 0;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-ink-mute);
}
.fs-detail__name {
  margin-top: var(--fs-space-2);
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: var(--fs-display-sm);
  line-height: 1;
  letter-spacing: var(--fs-ls-display);
  text-transform: uppercase;
}
.fs-detail__price {
  margin-top: var(--fs-space-3);
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.75rem, 4.5vw, 2.5rem);
  line-height: 1;
  letter-spacing: -0.025em;
  color: var(--fs-blue);
}
.fs-detail__line {
  margin-top: var(--fs-space-3);
  font-size: var(--fs-text-md);
  color: var(--fs-ink-soft);
  max-width: 26rem;
}
.fs-detail__media img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--fs-r-lg);
  box-shadow: var(--fs-lift);
}
.fs-detail__subhead {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-ink-mute);
}
.fs-detail__list { display: grid; gap: 0.5rem; margin-top: var(--fs-space-4); }
.fs-detail__list li {
  position: relative;
  padding-left: 1.375rem;
  font-size: var(--fs-text-sm);
  line-height: 1.55;
  color: var(--fs-ink-soft);
}
.fs-detail__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.68em;
  width: 0.5rem;
  height: 1px;
  background: var(--fs-blue);
}
.fs-detail__best {
  margin-top: var(--fs-space-4);
  font-size: var(--fs-text-sm);
  line-height: 1.65;
  color: var(--fs-ink-mute);
}
.fs-detail__best span {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fs-blue-text);
  margin-right: 0.375rem;
}
.fs-detail__body .fs-btn { margin-top: var(--fs-space-6); }

@media (min-width: 48rem) {
  .fs-detail {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.62fr);
    grid-template-areas:
      "head media"
      "body media";
    grid-template-rows: auto 1fr;
    row-gap: var(--fs-space-6);
  }
  .fs-detail__head  { grid-area: head; }
  .fs-detail__body  { grid-area: body; }
  .fs-detail__media { grid-area: media; }
  .fs-detail__list {
    display: block;
    columns: 2;
    column-gap: clamp(1rem, 3vw, 2rem);
  }
  .fs-detail__list li { break-inside: avoid; margin-bottom: 0.5rem; }
  .fs-detail__numeral { top: -0.2em; }
}

.fs-custom-overview { padding-block: var(--fs-section-y); background: var(--fs-surface-ice); }

/* Definition lists carry the fuller detail without turning into cards. */
.fs-deflist { margin-top: clamp(1.75rem, 4vw, 2.5rem); }
.fs-deflist dt {
  margin-top: var(--fs-space-5);
  padding-top: var(--fs-space-3);
  border-top: 1px solid var(--fs-hairline-b);
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: var(--fs-ls-micro);
  text-transform: uppercase;
  color: var(--fs-blue-text);
}
.fs-deflist dd {
  margin-top: var(--fs-space-2);
  font-size: var(--fs-text-sm);
  line-height: 1.7;
  color: var(--fs-ink-soft);
  max-width: 34rem;
}
@media (min-width: 48rem) {
  .fs-deflist--wide { display: grid; grid-template-columns: 12rem minmax(0, 1fr); column-gap: clamp(1.5rem, 4vw, 3rem); }
  .fs-deflist--wide dt { grid-column: 1; }
  .fs-deflist--wide dd { grid-column: 2; margin-top: var(--fs-space-5); padding-top: var(--fs-space-3); }
}

.fs-eventtypes { padding-block: var(--fs-section-y); background: var(--fs-white); }
.fs-eventtypes__list {
  display: grid;
  gap: clamp(1.75rem, 4vw, 2.5rem);
  margin-top: clamp(2rem, 5vw, 3rem);
}
.fs-eventtypes__list dt {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.125rem, 2.6vw, 1.375rem);
  line-height: 1.1;
  letter-spacing: var(--fs-ls-display);
  text-transform: uppercase;
}
.fs-eventtypes__list dd {
  margin-top: var(--fs-space-3);
  padding-top: var(--fs-space-3);
  border-top: 1px solid var(--fs-hairline-b);
  font-size: var(--fs-text-sm);
  line-height: 1.7;
  color: var(--fs-ink-soft);
}
@media (min-width: 48rem) {
  .fs-eventtypes__list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1.75rem, 4vw, 3rem); }
}
@media (min-width: 62rem) {
  .fs-eventtypes__list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.fs-expect { padding-block: var(--fs-section-y); background: var(--fs-surface-ice); }
.fs-expect__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fs-space-4);
  margin-top: clamp(2.5rem, 6vw, 3.5rem);
}


/* == 27  WALK UP, PICK YOUR MOMENT, MAKE IT YOURS ========================= */

/* Three lines and one paragraph. The old version of this explained every
   screen interaction; the statement carries it instead. */
.fs-howto {
  padding-block: var(--fs-section-y);
  background: var(--fs-surface-ice);
}
.fs-howto__grid {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: center;
}
.fs-howto__copy { container-type: inline-size; }
.fs-howto__title {
  font-size: clamp(1.75rem, 11cqw, 3.5rem);
  margin-bottom: var(--fs-space-5);
}
.fs-howto__title em { font-style: normal; color: var(--fs-blue); }

/* position: relative matters — the film-edge motif is absolutely positioned
   just outside this frame, and without a positioned ancestor it escaped to
   the page box and pushed the document 9px wide. */
.fs-howto__media {
  position: relative;
  max-width: 26rem;
}
.fs-howto__media img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--fs-r-lg);
  box-shadow: var(--fs-lift);
}

@media (min-width: 48rem) {
  .fs-howto__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 0.68fr); }
  .fs-howto__media { max-width: none; }
}


/* == 28  PACKAGE LADDER =================================================== */

/* The whole comparison in one glance. Every rung carries a fill that grows a
   quarter at a time, so "each one adds one thing" is something you can see
   before you read a word of it. */
.fs-ladder {
  padding-block: var(--fs-section-y);
  background: var(--fs-surface-ice);
}
.fs-ladder__list {
  display: grid;
  gap: 0.625rem;
  margin-top: clamp(2rem, 5vw, 3rem);
}
.fs-ladder__rung a {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.5rem 1rem;
  overflow: hidden;
  min-height: 3.5rem;
  padding: 0.875rem clamp(1rem, 3vw, 1.5rem);
  border: 1px solid var(--fs-hairline-b);
  border-radius: var(--fs-r-md);
  background: rgb(var(--fs-c-white) / .72);
  transition: border-color var(--fs-t) var(--fs-ease),
              background-color var(--fs-t) var(--fs-ease),
              transform var(--fs-t-fast) var(--fs-ease);
}
/* the growing quarter-fill */
.fs-ladder__rung a::before {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 3px;
  background: var(--fs-blue);
  transform-origin: left;
  transform: scaleX(var(--_fill, 0.25));
  transition: transform var(--fs-t-slow) var(--fs-ease);
}
.fs-ladder__rung:nth-child(1) a { --_fill: 0.25; }
.fs-ladder__rung:nth-child(2) a { --_fill: 0.5; }
.fs-ladder__rung:nth-child(3) a { --_fill: 0.75; }
.fs-ladder__rung:nth-child(4) a { --_fill: 1; }
.fs-ladder__rung a:hover {
  border-color: var(--fs-blue);
  background: var(--fs-white);
  transform: translateY(-2px);
}

.fs-ladder__hours {
  font-family: var(--fs-font-display);
  font-weight: 800;
  font-size: clamp(1.5rem, 5vw, 2.125rem);
  line-height: 0.9;
  letter-spacing: -0.01em;
  color: var(--fs-blue);
  font-variant-numeric: tabular-nums;
}
.fs-ladder__unit {
  font-size: 0.4em;
  font-weight: 600;
  letter-spacing: 0.14em;
  margin-left: 0.15em;
  vertical-align: 0.55em;
}
.fs-ladder__step {
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fs-ink);
}
.fs-ladder__name {
  grid-column: 2;
  font-size: var(--fs-text-sm);
  color: var(--fs-ink-mute);
}
.fs-ladder__price {
  grid-row: 1 / span 2;
  grid-column: 3;
  font-family: var(--fs-font-display);
  font-weight: 700;
  font-size: var(--fs-text-md);
  color: var(--fs-ink);
}
.fs-ladder__note {
  margin-top: var(--fs-space-5);
  font-size: var(--fs-text-sm);
  color: var(--fs-ink-mute);
}

@media (min-width: 48rem) {
  .fs-ladder__list { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.75rem; }
  .fs-ladder__rung a {
    grid-template-columns: 1fr;
    align-items: start;
    align-content: start;
    gap: 0.375rem;
    min-height: 11rem;
    padding: clamp(1rem, 2vw, 1.375rem);
  }
  .fs-ladder__hours { font-size: clamp(1.875rem, 3.4vw, 2.75rem); }
  .fs-ladder__name  { grid-column: 1; }
  .fs-ladder__price { grid-row: auto; grid-column: 1; margin-top: auto; font-size: var(--fs-text-lg); }
}


/* == 28b  ADD-ONS ========================================================= */

/* A price list, deliberately styled like one. The packages above already
   carry the section's one visual move; this is the quiet page after it, so
   the rows are hairlines and the only colour is the price. */
.fs-addons {
  padding-block: var(--fs-section-y);
}
.fs-addon__grid {
  display: grid;
  gap: 0;
  margin-top: clamp(2rem, 5vw, 3rem);
  border-top: 1px solid var(--fs-hairline);
}
.fs-addon {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: var(--fs-space-4);
  padding-block: clamp(0.875rem, 2vw, 1.125rem);
  border-bottom: 1px solid var(--fs-hairline);
}
.fs-addon__name {
  margin: 0;
  font-size: var(--fs-text-base);
  color: var(--fs-ink);
}
/* The qualifier rides under the name rather than beside it: at 375px
   "subject to availability" on the same line pushed the price off the row. */
.fs-addon__note {
  display: block;
  margin-top: 0.125rem;
  font-size: var(--fs-text-sm);
  color: var(--fs-ink-mute);
}
.fs-addon__price {
  margin: 0;
  font-family: var(--fs-font-display);
  font-size: var(--fs-text-md);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--fs-blue-text);
  white-space: nowrap;
}
.fs-addons__note {
  max-width: 44rem;
  margin-top: var(--fs-space-5);
  font-size: var(--fs-text-sm);
  line-height: 1.7;
  color: var(--fs-ink-mute);
}

/* Two columns once a row would otherwise be mostly empty space. The border
   is moved to the cells so the two columns keep their own rules. */
@media (min-width: 48rem) {
  .fs-addon__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(2rem, 5vw, 4rem);
  }
}


/* == 29  404 ============================================================== */

.fs-notfound { padding-block: clamp(3rem, 8vw, 6rem) clamp(4rem, 10vw, 7rem); }
.fs-notfound__star {
  position: absolute;
  right: -10%;
  bottom: -40%;
  width: min(28rem, 60vw);
  aspect-ratio: 1;
  background: url("../img/logo/fs-star-motif.png") no-repeat center / contain;
  opacity: 0.07;
  transform: rotate(16deg);
  pointer-events: none;
}
.fs-notfound__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fs-space-4);
  margin-top: clamp(1.75rem, 4vw, 2.5rem);
}
.fs-notfound__links {
  display: grid;
  gap: 0.125rem;
  margin-top: clamp(2.5rem, 6vw, 3.5rem);
  padding-top: var(--fs-space-5);
  border-top: 1px solid var(--fs-hairline);
  max-width: 32rem;
}
.fs-notfound__links a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  font-size: var(--fs-text-sm);
  color: var(--fs-ink-soft);
  transition: color var(--fs-t) var(--fs-ease);
}
.fs-notfound__links a:hover { color: var(--fs-blue-text); }


/* == 30  NO-JAVASCRIPT FALLBACKS ========================================== */

/* Every interactive piece degrades to readable, complete content. */
.fs-no-js .fs-menu { display: block; }
.fs-no-js .fs-nav__toggle { display: none; }

.fs-no-js .fs-selector { display: none; }
.fs-no-js .fs-package-stack { display: block; }
.fs-no-js .fs-package {
  opacity: 1;
  visibility: visible;
  padding-top: var(--fs-space-7);
  border-top: 1px solid var(--fs-hairline);
}
.fs-no-js .fs-package + .fs-package { margin-top: var(--fs-space-7); }
.fs-no-js .fs-package__numeral { color: rgb(var(--fs-c-blue) / .12); }


/* == 31  THE HIDDEN ATTRIBUTE ============================================= */

/* `[hidden] { display: none }` lives in the user-agent origin, so ANY author
   rule that sets `display` on the same element wins and the element stays on
   screen — .fs-btn and .fs-form__panel are both toggled with
   the attribute and all set display. Rather than making every component
   remember that, it is stated once here, last in the file, at single-class
   specificity: it beats every component rule above by source order, and the
   one rule that must override it — .fs-no-js .fs-menu, which deliberately
   shows the menu that markup ships hidden — outranks it on specificity. No
   !important needed either way. */
.full-scene-site [hidden] { display: none; }
