/* ============================================================================
   Nova Lounge design tokens

   The identity is built around open water, polished metal, and daylight. The
   background carries the colour; interface surfaces stay quiet and translucent.
   Legacy vessel-token names remain as aliases because the booking UI already uses
   them extensively, but their values now resolve to the blue and silver system.
   ========================================================================== */
:root {
  --atlantic: #0754c7;
  --electric-blue: #138bff;
  --sky-blue: #64c8ff;
  --silver: #e3edf5;
  --steel: #afc0d0;
  --ink-navy: #061a3a;

  --ground: var(--atlantic);
  --ground-2: #073f9a;
  --ground-3: #061f54;

  --ink: #f8fbff;
  --ink-muted: #d7e4ef;
  --ink-faint: #e3edf5;

  /* Compatibility aliases for the original component layer. */
  --cognac: var(--sky-blue);
  --cognac-deep: var(--atlantic);
  --oxblood: #08347a;
  --cream: var(--silver);
  --hull: #0f72dc;

  --success: #42d39a;
  --danger: #ff7d7d;

  --surface: rgba(5, 31, 79, 0.66);
  --surface-strong: rgba(4, 24, 64, 0.84);
  --surface-soft: rgba(227, 237, 245, 0.12);
  --line: rgba(227, 237, 245, 0.24);
  --line-strong: rgba(227, 237, 245, 0.44);
  --shadow: 0 24px 64px rgba(4, 26, 69, 0.22);

  /* Tight radii read as considered; 8px on everything reads as a default. */
  --radius: 3px;
  --radius-lg: 6px;

  --font-display: "Plus Jakarta Sans", "Helvetica Neue", Arial, sans-serif;
  --font-body: "Plus Jakarta Sans", "Helvetica Neue", Arial, sans-serif;
  --font-utility: var(--font-body);

  /* Fixed type steps prevent viewport-width scaling from making text unpredictable. */
  --text-12: 0.75rem;
  --text-14: 0.875rem;
  --text-16: 1rem;
  --text-18: 1.125rem;
  --text-22: 1.375rem;
  --text-30: 1.875rem;
  --text-44: 3.5rem;
  --text-64: 5rem;

  /* --- Spacing scale ------------------------------------------------------
     ONE scale. Every margin, gap and padding in this file resolves to a step
     below. A value that is not on the scale is a defect - see LAYOUT_RULES.md
     at the repo root, and `measure.mjs --layout` which enforces it.          */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  /* Section rhythm */
  --band-y: clamp(72px, 9vw, 132px);
  --gutter: clamp(20px, 4vw, 48px);

  /* THE SPINE. Every section's content resolves to this width, so headings and
     the content they head share a left edge. Four different max-widths (1080 /
     1120 / 1180) put four different left edges on the page. */
  --content-max: 1180px;
  --story-rail: 520px;
  --gallery-max-height: 800px;

  /* Inset for an icon drawn inside a field, measured from the field's border.
     A chevron or a picker glyph hard against the edge is the tell. */
  --field-icon-inset: var(--space-3);
}

* {
  box-sizing: border-box;
  letter-spacing: 0 !important;
}

html {
  scroll-behavior: auto;
  /* The header is sticky and 72px tall. Without this, every in-page anchor lands
     with its own heading hidden underneath it - "Choose a date" was sitting behind
     the header on a #booking jump. */
  /* No top header any more - the nav docks at the bottom - so an anchor only
     needs to clear its own breathing room. */
  scroll-padding-top: var(--space-6);
}

body {
  min-width: 320px;
  margin: 0;
  color: var(--ink);
  background: var(--atlantic);
  font-family: var(--font-body);
  font-size: var(--text-16);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body.no-webgl {
  background: linear-gradient(135deg, #0643a4 0%, #0c6cdb 36%, #3caaf3 67%, #d7e6f0 100%);
}

button,
input,
select {
  font: inherit;
}

button,
a,
input,
select {
  outline-offset: 3px;
}

button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible {
  outline: 3px solid var(--cognac);
}

img {
  display: block;
  max-width: 100%;
}

#webglBackground,
.ambient-scrim {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
}

#webglBackground {
  z-index: -3;
  pointer-events: none;
  background: var(--ground);
}

/* Warm scrim, not a blue one. This sits between the WebGL cabin and the UI: its
   job is to hold text contrast without draining the warmth out of the render. */
.ambient-scrim {
  z-index: -2;
  pointer-events: none;
  background:
    linear-gradient(90deg, rgba(10, 8, 6, 0.88), rgba(10, 8, 6, 0.42) 46%, rgba(10, 8, 6, 0.80)),
    linear-gradient(180deg, rgba(10, 8, 6, 0.30), rgba(10, 8, 6, 0.88) 76%);
  transition: background 500ms ease;
}

body.particle-background-ready[data-page="home"] .ambient-scrim {
  background:
    /* NOT darkened to lift particle contrast - the scrim is z-index -2 and the
       particle canvas is -3, so anything added here dims the particles by exactly as
       much as the background and gains nothing. The page ground below the canvas is
       darkened instead. This layer keeps its original job: text legibility. */
    linear-gradient(90deg, rgba(10, 8, 6, 0.94) 8%, rgba(10, 8, 6, 0.72) 42%, rgba(10, 8, 6, 0.18) 72%, rgba(10, 8, 6, 0.48)),
    linear-gradient(180deg, rgba(10, 8, 6, 0.18), rgba(10, 8, 6, 0.34) 64%, rgba(10, 8, 6, 0.88));
}

/* ===========================================================================
   Bottom-docked floating navigation

   The band is fixed to the BOTTOM and is transparent; only the pill inside it is
   visible. It never reacts to scroll - verified on the reference, whose bar is
   byte-identical at the top of the page and 11,500px down.

   --nav-h is the band height and --nav-inset the iPhone home-indicator gap. Every
   full-height layout on the site subtracts both, so the bar can never sit on top
   of a control.
   ========================================================================= */

:root {
  --nav-h: 77px;
  --nav-inset: env(safe-area-inset-bottom, 0px);
  --nav-reserve: calc(var(--nav-h) + var(--nav-inset));
}

@media (max-width: 900px) {
  :root { --nav-h: 64px; }
}

.site-header {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 60;
  display: block;
  min-height: var(--nav-h);
  padding: 0;
  background: none;
  border: 0;
  backdrop-filter: none;
  pointer-events: none;   /* the band is empty air; only the pill takes input */
}

.site-header > * {
  pointer-events: auto;
}

/* Nothing may hide behind the bar. */
main {
  padding-bottom: calc(var(--nav-reserve) + var(--space-6));
}

.site-footer {
  padding-bottom: calc(var(--nav-reserve) + var(--space-6));
}

.brand-mark,
.site-nav a,
.button,
.small-button,
.segment,
.date-tile,
.slot-tile {
  -webkit-tap-highlight-color: transparent;
}

.brand-mark {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  gap: var(--space-4);
  color: var(--ink);
  text-decoration: none;
  font-family: var(--font-display);
  font-size: var(--text-18);
  font-weight: 500;
  letter-spacing: 0.02em;
  font-variation-settings: "SOFT" 0, "WONK" 0, "opsz" 20;
}

/* A filled circle with a letter in it is the default placeholder logo. A hairline
   ring reads as a signet - closer to a members' club than an avatar. */
.brand-symbol {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  color: var(--cream);
  background: transparent;
  border: 1px solid var(--cognac);
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: var(--text-16);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-indent: 0.06em;
}

.brand-mark:hover .brand-symbol {
  border-color: var(--cream);
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  overflow-x: auto;
}

.site-nav a {
  display: inline-flex;
  align-items: center;
  min-width: 54px;
  min-height: 44px;
  padding: 0 var(--space-4);
  color: var(--ink-muted);
  text-decoration: none;
  border-radius: var(--radius);
}

.site-nav a:hover {
  color: var(--ink);
  background: var(--surface-soft);
}

.band {
  position: relative;
  padding: var(--band-y) var(--gutter);
}

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  min-height: 88svh;
  padding-top: clamp(64px, 10vw, 120px);
  padding-bottom: var(--band-y);
  overflow: hidden;
}

/* On the spine like every other section. The hero used to start at the raw gutter
   (48px) while every section below it started at 130px, so the page had no single
   left edge. The text measure is held by max-width on h1 / .hero-lede instead. */
.hero-copy {
  width: min(var(--content-max), 100%);
  margin-inline: auto;
  padding: 0;
  background: none;
}

.eyebrow {
  margin: 0 0 18px;
  color: var(--cognac);
  font-family: var(--font-utility);
  font-size: var(--text-12);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

/* A hairline before the eyebrow: cheap to draw, and it reads as typeset rather
   than as text that happens to be small and orange. */
.eyebrow::before {
  content: "";
  display: inline-block;
  width: 28px;
  height: 1px;
  margin-right: 12px;
  vertical-align: 0.3em;
  background: var(--cognac);
}

h1,
h2,
h3,
p {
  overflow-wrap: anywhere;
}

h1,
h2,
h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.015em;
  /* Fraunces axes: slightly soft terminals, no wonk. Warm without being novelty. */
  font-variation-settings: "SOFT" 24, "WONK" 0, "opsz" 96;
}

h1 {
  max-width: 15ch;
  font-size: var(--text-64);
  letter-spacing: -0.025em;
}

h2 {
  max-width: 20ch;
  font-size: var(--text-44);
}

h3 {
  font-size: var(--text-22);
  line-height: 1.2;
  letter-spacing: -0.01em;
  font-variation-settings: "SOFT" 24, "WONK" 0, "opsz" 24;
}

.hero-lede {
  max-width: 46ch;
  margin: 24px 0 0;
  color: var(--ink-muted);
  font-size: var(--text-18);
  line-height: 1.65;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: 28px;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  min-width: 48px;
  padding: 0 var(--space-5);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-14);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease, transform 160ms ease;
}

.button:disabled,
.small-button:disabled,
.date-tile:disabled,
.slot-tile:disabled {
  cursor: not-allowed;
  opacity: 0.58;
}

/* Cognac, sampled off the leather - the accent belongs to the vessel. The old
   #ff7a3d appeared nowhere on the boat and fought the interior it sat in front of. */
.button-primary {
  color: #17100b;
  background: var(--cognac);
  border-color: var(--cognac);
}

.button-primary:hover:not(:disabled) {
  background: var(--cream);
  border-color: var(--cream);
}

.button-secondary {
  color: var(--ink);
  background: transparent;
  border-color: var(--line-strong);
}

.button-secondary:hover:not(:disabled) {
  color: var(--cream);
  border-color: var(--cognac);
  background: var(--surface-soft);
}

.button:hover:not(:disabled) {
  transform: translateY(-1px);
}

.stretch {
  width: 100%;
}

/* Was: a 720px studio render absolutely positioned at right:-80px, z-index:-1.
   On a 390px viewport it measured 390x243.75 at x=160 - starting 41% across, running
   160px off the right edge, and sitting UNDER hero-actions (368-477) and hero-status
   (529-681). The hard edge of its grey studio floor cut vertically through the CTA.

   Now a full-bleed cabin plate: it stands in for the WebGL interior, so the hero
   previews the finished effect instead of pasting a rectangle over the controls. */
.hero-boat {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 70%;
  /* Heavily subdued on purpose. The dam water reads green through the windows, which
     fights the leather; desaturating lets the cognac carry the frame. This is a
     stand-in for the WebGL cabin, so it has to behave like ambience, not like a photo
     competing with the headline. */
  opacity: 0.4;
  filter: saturate(0.42) contrast(1.06) brightness(0.92);
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 24%, #000 60%, transparent 96%);
  mask-image: linear-gradient(180deg, transparent, #000 24%, #000 60%, transparent 96%);
  transition: opacity 500ms ease;
}

body.particle-background-ready[data-page="home"] .hero-boat {
  opacity: 0;
}

@media (min-width: 761px) {
  body.particle-background-ready[data-page="home"] .hero::after {
    background:
      linear-gradient(200deg, rgba(138, 79, 52, 0.12), transparent 55%),
      linear-gradient(90deg, rgba(10, 8, 6, 0.94) 8%, rgba(10, 8, 6, 0.68) 43%, rgba(10, 8, 6, 0.10) 70%, rgba(10, 8, 6, 0.24)),
      linear-gradient(180deg, rgba(10, 8, 6, 0.28), transparent 44%, rgba(10, 8, 6, 0.70));
  }
}

/* Keeps hero text off the brightest part of the plate at every width. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    /* warm cast pulls the remaining green back toward the cabin's own light */
    linear-gradient(200deg, rgba(138, 79, 52, 0.20), transparent 55%),
    linear-gradient(90deg, rgba(10, 8, 6, 0.95) 10%, rgba(10, 8, 6, 0.70) 48%, rgba(10, 8, 6, 0.42)),
    linear-gradient(180deg, rgba(10, 8, 6, 0.62), transparent 32%, rgba(10, 8, 6, 0.92));
}

/* Bordered pills read as chips in a dashboard. Hairline-divided figures read as
   a specification plate, which is what these actually are. */
/* Grid, not flex-wrap. Wrapping dropped the third figure onto its own line at 390px
   and left a dangling divider on the second - three fixed columns cannot orphan. */
/* The hero emblem.

   This element is EMPTY and paints nothing. The glass layer (glass-buttons.js) draws the
   Nova badge into the ocean canvas underneath, reading this box only for where it goes and
   how big it is - exactly as it does for a card. Painting anything here would put a surface
   between the glass and the water, and overOpenWater() would then correctly refuse to draw
   it at all.

   It sits on the right, where the hero's scrim has faded to 0.02 and the copy has ended.
   Below 1360px there is no such room: measured with tools/ui/emblemoverlap.mjs, it overlapped
   the headline's words by 54px at 1181 and cleared them by only 10px at 1280. A decorative
   layer parked over the headline is a defect this project has already shipped once, so below
   that width it is removed rather than shrunk. */
.hero-emblem {
  position: absolute;
  top: clamp(48px, 10vw, 144px);
  right: clamp(16px, 6vw, 96px);
  width: clamp(260px, 26vw, 400px);
  aspect-ratio: 1;
  pointer-events: none;
  background: none;
}
@media (max-width: 1359px) {
  .hero-emblem { display: none; }
}

.hero-status {
  align-self: end;
  display: grid;
  /* Capped columns, not thirds of the spine: at 1180px wide a third is ~393px and
     the three figures drift apart. The rule above them still spans the full spine. */
  grid-template-columns: repeat(3, minmax(0, 210px));
  justify-content: start;
  gap: 0;
  width: min(var(--content-max), 100%);
  max-width: min(var(--content-max), 100%);
  margin-inline: auto;
  margin-top: clamp(40px, 6vw, 72px);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}

.hero-status span {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-1) clamp(12px, 3vw, 24px);
  border-right: 1px solid var(--line);
  color: var(--ink-faint);
  font-size: var(--text-12);
  letter-spacing: 0.1em;
  line-height: 1.35;
  text-transform: uppercase;
}

.hero-status span:first-child {
  padding-left: 0;
}

.hero-status span:last-child {
  border-right: 0;
  padding-right: 0;
}

.hero-status strong {
  margin-right: 0;
  color: var(--cream);
  font-family: var(--font-display);
  font-size: var(--text-30);
  font-weight: 400;
  letter-spacing: -0.01em;
  text-transform: none;
  font-variation-settings: "SOFT" 24, "WONK" 0, "opsz" 36;
}

.experience-band,
.booking-band,
.admin-band {
  background: linear-gradient(180deg, rgba(10, 8, 6, 0.72), rgba(10, 8, 6, 0.90));
}

.section-heading {
  width: min(var(--content-max), 100%);
  margin: 0 auto 28px;
}

.section-heading p:not(.eyebrow) {
  max-width: 690px;
  margin: 14px 0 0;
  color: var(--ink-muted);
}

.asset-grid {
  display: grid;
  grid-template-columns: 1.25fr 0.85fr;
  gap: var(--space-4);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
}

.asset-card {
  position: relative;
  min-height: 280px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-strong);
}

.asset-card.wide {
  grid-row: span 2;
  min-height: 580px;
}

.asset-card img {
  width: 100%;
  height: 100%;
  min-height: inherit;
  object-fit: cover;
  opacity: 0.88;
}

.asset-card div {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--space-4);
  background: linear-gradient(180deg, transparent, rgba(10, 8, 6, 0.94) 34%);
}

.asset-card h3 {
  max-width: 520px;
  color: var(--cream);
}

/* The exterior render ships on a near-white studio backdrop. Dropped in raw it read
   as an asset export parked between two warm photographs. Pulled down and warmed so
   it sits in the same room as the rest of the grid. */
.asset-card.is-render {
  background: var(--ground);
}

.asset-card.is-render img {
  opacity: 1;
  filter: brightness(0.42) contrast(1.16) saturate(0.78) sepia(0.22);
  transform: scale(1.06);
}

/* Was four columns of 260-280px inside 1320px. The calendar ran ~1500px tall while
   the slot panel ran ~550px, so three quarters of the band was dead space. Two
   columns, two rows: date + slot, then details + checkout. */
.booking-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: var(--space-5);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
  align-items: start;
}

/* Every panel is a grid with a scale gap. This is the structural fix for the whole
   class of "the button is flush against the field" bug: children cannot end up
   touching by omission, because the container owns the spacing, not each child's
   ad-hoc margin. Two forms had a measured 0px gap above their submit button. */
.tool-panel {
  display: grid;
  align-content: start;
  gap: var(--space-5);
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  backdrop-filter: blur(20px);
  /* A panel never lets its content out. min-width:0 stops a wide grid child from
     forcing the panel wider than its column; overflow-wrap catches long unbroken
     strings, which is what pushed a <strong> 9px past the date panel's edge. */
  min-width: 0;
  overflow-wrap: anywhere;
}

.tool-panel > * {
  min-width: 0;
}

.panel-heading {
  display: grid;
  gap: var(--space-2);
  margin-bottom: 0;
}

.panel-heading p {
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--text-14);
}

/* Seven across, because that is what a calendar is. Three across made 21 days into
   a 7-row list ~1500px tall that read as a table of dates, not a month. */
.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--space-2);
}

.date-tile,
.slot-tile,
.segment,
.small-button {
  color: var(--ink);
  background: var(--surface-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color 140ms ease, border-color 140ms ease, color 140ms ease;
}

.date-tile:hover:not(:disabled),
.slot-tile:hover:not(:disabled),
.segment:hover:not(.is-active) {
  border-color: var(--line-strong);
  background: rgba(7, 84, 199, 0.10);
}

.date-tile {
  display: grid;
  gap: 0;
  align-content: start;
  justify-items: center;
  min-height: 78px;
  padding: var(--space-3) var(--space-1);
  text-align: center;
}

.date-tile span,
.date-tile small,
.slot-tile small,
.payload-list span,
.admin-list small {
  color: var(--ink-faint);
  font-size: var(--text-12);
}

/* At 7 across a tile is ~90px. The status word has to shrink or "Non-operational"
   wraps to three lines and doubles the row height. */
.date-tile small {
  font-size: 0.6875rem;
  line-height: 1.25;
  letter-spacing: 0.02em;
}

.date-tile span {
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.625rem;
}

.date-tile strong {
  font-family: var(--font-display);
  font-size: var(--text-22);
  font-weight: 400;
  line-height: 1.15;
  font-variation-settings: "SOFT" 24, "WONK" 0, "opsz" 24;
}

.date-tile.is-selected,
.slot-tile.is-selected,
.segment.is-active {
  color: var(--cream);
  background: rgba(7, 84, 199, 0.12);
  border-color: var(--cognac);
}

.date-tile.is-selected strong,
.slot-tile.is-selected .slot-range {
  color: var(--cognac);
}

.slot-grid {
  display: grid;
  gap: var(--space-3);
}

.slot-tile {
  display: grid;
  gap: var(--space-2);
  min-height: 118px;
  padding: 12px;
  text-align: left;
}

.slot-range {
  color: var(--cream);
  font-family: var(--font-display);
  font-size: var(--text-18);
  font-weight: 400;
  letter-spacing: 0.01em;
  font-variation-settings: "SOFT" 24, "WONK" 0, "opsz" 20;
}

.meter {
  display: block;
  width: 100%;
  height: 8px;
  overflow: hidden;
  background: rgba(7, 84, 199, 0.12);
  border-radius: 999px;
}

.meter span {
  display: block;
  height: 100%;
  background: var(--cognac);
}

.slot-note {
  display: grid;
  gap: var(--space-2);
  min-height: 118px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.slot-note span {
  color: var(--ink-muted);
}

.slot-note.is-open {
  border-color: rgba(112, 197, 155, 0.62);
}

.slot-note.is-blocked {
  border-color: rgba(255, 128, 111, 0.62);
}

label {
  display: grid;
  gap: var(--space-2);
  color: var(--ink-muted);
  font-size: var(--text-14);
}

input,
select {
  width: 100%;
  min-height: 46px;
  padding: 0 var(--space-3);
  color: var(--ink);
  background: rgba(7, 84, 199, 0.07);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
}

/* Icons inside fields get a real inset from the border. The browser's own select
   chevron ignores padding-right and parks itself a few px off the edge, which reads
   as the control being unfinished. Draw our own so the inset is a rule we control:
   --field-icon-inset, the same on every field on the site. */
select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--space-6);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%230754c7' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--field-icon-inset) center;
}

input[type="time"],
input[type="date"] {
  min-width: 0;
  /* Same inset, applied to the native picker glyph via the field's own padding. */
  padding-right: var(--field-icon-inset);
}

input[type="time"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator {
  margin-left: var(--space-2);
  margin-right: 0;
  cursor: pointer;
  opacity: 0.55;
  filter: invert(85%) sepia(18%) saturate(420%) hue-rotate(340deg);
  transition: opacity 140ms ease;
}

input[type="time"]:hover::-webkit-calendar-picker-indicator,
input[type="date"]:hover::-webkit-calendar-picker-indicator,
input[type="time"]:focus::-webkit-calendar-picker-indicator,
input[type="date"]:focus::-webkit-calendar-picker-indicator {
  opacity: 1;
}

select option {
  color: #061018;
}

.field-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
}

.booking-form {
  display: grid;
  gap: var(--space-4);
}

.segmented-control {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  border: 0;
}

.segmented-control legend {
  grid-column: 1 / -1;
  color: var(--ink-muted);
  font-size: var(--text-14);
}

.segment {
  min-height: 48px;
  padding: 8px;
}

#bookingPermission.is-ok {
  color: var(--success);
}

.price-list {
  display: grid;
  gap: var(--space-3);
  margin: 0;
}

.price-list div,
.payload-list li,
.admin-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line);
}

.price-list dt,
.price-list dd {
  margin: 0;
}

.price-list dd,
.price-list strong {
  color: var(--ink);
}

.price-list .total {
  color: var(--cream);
  font-size: var(--text-18);
  font-weight: 700;
  border-bottom: 0;
}

.payfast-form {
  margin-bottom: 14px;
}

.payload-list,
.admin-list {
  display: grid;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.payload-list li {
  align-items: flex-start;
  font-size: var(--text-12);
}

.payload-list strong {
  max-width: 58%;
  color: var(--ink-muted);
  text-align: right;
  overflow-wrap: anywhere;
}

/* Was three ~430px columns, which left the weekly-structure panel too narrow for a
   time field, a length field and an end time on one line - the native time picker's
   clock icon collided with the select next to it. Two columns, and the panel that
   actually needs room gets the full width. */
.admin-layout {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
  align-items: start;
}

.admin-layout > .admin-form,
.admin-layout > .admin-toolbar,
.admin-layout > .admin-columns,
.admin-layout > .table-panel {
  grid-column: 1 / -1;
}

/* Explicit columns, not grid auto-flow.
   Auto-flow places by source order, so a very tall panel (Calendar controls, ~1500px)
   next to a short one (Promo codes, ~400px) leaves a 1100px hole, and the next panel
   starts a fresh row below the tall one rather than filling that hole. Declaring the
   columns lets the short panels stack against the tall one. */
.admin-columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-5);
  align-items: start;
}

.admin-col {
  display: grid;
  gap: var(--space-5);
  align-content: start;
  min-width: 0;
}

@media (max-width: 900px) {
  .admin-columns {
    grid-template-columns: minmax(0, 1fr);
  }
}

.admin-login {
  width: min(460px, 100%);
  margin: 0 auto;
  display: grid;
  gap: var(--space-4);
}

.admin-toolbar {
  --ink: #061a3a;
  --ink-muted: #405b78;
  --line: rgba(6, 51, 111, 0.17);
  --line-strong: rgba(6, 51, 111, 0.34);
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: rgba(230, 239, 247, 0.92);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.admin-toolbar .button-secondary {
  color: var(--ink);
  border-color: var(--line-strong);
}

.admin-toolbar .button-secondary:hover:not(:disabled) {
  color: #ffffff;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(255, 255, 255, 0.08));
  border-color: rgba(255, 255, 255, 0.5);
}

.admin-toolbar p {
  margin: 0;
  color: var(--ink-muted);
}

.admin-toolbar div,
.admin-actions,
.inline-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.admin-form {
  display: grid;
  gap: var(--space-4);
}

.day-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  border: 0;
}

.day-grid legend {
  grid-column: 1 / -1;
  color: var(--ink-muted);
  font-size: var(--text-14);
}

/* Was: checkbox + label side by side in a ~50px grid cell. The checkbox (20px) plus
   its margin and the tile padding took ~44px, so the label could not shrink and spilled
   out over the next tile. Now the whole tile is the control: the input is visually
   hidden but still focusable, and the tile itself carries the state. */
.day-grid label {
  display: grid;
  place-items: center;
  min-height: 52px;
  padding: 8px 4px;
  background: var(--surface-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink-muted);
  font-size: var(--text-14);
  cursor: pointer;
  user-select: none;
  transition: background-color 140ms ease, border-color 140ms ease, color 140ms ease;
}

.day-grid label span {
  letter-spacing: 0.06em;
}

.day-grid input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.day-grid label:hover {
  border-color: var(--line-strong);
  color: var(--ink);
}

.day-grid label:has(input:checked) {
  color: var(--cream);
  background: rgba(7, 84, 199, 0.12);
  border-color: var(--cognac);
}

.day-grid label:has(input:focus-visible) {
  outline: 3px solid var(--cognac);
  outline-offset: 3px;
}

/* An open day is stated in words as well as colour - state never rests on hue alone. */
.day-grid label::after {
  content: "Closed";
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.day-grid label:has(input:checked)::after {
  content: "Open";
  color: var(--cognac);
}

/* --- Sittings editor ---------------------------------------------------- */

.date-slot-state {
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--text-14);
}

.date-slot-state strong {
  color: var(--cream);
  font-weight: 600;
}

.date-slot-state em {
  color: var(--cognac);
  font-style: normal;
  font-weight: 600;
}

.sitting-editor {
  margin: 0;
  padding: var(--space-5);
  background: rgba(7, 84, 199, 0.05);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}

.sitting-count {
  display: flex;
  align-items: end;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--line);
}

.sitting-count label {
  display: grid;
  gap: var(--space-2);
  width: 132px;
}

.sitting-count p {
  flex: 1 1 220px;
  margin: 0 0 10px;
  color: var(--ink-faint);
  font-size: var(--text-14);
}

.sitting-list {
  display: grid;
  gap: var(--space-3);
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
  counter-reset: sitting;
}

/* Fixed field widths, not fractions. On the full-width panel a 1fr time input grew
   to ~670px, which reads as an empty runway rather than a field. */
.sitting-row {
  display: grid;
  grid-template-columns: 34px minmax(120px, 190px) minmax(130px, 230px) minmax(0, 1fr);
  align-items: end;
  gap: var(--space-4);
  padding: 12px 16px;
  background: var(--surface-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.sitting-index {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  margin-bottom: 4px;
  color: var(--cognac);
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: var(--text-14);
}

.sitting-field {
  display: grid;
  gap: var(--space-2);
  min-width: 0;
}

.sitting-field > span {
  color: var(--ink-faint);
  font-size: var(--text-12);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.sitting-ends {
  display: grid;
  gap: var(--space-1);
  justify-items: end;
  padding-bottom: 12px;
  text-align: right;
}

.sitting-ends small {
  color: var(--ink-faint);
  font-size: var(--text-12);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.sitting-ends strong {
  color: var(--cream);
  font-family: var(--font-display);
  font-size: var(--text-18);
  font-weight: 400;
}

/* Overlap is a warning, never a block - staff may genuinely want back-to-back
   sittings that share a changeover. Say so and let them decide. */
.sitting-row.has-clash {
  border-color: var(--cognac-deep);
  background: rgba(7, 84, 199, 0.10);
}

.sitting-warning {
  margin: 14px 0 0;
  padding: 12px 14px;
  color: var(--ink);
  background: rgba(7, 84, 199, 0.12);
  border: 1px solid var(--cognac-deep);
  border-radius: var(--radius);
  font-size: var(--text-14);
}

/* Two columns with four children made the third child (the length select) flow back
   into the 34px index column - it measured 36px wide on a phone. Pin every field to
   column 2 and let the number badge span the rows beside them. */
@media (max-width: 620px) {
  .sitting-row {
    grid-template-columns: 34px minmax(0, 1fr);
    row-gap: 12px;
    align-items: start;
  }

  .sitting-index {
    grid-column: 1;
    grid-row: 1 / span 3;
    margin-bottom: 0;
  }

  .sitting-field,
  .sitting-ends {
    grid-column: 2;
    max-width: none;
  }

  .sitting-ends {
    justify-items: start;
    padding-bottom: 0;
    text-align: left;
  }
}

.check-row {
  display: flex;
  align-items: center;
  min-height: 46px;
  padding: 8px 12px;
  background: var(--surface-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.check-row input {
  width: 20px;
  min-height: 20px;
  margin: 0 10px 0 0;
  flex: none;
  accent-color: var(--cognac);
}

.date-admin,
.table-panel {
  display: grid;
  gap: var(--space-4);
}

.inline-form label {
  flex: 1 1 220px;
}

.small-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44, not 40. These are the destructive ones - Remove, Cancel - and they were the
     only controls in the panel under the touch floor. */
  min-height: 44px;
  min-width: 72px;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-12);
  letter-spacing: 0.04em;
}

.table-panel {
  grid-column: span 2;
}

.table-scroll {
  overflow-x: auto;
}

table {
  width: 100%;
  min-width: 720px;
  border-collapse: collapse;
  font-size: var(--text-14);
}

th,
td {
  padding: var(--space-3);
  border-bottom: 1px solid var(--line);
  text-align: left;
}

th {
  color: var(--cream);
  font-size: var(--text-12);
  text-transform: uppercase;
}

.event-list li {
  display: grid;
  align-items: start;
}

.toast {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 40;
  max-width: min(420px, calc(100vw - 36px));
  padding: var(--space-3) var(--space-4);
  color: var(--ink);
  background: var(--surface-strong);
  border: 1px solid var(--cognac);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity 180ms ease, transform 180ms ease;
}

.toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (max-width: 1180px) {
  .booking-layout,
  .admin-layout {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .checkout-panel,
  .table-panel {
    grid-column: span 2;
  }
}

@media (max-width: 760px) {
  .site-header {
    align-items: flex-start;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
  }

  .site-nav {
    width: 100%;
  }

  .site-nav a {
    flex: 1 0 auto;
    justify-content: center;
  }

  .band {
    padding: var(--space-8) var(--space-4);
  }

  .hero {
    min-height: 68svh;
    padding-top: 34px;
  }

  .hero-copy {
    padding: var(--space-5) 0;
    background: transparent;
  }

  h1 {
    font-size: var(--text-44);
  }

  h2 {
    font-size: var(--text-30);
  }

  h3 {
    font-size: var(--text-18);
  }

  .hero-lede {
    font-size: var(--text-16);
  }

  .hero-boat {
    right: -160px;
    bottom: 92px;
    width: 720px;
    opacity: 0.34;
  }

  .asset-grid,
  .booking-layout,
  .admin-layout,
  .field-grid {
    grid-template-columns: 1fr;
  }

  .asset-card.wide,
  .checkout-panel,
  .table-panel {
    grid-column: auto;
    grid-row: auto;
  }

  .asset-card,
  .asset-card.wide {
    min-height: 270px;
  }

  .calendar-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .segmented-control {
    grid-template-columns: 1fr;
  }

  .day-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .admin-toolbar,
  .price-list div,
  .payload-list li,
  .admin-list li {
    align-items: stretch;
    flex-direction: column;
  }

  .payload-list strong {
    max-width: 100%;
    text-align: left;
  }

  .button {
    width: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* Instant jumps instead of smooth scrolling is still honoured: it is a
     navigation behaviour, not one of the button effects. */
  html {
    scroll-behavior: auto;
  }

  /* .button:hover and the glass/carve effects are deliberately NOT disabled here.
     See carve.js for the instruction and the trade-off. */
  .toast {
    transition: none;
  }
}

/* A <legend> carries a 2px UA padding that lands off the spacing scale. */
legend {
  padding: 0;
}

/* ===========================================================================
   Multi-page chrome
   ========================================================================= */

.site-header [aria-current="page"] {
  color: var(--cream);
  background: var(--surface-soft);
}

.header-cta {
  min-height: 44px;
  padding: 0 var(--space-5);
}

.site-footer {
  position: relative;
  padding: var(--band-y) var(--gutter) var(--space-6);
  border-top: 1px solid var(--line);
  background: linear-gradient(180deg, transparent, rgba(10, 8, 6, 0.9));
}

.footer-inner {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
}

.footer-brand p {
  max-width: 34ch;
  margin: var(--space-4) 0 0;
  color: var(--ink-muted);
  font-size: var(--text-14);
}

.footer-nav {
  display: grid;
  gap: var(--space-3);
  align-content: start;
}

.footer-nav h2 {
  margin: 0 0 var(--space-1);
  color: var(--cognac);
  font-family: var(--font-body);
  font-size: var(--text-12);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.footer-nav a,
.footer-nav span {
  color: var(--ink-muted);
  font-size: var(--text-14);
  text-decoration: none;
}

.footer-nav a:hover {
  color: var(--cream);
}

.footer-base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-4);
  width: min(var(--content-max), 100%);
  margin: var(--space-8) auto 0;
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
  color: var(--ink-faint);
  font-size: var(--text-12);
}

.footer-base p { margin: 0; }
.footer-base a { color: var(--ink-faint); }
.footer-base a:hover { color: var(--cognac); }

.page-head {
  padding-bottom: var(--space-6);
}

.page-head h1 {
  max-width: 20ch;
  font-size: var(--text-44);
}

.band-more {
  width: min(var(--content-max), 100%);
  margin: var(--space-8) auto 0;
}

.cta-band {
  text-align: left;
}

.spec-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
  align-items: stretch;
}

.spec-grid p {
  margin: var(--space-3) 0 0;
  color: var(--ink-muted);
  font-size: var(--text-14);
}

/* ===========================================================================
   Booking console - one screen, no scrolling mid-booking
   ========================================================================= */

.book-main {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: var(--space-4);
  /* HEIGHT, not min-height. min-height sets a floor and the grid row then grows to
     fit its content - the panels measured 980px on a 900px viewport and the page
     scrolled 763px. A fixed height forces the panels to the screen and makes them
     scroll inside themselves instead. */
  /* Was `100svh - 72px` for a top header that no longer exists. The nav is now a
     bottom dock, so the console gives back exactly the band height plus the iPhone
     home-indicator inset - otherwise the bar lands on the Reserve button, which is
     the one control this whole layout exists to keep on screen. */
  height: calc(100svh - var(--nav-reserve));
  overflow: hidden;
  padding: var(--space-5) var(--gutter) var(--space-4);
}

.book-head {
  width: min(var(--content-max), 100%);
  margin: 0 auto;
}

/* Smaller than a normal page title: on a 800px laptop the full display size ate
   217px of the 728px available and pushed the pay button off screen. */
.book-head h1 {
  max-width: 24ch;
  margin: 0;
  font-size: var(--text-30);
}

.book-head .eyebrow {
  margin-bottom: var(--space-2);
}

.book-main .booking-layout {
  height: 100%;
  min-height: 0;
}

.book-main .booking-layout {
  /* The date column has a FLOOR, not a fraction. Seven tiles plus six 8px gaps plus
     the panel's 64px of padding needs 420px; below that the tiles measured 39.73px
     wide, under the 44px touch floor. */
  grid-template-columns: minmax(420px, 1.05fr) minmax(0, 0.8fr) minmax(0, 1.1fr);
  /* stretch, not start: this is what made the two panels in a row end at different
     heights - row 1 bottoms measured 153px apart. */
  align-items: stretch;
  min-height: 0;
}

/* Each panel scrolls inside itself. A date with eight sittings must not be able to
   push the pay button below the fold - that is the whole point of the console. */
.book-main .tool-panel {
  min-height: 0;
  overflow: hidden;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: var(--space-4);
}

.book-main .calendar-grid,
.book-main .slot-grid {
  min-height: 0;
  overflow-y: auto;
  align-content: start;
  padding-right: var(--space-1);
}

/* 8px gaps left the tiles 43.14px - 0.86px under the touch floor once the panel
   padding and the scroll gutter were taken out. A tighter gap buys the width back
   and reads better on a date picker anyway. */
.book-main .calendar-grid {
  gap: var(--space-1);
}

.book-main .detail-panel {
  grid-template-rows: auto minmax(0, 1fr) auto;
}

/* The FIELDS scroll; the submit button does not. Putting the button inside the
   scrolling area hid it at 1366x768 - the one viewport where the panel is shortest. */
.book-main .booking-form {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  gap: var(--space-4);
  min-height: 0;
}

.book-main .booking-fields {
  display: grid;
  gap: var(--space-4);
  align-content: start;
  min-height: 0;
  overflow-y: auto;
  padding-right: var(--space-1);
}

.field-note {
  display: block;
  margin-top: var(--space-1);
  color: var(--ink-faint);
  font-size: var(--text-12);
  line-height: 1.4;
}

/* Pinned to the base of the panel. Never scrolls. */
.booking-summary {
  display: grid;
  gap: var(--space-3);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

.payload-details summary {
  cursor: pointer;
  color: var(--ink-faint);
  font-size: var(--text-12);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.payload-details[hidden] { display: none; }

.payload-details .payload-list {
  max-height: 140px;
  overflow-y: auto;
  margin-top: var(--space-3);
}

/* On a phone the console cannot be one screen, and pretending otherwise breaks it.
   Release every height constraint and let the page scroll normally. */
@media (max-width: 900px) {
  .book-main {
    height: auto;
    min-height: 0;
    grid-template-rows: auto auto;
    overflow: visible;
    padding-inline: var(--space-2);
  }

  .book-main .booking-layout {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto;
    align-items: start;
    height: auto;
  }

  .book-main .tool-panel,
  .book-main .booking-form,
  .book-main .calendar-grid,
  .book-main .slot-grid {
    min-height: 0;
    max-height: none;
    overflow: visible;
  }

  .book-main .tool-panel,
  .book-main .booking-form {
    grid-template-rows: auto;
  }

  /* The month stays a calendar on phones. The earlier two-column fallback made
     thirty dates sixteen rows tall and pushed them through the next two panels. */
  .book-main .calendar-grid {
    grid-template-columns: repeat(7, minmax(0, 1fr));
    padding-right: 0;
  }

  /* Spend the calendar panel's horizontal padding on 44px date targets. */
  .book-main .calendar-panel {
    padding-inline: var(--space-1);
  }
}

/* ===========================================================================
   Account page
   ========================================================================= */

.account-layout {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: var(--space-5);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
  align-items: start;
}

.account-empty {
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--text-14);
}

.booking-list {
  display: grid;
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.booking-card {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-5);
  background: var(--surface-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
}

.booking-card.is-cancelled {
  opacity: 0.6;
}

.booking-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.booking-card-head .eyebrow {
  margin: 0;
}

.booking-status {
  padding: var(--space-1) var(--space-3);
  color: var(--cream);
  background: rgba(192, 138, 98, 0.18);
  border: 1px solid var(--cognac);
  border-radius: var(--radius);
  font-size: var(--text-12);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.booking-card.is-cancelled .booking-status {
  color: var(--danger);
  background: transparent;
  border-color: var(--danger);
}

.booking-card h3 {
  margin: 0;
  font-size: var(--text-22);
}

.booking-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: var(--space-3);
  margin: 0;
}

/* 8px, not 4: LAYOUT_RULES' minimum between stacked siblings (measure.mjs --layout flagged it). */
.booking-facts div { display: grid; gap: var(--space-2); }
.booking-facts dt {
  color: var(--ink-faint);
  font-size: var(--text-12);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.booking-facts dd {
  margin: 0;
  color: var(--cream);
  font-family: var(--font-display);
  font-size: var(--text-18);
}

@media (max-width: 900px) {
  .account-layout,
  .footer-inner {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Footer links measured 22.39px tall - the only controls on the new pages under the
   44px touch floor. The text stays 14px; the target grows around it. */
.footer-nav a,
.footer-base a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.footer-nav span {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
}

/* --- Booking console corrections ---------------------------------------------
   Found by looking at a capture after the numbers said the layout was fine. */

/* Panel headings are h2 for document structure, but a page-title size inside a
   panel wastes the vertical budget the console is fighting for - "Choose a
   sitting" wrapped onto two lines. */
.book-main .panel-heading h2 {
  font-size: var(--text-22);
  max-width: none;
}

/* overflow-wrap:anywhere on the panel was breaking "Available" into "Availa/ble"
   inside a 46px tile. Tiles get normal word wrapping; long unbroken strings are
   not a risk in a date grid. */
.date-tile,
.date-tile * {
  overflow-wrap: normal;
  word-break: keep-all;
  hyphens: none;
}

/* The pay button only works once a booking exists. Showing it disabled the rest of
   the time cost ~90px of the panel that the form fields needed. */
.booking-summary .payfast-form:has(button[disabled]) {
  display: none;
}

.book-main .price-list div,
.book-main .price-list .total {
  padding: var(--space-2) 0;
}

/* Tighter form rhythm inside the console. Name and Email now share a row, and the
   note is one line under the pair rather than hanging off the email field. */
.book-main .field-note {
  margin: 0;
}

.book-main .booking-fields label {
  gap: var(--space-2);
}

.book-main .segmented-control legend {
  margin-bottom: var(--space-2);
}

/* Measured: the fields needed 334px and had 248 - short by 86. The price list was
   taking 209px for four rows (52px each) and the segmented control 100px. Tightening
   both inside the console buys back ~93px, which is where the fields go. */
.book-main .price-list {
  gap: var(--space-1);
}

.book-main .price-list div {
  padding: var(--space-1) 0;
}

.book-main .segment {
  padding: var(--space-2) var(--space-3);
  min-height: 44px;
}

.book-main .segmented-control {
  gap: var(--space-1);
}

/* Short laptops. Below ~860px of viewport height the console cannot show the page
   title AND the whole flow, so the title gives way first - it is the only thing on
   screen that isn't part of booking. */
@media (min-width: 901px) and (max-height: 880px) {
  .book-main {
    padding-top: var(--space-4);
    gap: var(--space-3);
  }

  .book-head .eyebrow { display: none; }
  .book-head h1 { font-size: var(--text-22); }
  .book-main .field-note { display: none; }
  .book-main .tool-panel { padding: var(--space-5); }
  .book-main .panel-heading p { display: none; }
}

/* Very short screens (1366x768 and similar). Trim the summary line that only repeats
   what the other two columns already show, and tighten the field rhythm. */
@media (min-width: 901px) and (max-height: 800px) {
  .book-main .booking-fields { gap: var(--space-3); }
  .book-main .price-list > div:first-child { display: none; }
  .book-main .booking-summary { padding-top: var(--space-3); }
  .book-main .book-head { display: none; }
}

/* ===========================================================================
   Month calendar

   Replaced a rolling 21-day window (which could never reach next month) with a
   real month grid you page through. The weekday header is rendered once above
   the grid, so each tile carries only its number and status.
   ========================================================================= */

.calendar-nav {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) 44px;
  align-items: center;
  gap: var(--space-2);
}

.calendar-nav strong {
  color: var(--cream);
  font-family: var(--font-display);
  font-size: var(--text-18);
  font-weight: 400;
  text-align: center;
  letter-spacing: 0.01em;
}

.calendar-step {
  display: grid;
  place-items: center;
  padding: 0;
  width: 44px;
  height: 44px;
  color: var(--ink);
  background: var(--surface-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: var(--text-18);
  line-height: 1;
  cursor: pointer;
  transition: border-color 140ms ease, color 140ms ease, background-color 140ms ease;
}

.calendar-step:hover:not(:disabled) {
  color: var(--cream);
  border-color: var(--cognac);
  background: rgba(7, 84, 199, 0.12);
}

.calendar-step:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.calendar-weekdays {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--space-1);
}

.calendar-weekdays span {
  padding-bottom: var(--space-1);
  color: var(--ink-faint);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-align: center;
  text-transform: uppercase;
}

/* Leading blanks so the 1st lands under its real weekday. */
.date-blank {
  display: block;
  min-height: 1px;
}

/* No weekday label on the tile any more - the header row carries it - so the tile
   is a number and a status word, and a six-row month still fits the panel. */
.date-tile {
  min-height: 56px;
  padding: var(--space-2) var(--space-1);
}

.date-tile.is-past {
  opacity: 0.28;
}

.book-main .calendar-panel {
  grid-template-rows: auto auto auto minmax(0, 1fr);
}

@media (min-width: 901px) and (max-height: 800px) {
  .date-tile { min-height: 48px; }
}

/* ===========================================================================
   Stepper — custom number picker

   The native number spinner is two grey arrows about 13px wide that belong to no
   design system and are far under a touch target. This replaces it entirely.
   ========================================================================= */

:root {
  --stepper-item-h: 44px;   /* must match ITEM_HEIGHT in stepper.js */
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.stepper {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) 44px;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1);
  background: rgba(7, 84, 199, 0.07);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  transition: border-color 160ms ease, background-color 160ms ease;
}

.stepper:focus-within {
  border-color: var(--cognac);
  background: rgba(7, 84, 199, 0.10);
}

/* A small settle when a step button is pressed - enough to feel mechanical,
   short enough never to be in the way. */
.stepper.is-bumped .stepper input {
  transform: scale(1.06);
}

.stepper input[type="number"] {
  appearance: none;
  -moz-appearance: textfield;
  min-height: 44px;
  padding: 0;
  color: var(--cream);
  background: transparent;
  border: 0;
  border-radius: 0;
  font-family: var(--font-display);
  font-size: var(--text-22);
  font-weight: 400;
  font-variation-settings: "SOFT" 24, "WONK" 0, "opsz" 24;
  text-align: center;
  cursor: pointer;
  transition: transform 180ms cubic-bezier(0.2, 0.9, 0.3, 1.2);
}

.stepper input[type="number"]::-webkit-outer-spin-button,
.stepper input[type="number"]::-webkit-inner-spin-button {
  appearance: none;
  margin: 0;
}

.stepper input:focus-visible {
  outline: none;
}

.stepper-btn {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  color: var(--ink-muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  transition: color 140ms ease, background-color 140ms ease, border-color 140ms ease;
}

.stepper-btn svg {
  width: 12px;
  height: 12px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
}

.stepper-btn:hover {
  color: var(--cream);
  background: rgba(7, 84, 199, 0.12);
  border-color: var(--cognac);
}

.stepper-btn:active {
  transform: translateY(1px);
}

/* --- the floating wheel --------------------------------------------------- */

.stepper-wheel {
  position: fixed;
  z-index: 60;
  overflow: hidden;
  background: rgba(4, 31, 79, 0.94);
  border: 1px solid var(--cognac);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  backdrop-filter: blur(20px) saturate(1.15);
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 180ms ease, transform 220ms cubic-bezier(0.2, 0.9, 0.3, 1.1);
}

.stepper-wheel.is-open {
  opacity: 1;
  transform: scale(1);
}

.stepper-wheel-list {
  height: 100%;
  overflow-y: auto;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
  touch-action: pan-y;
  cursor: grab;
  /* Fades the ends so numbers dissolve rather than being cut off by the border. */
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 26%, #000 74%, transparent);
  mask-image: linear-gradient(180deg, transparent, #000 26%, #000 74%, transparent);
}

.stepper-wheel-list::-webkit-scrollbar { display: none; }

.stepper-wheel.is-dragging .stepper-wheel-list {
  cursor: grabbing;
  scroll-snap-type: none;
}

.stepper-wheel-item {
  display: grid;
  place-items: center;
  width: 100%;
  height: var(--stepper-item-h);
  padding: 0;
  color: var(--ink);
  background: transparent;
  border: 0;
  scroll-snap-align: center;
  font-family: var(--font-display);
  font-size: var(--text-22);
  font-weight: 400;
  font-variation-settings: "SOFT" 24, "WONK" 0, "opsz" 24;
  cursor: inherit;
  transition: color 160ms ease;
  will-change: transform, opacity;
}

.stepper-wheel-item.is-centre {
  color: var(--cognac);
}

/* Two hairlines marking the selection line, the way a physical dial would. */
.stepper-wheel-rail {
  position: absolute;
  inset: 50% 0 auto;
  height: var(--stepper-item-h);
  transform: translateY(-50%);
  pointer-events: none;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

/* The stepper is a button too, so its motion is not gated either - same
   instruction as the carve and the glass specular. */

/* ===========================================================================
   LIQUID GLASS

   Technique researched via the refero-styles MCP, then rebuilt in this project's
   own palette. Two references, neither copied:

   Apple (styles.refero.design) — typographic discipline: negative tracking that
     tightens as size grows, weight-300 "whisper" subheads, one accent one job,
     hierarchy from surface shifts rather than decoration. Their palette is a light
     #f5f5f7 canvas with a blue accent and NO shadows; adopting that would have
     erased Nova Lounge, so only the method carried over.

   Authkit — "frosted glass cathedral at midnight". The load-bearing rule:
     "build elevation from inset frost highlights + soft outer halos rather than
     conventional drop-shadows" and "never solid borders — 1px inset strokes".
     Theirs is cool blue-white and their system forbids warm gradients. Ours is the
     exact inverse: warm cream frost, and cool tints are forbidden.

   Why glass suits THIS site specifically: the WebGL cabin will move behind these
   panels. Translucent layers over moving content is the problem glass solves.
   ========================================================================= */

:root {
  /* The frost is cream, not blue-white - it is lit by the cabin, not by moonlight. */
  --glass-tint: rgba(232, 210, 182, 0.045);
  --glass-tint-strong: rgba(232, 210, 182, 0.075);
  --glass-edge: rgba(232, 210, 182, 0.14);
  --glass-edge-lit: rgba(232, 210, 182, 0.30);
  --glass-halo: rgba(192, 138, 98, 0.10);
  --glass-blur: 22px;

  /* Apple's tracking method: the bigger the type, the tighter it sets. */
  --track-display: -0.028em;
  --track-heading: -0.018em;
  --track-body: -0.011em;
}

/* The material. Elevation is an inset top highlight plus a warm halo - never a
   drop shadow, which on a dark ground reads as a smudge rather than as depth. */
.glass {
  position: relative;
  isolation: isolate;
  background: linear-gradient(160deg, var(--glass-tint-strong), var(--glass-tint) 46%, transparent);
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.35);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.35);
  box-shadow:
    inset 0 1px 0 var(--glass-edge-lit),
    inset 0 0 42px var(--glass-halo),
    0 24px 60px rgba(0, 0, 0, 0.45);
  transition: box-shadow 240ms ease, transform 240ms cubic-bezier(0.2, 0.9, 0.3, 1);
}

/* Hairline edge drawn as a gradient stroke: bright where light would catch the
   top-left, fading away underneath. A solid 1px border cannot do this. */
.glass::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  padding: 1px;
  border-radius: inherit;
  background: linear-gradient(150deg, var(--glass-edge-lit), var(--glass-edge) 34%, transparent 70%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}

/* The wet look: a specular smear that tracks the pointer. --mx/--my are written
   by glass.js on a rAF; with no pointer it rests off-panel and is invisible. */
.glass::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: radial-gradient(
    380px circle at var(--mx, 50%) var(--my, -30%),
    rgba(232, 210, 182, 0.13),
    rgba(192, 138, 98, 0.05) 38%,
    transparent 62%
  );
  opacity: 0;
  transition: opacity 320ms ease;
  pointer-events: none;
}

.glass:hover::after,
.glass:focus-within::after {
  opacity: 1;
}

.glass-lift:hover {
  transform: translateY(-3px);
  box-shadow:
    inset 0 1px 0 var(--glass-edge-lit),
    inset 0 0 52px var(--glass-halo),
    0 32px 70px rgba(0, 0, 0, 0.5);
}

/* No reduced-motion block for the glass specular. It used to carry
   `.glass::after { display: none }`, which hid the highlight outright - the CSS
   half of why the effect looked dead. Client instruction: these run always. */

/* --- Home page: glass sections ------------------------------------------- */

/* Apple's tracking method applied to our own faces: the larger the type, the
   tighter it sets. That negative tracking is what makes big display type read as
   architecture rather than as enlarged body copy. */
h1 { letter-spacing: var(--track-display); }
h2, h3 { letter-spacing: var(--track-heading); }
.hero-lede,
.section-heading p:not(.eyebrow) { letter-spacing: var(--track-body); }

/* The "whisper voice" - a subhead at 300 says the same words more quietly. */
.hero-lede {
  font-weight: 300;
}

.hero-status {
  padding: var(--space-5) var(--space-6);
  border-top: 0;
}

.ways-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
  align-items: stretch;
}

/* Rows, not align-content:start. The cards are equal height, but with content
   stacked from the top a 3-line description pushed its price rule 23px below the
   2-line card's. The description row takes the slack so the price line is level
   across all three - a rule that wanders is more obvious than one that is low. */
.way-card {
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: var(--space-3);
  padding: var(--space-6);
}

.way-index {
  margin: 0;
  color: var(--cognac);
  font-family: var(--font-display);
  font-size: var(--text-18);
  letter-spacing: 0.08em;
}

.way-card h3 {
  margin: 0;
}

.way-card p {
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--text-14);
}

.way-price {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--glass-edge);
}

.way-price strong {
  color: var(--cream);
  font-family: var(--font-display);
  font-size: var(--text-22);
  font-weight: 400;
}

.cta-panel {
  display: grid;
  justify-items: start;
  gap: var(--space-4);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
  padding: clamp(32px, 5vw, 64px);
}

.cta-panel h2 {
  margin: 0;
  max-width: 18ch;
}

.cta-panel p {
  margin: 0;
  max-width: 46ch;
  color: var(--ink-muted);
}

.cta-panel .button {
  margin-top: var(--space-3);
}

/* The section that introduces the glass gets a warm floor-glow behind it, so the
   plates have something to be lit BY. Glass over flat black reads as grey plastic. */
.ways-band,
.cta-band {
  isolation: isolate;
}

.ways-band::before,
.cta-band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(80% 60% at 50% 40%, rgba(192, 138, 98, 0.10), transparent 68%);
}

/* --- Liquid carve --------------------------------------------------------- */

:root {
  --carve-control-blob: rgba(192, 138, 98, 0.34);
  --carve-secondary-blob: rgba(192, 138, 98, 0.45);
  --carve-container-blob: rgba(192, 138, 98, 0.16);
  --carve-container-surface: rgba(232, 210, 182, 0.04);
  --carve-ghost-surface: rgba(232, 210, 182, 0.06);
}

.carve-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 0;
}

.is-carved {
  position: relative;
  isolation: isolate;
}

/* Text and controls stay above the material. Media stays beneath it, allowing the
   same warm cursor pool to travel over photographs as well as plain panels. */
.is-carved > :not(.carve-svg):not(img):not(picture):not(video):not(canvas),
.carve-label {
  position: relative;
  z-index: 1;
}

[data-carve-kind="container"] > :is(img, picture, video, canvas) {
  position: relative;
  z-index: -1;
}

[data-carve-kind="button"] {
  --carve-blob: var(--carve-control-blob);
}

[data-carve-kind="container"] {
  --carve-blob: var(--carve-container-blob);
  --carve-surface: var(--carve-container-surface);
  cursor: default;
}

[data-carve-kind="container"].glass-lift:hover {
  transform: none;
}

.button-secondary {
  --carve-surface: var(--carve-ghost-surface);
  --carve-blob: var(--carve-secondary-blob);
}

.button-primary { --carve-blob: var(--cream); }

.stepper-btn {
  --carve-surface: var(--carve-container-surface);
}

/* Once enhanced, the liquid carve is the hover treatment. Keeping the old full
   surface swap underneath it turns the effect inside out and obscures its path. */
.button.is-carved:hover:not(:disabled) {
  transform: none;
}

.button-primary.is-carved:hover:not(:disabled) {
  color: #17100b;
  background: var(--cognac);
  border-color: var(--cognac);
}

.button-secondary.is-carved:hover:not(:disabled) {
  color: var(--ink);
  background: transparent;
  border-color: var(--line-strong);
}

:is(.date-tile, .slot-tile, .segment, .small-button, .calendar-step).is-carved:hover:not(:disabled) {
  color: var(--ink);
  background: var(--surface-soft);
  border-color: var(--line);
}

:is(.date-tile.is-selected, .slot-tile.is-selected, .segment.is-active).is-carved:hover:not(:disabled) {
  color: var(--cream);
  background: rgba(192, 138, 98, 0.16);
  border-color: var(--cognac);
}

.stepper-btn.is-carved:hover:not(:disabled) {
  color: var(--ink-muted);
  background: transparent;
  border-color: transparent;
}

/* ===========================================================================
   Home page scroll composition
   ======================================================================== */

.home-story {
  position: relative;
}

body[data-page="home"] .experience-band,
body[data-page="home"] .ways-band,
body[data-page="home"] .cta-band {
  background: transparent;
}

body[data-page="home"] .ways-band::before,
body[data-page="home"] .cta-band::before {
  display: none;
}

body[data-page="home"][data-copy-side="left"] .ambient-scrim {
  background:
    linear-gradient(90deg, rgba(10, 8, 6, 0.95) 8%, rgba(10, 8, 6, 0.74) 42%, rgba(10, 8, 6, 0.16) 72%, rgba(10, 8, 6, 0.46)),
    linear-gradient(180deg, rgba(10, 8, 6, 0.18), rgba(10, 8, 6, 0.34) 64%, rgba(10, 8, 6, 0.88));
}

body[data-page="home"][data-copy-side="right"] .ambient-scrim {
  background:
    linear-gradient(90deg, rgba(10, 8, 6, 0.42), rgba(10, 8, 6, 0.14) 30%, rgba(10, 8, 6, 0.72) 62%, rgba(10, 8, 6, 0.95) 92%),
    linear-gradient(180deg, rgba(10, 8, 6, 0.18), rgba(10, 8, 6, 0.36) 64%, rgba(10, 8, 6, 0.90));
}

body[data-page="home"][data-copy-side="full"] .ambient-scrim {
  background:
    linear-gradient(90deg, rgba(10, 8, 6, 0.62), rgba(10, 8, 6, 0.34) 50%, rgba(10, 8, 6, 0.62)),
    linear-gradient(180deg, rgba(10, 8, 6, 0.28), rgba(10, 8, 6, 0.52) 68%, rgba(10, 8, 6, 0.92));
}

.story-stage {
  display: grid;
  grid-template-columns: min(var(--content-max), 100%);
  justify-content: center;
  align-items: center;
  min-height: clamp(960px, 145svh, 1320px);
}

.story-spine {
  display: grid;
  width: min(var(--content-max), 100%);
  min-width: 0;
}

.story-copy {
  display: grid;
  gap: var(--space-5);
  width: min(var(--story-rail), 100%);
  min-width: 0;
}

.story-stage-copy-left .story-copy {
  justify-self: start;
}

.story-stage-copy-right .story-copy {
  justify-self: end;
}

.story-copy .eyebrow,
.story-copy h2,
.story-copy > p,
.gallery-heading .eyebrow,
.gallery-heading h2,
.gallery-heading > p {
  margin: 0;
}

.story-copy > p,
.gallery-heading > p {
  color: var(--ink-muted);
  font-weight: 300;
  line-height: 1.7;
}

.story-copy > .button {
  justify-self: start;
  margin-top: var(--space-2);
}

.story-facts {
  display: grid;
  gap: 0;
  margin: var(--space-3) 0 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.story-facts div {
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr);
  gap: var(--space-4);
  padding: var(--space-4) 0;
}

.story-facts div + div {
  border-top: 1px solid var(--line);
}

.story-facts dt,
.story-facts dd {
  margin: 0;
}

.story-facts dt {
  color: var(--cognac);
  font-size: var(--text-12);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.story-facts dd {
  color: var(--cream);
}

.ways-list {
  display: grid;
  gap: 0;
  margin-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.ways-list .way-card {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5) 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
}

.ways-list .way-card > div {
  display: grid;
  gap: var(--space-2);
  min-width: 0;
}

.ways-list .way-card h3,
.ways-list .way-card p {
  margin: 0;
}

.ways-list .way-card > div p {
  color: var(--ink-muted);
  font-size: var(--text-14);
}

.ways-list .way-index {
  align-self: start;
  padding-top: var(--space-1);
}

.ways-list .way-price {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  border: 0;
  text-align: right;
}

.ways-list .way-price strong,
.ways-list .way-price span {
  white-space: nowrap;
}

.ways-list .way-price span {
  color: var(--ink-faint);
  font-size: var(--text-12);
  text-transform: uppercase;
}

/* --- Magnetic carousel (magnetic-carousel.js sets each bar's width and height) --- */

.gallery-band {
  display: grid;
  align-content: center;
  gap: var(--space-7);
  min-height: clamp(760px, 100svh, 1080px);
}

.gallery-heading {
  display: grid;
  gap: var(--space-4);
  width: min(var(--content-max), 100%);
  margin-inline: auto;
}

.gallery-heading > p {
  max-width: 48ch;
}

.magnetic-carousel {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: min(var(--content-max), 100%);
  margin-inline: auto;
  touch-action: pan-y;
}

/* Catches a click in the space around an open photo, which closes it. Inert otherwise,
   so the pointer reaches the bars. */
.magnetic-carousel-backdrop {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.magnetic-carousel.is-open .magnetic-carousel-backdrop {
  pointer-events: auto;
}

.carousel-bar {
  position: relative;
  z-index: 2;
  flex: none;
  padding: 0;
  overflow: hidden;
  border: 0;
  border-radius: var(--radius);
  background: var(--ground-3);
  cursor: pointer;
  will-change: width, height;
}

.carousel-bar.is-open {
  z-index: 3;
}

.carousel-bar img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}

.carousel-bar:focus-visible {
  outline: 3px solid var(--sky-blue);
  outline-offset: 3px;
}

/* Only opening and closing animate in CSS; hover steering is driven per frame. */
.magnetic-carousel.is-animating .carousel-bar {
  transition:
    width 300ms ease-in-out,
    height 300ms ease-in-out,
    filter 300ms ease-in-out,
    opacity 300ms ease-in-out;
}

.magnetic-carousel.is-open .carousel-bar:not(.is-open) {
  filter: blur(2px);
  opacity: 0.6;
}

.reservation-copy {
  padding-block: var(--space-8);
}

@media (max-width: 760px) {
  body[data-page="home"][data-copy-side] .ambient-scrim {
    background:
      linear-gradient(90deg, rgba(10, 8, 6, 0.82), rgba(10, 8, 6, 0.52) 54%, rgba(10, 8, 6, 0.68)),
      linear-gradient(180deg, rgba(10, 8, 6, 0.28), rgba(10, 8, 6, 0.60) 72%, rgba(10, 8, 6, 0.94));
  }

  .story-stage {
    min-height: 108svh;
  }

  .story-stage-copy-right .story-copy,
  .story-stage-copy-left .story-copy {
    justify-self: start;
  }

  .story-facts div {
    grid-template-columns: 96px minmax(0, 1fr);
  }

  .ways-list .way-card {
    grid-template-columns: 32px minmax(0, 1fr);
  }

  .ways-list .way-price {
    grid-column: 2;
    justify-items: start;
    text-align: left;
  }

  .gallery-band {
    gap: var(--space-6);
    min-height: 0;
  }
}

body[data-page="home"] .eyebrow,
body[data-page="home"] .story-facts dt,
body[data-page="home"] .ways-list .way-index {
  color: #d5efff;
}

.page-head .eyebrow,
.book-head .eyebrow {
  color: #d5efff;
}

/* ===========================================================================
   Blue and silver identity

   Auros informed the restraint, scale, and open composition. Nova keeps its own
   subject: daylight on water, polished metal, and a luminous particle vessel.
   ======================================================================== */

:root {
  --glass-tint: rgba(227, 237, 245, 0.10);
  --glass-tint-strong: rgba(227, 237, 245, 0.18);
  --glass-edge: rgba(227, 237, 245, 0.24);
  --glass-edge-lit: rgba(255, 255, 255, 0.62);
  --glass-halo: rgba(100, 200, 255, 0.18);
  --carve-control-blob: rgba(19, 139, 255, 0.42);
  --carve-secondary-blob: rgba(100, 200, 255, 0.48);
  --carve-container-blob: rgba(227, 237, 245, 0.22);
  --carve-container-surface: rgba(227, 237, 245, 0.10);
  --carve-ghost-surface: rgba(227, 237, 245, 0.14);
  --track-display: 0;
  --track-heading: 0;
  --track-body: 0;
}

body,
#webglBackground {
  background:
    linear-gradient(25deg, rgba(255, 255, 255, 0.10), transparent 36%, rgba(185, 224, 248, 0.22) 72%, transparent),
    linear-gradient(135deg, #0643a4 0%, #0c6cdb 36%, #3caaf3 67%, #d7e6f0 100%);
}

.ambient-scrim {
  background:
    linear-gradient(180deg, rgba(3, 31, 84, 0.14), rgba(3, 29, 78, 0.46)),
    linear-gradient(90deg, rgba(4, 32, 84, 0.32), transparent 58%);
}

body.particle-background-ready[data-page="home"] .ambient-scrim,
body[data-page="home"][data-copy-side="left"] .ambient-scrim {
  background:
    linear-gradient(90deg, rgba(3, 24, 66, 0.88) 0%, rgba(5, 49, 116, 0.70) 36%, rgba(6, 71, 151, 0.12) 68%, rgba(6, 77, 158, 0.02)),
    linear-gradient(180deg, rgba(4, 42, 102, 0.08), rgba(3, 32, 82, 0.38));
}

body[data-page="home"][data-copy-side="right"] .ambient-scrim {
  background:
    linear-gradient(90deg, rgba(6, 77, 158, 0.02), rgba(6, 71, 151, 0.10) 32%, rgba(5, 49, 116, 0.70) 64%, rgba(3, 24, 66, 0.90) 100%),
    linear-gradient(180deg, rgba(4, 42, 102, 0.08), rgba(3, 32, 82, 0.40));
}

body[data-page="home"][data-copy-side="full"] .ambient-scrim {
  background: linear-gradient(180deg, rgba(3, 31, 84, 0.28), rgba(3, 29, 78, 0.56));
}

/* The band stays transparent. This rule was left over from the old top-header and
   was painting a full-width 1440x77 light bar behind the pill - the only light
   element on the page. The pill is the entire visible navigation. */
.site-header {
  color: inherit;
  background: none;
  border-bottom-color: transparent;
  backdrop-filter: none;
}

.site-header .brand-mark {
  color: var(--ink-navy);
}

.site-header .brand-symbol {
  color: var(--atlantic);
  border-color: rgba(7, 84, 199, 0.54);
}

.site-header .site-nav a {
  color: #365472;
}

.site-header .site-nav a:hover,
.site-header [aria-current="page"] {
  color: var(--ink-navy);
  background: rgba(7, 84, 199, 0.08);
}

.brand-mark,
h1,
h2,
h3,
.hero-status strong,
.date-tile strong,
.way-price strong,
.stepper-wheel-item {
  font-family: var(--font-display);
  font-variation-settings: normal;
}

.brand-mark {
  font-weight: 600;
}

h1,
h2,
h3 {
  font-weight: 500;
}

h1 {
  line-height: 0.98;
}

h2 {
  line-height: 1;
}

h3 {
  line-height: 1.16;
}

.hero-lede,
.story-copy > p,
.gallery-heading > p {
  font-weight: 400;
}

.eyebrow {
  color: var(--sky-blue);
  font-weight: 600;
}

.eyebrow::before {
  width: 32px;
  background: var(--silver);
}

.button-primary {
  color: #ffffff;
  background: var(--atlantic);
  border-color: rgba(255, 255, 255, 0.28);
}

.button-primary:hover:not(:disabled) {
  color: #ffffff;
  background: var(--ink-navy);
  border-color: var(--silver);
}

.button-secondary {
  color: var(--ink);
  background: rgba(227, 237, 245, 0.08);
  border-color: rgba(227, 237, 245, 0.52);
}

.button-secondary:hover:not(:disabled) {
  color: var(--ink-navy);
  background: var(--silver);
  border-color: var(--silver);
}

.button-primary.is-carved:hover:not(:disabled) {
  color: #ffffff;
  background: var(--atlantic);
  border-color: rgba(255, 255, 255, 0.28);
}

.hero::after {
  background:
    linear-gradient(90deg, rgba(3, 24, 66, 0.86) 5%, rgba(5, 48, 113, 0.60) 42%, rgba(8, 86, 170, 0.02) 72%),
    linear-gradient(180deg, rgba(5, 52, 118, 0.10), rgba(3, 27, 71, 0.30));
}

@media (min-width: 761px) {
  body.particle-background-ready[data-page="home"] .hero::after {
    background:
      linear-gradient(90deg, rgba(3, 24, 66, 0.88) 5%, rgba(5, 48, 113, 0.62) 42%, rgba(8, 86, 170, 0.02) 72%),
      linear-gradient(180deg, rgba(5, 52, 118, 0.06), rgba(3, 27, 71, 0.28));
  }
}

body[data-page="home"] .hero-status {
  padding: var(--space-5) 0 0;
  background: transparent;
  border-top: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  backdrop-filter: none;
}

body[data-page="home"] .hero-status::before,
body[data-page="home"] .hero-status::after {
  display: none;
}

.hero-status strong,
.story-facts dd,
.ways-list .way-price strong {
  color: var(--silver);
}

.experience-band,
.booking-band,
.admin-band,
.account-band,
.spec-band {
  background: transparent;
}

.tool-panel {
  --ink: #061a3a;
  --ink-muted: #405b78;
  --ink-faint: #526d88;
  --cognac: #0754c7;
  --cream: #0754c7;
  --surface: rgba(245, 249, 252, 0.94);
  --surface-strong: rgba(219, 232, 242, 0.78);
  --surface-soft: rgba(7, 84, 199, 0.08);
  --line: rgba(6, 51, 111, 0.17);
  --line-strong: rgba(6, 51, 111, 0.34);
  --carve-container-surface: rgba(7, 84, 199, 0.07);
  --carve-ghost-surface: rgba(7, 84, 199, 0.09);
  color: var(--ink);
  background: linear-gradient(145deg, rgba(249, 252, 254, 0.96), rgba(218, 232, 242, 0.90));
  border-color: rgba(255, 255, 255, 0.60);
  box-shadow: 0 24px 64px rgba(4, 31, 78, 0.20), inset 0 1px 0 #ffffff;
  backdrop-filter: blur(28px) saturate(1.08);
}

.tool-panel .button-primary {
  color: #ffffff;
}

.tool-panel .button-secondary {
  color: var(--ink);
  background: transparent;
  border-color: var(--line-strong);
}

.tool-panel .button-secondary:hover:not(:disabled) {
  color: #ffffff;
  background: var(--atlantic);
  border-color: var(--atlantic);
}

.date-tile.is-selected,
.slot-tile.is-selected,
.segment.is-active,
:is(.date-tile.is-selected, .slot-tile.is-selected, .segment.is-active).is-carved:hover:not(:disabled) {
  color: var(--atlantic);
  background: rgba(7, 84, 199, 0.12);
  border-color: var(--atlantic);
}

.date-tile.is-selected strong,
.slot-tile.is-selected .slot-range {
  color: var(--atlantic);
}

.booking-status {
  color: var(--atlantic);
  background: rgba(7, 84, 199, 0.10);
  border-color: var(--atlantic);
}

.glass {
  background: linear-gradient(160deg, var(--glass-tint-strong), var(--glass-tint) 46%, rgba(227, 237, 245, 0.04));
  box-shadow:
    inset 0 1px 0 var(--glass-edge-lit),
    inset 0 0 42px var(--glass-halo),
    0 24px 60px rgba(4, 31, 78, 0.20);
}

.glass::after {
  background: radial-gradient(
    380px circle at var(--mx, 50%) var(--my, -30%),
    rgba(255, 255, 255, 0.22),
    rgba(100, 200, 255, 0.10) 38%,
    transparent 62%
  );
}

.asset-card div {
  background: linear-gradient(180deg, transparent, rgba(3, 29, 78, 0.92) 42%);
}

.asset-card.is-render img {
  filter: brightness(0.72) contrast(1.08) saturate(0.84) hue-rotate(3deg);
}

.site-footer {
  background: linear-gradient(180deg, rgba(4, 36, 92, 0.12), rgba(3, 25, 66, 0.94) 34%, #031742);
}

.site-footer .brand-mark,
.site-footer .footer-nav a:hover {
  color: var(--ink);
}

.site-footer .brand-symbol {
  color: var(--silver);
  border-color: rgba(227, 237, 245, 0.62);
}

@media (max-width: 760px) {
  :root {
    --text-44: 2.625rem;
    --text-64: 3.5rem;
  }

  .site-header {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-3);
  }

  .site-header .brand-mark {
    min-width: 0;
  }

  .site-header .brand-mark > span:last-child {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .header-cta {
    grid-column: 2;
    grid-row: 1;
    padding-inline: var(--space-4);
  }

  .site-nav {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    overflow: visible;
  }

  .site-nav a {
    min-width: 0;
    padding-inline: var(--space-2);
  }

  body[data-page="home"][data-copy-side] .ambient-scrim {
    background:
      linear-gradient(90deg, rgba(3, 38, 92, 0.46), rgba(6, 83, 170, 0.22) 72%, rgba(14, 110, 205, 0.08)),
      linear-gradient(180deg, rgba(4, 53, 120, 0.06), rgba(3, 40, 96, 0.34));
  }

  .hero::after,
  body.particle-background-ready[data-page="home"] .hero::after {
    background:
      linear-gradient(90deg, rgba(3, 38, 92, 0.50), rgba(6, 83, 170, 0.20) 74%, rgba(14, 110, 205, 0.06)),
      linear-gradient(180deg, rgba(4, 53, 120, 0.04), rgba(3, 40, 96, 0.28));
  }
}

.ways-list .way-card {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
}

.ways-list .way-index {
  grid-column: 1;
  grid-row: 1;
  padding-top: 0;
  color: var(--sky-blue);
}

.ways-list .way-card > div {
  grid-column: 1;
  grid-row: 2;
}

.ways-list .way-price {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: end;
}

@media (max-width: 760px) {
  .ways-list .way-card {
    grid-template-columns: minmax(0, 1fr);
  }

  .ways-list .way-price {
    grid-column: 1;
    grid-row: 3;
    justify-items: start;
    text-align: left;
  }
}

/* One continuous atmosphere sits behind the fixed particle canvas. Scroll only
   changes these numeric transforms; no section owns or swaps the gradient.

   The ground was #0754c7, a bright blue the ocean canvas covers everywhere EXCEPT in the
   sliver Safari leaves at the bottom of an iPhone while its toolbar animates - which is
   exactly where it showed, as a hard blue strip under the URL bar. It is now the water's
   own colour along that edge, measured across the pages at mid-scroll (#0f324c to #3a3d44,
   averaging this), so the same sliver reads as more water.

   Transparent was tried first and is wrong: with nothing to resolve a backdrop against, the
   contrast audit degenerates to 1:1 on fifteen elements it had been clearing. The ground has
   to be a colour, just not a loud one. */
body {
  position: relative;
  isolation: isolate;
  background: #1d344c;
}

body::before {
  display: none;
}

.ambient-gradient {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 145vmax;
  height: 145vmax;
  z-index: -4;
  pointer-events: none;
  /* Darkened so the particles have something to read against.

     This layer is z-index -4, BELOW the particle canvas at -3, which is why the
     darkening belongs here: anything added to .ambient-scrim (-2) sits on top of the
     particles and dims them by exactly as much as the ground, gaining nothing.

     The old stops ran to rgba(255,255,255,0.62) and a ramp ending at #dbe8f1 -
     effectively white. A pearl particle against near-white has nowhere to go. The
     ceiling now stops in mid-blue, so the full palette clears the background
     everywhere instead of only over the dark corner. */
  background:
    radial-gradient(circle at 22% 24%, rgba(196, 226, 255, 0.26), transparent 18%),
    radial-gradient(circle at 73% 32%, rgba(86, 168, 232, 0.34), transparent 29%),
    radial-gradient(circle at 68% 82%, rgba(150, 190, 226, 0.30), transparent 24%),
    linear-gradient(128deg, #052b6e 0%, #0a55a2 38%, #2f7cc4 72%, #4d93d4 100%);
  transform: translate3d(-50%, -50%, 0) rotate(0deg);
  transform-origin: 50% 50%;
  will-change: transform;
}

#webglBackground {
  background: transparent;
}

/* The glass canvas stays a BACKGROUND layer (z-index -3, set above). As a foreground
   layer it had to copy the page's text into a texture to refract it, which cost a full
   layout per word on every scroll. */

.ambient-scrim,
body.particle-background-ready[data-page="home"] .ambient-scrim,
body[data-page="home"][data-copy-side="left"] .ambient-scrim,
body[data-page="home"][data-copy-side="right"] .ambient-scrim,
body[data-page="home"][data-copy-side="full"] .ambient-scrim {
  /* THIS is the scrim that wins - there are earlier .ambient-scrim blocks in this
     file and they are all overridden here. Check the computed style before editing
     any of them. */
  --scrim-focus-x: 8%;
  background:
    radial-gradient(
      ellipse 74% 118% at var(--scrim-focus-x) 48%,
      rgba(3, 22, 62, 0.78) 0%,
      rgba(4, 39, 93, 0.54) 38%,
      rgba(6, 69, 145, 0.12) 70%,
      transparent 88%
    ),
    linear-gradient(180deg, rgba(4, 45, 105, 0.08), rgba(3, 29, 76, 0.34));
  transition: none;
}

.hero::after,
body.particle-background-ready[data-page="home"] .hero::after {
  display: none;
}

@media (max-width: 760px) {
  .ambient-scrim,
  body.particle-background-ready[data-page="home"] .ambient-scrim,
  body[data-page="home"][data-copy-side] .ambient-scrim {
    background:
      radial-gradient(
        ellipse 104% 110% at var(--scrim-focus-x) 50%,
        rgba(3, 30, 76, 0.58) 0%,
        rgba(4, 50, 112, 0.34) 46%,
        rgba(9, 93, 181, 0.08) 78%,
        transparent 94%
      ),
      linear-gradient(180deg, rgba(4, 53, 120, 0.04), rgba(3, 40, 96, 0.28));
  }
}

/* --- the dock and the pill ------------------------------------------------ */

.nav-dock {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding-bottom: var(--nav-inset);
  pointer-events: none;
}

.nav-dock > * {
  pointer-events: auto;
}

.nav-pill {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  padding: var(--space-1);
  list-style: none;
  border-radius: 999px;
  background: rgba(14, 11, 9, 0.82);
  border: 1px solid var(--line-strong);
  box-shadow:
    inset 0 1px 0 rgba(232, 210, 182, 0.22),
    0 18px 44px rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(22px) saturate(1.3);
  -webkit-backdrop-filter: blur(22px) saturate(1.3);
}

.nav-slot {
  position: relative;
  display: flex;
}

/* The sliding indicator. One element behind every item, moved by JS - not a
   background on each item, which cannot animate between siblings. */
.nav-indicator {
  position: absolute;
  top: var(--space-1);
  left: 0;
  height: calc(100% - var(--space-2));
  border-radius: 999px;
  background: var(--cognac);
  opacity: 0;
  transition: transform 380ms cubic-bezier(0.22, 1, 0.36, 1),
              width 380ms cubic-bezier(0.22, 1, 0.36, 1),
              opacity 200ms ease;
  pointer-events: none;
}

.nav-item {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 0 var(--space-5);
  color: var(--ink-muted);
  background: none;
  border: 0;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: var(--text-14);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-decoration: none;
  cursor: pointer;
  transition: color 200ms ease;
  -webkit-tap-highlight-color: transparent;
}

.nav-item:hover,
.nav-item:focus-visible,
.nav-item.is-current {
  color: #17100b;
}

.nav-item .nav-icon {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
}

.nav-item .nav-icon svg { width: 20px; height: 20px; }

.nav-item[data-nav-toggle] { padding-right: var(--space-4); }

.nav-chevron {
  width: 12px;
  height: 12px;
  transition: transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
}

.nav-item[aria-expanded="true"] .nav-chevron { transform: rotate(180deg); }

/* --- flyout, opening UPWARD from the bar ---------------------------------- */

.nav-flyout {
  position: absolute;
  bottom: calc(100% + var(--space-3));
  left: 50%;
  width: min(420px, calc(100vw - var(--space-6)));
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: rgba(14, 11, 9, 0.94);
  border: 1px solid var(--line-strong);
  box-shadow:
    inset 0 1px 0 rgba(232, 210, 182, 0.20),
    0 26px 60px rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(24px) saturate(1.3);
  -webkit-backdrop-filter: blur(24px) saturate(1.3);
  opacity: 0;
  transform: translate(-50%, 10px) scale(0.98);
  transition: opacity 260ms ease, transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}

.nav-flyout.is-open {
  opacity: 1;
  transform: translate(-50%, 0) scale(1);
}

.nav-flyout[hidden] { display: none; }

.nav-flyout-body {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-5);
}

.nav-flyout-col { display: grid; gap: var(--space-3); align-content: start; }

.nav-flyout-title {
  margin: 0;
  color: var(--cognac);
  font-size: var(--text-12);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.nav-flyout-times,
.nav-flyout-prices {
  display: grid;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-flyout-times li {
  color: var(--cream);
  font-family: var(--font-display);
  font-size: var(--text-18);
}

.nav-flyout-prices li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--ink-muted);
  font-size: var(--text-14);
}

.nav-flyout-prices strong {
  color: var(--cream);
  font-family: var(--font-display);
  font-size: var(--text-18);
  font-weight: 400;
}

.nav-flyout-prices small {
  grid-column: 1 / -1;
  color: var(--ink-faint);
  font-size: var(--text-12);
}

.nav-flyout-cta {
  width: 100%;
  margin-top: var(--space-5);
}

/* --- the shade behind an open menu ---------------------------------------- */

/* The Book flyout belongs to the bar, not to the button that opens it. Dropping the
   slot's positioning makes the pill the flyout's containing block, so it centres on the
   whole bar; the sliding indicator is a child of the pill and is unaffected. Corners match
   the cards - the old --radius-lg is 6px, which read as sharp beside the glass. */
.site-header .nav-slot { position: static; }

.nav-flyout {
  border-radius: var(--radius-card);
  width: min(460px, calc(100vw - var(--space-6)));
  transform: translate(calc(-50% + var(--nav-flyout-shift, 0px)), 10px) scale(0.98);
}

.nav-flyout.is-open {
  transform: translate(calc(-50% + var(--nav-flyout-shift, 0px)), 0) scale(1);
}

.nav-shade {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: rgba(10, 8, 6, 0.72);
  opacity: 0;
  visibility: hidden;
  transition: opacity 300ms ease-in-out, visibility 0s linear 300ms;
}

.nav-shade.is-lit {
  opacity: 1;
  visibility: visible;
  transition: opacity 300ms ease-in-out, visibility 0s;
}

/* --- compact mode --------------------------------------------------------- */

.nav-compact {
  display: none;
  align-items: center;
  gap: var(--space-3);
  min-height: 48px;
  padding: 0 var(--space-5);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-14);
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.nav-compact-bars {
  display: grid;
  gap: 4px;
  width: 16px;
}

.nav-compact-bars i {
  display: block;
  height: 1.5px;
  background: var(--cognac);
  border-radius: 2px;
  transition: transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
}

.nav-compact[aria-expanded="true"] .nav-compact-bars i:first-child { transform: translateY(2.75px) rotate(45deg); }
.nav-compact[aria-expanded="true"] .nav-compact-bars i:last-child { transform: translateY(-2.75px) rotate(-45deg); }

.nav-drawer {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 1;
  max-height: min(76svh, 620px);
  overflow-y: auto;
  padding: var(--space-6) var(--gutter) calc(var(--nav-reserve) + var(--space-6));
  background: rgba(14, 11, 9, 0.96);
  border-top: 1px solid var(--line-strong);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  backdrop-filter: blur(24px) saturate(1.3);
  -webkit-backdrop-filter: blur(24px) saturate(1.3);
  transform: translateY(100%);
  transition: transform 380ms cubic-bezier(0.22, 1, 0.36, 1);
}

.nav-drawer.is-open { transform: translateY(0); }
.nav-drawer[hidden] { display: none; }

.nav-drawer-inner {
  display: grid;
  gap: var(--space-2);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
}

.nav-drawer-inner > a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 56px;
  padding: 0 var(--space-4);
  color: var(--ink);
  border-radius: var(--radius);
  border: 1px solid transparent;
  font-family: var(--font-display);
  font-size: var(--text-22);
  text-decoration: none;
}

.nav-drawer-inner > a[aria-current="page"] {
  color: var(--cream);
  background: rgba(192, 138, 98, 0.14);
  border-color: var(--cognac);
}

.nav-drawer-inner > a .nav-chevron { transform: rotate(90deg); }

.nav-drawer-flyout {
  margin-top: var(--space-4);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}

.nav-drawer-cta {
  flex: 0 0 auto;
  align-self: center;
  width: min(var(--content-max), 100%);
  margin-top: var(--space-2);
}

@media (max-width: 900px) {
  .nav-pill { display: none; }
  .nav-compact { display: inline-flex; }
}

@media (min-width: 901px) {
  .nav-drawer { display: none !important; }
}

/* The current item carries its OWN cognac background, not just the sliding
   indicator. The indicator is a sibling element, so without this the active label
   is dark-on-dark (measured 1.04:1) any time JS has not positioned it - on a slow
   load, a script error, or with JS off. The indicator then only appears on hover,
   and the current item steps back while it is elsewhere so there are never two
   highlights at once. */
.nav-item.is-current {
  background: var(--cognac);
  transition: background-color 200ms ease, color 200ms ease;
}

.nav-pill.is-pointing .nav-item.is-current {
  background: transparent;
  color: var(--ink-muted);
}

.nav-pill.is-pointing .nav-item:hover,
.nav-pill.is-pointing .nav-item:focus-visible {
  color: #17100b;
}

/* ===========================================================================
   Pill reveal

   Three overlapping moves, not one:
     1. the pill springs up from below the fold and SETTLES with squash-and-stretch
        (wide-and-flat on arrival, tall-and-narrow on the rebound) - that overshoot
        is what reads as jello rather than as a slide
     2. the capsule widens from a circle to its measured width, slightly behind the
        rise so the two are felt as one gesture
     3. the items fade up in sequence once there is room for them

   The natural width is measured in JS: `width: auto` cannot be transitioned, so the
   pill is collapsed to a circle and expanded to a real pixel value, then released
   back to auto once it lands.
   ========================================================================= */

.nav-pill {
  transform-origin: 50% 100%;
  will-change: transform, width;
}

/* Pre-reveal: below the fold, collapsed to a capsule, contents held back. */
.nav-pill:not(.is-revealed) {
  transform: translateY(180%) scale(0.55);
  opacity: 0;
  pointer-events: none;
}

.nav-pill:not(.is-revealed) .nav-slot,
.nav-pill:not(.is-revealed) .nav-indicator {
  opacity: 0;
}

.nav-pill.is-revealed {
  opacity: 1;
  animation: navPillSettle 1150ms cubic-bezier(0.22, 1, 0.36, 1) both;
  /* The width transition is deliberately slower and later than the rise, so the
     capsule looks like it is being pulled open rather than scaled. */
  transition: width 900ms cubic-bezier(0.16, 1.02, 0.24, 1) 90ms;
}

/* Squash and stretch. A pill that only translates reads as a slide; the width/height
   counter-scaling is the whole difference between "moved" and "landed". */
@keyframes navPillSettle {
  0%   { transform: translateY(180%) scale(0.72, 0.72); }
  38%  { transform: translateY(-14%) scale(1.10, 0.90); }
  56%  { transform: translateY(4%)   scale(0.95, 1.07); }
  72%  { transform: translateY(-3%)  scale(1.04, 0.97); }
  86%  { transform: translateY(1%)   scale(0.99, 1.02); }
  100% { transform: translateY(0)    scale(1, 1); }
}

.nav-pill.is-revealed .nav-slot {
  animation: navItemIn 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* Stagger. Each item arrives after the capsule has opened far enough to hold it. */
.nav-pill.is-revealed .nav-slot:nth-child(2) { animation-delay: 330ms; }
.nav-pill.is-revealed .nav-slot:nth-child(3) { animation-delay: 400ms; }
.nav-pill.is-revealed .nav-slot:nth-child(4) { animation-delay: 470ms; }
.nav-pill.is-revealed .nav-slot:nth-child(5) { animation-delay: 540ms; }

@keyframes navItemIn {
  0%   { opacity: 0; transform: translateY(8px) scale(0.9); filter: blur(2px); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
}

/* The indicator gets NO reveal animation. It had one with fill-mode:both, and a
   filled animation value outranks an inline style - so the leave handler setting
   opacity 0 was silently ignored and the highlight stayed lit on whichever item the
   cursor last touched, long after the pointer had gone. It rests hidden and is
   driven entirely by JS on hover. */

/* The compact Menu button gets the same entrance, minus the widening. */
.nav-compact:not(.is-revealed) {
  transform: translateY(180%) scale(0.55);
  opacity: 0;
  pointer-events: none;
}

.nav-compact.is-revealed {
  opacity: 1;
  animation: navPillSettle 1150ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* The ocean paints the background canvas edge to edge and opaque, so the DOM gradient
   underneath it is never seen. */
body.ocean-background-ready .ambient-gradient {
  display: none;
}

/* ===========================================================================
   Experiences - the occasions the boat is booked for, listed prominently on home.
   Two columns inside a wider rail than the other story sections: eight items in the
   520px rail made a list taller than the viewport.
   ========================================================================= */
.experiences-band .story-copy {
  width: min(760px, 100%);
}

.experience-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--space-6);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}

.experience-list li {
  display: grid;
  align-content: start;
  gap: var(--space-2);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line);
}

.experience-list h3 {
  margin: 0;
  font-size: var(--text-18);
}

.experience-list p {
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--text-14);
}

@media (max-width: 760px) {
  .experience-list {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ===========================================================================
   My bookings - the lookup and a help panel fill the band side by side. With results,
   the results take the wide right column and help sits under the lookup.
   ========================================================================= */
.account-layout {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-areas: "lookup help";
}

.account-layout.has-results {
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  grid-template-areas:
    "lookup results"
    "help results";
}

.account-lookup {
  grid-area: lookup;
}

.account-help {
  grid-area: help;
}

.account-results {
  grid-area: results;
  display: grid;
  align-content: start;
  gap: var(--space-5);
  min-width: 0;
}

.account-help-list {
  display: grid;
  margin: 0;
  border-top: 1px solid var(--line);
}

.account-help-list div {
  display: grid;
  grid-template-columns: minmax(96px, 0.4fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line);
}

.account-help-list dt {
  color: var(--ink-muted);
  font-size: var(--text-14);
}

.account-help-list dd {
  margin: 0;
  min-height: 44px;
  display: flex;
  align-items: center;
}

.account-help-list a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: inherit;
}

@media (max-width: 860px) {
  .account-layout,
  .account-layout.has-results {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "lookup"
      "results"
      "help";
  }
}

/* ===========================================================================
   Boat page - one photo card per space aboard. The client's photos are portrait
   (roughly 3:4), so the cards keep that ratio rather than cropping them to landscape.
   ========================================================================= */
.space-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
}

.space-grid .asset-card {
  min-height: 0;
  aspect-ratio: 3 / 4;
  max-width: 100%;
}

.space-grid .asset-card img {
  display: block;
  height: 100%;
  min-height: 0;
  opacity: 1;
}

.space-grid .asset-card div {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-5) var(--space-5);
  background: linear-gradient(180deg, transparent, rgba(3, 18, 44, 0.9) 42%);
}

.space-grid .asset-card .eyebrow,
.space-grid .asset-card h3,
.space-grid .asset-card p {
  margin: 0;
}

.space-grid .asset-card p:not(.eyebrow) {
  color: #d7e4ef;
  font-size: var(--text-14);
}

@media (max-width: 960px) {
  .space-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 600px) {
  .space-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .space-grid .asset-card {
    aspect-ratio: 4 / 5;
  }
}

/* ===========================================================================
   Content-led layout and glass buttons (15 September 2026)

   The home page was laid out for the particle system: story sections were forced to
   1.45 screens tall and their copy parked in a 520px rail on alternating sides, leaving
   the other half of the screen for a 3D object. Measured over the ocean, 38-82% of each
   section was empty and the page ran 8.5 screens. Sections now size to their content on
   the shared spine, and the type pairs a display serif with the sans.
   ========================================================================= */

:root {
  /* One family for the whole site. The display serif's hairlines broke up over moving
     water; a single sans at a heavier weight holds its shape there. */
  --font-serif: "Plus Jakarta Sans", "Helvetica Neue", Arial, sans-serif;
  --water-panel: rgba(3, 22, 58, 0.55);
  --water-panel-edge: rgba(214, 236, 255, 0.16);
  --radius-card: 16px;
  /* Two layers: a tight edge and a soft halo. The sky and the far haze are a bright light
     blue now, so white text there needs more separation than the old single soft shadow. */
  --text-over-water: 0 1px 2px rgba(0, 28, 64, 0.55), 0 0 18px rgba(0, 36, 84, 0.5);
}

/* --- Type ------------------------------------------------------------------ */

h1,
h2 {
  font-family: var(--font-serif);
  font-weight: 600;
  /* A sans display needs its letters pulled in a little at these sizes; the global
     letter-spacing reset is why this needs !important. */
  letter-spacing: -0.02em !important;
  font-variation-settings: normal;
}

h1 {
  max-width: 14ch;
  font-size: 5.25rem;
  line-height: 1;
}

h2 {
  max-width: 18ch;
  font-size: 3.25rem;
  line-height: 1.05;
}

/* Panel headings on the booking console and account pages are small working labels,
   not display type: they stay in the sans. */
.tool-panel h2,
.panel-heading h2 {
  max-width: none;
  font-family: var(--font-body);
  font-weight: 600;
  letter-spacing: 0 !important;
}

h3 {
  font-family: var(--font-body);
  font-weight: 500;
  font-variation-settings: normal;
}

/* Pale text colours went white when the sky turned bright blue: #cfe9ff and #d7e4ef
   nearly vanished against the light haze near the top of the screen. */
.eyebrow {
  color: #ffffff;
  font-size: 0.8125rem;
  letter-spacing: 0.16em !important;
  text-shadow: 0 0 2px rgba(0, 30, 70, 0.55), 0 1px 10px rgba(0, 34, 80, 0.55);
}

.tool-panel .eyebrow {
  color: #ffffff;
}

.hero-lede {
  max-width: 44ch;
  color: #f4f9fd;
  font-size: 1.25rem;
  line-height: 1.6;
}

.hero-status span {
  font-size: 0.8125rem;
  letter-spacing: 0.08em !important;
}

/* The figures' first-child rule never matched: carve.js puts its SVG first. */
.hero-status span:first-of-type {
  padding-left: 0;
}

.hero-status strong {
  font-family: var(--font-serif);
  font-size: 2.25rem;
  font-weight: 400;
  line-height: 1;
}

.footer-nav h2 {
  max-width: none;
  color: #cfe8ff;
  font-family: var(--font-body);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.14em !important;
}

.hero-copy,
.section-intro,
.venue-copy,
.gallery-heading,
.page-head .section-heading,
.section-heading {
  text-shadow: var(--text-over-water);
}

.page-head h1 {
  max-width: 18ch;
  font-size: 4rem;
  line-height: 1.02;
}

.page-head .section-heading p:not(.eyebrow) {
  max-width: 58ch;
  margin-top: var(--space-4);
  font-size: 1.125rem;
  line-height: 1.6;
}

/* A fixed, gentle darkening top and bottom. The shading used to swing left and right
   after the copy, which only made sense while a 3D object held the opposite side. */
/* No navy wash over the sky: it used to be 52% navy at the top, which is most of why the
   sky read grey and overcast. The top now deepens in a saturated sky blue instead, as a
   real sky does overhead, which also gives the small labels at the top of each page
   something to read against. The bottom of the screen deepens over the water. */
body .ambient-scrim {
  background:
    linear-gradient(180deg, rgba(14, 82, 160, 0.34) 0%, rgba(14, 82, 160, 0.14) 12%, rgba(14, 82, 160, 0) 24%),
    linear-gradient(180deg, rgba(2, 14, 38, 0) 55%, rgba(2, 14, 38, 0.36) 100%) !important;
}

/* --- Sections -------------------------------------------------------------- */

.section-inner {
  display: grid;
  gap: var(--space-7);
  width: min(var(--content-max), 100%);
  min-width: 0;
  margin-inline: auto;
}

.section-inner > * {
  min-width: 0;
}

.section-intro {
  display: grid;
  gap: var(--space-4);
}

.section-intro .eyebrow,
.section-intro h2,
.section-intro p,
.venue-copy .eyebrow,
.venue-copy h2,
.venue-copy p {
  margin: 0;
}

.section-lede {
  max-width: 58ch;
  color: #f4f9fd;
  font-size: 1.125rem;
  line-height: 1.6;
}

.section-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.gallery-band {
  min-height: 0;
}

/* Experiences: eight occasions as a card grid, four across. */
.experience-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* CSS glass for cards; over open water glass-buttons.js draws real glass instead (see
   "Containers: glass" at the end of the file). */
.experience-card,
.price-card {
  min-width: 0;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-card);
  background: linear-gradient(180deg, rgba(140, 202, 246, 0.26), rgba(117, 189, 242, 0.16));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 18px 40px rgba(0, 0, 0, 0.2);
  -webkit-backdrop-filter: blur(6px) saturate(1.2);
  backdrop-filter: blur(6px) saturate(1.2);
}

.experience-card {
  display: grid;
  align-content: start;
  gap: var(--space-3);
  padding: var(--space-5);
}

.experience-card h3 {
  margin: 0;
  font-size: 1.25rem;
  line-height: 1.25;
}

.experience-card p {
  margin: 0;
  color: var(--ink-muted);
  font-size: 1rem;
  line-height: 1.55;
}

/* The boat: photo beside the copy. The half of the screen once kept empty for the 3D
   model now shows the real lounge. */
.venue-layout {
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  align-items: center;
  gap: var(--space-8);
}

.venue-photo {
  max-width: 100%;
  margin: 0;
  overflow: hidden;
  aspect-ratio: 4 / 5;
  border: 1px solid var(--water-panel-edge);
  border-radius: var(--radius-card);
}

.venue-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.venue-copy {
  display: grid;
  gap: var(--space-5);
  min-width: 0;
}

.venue-copy .story-facts {
  margin: 0;
}

.story-facts dt {
  color: #ffffff;
  font-size: 0.8125rem;
  line-height: 1.5;
  letter-spacing: 0.12em !important;
}

.story-facts dd {
  font-size: 1rem;
  line-height: 1.5;
}

/* Ways to book: three price cards side by side, so prices compare along a row. */
.price-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
}

.price-card {
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: var(--space-3);
  padding: var(--space-6);
}

.price-card p {
  margin: 0;
  color: var(--ink-muted);
  font-size: 1rem;
  line-height: 1.55;
}

.price-card .price-label {
  color: #ffffff;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.14em !important;
  text-transform: uppercase;
}

.price-card h3 {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 2rem;
  font-weight: 400;
  line-height: 1.1;
}

.price-card .price-amount {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  padding-top: var(--space-4);
  border-top: 1px solid var(--water-panel-edge);
}

.price-amount strong {
  color: #ffffff;
  font-family: var(--font-serif);
  font-size: 2.5rem;
  font-weight: 400;
  line-height: 1;
}

.price-amount span {
  color: var(--ink-faint);
  font-size: 0.8125rem;
  letter-spacing: 0.12em !important;
  text-transform: uppercase;
}

/* Closing: short and centred. */
.closing-inner {
  justify-items: center;
  gap: var(--space-6);
  text-align: center;
}

.closing-inner .section-intro {
  justify-items: center;
}

.closing-inner .eyebrow::before {
  display: none;
}

.closing-inner .section-actions {
  justify-content: center;
}

.closing-contact {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-2) var(--space-5);
  margin: 0;
  color: var(--ink-muted);
  font-size: 1rem;
}

.closing-contact a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: inherit;
}

/* The footer's own padding rule (earlier in the file) dropped the room kept for the
   fixed nav pill, so on a phone the pill sat over the credit line. The top is one step
   of the scale: the closing section's own band padding already sits above it. */
.site-footer {
  padding-top: var(--space-8);
  padding-bottom: calc(var(--nav-reserve) + var(--space-6));
}

/* --- Buttons: glass ------------------------------------------------------------
   Everywhere a button cannot see the ocean (on a panel, or without WebGL2) this CSS
   glass is what shows. Over open water glass-buttons.js draws real refracting glass
   under the button and the element switches to .is-glass below. */

.button {
  min-height: 52px;
  padding: 0 var(--space-6);
  color: #ffffff;
  font-size: var(--text-14);
  font-weight: 600;
  letter-spacing: 0.1em !important;
  border: 1px solid rgba(230, 244, 255, 0.34);
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.05));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45), 0 8px 24px rgba(2, 16, 44, 0.28);
  -webkit-backdrop-filter: blur(14px) saturate(1.5);
  backdrop-filter: blur(14px) saturate(1.5);
  transition: background-color 160ms ease, border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease;
}

/* Clear glass for every button: primary and secondary look the same, with no tint. */
.button-primary,
.button-secondary {
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.34);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.05));
}

.button:hover:not(:disabled) {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 12px 28px rgba(0, 0, 0, 0.3);
}

.button-primary:hover:not(:disabled),
.button-secondary:hover:not(:disabled) {
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.5);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(255, 255, 255, 0.08));
}

/* Panels are glass too now, so their buttons are the same clear glass with a white
   label. These beat the older .tool-panel button rules earlier in the file. */
.tool-panel .button-primary,
.tool-panel .button-secondary {
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.34);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.05));
}

.tool-panel .button-primary:hover:not(:disabled),
.tool-panel .button-secondary:hover:not(:disabled) {
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.5);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(255, 255, 255, 0.08));
}

/* Over open water the WebGL glass is the whole button. No CSS border: it sat exactly on
   the glass rim and read as a second outline. */
.button.is-glass,
.button.is-glass:hover:not(:disabled) {
  color: #ffffff;
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.45);
}

/* --- Narrow screens -------------------------------------------------------- */

@media (max-width: 1100px) {
  .experience-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 860px) {
  .venue-layout,
  .price-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .venue-layout {
    gap: var(--space-6);
  }

  .venue-photo {
    aspect-ratio: 4 / 3;
  }
}

@media (max-width: 760px) {
  h1 {
    font-size: 3rem;
  }

  h2 {
    font-size: 2.375rem;
  }

  .tool-panel h2,
  .panel-heading h2 {
    font-size: 1.25rem;
  }

  .hero-lede {
    font-size: 1.125rem;
  }

  .hero-status strong {
    font-size: 1.75rem;
  }

  /* Three columns are ~110px each here and "Hartbeespoort" does not fit in one:
     two figures side by side, the place on its own row beneath. */
  body[data-page="home"] .hero-status {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: var(--space-4);
  }

  /* of-type: carve.js inserts its SVG as the first child of .hero-status */
  .hero-status span:nth-of-type(2) {
    border-right: 0;
    padding-right: 0;
  }

  .hero-status span:last-child {
    grid-column: 1 / -1;
    padding-left: 0;
  }

  .section-inner {
    gap: var(--space-6);
  }

  .experience-card {
    padding: var(--space-4);
  }

  .experience-card h3 {
    font-size: 1.0625rem;
  }

  .experience-card p {
    font-size: 0.9375rem;
  }

  .price-card {
    padding: var(--space-5);
  }
}

/* ===========================================================================
   Containers: glass (15 September 2026)

   Cards and working panels are glass like the buttons. Over open water glass-buttons.js
   draws real glass under each one and the element turns transparent (.is-glass-panel);
   the CSS below is the frosted fallback everywhere else. Every page container uses the
   home-page card material; booking, account and crew panels only keep their local light-text
   tokens for form readability.
   ========================================================================= */

:root {
  --water-panel-edge: rgba(255, 255, 255, 0.18);
}

.booking-card,
.tool-panel,
.admin-toolbar {
  color-scheme: dark;
  --ink: #f8fbff;
  --ink-muted: #eaf3fa;
  --ink-faint: #dde9f2;
  --atlantic: #ffffff;
  --cognac: #ffffff;
  --cream: #ffffff;
  --surface: rgba(255, 255, 255, 0.06);
  --surface-strong: rgba(255, 255, 255, 0.1);
  --surface-soft: rgba(255, 255, 255, 0.07);
  --line: rgba(255, 255, 255, 0.2);
  --line-strong: rgba(255, 255, 255, 0.36);
  --carve-container-surface: rgba(255, 255, 255, 0.04);
  --carve-ghost-surface: rgba(255, 255, 255, 0.06);
  --carve-control-blob: rgba(255, 255, 255, 0.2);
  --carve-secondary-blob: rgba(255, 255, 255, 0.26);
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-card);
  background: linear-gradient(180deg, rgba(140, 202, 246, 0.26), rgba(117, 189, 242, 0.16));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 18px 40px rgba(0, 0, 0, 0.2);
  -webkit-backdrop-filter: blur(6px) saturate(1.2);
  backdrop-filter: blur(6px) saturate(1.2);
}

/* Fields, tiles and meters inside panels: white light instead of Atlantic tints. */
.tool-panel :is(input, select, textarea) {
  color: var(--ink);
  background-color: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.32);
}

.tool-panel select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23ffffff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.tool-panel select option {
  color: #f8fbff;
  background: #0b2340;
}

.tool-panel ::placeholder {
  color: rgba(248, 251, 255, 0.6);
}

.tool-panel input::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: 0.75;
}

.tool-panel :is(.date-tile, .slot-tile):hover:not(:disabled),
.tool-panel .segment:hover:not(.is-active) {
  border-color: var(--line-strong);
  background: rgba(255, 255, 255, 0.14);
}

.tool-panel :is(.date-tile.is-selected, .slot-tile.is-selected, .segment.is-active),
.tool-panel :is(.date-tile.is-selected, .slot-tile.is-selected, .segment.is-active).is-carved:hover:not(:disabled) {
  color: #ffffff;
  background: rgba(255, 255, 255, 0.22);
  border-color: #ffffff;
}

.tool-panel .meter {
  background: rgba(255, 255, 255, 0.16);
}

.tool-panel .stepper {
  background: rgba(255, 255, 255, 0.08);
}

.tool-panel .stepper:focus-within {
  background: rgba(255, 255, 255, 0.12);
}

.tool-panel .stepper-btn:hover,
.tool-panel .calendar-step:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.16);
}

.tool-panel .booking-status {
  background: rgba(255, 255, 255, 0.1);
}

.tool-panel .day-grid label:has(input:checked),
.tool-panel .sitting-row.has-clash,
.tool-panel .sitting-warning {
  background: rgba(255, 255, 255, 0.14);
}

.tool-panel .sitting-editor {
  background: rgba(255, 255, 255, 0.05);
}

/* The Boat page's space cards: photo on top, text on the glass below, instead of text
   laid over the photo on a dark gradient. */
.space-grid .space-card {
  display: grid;
  grid-template-rows: auto 1fr;
  aspect-ratio: auto;
  min-height: 0;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-card);
  background: linear-gradient(180deg, rgba(140, 202, 246, 0.26), rgba(117, 189, 242, 0.16));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 18px 40px rgba(0, 0, 0, 0.2);
  -webkit-backdrop-filter: blur(6px) saturate(1.2);
  backdrop-filter: blur(6px) saturate(1.2);
}

.space-grid .space-card img {
  width: 100%;
  height: auto;
  min-height: 0;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.space-grid .space-card div {
  position: static;
  align-content: start;
  padding: var(--space-5);
  background: none;
}

/* Over open water the WebGL glass is the whole container. */
:is(.experience-card, .price-card, .space-card, .booking-card, .tool-panel, .admin-toolbar).is-glass-panel {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.is-glass-panel {
  text-shadow: 0 1px 2px rgba(0, 28, 64, 0.5), 0 0 14px rgba(0, 36, 84, 0.42);
}

/* Secondary text in the tinted cards stays near-white: the glass can sit over the bright
   haze at the top of the screen. */
.experience-card,
.price-card,
.space-card {
  --ink-muted: #f4f9fd;
  --ink-faint: #eaf3fa;
}

.space-grid .space-card p:not(.eyebrow) {
  color: #f4f9fd;
}

.is-glass-panel :is(input, select, textarea) {
  text-shadow: none;
}

/* On the dark glass the success green measured 3.55:1; a paler mint clears AA. */
.tool-panel #bookingPermission.is-ok {
  color: #b8f5da;
}

/* The canvas is sized to the LARGE viewport. At 100% it tracked a phone's address bar,
   resizing on every show and hide, and each resize wiped the canvas black for a frame. */
#webglBackground {
  bottom: auto;
  height: 100lvh;
}

/* Touch screens: the glass is a transparent canvas IN THE PAGE, above the fixed ocean and
   under the tint, as it was when it was drawn into the ocean canvas (why: glass-buttons.js,
   createPageGlass). The stage is the document's height and clips it; the script sets that
   height and where in the stage the canvas sits. */
.glass-stage {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -3;
  width: 100%;
  overflow: hidden;
  overflow: clip;
  pointer-events: none;
}

.page-glass-canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  pointer-events: none;
  will-change: transform;
}

.emblem-glass-canvas {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  will-change: transform;
}

/* Panels that scroll inside themselves get a thin light scrollbar, not the dark native one. */
.tool-panel,
.tool-panel * {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.34) transparent;
}

/* ===========================================================================
   Navigation: glass (15 September 2026)

   The pill, its selected item, the Book flyout, the compact Menu button and the drawer
   are glass, drawn by glass-buttons.js (createNavGlass) into a canvas of their own,
   .nav-glass-canvas, which sits in the header above the page and below the nav's labels.
   The nav is fixed over content that scrolls behind it, so it cannot share the background
   canvas: the page's text and photos would draw over it. The selected item is the same
   glass with a sky-blue tint.

   Until that canvas is ready (.is-glass-nav on the header), and in browsers without
   WebGL2, the CSS glass below stands in.
   ========================================================================= */

.nav-glass-canvas {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 0;
  pointer-events: none;
}

.nav-pill {
  border: 1px solid rgba(230, 244, 255, 0.34);
  background: linear-gradient(180deg, rgba(120, 186, 236, 0.4), rgba(28, 88, 156, 0.46));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42), 0 12px 32px rgba(2, 16, 44, 0.32);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  backdrop-filter: blur(16px) saturate(1.4);
}

.nav-item,
.nav-compact {
  color: #ffffff;
  text-shadow: var(--text-over-water, 0 1px 2px rgba(0, 28, 64, 0.55), 0 0 18px rgba(0, 36, 84, 0.5));
}

/* Dark ink on the sky-blue selection. */
.nav-item:hover,
.nav-item:focus-visible,
.nav-item.is-current,
.nav-pill.is-pointing .nav-item:hover,
.nav-pill.is-pointing .nav-item:focus-visible {
  color: var(--ink-navy);
  text-shadow: none;
}

.nav-pill.is-pointing .nav-item.is-current:not(:hover):not(:focus-visible) {
  color: #ffffff;
  text-shadow: var(--text-over-water, 0 1px 2px rgba(0, 28, 64, 0.55), 0 0 18px rgba(0, 36, 84, 0.5));
}

.nav-compact-bars i {
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(0, 28, 64, 0.5);
}

.nav-flyout,
.nav-drawer {
  color: #ffffff;
  border-color: rgba(230, 244, 255, 0.28);
  background: linear-gradient(180deg, rgba(40, 112, 182, 0.8), rgba(12, 62, 124, 0.86));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0 24px 56px rgba(2, 16, 44, 0.34);
  text-shadow: 0 1px 2px rgba(0, 28, 64, 0.5), 0 0 14px rgba(0, 36, 84, 0.42);
}

.nav-flyout-title,
.nav-flyout-times li,
.nav-flyout-prices strong,
.nav-drawer-inner > a {
  color: #ffffff;
}

.nav-flyout-prices li {
  color: #eef6fc;
}

.nav-flyout-prices small {
  color: #e2eef7;
}

.nav-drawer-inner > a[aria-current="page"] {
  color: #ffffff;
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.42);
}

.nav-drawer-flyout {
  border-top-color: rgba(255, 255, 255, 0.22);
}

/* The Menu button closes the drawer, so it stays on top of it. The drawer's schedule scrolls
   inside its own region; Check dates stays outside that scroller so its label and WebGL glass
   cannot be composited at different scroll positions. */
.nav-dock {
  z-index: 2;
}

.nav-drawer {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.nav-drawer-inner {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* "More below", under the drawer's scrolling contents (why: chrome.js). */
.nav-drawer {
  gap: var(--space-2);
}

.nav-drawer-more {
  display: inline-flex;
  flex-shrink: 0;
  align-self: center;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 0 var(--space-4);
  border: 0;
  background: none;
  color: #ffffff;
  font-family: var(--font-body);
  font-size: var(--text-12);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
  transition: opacity 200ms ease, visibility 0s;
}

.nav-drawer-more[hidden] {
  display: none;
}

.nav-drawer-more.is-spent {
  opacity: 0;
  visibility: hidden;
  transition: opacity 200ms ease, visibility 0s linear 200ms;
}

/* The chevron's 12px box centres on the flex line, but the uppercase label has no
   descenders, so its ink centres about 1px HIGHER than the line does and the chevron reads
   as sitting low. Measured, not guessed: text ink centre 21.3px against the chevron's 22.2
   in the 44px button. The correction rides in the bob's own keyframes, because they animate
   `translate` and would otherwise overwrite it. */
.nav-drawer-more .nav-chevron {
  transform: rotate(180deg);
  translate: 0 -1px;
  animation: nav-more-bob 1.6s ease-in-out infinite;
}

@keyframes nav-more-bob {
  0%, 100% { translate: 0 -3px; }
  50% { translate: 0 1px; }
}

@media (prefers-reduced-motion: reduce) {
  .nav-drawer-more .nav-chevron { animation: none; }
}

.nav-shade {
  background: rgba(2, 16, 40, 0.5);
}

/* The glass layer is ready: the nav's own surfaces step aside for it. */
.is-glass-nav .nav-pill,
.is-glass-nav .nav-flyout,
.is-glass-nav .nav-drawer {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.is-glass-nav .nav-indicator,
.is-glass-nav .nav-item.is-current,
.is-glass-nav .nav-pill.is-pointing .nav-item.is-current {
  background: transparent;
}


/* ===========================================================================
   Fit to the screen (17 September 2026)

   Measured on the laptop this site is reviewed on: a 1920x1080 panel at Windows' 125% is
   1536x864 CSS px, and Chrome's tabs, toolbar and the taskbar leave a 1536x730 viewport.
   There (tools/ui/sectionfit.mjs):

     - the hero ran 878px, so its figures sat below the fold
     - every home section ran 1.20-1.34 screens: section rhythm followed the WIDTH only,
       so a 1536px-wide screen got 132px above and below every section - 264px of 730
     - the boat photo alone (4:5 at 507px wide) was 634px, taller than the screen left
       above the nav

   Rhythm now follows the height as well, the hero is exactly one screen with everything
   in it above the fold, and the tall pieces are capped by the screen they sit on.
   ========================================================================= */

:root {
  --band-y: clamp(64px, min(9vw, 11svh), 132px);
}

/* --- Hero: one screen ------------------------------------------------------ */

/* The nav stays hidden until the visitor scrolls on the home page (chrome.js), so the
   hero has the whole screen. The copy is centred in what is left above the figures; the
   figures sit at the bottom. The padding and the gap are the section's own rhythm. */
.hero {
  --hero-pad-top: clamp(32px, 8svh, 96px);
  --hero-pad-bottom: clamp(24px, 5svh, 48px);
  --hero-gap: clamp(24px, 5svh, 48px);
  min-height: 100svh;
  grid-template-rows: minmax(0, 1fr) auto;
  align-content: stretch;
  row-gap: var(--hero-gap);
  padding-top: var(--hero-pad-top);
  padding-bottom: var(--hero-pad-bottom);
}

.hero-copy {
  align-self: center;
}

body[data-page="home"] .hero-status {
  margin-top: 0;
}

/* Fixed steps by screen height, not a fluid size: at 84px the headline's three lines were
   a third of a 730px screen on their own. */
@media (min-width: 761px) and (max-height: 800px) {
  .hero h1 {
    font-size: 4.5rem;
  }
}

@media (min-width: 761px) and (max-height: 680px) {
  .hero h1 {
    font-size: 4rem;
  }

  .hero-lede {
    font-size: 1.125rem;
  }
}

/* --- Hero emblem ------------------------------------------------------------

   Large, on the right, BEHIND the right-hand ends of the headline and the lede. The box
   is still empty (see the earlier .hero-emblem note): glass-buttons.js draws the badge
   under it and leans it towards the cursor.

   What it must never sit under is a button. The buttons are glass drawn from the water
   itself, so one over the emblem would show plain water - a hole cut through the badge.
   So its left edge is fixed relative to the spine, just past the buttons (they end about
   523px in), and the circle's curve buys the rest of the clearance at their height.

   Size: 88% of the screen's height, or whatever width is left to the right of that edge,
   whichever is smaller. Vertically it is centred on the copy, not on the hero: the hero
   also holds the figures below. 96px is the figures' height.

   Widths are in cqw (the hero's content width), never %: the size is also used in `top`,
   where a % would resolve against the hero's HEIGHT. */
.hero {
  container-type: inline-size;
}

.hero-emblem {
  --emblem-edge: calc(var(--gutter) + max(0px, (100cqw - var(--content-max)) / 2) + 560px);
  --emblem-size: min(88svh, 100cqw + var(--gutter) * 1.5 - var(--emblem-edge), 900px);
  top: max(16px, calc(50% + (var(--hero-pad-top) - var(--hero-pad-bottom) - var(--hero-gap) - 96px) / 2 - var(--emblem-size) / 2));
  right: auto;
  left: var(--emblem-edge);
  width: var(--emblem-size);
}

/* Replaces the earlier 1360px cut-off, which existed because the badge sat over the
   headline's words; sitting behind them is now the point. Below 900px the buttons stack
   and there is no right-hand side left to put it on. */
@media (max-width: 1359px) {
  .hero-emblem { display: block; }
}

/* On the shortest screens 88% of the height pressed the badge down onto the third figure
   (17px at 1366x657). */
@media (max-height: 700px) {
  .hero-emblem {
    --emblem-size: min(84svh, 100cqw + var(--gutter) * 1.5 - var(--emblem-edge), 900px);
  }
}

/* Phones (20 September 2026). The same object as on desktop, but a phone has no room beside
   the copy, so the badge sits behind it - whole, never cropped. Two edges fix its size, and
   neither is a matter of taste:
     - it starts below env(safe-area-inset-top). At the top of the page Safari fills the
       status bar strip with a flat colour (see the bar notes further down), so anything
       bleeding off the top is cut by a hard line - and the first thing lost is the NOVA
       wordmark, the part of the badge worth having.
     - it ends above the buttons. They are glass drawn from the water itself, so a button
       over the badge shows plain water: a hole cut clean through it.
   The hero's own safe-area border already puts `top: 0` below the status bar strip, because
   an absolutely positioned box resolves its offsets against the padding box.
   Landscape has no such band at all, so the badge stands down, as it does on small desktops.
   Both clearances are measured, not assumed: tools/ui/emblemfit.mjs. */
@media (max-width: 899px) {
  .hero-emblem {
    display: block;
    /* 100cqw is the hero's CONTENT width, so the gutters are added back: the badge is a
       full-bleed disc with a hair of air each side, capped by the band's height. */
    --emblem-size: min(100cqw + var(--gutter) * 2 - 20px, 48svh);
    top: 8px;
    left: 0;
    right: 0;
    width: var(--emblem-size);
    margin-inline: auto;
  }
}

@media (max-width: 899px) and (max-height: 600px) {
  .hero-emblem { display: none; }
}

/* The figures' hairline used to span the whole spine and would cross the bottom of the
   badge; it now spans the figures themselves. */
@media (min-width: 900px) {
  body[data-page="home"] .hero-status {
    padding-top: 0;
    border-top: 0;
  }

  body[data-page="home"] .hero-status span {
    padding-top: var(--space-5);
    border-top: 1px solid var(--line);
  }
}

/* --- Sections: capped by the screen --------------------------------------- */

@media (min-width: 761px) and (max-height: 800px) {
  .section-inner {
    gap: var(--space-6);
  }

  .space-grid .space-card img {
    aspect-ratio: 16 / 10;
  }
}

/* The photo keeps 4:5 while there is room, and is never taller than the screen left
   between the nav and the section's own rhythm. */
.venue-photo {
  max-height: calc(100svh - var(--nav-reserve) - 2 * var(--band-y));
}

@media (max-width: 860px) {
  .venue-photo {
    max-height: none;
  }
}
/* The shortest laptop screens (1366x768 panels leave about 657px): the section headings
   step down as the hero's does, or Experiences and Ways to book ran past the nav. */
@media (min-width: 761px) and (max-height: 680px) {
  h2 {
    font-size: 2.75rem;
  }

  .section-inner {
    gap: var(--space-5);
  }

  .experience-card {
    padding: var(--space-4) var(--space-5);
  }
}

/* --- Hero on a phone -------------------------------------------------------

   With Safari's toolbars showing, a 390px iPhone leaves a 664px viewport, and the hero
   measured 822px there: the figures were a screen-and-a-bit down. The copy's own 24px
   padding (from the particle-era layout) goes, the headline steps down to 40px, and the
   section's rhythm is the scale's 24px. */
@media (max-width: 760px) {
  .hero {
    --hero-pad-top: var(--space-5);
    --hero-pad-bottom: var(--space-5);
    --hero-gap: var(--space-6);
  }

  .hero-copy {
    padding: 0;
  }

  .hero h1 {
    font-size: 2.5rem;
  }

  .hero-lede {
    font-size: 1rem;
  }

  /* The place is already the eyebrow at the top of the hero, and as a third figure it took
     a row of its own (Hartbeespoort does not fit a third of a phone). Two figures, one row. */
  body[data-page="home"] .hero-status span:last-of-type {
    display: none;
  }
}
/* The hero's copy runs over the badge now, and the badge's lit edges are the brightest
   thing behind it: measured under the glyphs (tools/ui/emblemtext.mjs), the brightest 5% of
   the pixels around the headline's letters reached 2.3:1 over the badge against 5.8:1 over
   the water. A tight dark ring under the existing halo separates a letter from a bright
   edge crossing behind it. */
.hero-copy {
  text-shadow: 0 1px 2px rgba(0, 28, 64, 0.55), 0 0 4px rgba(0, 24, 60, 0.45), 0 0 18px rgba(0, 36, 84, 0.5);
}
/* Section headings and ledes broke with a single word left on the last line - "Choose how
   you come / aboard.", "...how much of it / is yours." - which reads as a line that ran out
   of room. The hero's headline is left to break as it does: the badge is placed against it. */
h2 {
  text-wrap: balance;
}

/* balance, not pretty: pretty only rescues a lone word, and "is yours." is two. These are
   two- and three-line paragraphs, well inside the lines balance works on. */
.section-lede,
.gallery-heading > p,
.page-head .section-heading p:not(.eyebrow) {
  text-wrap: balance;
}
/* ===========================================================================
   Booking console: the whole screen, with room above and below (17 September 2026)

   The console was `100svh - the nav band` tall, so the footer began exactly where the nav
   band does: its top rule and its three headings showed behind the nav on the first screen
   (77px of footer at every size measured). And on laptops, where the title gives way, the
   panels started 16px from the top of the screen.

   Now the console is exactly one screen: 48px above the panels, and below them the nav
   band plus 32px, so the panels end clear of the pill and the footer starts below the fold.
   On tall screens the panels stop at 760px - past that it was empty glass, 250px of it
   under the calendar at 1920x955 - and the console centres in the screen instead.
   Measured with tools/ui/bookfit.mjs.
   ========================================================================= */

@media (min-width: 901px) {
  .book-main {
    height: 100svh;
    grid-template-rows: auto minmax(0, 760px);
    align-content: center;
    gap: var(--space-4);
    padding: var(--space-7) var(--gutter) calc(var(--nav-reserve) + var(--space-6));
  }
}

/* The title is hidden on these screens (see "Short laptops" above), so the panels are the
   only row. Left as two rows, the panels landed in the title's auto-sized row and stopped
   growing with the screen. */
@media (min-width: 901px) and (max-height: 800px) {
  .book-main {
    grid-template-rows: minmax(0, 760px);
  }
}

@media (min-width: 901px) and (max-height: 700px) {
  .book-main {
    padding-top: var(--space-6);
    padding-bottom: calc(var(--nav-reserve) + var(--space-5));
  }
}
/* Screens 801-960px tall show the title, the permission line and the field note, and the
   form ran 71px into its own scroll at 1440x900. The eyebrow over "Book your cruise." only
   repeats it, so it goes first, and the space above tightens a step. */
@media (min-width: 901px) and (min-height: 801px) and (max-height: 960px) {
  .book-main {
    padding-top: var(--space-6);
  }

  .book-head .eyebrow {
    display: none;
  }

  .book-main .booking-fields {
    gap: var(--space-3);
  }
}
/* ===========================================================================
   The Nova badge (17 September 2026)

   The client's badge replaces the placeholder "N" in a circle in the nav pill, the footer
   and the phone menu. It is a CSS mask filled with currentColor rather than an <img>, so it
   takes the label colour it sits among - white on the glass pill with the labels' shadow,
   navy on the sky-blue selection - exactly as the placeholder SVG did. The mask is the
   3000px artwork exported at 256px (emblem-3d/scripts/export_logo.py): sharp to about 85
   CSS px at 3x. The badge is a solid disc with cut-outs, so it never goes below 24px, where
   its lettering is still a texture rather than noise.
   ========================================================================= */

.brand-logo {
  display: block;
  flex: none;
  width: 100%;
  height: 100%;
  background-color: currentColor;
  -webkit-mask: url("./assets/brand/logo-mask.png") center / contain no-repeat;
  mask: url("./assets/brand/logo-mask.png") center / contain no-repeat;
}

/* Nav pill: 32px inside the 44px item, so the pill keeps its height. A shadow cannot be put
   on the masked element itself - the mask would clip it away - so it is on the holder. */
.nav-item .nav-icon {
  width: 32px;
  height: 32px;
  filter: drop-shadow(0 1px 2px rgba(0, 28, 64, 0.55));
}

.nav-item:hover .nav-icon,
.nav-item:focus-visible .nav-icon,
.nav-item.is-current .nav-icon {
  filter: none;
}

.nav-pill.is-pointing .nav-item.is-current:not(:hover):not(:focus-visible) .nav-icon {
  filter: drop-shadow(0 1px 2px rgba(0, 28, 64, 0.55));
}

/* Phone menu: the badge leads the Home row. */
.nav-drawer-home {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
}

.nav-drawer-home .brand-logo {
  width: 32px;
  height: 32px;
}

/* Footer: the badge beside the name, where the ringed N was. */
.site-footer .brand-mark .brand-logo {
  width: 48px;
  height: 48px;
}
/* ===========================================================================
   Live booking system (17 September 2026)

   The pieces the real booking system added to existing layouts: the promo code link on the
   booking panel, the booking status page, the crew workspace's booking details and status
   badges. Everything else reuses the panels, lists and tables already styled above.
   ========================================================================= */

/* A button that reads as a link, still a 44px target. */
.text-button {
  display: inline-flex;
  align-items: center;
  justify-self: start;
  min-height: 44px;
  padding: 0;
  color: var(--ink);
  background: none;
  border: 0;
  font: inherit;
  font-size: var(--text-14);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 4px;
  cursor: pointer;
}

.promo-row {
  display: grid;
  gap: var(--space-2);
}

/* The link and the field swap with the hidden attribute, which any display rule would override. */
.promo-row [hidden] {
  display: none;
}

.promo-row label {
  display: grid;
  gap: var(--space-2);
}

.promo-status {
  min-height: 1.2em;
  color: var(--ink-muted);
  font-size: var(--text-12);
}

.promo-status.is-ok {
  color: #b8f5da;
}

.calendar-loading {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--ink-muted);
}

/* The booking status page: the booking beside the help panel. */
.booking-status-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
  gap: var(--space-5);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
  align-items: start;
}

.booking-status-panel {
  display: grid;
  gap: var(--space-5);
}

/* The help panel is shared with My bookings, where it is placed by a named grid area. Here that
   name has no template to land in and pushed the booking into a 226px column. */
.booking-status-layout .account-help {
  grid-area: auto;
}

.hold-countdown {
  margin: 0;
  color: var(--ink);
  font-size: var(--text-18);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* My bookings, opened from the emailed link: the list, with help beside it. */
.account-layout.is-list {
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
  grid-template-areas: "results help";
}

.account-results {
  display: grid;
  gap: var(--space-4);
}

@media (max-width: 860px) {
  .booking-status-layout,
  .account-layout.is-list {
    grid-template-columns: minmax(0, 1fr);
  }

  .account-layout.is-list {
    grid-template-areas: "results" "help";
  }
}

/* Crew workspace */
.attention-panel {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--space-4);
  border-left: 4px solid #ffc46b;
}

.booking-filters {
  align-items: end;
}

.status-pill {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 0 var(--space-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: var(--text-12);
  font-weight: 600;
  white-space: nowrap;
}

/* State is never colour alone: each badge carries its word, and the colour only adds to it. */
.status-pill[data-status="confirmed"] { border-color: rgba(184, 245, 218, 0.6); }
.status-pill[data-status="needs_attention"] { border-color: #ffc46b; border-width: 2px; }
.status-pill[data-status="pending_payment"] { border-style: dashed; }
.status-pill[data-status="cancelled"],
.status-pill[data-status="expired"] { opacity: 0.75; }

tr.is-open td {
  border-bottom-color: transparent;
}

.booking-detail-row td {
  padding-top: 0;
}

.booking-detail {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.booking-detail-actions {
  display: grid;
  gap: var(--space-4);
}

.booking-detail-actions .inline-form {
  align-items: end;
}
/* The booking form gained a phone number and the promo link (17 September 2026), and ran
   11px into its own scroll at 1536x730. Rows 8px apart on screens up to 960px tall. */
@media (min-width: 901px) and (max-height: 960px) {
  .book-main .booking-fields {
    gap: var(--space-2);
  }
}
/* Preview deployments only (no real database yet): a note that bookings are tests. Fixed at
   the top, inside the 32-48px every booking page keeps above its content. */
.preview-pill {
  position: fixed;
  top: var(--space-2);
  left: 50%;
  z-index: 70;
  max-width: calc(100% - 2 * var(--space-4));
  margin: 0;
  padding: var(--space-1) var(--space-4);
  transform: translateX(-50%);
  color: #061a3a;
  background: #ffe2a8;
  border-radius: 999px;
  box-shadow: 0 4px 16px rgba(2, 16, 44, 0.3);
  font-size: var(--text-12);
  font-weight: 600;
  line-height: 1.5;
  text-align: center;
  pointer-events: none;
}
/* ===========================================================================
   iPhone Safari's status bar and toolbar (19 September 2026)

   Read from WebKit's source (LocalFrameView::fixedContainerEdges), not guessed. Safari 26
   ignores theme-color. For each bar it hit-tests a point 4px inside that edge of the
   screen - ignoring pointer-events - and walks up from what it hits. If it meets a
   position:fixed or sticky element, that element "owns" the edge and the bar is painted
   SOLID: in its background-color, or, when it has none, in a colour sampled from the
   screen once and then kept. Only when no fixed or sticky element is found does the bar
   stay clear and show the page scrolling under it. What does not count: an element whose
   own box is visibility:hidden (even with visible positioned children), one narrower than
   90% of the screen, and one sized like the screen with a negative z-index - unless it is
   a childless "dimming layer", which always counts.

   So on this site:
     - the nav dock is fixed at the bottom edge, and was what Safari sampled - the solid
       blue under the toolbar. The header's own box is now visibility:hidden, with its
       dock, drawer and glass visible, so Safari looks past it; the nav's glass canvas is
       positioned inside the header rather than fixed in its own right.
     - the menu's dimming layer is given a child, so while the menu is open it counts as
       an ordinary negative-z layer rather than a dimming layer.
     - the ocean canvas is fixed but sits under the page's own content, which is what the
       hit-test finds first.

   At the very top of the page there is nothing above it to show: WebKit clips the page
   out of the status bar until it has scrolled that far, and fills the strip with the
   page's background colour; the same goes for below the end of the page. That colour is
   set to the water at whichever end the visitor is nearer (--under-page-*, site.js).

   Every page's viewport meta has viewport-fit=cover. Content stays clear of the status
   bar, the Dynamic Island and, in landscape, the notch: the insets are added as transparent
   borders ON TOP of each band's own padding, so nothing moves anywhere the insets are zero
   (desktop, Android, iPhones without a notch). The bottom inset was already part of
   --nav-reserve. tools/ui/edgesample.mjs replays Safari's edge hit-test.
   ========================================================================= */

:root {
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  /* The water in the row the status bar strip BUTTS AGAINST - about 59px down on an iPhone,
     not the top row (tools/ui/underpage.mjs). The strip is a flat fill of this colour, so
     what has to match is the water at the seam below it, not the average of the band the
     strip covers.
     The first pass averaged the top twelve rows across four pages, which pulled the colour
     a long way light - #497ead against the #4176a1 at the seam - and that error is what
     made the status bar strip read as a band of its own on the phone.

     The water moves through about 30 units at that edge, so no ONE colour matches it at
     every instant. Sampling the live water was tried and taken out again: readPixels on the
     ocean canvas gets the water BEFORE the page's own scrims are composited over it
     (.ambient-scrim and the hero's gradients), which measured up to 45 units too light -
     further off than the fixed colour it was meant to replace. Doing better means reading
     the composited page, which a page cannot do cheaply. */
  --under-page-top: #4176a1;
  --under-page-bottom: #03163f;
}

html {
  background-color: var(--under-page-top);
}

html.is-lower-half {
  background-color: var(--under-page-bottom);
}

.site-header {
  visibility: hidden;
}

.nav-dock,
.nav-drawer,
.nav-glass-canvas {
  visibility: visible;
}

.nav-glass-canvas {
  position: absolute;
}

/* Safari's bottom toolbar floats OVER the page, so anything pinned to the bottom edge sits
   underneath it: the menu panel was cut off with its lower corners off the screen entirely.
   The LARGE viewport is the whole screen and the DYNAMIC one is what is left while the
   toolbar is showing, so the difference between them is how much the toolbar covers right
   now. It is 0 wherever there is no such toolbar - desktop, Android, and Safari once the
   toolbar collapses - so nothing moves there. The nav band and the drawer take the same
   offset, so the Menu pill stays seated in the panel instead of sliding out of its foot.

   ONLY WHILE THE MENU IS OPEN, which is not timidity: the dynamic viewport changes as Safari
   collapses its toolbar DURING A SCROLL. Carrying that into --nav-reserve would re-pad every
   page's foot mid-scroll, changing the document's height and shifting the scroll position
   under the reader - the same class of fault as the background jitter. Lifting the bar for
   good would also float it over the last of the footer, which its padding does not expect.
   With the menu open the page is not scrolling, so the offset costs nothing. */
:root {
  --chrome-bottom: calc(100lvh - 100dvh);
}

.site-header:has(.nav-drawer.is-open),
.site-header:has(.nav-drawer.is-open) .nav-drawer {
  bottom: var(--chrome-bottom);
}

.nav-drawer {
  /* Round on all four corners now that the lower two are on screen. The glass layer follows
     the box exactly (glass-buttons.js); it used to be drawn one corner-radius taller so that
     the bottom curve fell off the screen and the panel read as flush with it. */
  border-radius: var(--radius-lg);
  /* The room the Menu pill needs, floating over the panel's foot: half the nav band, half the
     pill, and air. It was --nav-reserve + 32px, a flat number that left a dead gap under
     "More below" and took that space from the menu's own content. */
  padding-bottom: calc((var(--nav-h) + var(--nav-inset)) / 2 + 48px + var(--space-4));
}

/* An empty child, so the open menu's shade is an ordinary layer rather than a childless
   "dimming layer", which Safari would let own an edge whatever else is there. */
.nav-shade > i {
  display: block;
  width: 0;
  height: 0;
}

/* The first band of every page: the home hero, the booking console, the page heads. */
.hero,
.page-head,
.book-main {
  border-top: var(--safe-top) solid transparent;
}

.band,
.site-footer,
.book-main {
  border-left: var(--safe-left) solid transparent;
  border-right: var(--safe-right) solid transparent;
}

.preview-pill {
  top: calc(var(--space-2) + var(--safe-top));
}
/* The nav band is --nav-h tall plus the home-indicator inset beneath it: the dock pads the
   inset off its bottom, so the band must include it. With the inset at 0 (every browser
   before viewport-fit=cover) the two were the same; at an iPhone's 34px the pill was pressed
   into the home-indicator zone, 16px from the bottom edge. */
.site-header {
  min-height: var(--nav-reserve);
}

/* ===========================================================================
   Plan your cruise (/enquire), 11 October 2026

   The enquiry form that stands in for the booking console while online booking is off
   (booking-mode.js). The form takes the wide column; the crew's numbers, the cruise times
   and the prices sit beside it, and drop below it under 900px.
   ========================================================================= */
.enquiry-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 0.7fr);
  gap: var(--space-5);
  width: min(var(--content-max), 100%);
  margin: 0 auto;
  align-items: start;
}

.enquiry-panel {
  gap: var(--space-4);
}

.enquiry-layout .panel-heading h2 {
  font-size: var(--text-22);
}

.enquiry-panel textarea {
  display: block;
  width: 100%;
  min-height: 96px;
  padding: var(--space-3);
  font: inherit;
  line-height: 1.5;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  resize: vertical;
}

.enquiry-panel textarea:focus-visible {
  outline: 3px solid var(--cognac);
  outline-offset: 3px;
}

/* "All day" for a charter: the field stays in place, visibly not in play. */
.enquiry-panel select:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.field-error {
  color: #ffd7d2;
  font-size: var(--text-12);
  line-height: 1.4;
}

.field-error:empty {
  display: none;
}

.enquiry-panel input[aria-invalid] {
  border-color: #ff9d90;
}

.enquiry-send {
  display: grid;
  gap: var(--space-3);
  padding-top: var(--space-2);
}

.enquiry-send .field-note {
  margin: 0;
}

.enquiry-send .field-note a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.enquiry-facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
}

.enquiry-facts h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-14);
  font-weight: 600;
}

.enquiry-times,
.enquiry-prices {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-14);
}

/* The name on its own line, then the price with its unit right after it. */
.enquiry-prices li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
}

.enquiry-prices {
  gap: var(--space-3);
}

.enquiry-prices span {
  flex-basis: 100%;
  color: var(--ink-muted);
}

.enquiry-prices small {
  color: var(--ink-faint);
  font-size: var(--text-12);
}

@media (max-width: 900px) {
  .enquiry-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 420px) {
  .enquiry-panel .field-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Three one-word options fit across a phone (about 100px each at 360px), so they stay in a
   row here rather than stacking into three full-width rows as the narrow-screen rule does. */
@media (max-width: 760px) {
  .enquiry-panel .segmented-control {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
