/* =========================================================
   IRAAVA — CINEMATIC HOMEPAGE
   ---------------------------------------------------------
   The dark "cinema" layer for index.html only. Everything
   here is scoped to `body.cinema` so the other 20 pages —
   which are light — are untouched by this file.

   READ THIS BEFORE CHANGING COLOURS
   styles.css owns the design system. This file DOES NOT
   redefine a single :root token. It derives a dark palette
   from them under body.cinema with a --cin-* prefix. In
   particular `--gold` is NOT gold on this site — it was
   repurposed to cerulean #2A8FA3 in the rebrand and drives
   .eyebrow, .h2 em and dividers on every light page. The warm
   gold the footage needs is --cin-gold, a new scoped value.
   Assigning it to --gold would restyle the whole site.

   STRUCTURE
   The page alternates full-viewport scrubbed video "acts"
   with solid interstitials that carry the platform story:

     hero(01) · how-booking-works · act I(02) · why-iraava ·
     act II(03) · live stays · offers · act III(04) ·
     for hosts · act IV(05) · act V(06) · closing CTA

   Each act is its own sticky stage rather than one shared
   reel, because static sections sit between them. An act
   therefore dissolves out to --cin-bg and the next dissolves
   in from it — a dissolve through black, which is a real
   film transition and the correct one when a scene sits
   between. Acts IV and V are the only consecutive pair.

   CONTRAST
   Every ratio below is measured, not estimated. Two rules
   fall out of the math and are easy to break by accident:
     · Teal #1E7A8C is a FILL only (white on it = 4.98:1).
       As ink on #0A1310 it is 3.79:1 and fails. Foreground
       teal lifts to #4FB3C7 (7.73:1).
     · There is no muted text over video. --on-dark-mute over
       a scrimmed frame measures 3.56:1. Over footage,
       secondary copy is full strength.
   ========================================================= */

body.cinema {
  /* ---- Grounds ---- */
  --cin-void:    #060C0A;   /* scrims — not pure black, which reads magenta
                               against this grade's teal shadows */
  --cin-bg:      #0A1310;   /* page ground — --emerald-deep driven down */
  --cin-surface: #12201A;   /* cards, panels */

  /* ---- Text ---- */
  --cin-text:       #F4F8F5;                  /* 17.59:1 on --cin-bg */
  --cin-mute:       rgba(234,242,237,0.72);   /* 8.87:1 — SOLID GROUNDS ONLY */
  --cin-on-video:   #EAF2ED;                  /* over footage, never dimmed */

  /* ---- Hairlines ---- */
  --cin-line:       rgba(234,242,237,0.10);
  --cin-line-hover: rgba(234,242,237,0.22);
  --cin-line-gold:  rgba(227,177,104,0.44);

  /* ---- Warm gold: marks VALUE (price, active chapter). Never a button. ---- */
  --cin-gold:      #E3B168;   /* 9.66:1 on --cin-bg, 8.62:1 on --cin-surface */
  --cin-gold-deep: #C8913F;
  --cin-gold-glow: rgba(227,177,104,0.26);

  /* ---- Teal: marks ACTION. Fill vs ink are different values, see header. ---- */
  --cin-teal:      #4FB3C7;   /* ink on dark — 7.73:1 */
  --cin-teal-fill: #1E7A8C;   /* = site --accent; white label = 4.98:1 */
  --cin-teal-deep: #155E6B;

  /* ---- Act geometry. Tune the page's total length here. ----
     lvh, deliberately, and never dvh: dvh changes as the mobile toolbar
     collapses, which changes an act's scroll travel *mid-scrub* and makes the
     footage visibly jump. lvh is a fixed number for a given orientation, so
     the scroll maths stays stable. The type inside is held to svh so it never
     hides under the toolbar — see .act__type. */
  --act-scroll:      200vh;   /* pinned scroll distance per act */
  --act-scroll-hero: 175vh;

  background: var(--cin-bg);
  color: var(--cin-text);
}

/* The lvh upgrade has to go through @supports rather than the usual
   two-declaration fallback. A custom property accepts any token, so on an
   engine without lvh the second declaration would simply win and hold an
   invalid length; the min-height consuming it then falls back to the
   property's initial value — auto — and every act collapses. */
@supports (height: 100lvh) {
  body.cinema { --act-scroll: 200lvh; --act-scroll-hero: 175lvh; }
}

/* The stage is dark before a single pixel of CSS below paints, so an
   image that loads slowly never flashes against white. */
html:has(body.cinema) { background: #0A1310; }

/* Note: there is deliberately no scroll-behavior override here. It only takes
   effect on the viewport's own scrolling element, so declaring it on body is
   inert, and styles.css's html{scroll-behavior:smooth} is harmless anyway —
   it governs fragment and programmatic scrolls only, never wheel or touch
   input, so it can never fight the scrub. Nothing on this page intercepts
   the scroll. */

/* Overscroll on iOS reveals the document background behind the page;
   without this it flashes white above the hero. */
body.cinema { overscroll-behavior-y: none; }


/* =========================================================
   1 · CHROME — nav, rail, cue
   ========================================================= */

/* The nav starts as a frame on the film: no bar, no blur. It only
   becomes a surface once the hero has been read. */
body.cinema .nav {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-bottom-color: transparent;
  box-shadow: none;
  transition: background .32s var(--ease), backdrop-filter .32s var(--ease),
              border-color .32s var(--ease), transform .36s var(--ease),
              padding .32s var(--ease);
}
/* .is-scrolled is deliberately NOT in this selector. script.js sets it at
   scrollY > 24, and treating it as solid would put the bar back a few pixels
   into the hero — the transparent-over-the-film state this page is built
   around would never be seen. home.js's .is-solid at 0.72 of the viewport is
   the intended trigger. */
body.cinema .nav.is-solid {
  background: rgba(10,19,16,0.62);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  backdrop-filter: blur(18px) saturate(140%);
  border-bottom: 1px solid var(--cin-line);
}
/* A bar parked across a composition during its hold is the fastest way
   to make cinema look like a template — so it leaves while you read. */
body.cinema .nav.is-away { transform: translateY(-100%); }

body.cinema .nav__links a,
body.cinema .nav__dropdown-trigger,
body.cinema .nav__auth,
body.cinema .nav__logo { color: var(--cin-text); }
body.cinema .nav__burger span { background: var(--cin-text); }

/* Site-wide, .nav__auth is a filled teal button — the same treatment as
   "Plan a Stay" next to it. Two identical fills over a full-bleed frame read as
   one heavy block and tell the eye nothing about which to press, so on this
   page only, Sign in steps back to an outline. The markup is untouched: the nav
   is hand-duplicated across twenty pages and must stay byte-identical. */
body.cinema .nav__auth {
  background: transparent;
  border-color: rgba(234,242,237,0.34);
  box-shadow: none;
}
body.cinema .nav__auth:hover {
  background: rgba(234,242,237,0.10);
  border-color: var(--cin-text);
  box-shadow: none;
}

/* The site's 4px top progress bar reads editorial-blog; this page uses a
   right-hand rail instead. */
body.cinema .scroll-progress { display: none; }

.cin-rail {
  position: fixed;
  right: 26px; top: 50%;
  translate: 0 -50%;
  width: 1px; height: 44vh;
  z-index: 60;
  background: var(--cin-line);
  opacity: 0;
  /* visibility, not just opacity. The rail holds six real <button>s; left at
     opacity 0 alone they stay focusable, so a keyboard user tabbing off the nav
     hits six invisible stops with no visible focus ring before ever reaching
     the search form. visibility:hidden takes them out of the tab order and
     still animates, because it is a discrete-but-animatable property. */
  visibility: hidden;
  pointer-events: none;
  transition: opacity .5s var(--ease), visibility .5s var(--ease);
}
.cin-rail.is-live {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}
.cin-rail__fill {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, var(--cin-gold), var(--cin-gold-deep));
  transform-origin: top;
  transform: scaleY(var(--rail, 0));   /* compositor-only, no layout */
}
.cin-rail__ticks {
  position: absolute; inset: 0;
  list-style: none; margin: 0; padding: 0;
}
.cin-rail__tick {
  position: absolute;
  right: 0;
  translate: 0 -50%;
}
.cin-rail__btn {
  display: flex; align-items: center; gap: 10px;
  background: none; border: 0; padding: 4px 0 4px 12px;
  cursor: pointer;
  font: 600 10.5px/1 var(--font-ui);
  letter-spacing: 0.24em;
  color: rgba(234,242,237,0.55);
  transition: color .28s var(--ease);
}
.cin-rail__btn::after {
  content: "";
  width: 7px; height: 1px;
  background: var(--cin-line);
  transition: width .28s var(--ease), height .28s var(--ease),
              background .28s var(--ease);
}
.cin-rail__tick.is-active .cin-rail__btn { color: var(--cin-text); }
.cin-rail__tick.is-active .cin-rail__btn::after {
  width: 14px; height: 2px;
  background: var(--cin-gold);
}
.cin-rail__btn:hover { color: var(--cin-text); }
.cin-rail__btn:focus-visible {
  outline: 2px solid var(--cin-teal);
  outline-offset: 3px;
}
.cin-rail__name {
  position: absolute;
  right: calc(100% + 14px); top: 50%;
  translate: 0 -50%;
  white-space: nowrap;
  font: italic 400 14px/1 var(--font-display);
  color: var(--cin-gold);
  opacity: 0;
  transition: opacity .24s var(--ease);
  pointer-events: none;
}
.cin-rail__tick.is-active .cin-rail__name { opacity: 1; }

/* Hero only, and it never comes back — a cue that reappears is a cue
   that failed. */
.cin-cue {
  position: absolute;
  bottom: 34px;
  left: clamp(24px, 6.5vw, 96px);
  display: grid; justify-items: start; gap: 12px;
  z-index: 5;
  transition: opacity .4s var(--ease);
}
.cin-cue.is-gone { opacity: 0; pointer-events: none; }
.cin-cue__label {
  font: 700 9.5px/1 var(--font-ui);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: rgba(234,242,237,0.72);
}
.cin-cue__line {
  position: relative;
  width: 1px; height: 44px;
  background: rgba(234,242,237,0.28);
  overflow: hidden;
}
.cin-cue__line::after {
  content: "";
  position: absolute; inset-inline: 0;
  height: 12px; top: -12px;
  background: var(--cin-gold);
  animation: cin-cue-drain 1900ms var(--ease) infinite;
}
@keyframes cin-cue-drain {
  0%   { top: -12px; opacity: 0; }
  30%  { opacity: 1; }
  70%  { opacity: 1; }
  100% { top: 44px;  opacity: 0; }
}


/* =========================================================
   2 · THE ACT — sticky stage + scrubbed video + type
   ========================================================= */

.act {
  position: relative;
  min-height: var(--act-scroll);
  /* Scroll anchoring reacts to a <video> gaining intrinsic dimensions at
     loadedmetadata by nudging the scroll position — mid-act that reads as the
     footage lurching. Off.
     Note there is deliberately no content-visibility here. It would be the
     obvious win for six full-viewport sections, but it applies size and paint
     containment, which fights the sticky pin inside and re-collapses the act's
     height as it leaves the viewport. The real cost on this page is video
     decoders, and home.js handles that by unloading clips that are out of
     range — a far bigger saving with none of the risk. */
  overflow-anchor: none;
}
.act--hero { min-height: var(--act-scroll-hero); }

/* The pinned frame. Everything inside is absolutely placed against it.
   No ancestor of this element may set overflow — that silently kills the pin.
   Notably it must never be nested inside .section--dark or .section--cream,
   both of which set overflow: hidden. */
.act__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100lvh;          /* see the note on --act-scroll above */
  overflow: clip;
  background: var(--cin-bg);
}

.act__video,
.act__poster {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* The frame's own slow push, written per frame by home.js. transform-origin
     is set opposite each act's type block so the push pulls the picture away
     from the words rather than into them. */
  transform: scale(var(--v-scale, 1));
  transform-origin: var(--v-origin, 50% 50%);
}
/* The poster sits under the video permanently — it is simultaneously the
   pre-decode frame, the no-JS fallback, the reduced-motion fallback, the
   save-data fallback and the 404 fallback. It is never merely a placeholder,
   which is why it is committed to git while the footage is not. */
.act__poster { z-index: 0; }
/* The video only appears once a frame is confirmed painted (is-ready is set
   from requestVideoFrameCallback). Revealing it any earlier shows one black
   frame, which on a full-bleed hero is very visible. */
.act__video {
  z-index: 1;
  opacity: 0;
  transition: opacity .45s linear;
}
.act__video.is-ready { opacity: 1; }

/* Each type block carries its OWN scrim. One global overlay would mud the
   whole film to keep a single paragraph legible. Alpha 0.55 at the type
   anchor holds #F4F8F5 at 5.53:1 against the brightest plausible pixel in
   this grade (a golden-hour blowout). */
.act__scrim {
  position: absolute; inset: 0;
  z-index: 2;
  pointer-events: none;
  background: var(--scrim, none), rgba(6,12,10,0.16);
}
.act--bottom { --scrim: linear-gradient(to top,
    rgba(6,12,10,0.78) 0%, rgba(6,12,10,0.55) 18%,
    rgba(6,12,10,0.24) 34%, rgba(6,12,10,0) 52%); }
.act--top    { --scrim: linear-gradient(to bottom,
    rgba(6,12,10,0.76) 0%, rgba(6,12,10,0.55) 17%,
    rgba(6,12,10,0.22) 33%, rgba(6,12,10,0) 50%); }
.act--side   { --scrim: linear-gradient(to left,
    rgba(6,12,10,0.72) 0%, rgba(6,12,10,0.48) 26%,
    rgba(6,12,10,0.14) 48%, rgba(6,12,10,0) 66%); }
.act--split  { --scrim: linear-gradient(to bottom,
    rgba(6,12,10,0.80) 0%,  rgba(6,12,10,0.34) 22%,
    rgba(6,12,10,0) 40%,    rgba(6,12,10,0) 58%,
    rgba(6,12,10,0.40) 76%, rgba(6,12,10,0.84) 100%); }

/* A brief warm bloom in the footage's own gold as an act arrives. The eye
   reads it as a light source passing rather than as a transition. */
.act__flare {
  position: absolute; inset: 0;
  z-index: 3;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: var(--flare, 0);
  background: radial-gradient(ellipse 120% 70% at 50% 55%,
    rgba(227,177,104,0.26) 0%, rgba(227,177,104,0.10) 38%, transparent 72%);
}

/* The stage is 100lvh so the footage still covers when the mobile toolbar
   collapses. Type is held to 100svh, anchored to the top, so it can never end
   up underneath that toolbar on load. */
.act__type {
  position: absolute;
  inset: 0 0 auto 0;
  height: 100vh;
  height: 100svh;
  z-index: 4;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  /* One full-height row. Without this the row is content-sized, so the
     align-self on .act__copy silently does nothing and every act ends up
     vertically centred — which also lifts the copy out of the strong part of
     its scrim. */
  grid-template-rows: 1fr;
  gap: 24px;
  padding-inline: clamp(24px, 6.5vw, 96px);
  padding-block: 152px 112px;   /* clears the nav, clears the rail band */
}
.act__copy {
  /* Set per act by the placement modifiers below. No two consecutive acts
     share a quadrant or an alignment axis — that variation is what keeps a
     viewer watching the film instead of predicting the layout. */
  position: relative;
  isolation: isolate;
  grid-column: var(--col, 3 / 11);
  align-self: var(--valign, center);
  justify-self: var(--halign, start);
  max-width: min(1240px, 87vw);
  /* Insurance under the scrim, not a replacement for it. */
  text-shadow: 0 1px 3px rgba(6,12,10,0.55), 0 12px 48px rgba(6,12,10,0.38);
}
.act--place-center { --col: 3 / 11; --valign: end;    --halign: center; }
.act--place-left   { --col: 1 / 7;  --valign: end;    --halign: start;  }
.act--place-right  { --col: 7 / 13; --valign: center; --halign: start;  }
.act--place-top    { --col: 3 / 11; --valign: start;  --halign: center; }
.act--place-topright { --col: 7 / 13; --valign: start; --halign: start; }

/* The finale is the only symmetrical composition on the page — headline at the
   top, CTA at the bottom, the glowing camp floating in the clear band between
   them. That symmetry is precisely what makes it read as an ending. */
.act--place-split .act__type {
  grid-template-rows: auto auto;
  align-content: space-between;
}
.act--place-split .act__copy {
  grid-column: 3 / 11;
  justify-self: center;
  text-align: center;
}

.act--place-center .act__copy,
.act--place-top .act__copy { text-align: center; }

/* THE LOCAL SCRIM — the single most important rule on this page.
   The directional scrims above are strongest at the frame's edges, which is
   where they unify the grade, but a type block sits in the middle of the frame
   where they have already fallen to zero. Measuring the real rendered backdrop
   under every block, at three points through every act, put 23 of 31 samples
   below WCAG — text that is readable for part of a clip and gone for the rest.
   That failure is invisible in a mockup and obvious in motion.

   So each block carries its own pool of shade, sized to itself and travelling
   with it, instead of relying on where it happens to sit. Peak alpha 0.68
   composites the brightest pixel measured in this footage (a golden-hour
   blowout, rgb(210,185,157)) down to roughly 7:1 for display type and comfort-
   ably past 4.5:1 for the eyebrow and lede. The falloff runs well past the text
   so it reads as a cinematographer's vignette rather than a dark box. */
.act__copy::before {
  content: "";
  position: absolute;
  /* The pool is far larger than the text it shades, and that is the whole
     trick. A radial gradient's radii are measured against the box, so radii
     bigger than 50% put the box edge partway down the falloff — the gradient
     gets cut off mid-slope and paints a hard-edged dark rectangle over the
     film. Radii of 50% with the last stop at 100% means the falloff finishes
     exactly at the edge and the corners fall outside the ellipse entirely, so
     there is no edge to see. Giving the pool roughly twice the text's height
     and 1.8x its width then keeps the plateau over the words while the visible
     falloff happens far out in empty frame. */
  inset: -52% -38%;
  z-index: -1;
  pointer-events: none;
  /* The centre stays at 50%. Sinking it to hide more of the falloff below the
     frame is tempting and measurably wrong: the eyebrow is the topmost line in
     the block and the one needing 4.5:1 rather than 3:1, so moving the plateau
     down failed six samples immediately. The shading is kept discreet by a
     lower peak instead — 0.70 still leaves every measured sample above its
     threshold with room to spare. */
  background: radial-gradient(ellipse 50% 50% at 50% 50%,
    rgba(6,12,10,0.73) 0%,
    rgba(6,12,10,0.71) 52%,
    rgba(6,12,10,0.60) 68%,
    rgba(6,12,10,0.31) 82%,
    rgba(6,12,10,0.08) 92%,
    rgba(6,12,10,0)    100%);
}
/* The finale's two blocks are short, so proportional insets barely bleed at
   all — they need far more. */
.act--place-split .act__copy::before { inset: -180% -40%; }

/* Every animated element is driven from one curve. --o and --y are written
   per frame by home.js; nothing here animates on a timer. */
.act__copy > * {
  opacity: var(--o, 1);
  transform: translate3d(0, var(--y, 0px), 0) scale(var(--s, 1));
  filter: blur(var(--b, 0px));
}


/* =========================================================
   3 · CINEMA TYPE
   ========================================================= */

.cin-eyebrow {
  display: block;
  margin: 0 0 clamp(16px, 1.6vw, 24px);
  font: 700 clamp(10.5px, 0.78vw, 12.5px)/1 var(--font-ui);
  letter-spacing: 0.28em;   /* wider than the site's 0.20em: over cinema,
                               wide-tracked small caps read as a title card */
  text-transform: uppercase;
  color: var(--cin-on-video);
}
.cin-eyebrow--gold { color: var(--cin-gold); }

.cin-display {
  margin: 0;
  font: 500 clamp(52px, 9.2vw, 150px)/0.94 var(--font-display);
  letter-spacing: -0.022em;   /* Playfair's sidebearings are set for text
                                 sizes; at 130px the default reads loose */
  color: var(--cin-text);
  text-wrap: balance;
  max-width: 16ch;
}
.act--place-center .cin-display,
.act--place-top .cin-display { margin-inline: auto; }

.cin-headline {
  margin: 0;
  font: 500 clamp(38px, 5.6vw, 84px)/1.02 var(--font-display);
  letter-spacing: -0.014em;
  color: var(--cin-text);
  text-wrap: balance;
  max-width: 18ch;
}
/* Italic over video is solid, never the site's gradient-clipped treatment —
   a gradient glyph has no single measurable contrast value. */
.cin-display em,
.cin-headline em {
  font-style: italic;
  color: var(--cin-text);
  background: none;
  -webkit-text-fill-color: currentColor;
}

.cin-lede {
  margin: clamp(18px, 2vw, 28px) 0 0;
  font: 400 clamp(16px, 1.18vw, 20px)/1.66 var(--font-ui);
  letter-spacing: 0.005em;
  color: var(--cin-on-video);   /* full strength: muted fails over footage */
  max-width: 46ch;
  text-wrap: pretty;
}
.act--place-center .cin-lede,
.act--place-top .cin-lede { margin-inline: auto; }

.cin-quote {
  display: block;
  margin: clamp(20px, 2.4vw, 32px) 0 0;
  font: italic 400 clamp(19px, 1.9vw, 27px)/1.3 var(--font-display);
  color: var(--cin-text);
  max-width: 24ch;
}
.act--place-center .cin-quote,
.act--place-top .cin-quote { margin-inline: auto; }

.cin-actions {
  display: flex; flex-wrap: wrap; gap: 14px;
  margin-top: clamp(24px, 2.8vw, 38px);
}
.act--place-center .cin-actions,
.act--place-top .cin-actions { justify-content: center; }

/* Buttons keep the site's shape and fill. The moment of transaction should
   feel like the rest of Iraava, not like the film. */
body.cinema .btn--primary {
  background: var(--cin-teal-fill);
  color: #fff;
  box-shadow: none;
}
body.cinema .btn--primary:hover { background: var(--cin-teal-deep); }
.cin-btn-ghost {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 14px 30px;
  border: 1px solid rgba(234,242,237,0.42);
  border-radius: 2px;
  font: 700 13px/1 var(--font-ui);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cin-text);
  text-decoration: none;
  transition: border-color .3s var(--ease), background .3s var(--ease);
}
.cin-btn-ghost:hover {
  border-color: var(--cin-text);
  background: rgba(234,242,237,0.06);
}
/* Gold-outlined ghost is legal on the solid page ground only — gold on
   scrimmed footage measures 3.03:1. */
.cin-section .cin-btn-ghost {
  border-color: var(--cin-line-gold);
  color: var(--cin-gold);
}
.cin-section .cin-btn-ghost:hover {
  border-color: var(--cin-gold);
  background: rgba(227,177,104,0.08);
}

.cin-link {
  display: inline-flex; align-items: center; gap: 8px;
  font: 600 12.5px/1 var(--font-ui);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cin-teal);
  text-decoration: none;
  padding-bottom: 4px;
  border-bottom: 1px solid currentColor;
  transition: color .25s var(--ease), gap .25s var(--ease);
}
.cin-link:hover { gap: 14px; }
.cin-link--onvideo { color: var(--cin-text); }

body.cinema a:focus-visible,
body.cinema button:focus-visible,
body.cinema [tabindex]:focus-visible {
  outline: 2px solid var(--cin-teal);
  outline-offset: 3px;
  border-radius: 2px;
}


/* =========================================================
   4 · HERO
   ========================================================= */

.act--hero .act__stage { height: 100vh; height: 100lvh; }

/* The hero is the one act with furniture at the bottom of the frame — the
   search bar and its micro line. Its type is centred in the space *above*
   that, not bottom-aligned into it. */
.act--hero .act__type { padding-block: 132px clamp(230px, 30vh, 300px); }
.act--hero .act__copy { align-self: center; }

/* The hero is the one act whose type must NOT be driven in by scroll: the
   visitor arrives at progress 0, so a scroll-linked entrance would greet them
   with an empty frame. It plays its own entrance on load instead, and home.js
   only ever drives its exit.
   `backwards`, not `both`: an animation outranks inline styles, so a forwards
   fill would permanently pin opacity and the scroll-driven exit would never be
   visible. With backwards the element hands control back once it has run. */
@keyframes cin-hero-in {
  from { opacity: 0; transform: translate3d(0, 34px, 0); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}
.has-js .act--hero [data-anim] {
  animation: cin-hero-in 1s var(--ease) backwards;
}
.has-js .act--hero [data-anim]:nth-child(2) { animation-delay: 110ms; }
.has-js .act--hero [data-anim]:nth-child(3) { animation-delay: 220ms; }
.has-js .cin-search { animation: cin-hero-in 1s var(--ease) 330ms backwards; }
@media (prefers-reduced-motion: reduce) {
  .has-js .act--hero [data-anim],
  .has-js .cin-search { animation: none; }
}

.cin-hero__copy {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 0;
}
.cin-hero__promise {
  margin: clamp(20px, 2.2vw, 30px) auto 0;
  font: 400 clamp(16px, 1.25vw, 21px)/1.6 var(--font-ui);
  color: var(--cin-on-video);
  max-width: 44ch;
}

/* The search bar is the booking funnel and must not be decorative. It reuses
   the site's .searchbar internals verbatim so searchfields.js + datepicker.js
   attach to it unchanged; only the shell is restyled for dark. */
.cin-search {
  position: absolute;
  left: 50%;
  /* 104px looked better but cost the date panel its headroom: flipped upward on
     a 1280x800 screen it slid under the nav. 76px keeps the calendar clear
     without the bar reading as stuck to the edge. */
  bottom: 76px;
  translate: -50% 0;
  z-index: 6;
  width: min(1080px, calc(100vw - 48px));
}
body.cinema .cin-search .searchbar {
  position: static;
  border-top: 0;
  border-radius: 3px;
  background: rgba(10,19,16,0.58);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
  backdrop-filter: blur(22px) saturate(150%);
  border: 1px solid rgba(234,242,237,0.16);
  /* No overflow:hidden here, ever. The Where, dates and Guests panels are
     absolutely-positioned descendants of this element; clipping the box to
     round off the submit button's corners also clips all three popovers out of
     existence. The corners are handled on the submit itself instead. */
}
body.cinema .cin-search .searchbar__submit { border-radius: 0 3px 3px 0; }

/* The date panel opens upward on this page. styles.css pins .dp-panel to
   `top: calc(100% + 10px)` with no flip — searchfields.js gained an `.is-above`
   escape for the Where and Guests popovers, but the older datepicker never did.
   On a bar that sits near the bottom of the hero the calendar then runs ~370px
   past the fold, taking its Apply button with it. The bar's position here is
   fixed and known, so the flip can simply be declared rather than measured.
   Width-and-height gated: on a short viewport there is no room above either,
   and the default downward placement is the lesser evil. */
@media (min-width: 721px) and (min-height: 640px) {
  body.cinema .cin-search .dp-panel {
    top: auto;
    bottom: calc(100% + 10px);
  }
}

@media (min-width: 721px) {
  body.cinema .cin-search .searchbar__inner { grid-template-columns: 1.4fr 1.4fr 0.9fr auto; }
  body.cinema .cin-search .searchbar__submit { grid-column: auto; }
  body.cinema .cin-search .searchbar__field,
  body.cinema .cin-search .searchbar__inner > .wh,
  body.cinema .cin-search .searchbar__inner > .dp { border-bottom: 0; }
  body.cinema .cin-search .searchbar__inner > .gs { border-bottom: 0; border-right: 1px solid rgba(234,242,237,0.14); }
}

/* Whichever way a panel opens, it must never be taller than the space it has.
   On a short desktop viewport (1024x768, say) the two-column bar sits low and
   the calendar is ~495px — it fits neither above nor below, and without a cap
   its Apply button is simply unreachable. Capping the height and letting the
   panel scroll internally means every control stays reachable at every size;
   overscroll-behavior keeps that scroll from chaining into the page and
   dragging the film underneath. */
@media (min-width: 721px) {
  body.cinema .cin-search .dp-panel,
  body.cinema .cin-search .wh-panel,
  body.cinema .cin-search .gs-panel {
    max-height: calc(100svh - 150px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

/* styles.css lifts bot.js's launcher by exactly --searchbar-h below 1024px,
   assuming the bar is flush with the viewport bottom as it was on the old hero.
   Here it floats, so the lift is short by that offset and the launcher lands on
   the bar. Add the offset back over the range where both rules are live. */
@media (min-width: 721px) {
  body.cinema.searchbar-on-screen .bot-btn {
    bottom: calc(28px + 76px + var(--searchbar-h, 96px));
  }
}

/* -------------------------------------------------------------
   SIGN-UP NUDGE, on the film

   signup-nudge.js reads its colours as var(--brand-token, <literal>)
   against styles.css, which this page also loads — so left alone the
   card arrives as a white rectangle on the footage. The film is dark in
   BOTH themes by design and carries its own fixed palette, so it has to
   say so here rather than relying on html[data-theme]. Same arrangement
   the Next app makes in web/app/globals.css, with --cin-* in place of
   the product tokens.

   The split between fill and ink is the point: --cin-teal-fill is the
   film's button ground (its search submit, its cards) and carries a
   white label at 4.98:1, while as TEXT on --cin-surface it would be
   3.4:1. So the button keeps the teal and the link and the mark take
   --cin-gold, which is 8.62:1 on that ground — the same division the
   film already makes everywhere else.
   ------------------------------------------------------------- */
body.cinema .signup-nudge {
  --paper: var(--cin-surface);
  --line: var(--cin-line);
  --sand: var(--cin-line);          /* the close button's hover wash */
  --ink: var(--cin-text);
  --ink-strong: var(--cin-text);
  --mute: var(--cin-mute);

  --accent: var(--cin-teal-fill);   /* the button ground */
  --accent-deep: var(--cin-teal-deep);
  --on-accent: #ffffff;
  --accent-ink: var(--cin-gold);    /* the link, the mark, the focus ring */

  box-shadow: 0 24px 64px rgba(6, 12, 10, 0.62);
}

/* The card copies bot.css's searchbar lift, so it needs the same correction
   the launcher gets above — the bar floats here rather than sitting flush. */
@media (min-width: 721px) {
  body.cinema.searchbar-on-screen .signup-nudge {
    bottom: calc(28px + 76px + var(--searchbar-h, 96px));
  }
}
/* Must lose to nothing: while body.dp-locked is set the sheet is position:fixed
   and a backdrop-filter on an ancestor would confine it to the bar. */
body.cinema.dp-locked .cin-search .searchbar {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
body.cinema .cin-search .searchbar__inner { padding: 0; }
body.cinema .cin-search .searchbar__field,
body.cinema .cin-search .searchbar__inner > .wh,
body.cinema .cin-search .searchbar__inner > .dp,
body.cinema .cin-search .searchbar__inner > .gs {
  border-right-color: rgba(234,242,237,0.14);
}
body.cinema .cin-search .searchbar__label { color: rgba(234,242,237,0.66); }
body.cinema .cin-search .searchbar__input { color: var(--cin-text); }
body.cinema .cin-search .searchbar__input::placeholder { color: rgba(234,242,237,0.5); }
body.cinema .cin-search .searchbar__input option { color: var(--ink-strong); }
body.cinema .cin-search .searchbar__submit { background: var(--cin-teal-fill); }
body.cinema .cin-search .searchbar__submit:hover { background: var(--cin-teal-deep); }
/* The popover triggers stand in for .searchbar__field, so they carry its ink. */
body.cinema .cin-search .wh-trigger,
body.cinema .cin-search .dp-trigger,
body.cinema .cin-search .gs-trigger { color: var(--cin-text); }
body.cinema .cin-search .wh-trigger__key,
body.cinema .cin-search .dp-trigger__key,
body.cinema .cin-search .gs-trigger__key { color: rgba(234,242,237,0.66); }

.cin-hero__micro {
  margin: 14px 0 0;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 8px 18px;
  font: 600 11.5px/1 var(--font-ui);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(234,242,237,0.78);
  text-shadow: 0 1px 3px rgba(6,12,10,0.7);
}
.cin-hero__micro a { color: var(--cin-text); }


/* =========================================================
   5 · INTERSTITIALS — solid ground between the acts
   ========================================================= */

.cin-section {
  position: relative;
  padding-block: clamp(88px, 9vw, 148px);
  background: var(--cin-bg);
}
.cin-section__inner {
  width: min(var(--container), 100% - clamp(40px, 8vw, 112px));
  margin-inline: auto;
}
.cin-section--tint { background: linear-gradient(180deg, #0A1310, #0C1714 55%, #0A1310); }

.cin-head { max-width: 720px; margin-bottom: clamp(44px, 5vw, 72px); }
.cin-head--center { margin-inline: auto; text-align: center; }
.cin-head .cin-eyebrow { color: var(--cin-gold); }
.cin-h2 {
  margin: 0;
  font: 500 clamp(30px, 3.4vw, 52px)/1.06 var(--font-display);
  letter-spacing: -0.012em;
  color: var(--cin-text);
  text-wrap: balance;
}
/* On the solid ground gold is legal (9.66:1), and it is the payoff line. */
.cin-h2 em { font-style: italic; color: var(--cin-gold); }
.cin-sub {
  margin: clamp(16px, 1.6vw, 22px) 0 0;
  font: 400 clamp(15.5px, 1.05vw, 18px)/1.72 var(--font-ui);
  color: var(--cin-mute);
  max-width: 56ch;
}
.cin-head--center .cin-sub { margin-inline: auto; }

/* Numbered steps — the booking mechanic. It is a real <ol> so the order is in
   the semantics, but the markers are drawn by .cin-step__num; without this the
   browser's own "1." sits next to the designed "01". */
.cin-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(28px, 3vw, 48px);
}
.cin-step__num {
  display: block;
  font: 700 12px/1 var(--font-ui);
  letter-spacing: 0.24em;
  color: var(--cin-gold);
  margin-bottom: 18px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--cin-line);
}
.cin-step__title {
  margin: 0 0 10px;
  font: 500 clamp(19px, 1.5vw, 24px)/1.2 var(--font-display);
  color: var(--cin-text);
}
.cin-step__body {
  margin: 0;
  font: 400 15.5px/1.7 var(--font-ui);
  color: var(--cin-mute);
}
.cin-note {
  margin: clamp(36px, 4vw, 56px) 0 0;
  padding-top: 24px;
  border-top: 1px solid var(--cin-line);
  font: 400 14px/1.7 var(--font-ui);
  color: var(--cin-mute);
  max-width: 62ch;
}

/* Pillars — the curation argument */
.cin-pillars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: clamp(28px, 3vw, 44px);
}
.cin-pillar__num {
  display: block;
  font: italic 400 22px/1 var(--font-display);
  color: var(--cin-gold);
  margin-bottom: 14px;
}
.cin-pillar__title {
  margin: 0 0 10px;
  font: 600 15px/1.4 var(--font-ui);
  letter-spacing: 0.02em;
  color: var(--cin-text);
}
.cin-pillar__body { margin: 0; font: 400 15px/1.7 var(--font-ui); color: var(--cin-mute); }

/* Facts — the host proposition */
.cin-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: clamp(24px, 2.6vw, 40px);
  margin-top: clamp(40px, 4vw, 60px);
}
.cin-fact__k {
  display: block;
  font: 700 11.5px/1 var(--font-ui);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--cin-gold);
  margin-bottom: 12px;
}
.cin-fact__v { margin: 0; font: 400 15px/1.7 var(--font-ui); color: var(--cin-mute); }

.cin-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(40px, 5vw, 88px);
  align-items: start;
}
@media (max-width: 900px) { .cin-split { grid-template-columns: 1fr; } }


/* =========================================================
   6 · LIVE INVENTORY BAND
   ========================================================= */

/* Commerce sits inside the same world: the finale grade blurred behind it,
   sealed top and bottom so it never reads as a different page. */
.cin-band { position: relative; overflow: clip; }
.cin-band__bg {
  position: absolute; inset: -60px;   /* bleed, so the blur has no soft edge */
  background: var(--band-img, none) center / cover no-repeat;
  filter: blur(40px) brightness(0.32) saturate(0.85);
  opacity: 0.5;
  pointer-events: none;
}
.cin-band__seal {
  position: absolute; inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg,
    var(--cin-bg) 0%, rgba(10,19,16,0.86) 40%,
    rgba(10,19,16,0.92) 72%, var(--cin-bg) 100%);
}
.cin-band > .cin-section__inner { position: relative; z-index: 1; }

.cin-chips {
  display: flex; flex-wrap: wrap; gap: 10px;
  margin-bottom: clamp(28px, 3vw, 40px);
}
.cin-chip {
  padding: 9px 18px;
  border: 1px solid var(--cin-line);
  border-radius: 999px;
  background: transparent;
  font: 600 12px/1 var(--font-ui);
  letter-spacing: 0.1em;
  color: var(--cin-mute);
  cursor: pointer;
  transition: border-color .25s var(--ease), color .25s var(--ease),
              background .25s var(--ease);
}
.cin-chip:hover { color: var(--cin-text); border-color: var(--cin-line-hover); }
.cin-chip.is-active {
  background: var(--cin-teal-fill);
  border-color: var(--cin-teal-fill);
  color: #fff;
}

/* A rail, not a grid — this page is a trailer; the catalogue is /stays. */
.cin-rail-cards {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(300px, 1fr);
  gap: 24px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: 8px;
  scrollbar-width: thin;
  scrollbar-color: rgba(234,242,237,0.22) transparent;
}
@media (min-width: 1000px) {
  .cin-rail-cards {
    grid-auto-flow: row;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-auto-columns: auto;
    overflow-x: visible;
  }
}
.cin-rail-cards::-webkit-scrollbar { height: 4px; }
.cin-rail-cards::-webkit-scrollbar-thumb { background: rgba(234,242,237,0.22); }

.cin-card {
  scroll-snap-align: start;
  display: flex; flex-direction: column;
  background: var(--cin-surface);
  border: 1px solid var(--cin-line);
  border-radius: 4px;             /* editorial, not app */
  background-image: linear-gradient(180deg, rgba(234,242,237,0.045) 0%, transparent 90px);
  overflow: hidden;
  text-decoration: none;
  /* No box-shadow, ever: on #12201A over #0A1310 a drop shadow is invisible
     as depth and visible as grey mud. Elevation is the hairline + top light. */
  transition: transform .5s var(--ease), border-color .5s var(--ease);
}
.cin-card:hover { transform: translateY(-3px); border-color: var(--cin-line-hover); }
.cin-card__media { position: relative; aspect-ratio: 4 / 3; overflow: hidden; }
.cin-card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .7s var(--ease);
}
.cin-card:hover .cin-card__media img { transform: scale(1.04); }
.cin-card__badge {
  position: absolute; left: 12px; top: 12px;
  padding: 6px 12px;
  background: rgba(6,12,10,0.72);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border-radius: 2px;
  font: 700 10px/1 var(--font-ui);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--cin-text);
}
.cin-card__body {
  padding: 22px 22px 24px;
  display: flex; flex-direction: column; gap: 8px;
  flex: 1;                 /* fill the card so the foot can sit at its base */
}
.cin-card__loc {
  font: 600 12px/1.5 var(--font-ui);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(234,242,237,0.62);
}
.cin-card__name {
  margin: 0;
  font: 500 clamp(19px, 1.5vw, 24px)/1.16 var(--font-display);
  color: var(--cin-text);
}
.cin-card__meta { margin: 0; font: 400 13.5px/1.6 var(--font-ui); color: var(--cin-mute); }
.cin-card__foot {
  margin-top: auto; padding-top: 16px;
  border-top: 1px solid var(--cin-line);
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
}
/* Gold appears on the price and nowhere else in this band. That scarcity is
   what makes it read as precious rather than as a highlighter. */
.cin-card__price { font: 700 15px/1.3 var(--font-ui); color: var(--cin-gold); white-space: nowrap; }
.cin-card__price span {
  font-weight: 400; font-size: 12px;
  color: var(--cin-mute); letter-spacing: 0.06em;
}
.cin-card__cta {
  font: 600 11.5px/1 var(--font-ui);
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--cin-teal);
  white-space: nowrap;
}

.cin-state {
  grid-column: 1 / -1;   /* it is a grid item of .cin-rail-cards, not a block */
  padding: clamp(40px, 5vw, 72px) 0;
  text-align: center;
  color: var(--cin-mute);
  font: 400 15.5px/1.7 var(--font-ui);
}
.cin-state strong {
  display: block;
  margin-bottom: 8px;
  font: 500 clamp(20px, 2vw, 27px)/1.2 var(--font-display);
  color: var(--cin-text);
}
.cin-band__foot {
  margin-top: clamp(32px, 3.4vw, 48px);
  display: flex; flex-wrap: wrap; align-items: center; gap: 16px 28px;
}
.cin-band__note { margin: 0; font: 400 13.5px/1.6 var(--font-ui); color: var(--cin-mute); }

/* Skeletons while the fetch is in flight — the band reserves its height so
   the sections below never jump (CLS). */
.cin-card--skeleton { pointer-events: none; }
.cin-card--skeleton .cin-card__media { background: rgba(234,242,237,0.06); }
.cin-card--skeleton .cin-sk {
  height: 12px; border-radius: 2px;
  background: rgba(234,242,237,0.08);
}
.cin-card--skeleton .cin-sk--lg { height: 20px; width: 70%; }
.cin-card--skeleton .cin-sk--sm { width: 45%; }
@media (prefers-reduced-motion: no-preference) {
  .cin-card--skeleton .cin-card__media,
  .cin-card--skeleton .cin-sk { animation: cin-pulse 1.6s var(--ease) infinite; }
}
@keyframes cin-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.45; } }


/* =========================================================
   7 · CLOSING + FOOTER TRIM
   ========================================================= */

.cin-closing { text-align: center; }
.cin-closing .cin-h2 { max-width: 20ch; margin-inline: auto; }
/* .cin-sub is width-capped for measure; centring the text inside a box that is
   still flush left just moves the ragged edge around. Centre the box too. */
.cin-closing .cin-sub { margin-inline: auto; }
.cin-closing .cin-actions { justify-content: center; margin-top: clamp(32px, 3.4vw, 46px); }
.cin-closing__micro {
  margin: clamp(32px, 3.4vw, 46px) 0 0;
  font: 400 14px/1.8 var(--font-ui);
  color: var(--cin-mute);
}
.cin-closing__micro a { color: var(--cin-teal); text-decoration: none; }
.cin-closing__micro a:hover { text-decoration: underline; }

/* The footer is already forest-green; nudge it to the cinema ground so the
   page ends in one tone instead of two. */
body.cinema .footer { background: #0A1310; border-top: 1px solid var(--cin-line); }

/* Skip link — the page is long and pinned, so a keyboard user must be able
   to reach the booking funnel without scrubbing through five acts. */
.cin-skip {
  position: absolute;
  left: 16px; top: -60px;
  z-index: 200;
  padding: 12px 20px;
  background: var(--cin-teal-fill);
  color: #fff;
  font: 700 13px/1 var(--font-ui);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  border-radius: 2px;
  transition: top .2s var(--ease);
}
.cin-skip:focus { top: 16px; }


/* =========================================================
   8 · RESPONSIVE
   ========================================================= */

/* Below 900px the rail has nowhere to live; it becomes a top bar. */
@media (max-width: 900px) {
  .cin-rail {
    right: auto; top: 0; left: 0;
    translate: none;
    width: 100%; height: 2px;
  }
  .cin-rail__ticks { display: none; }
  .cin-rail__fill {
    transform-origin: left;
    transform: scaleX(var(--rail, 0));
    background: linear-gradient(90deg, var(--cin-teal-fill), var(--cin-gold));
  }
}

/* Below this the search bar reaches the side margins and the cue would sit on
   top of it. */
@media (max-width: 1180px) { .cin-cue { display: none; } }

@media (max-width: 720px) {
  /* Acts stop pinning. Six pinned, decoding videos is more than a phone
     should be asked to hold, and a 45% hold on a small screen just reads as
     a stall. Each act becomes one screen with its type at rest. */
  body.cinema { --act-scroll: 100vh; --act-scroll: 100lvh;
                --act-scroll-hero: 100vh; --act-scroll-hero: 100lvh; }
  .act__stage { position: relative; }
  .act__type {
    padding-inline: clamp(20px, 6vw, 28px);
    padding-block: 108px 88px;
    align-content: end;
  }
  .act__copy {
    grid-column: 1 / -1 !important;
    justify-self: start !important;
    text-align: left !important;
  }
  .act--place-center .cin-display,
  .act--place-top .cin-display,
  .act--place-center .cin-lede,
  .act--place-top .cin-lede,
  .act--place-center .cin-quote,
  .act--place-top .cin-quote { margin-inline: 0; }
  .act--place-center .cin-actions,
  .act--place-top .cin-actions { justify-content: flex-start; }

  .cin-display { font-size: clamp(44px, 12vw, 64px); max-width: 14ch; }
  .cin-headline { font-size: clamp(32px, 9vw, 46px); }
  .cin-lede { max-width: 34ch; }

  /* The search bar stays absolutely positioned here, rather than dropping into
     the flow. In flow its margin-top collapses straight out through .act__stage
     — overflow: clip, unlike hidden, does not establish a block formatting
     context — which pushed the entire document down by that margin and left a
     band of background above the hero. It also landed behind the absolutely
     positioned type, hiding the one element on this screen that starts a
     booking. */
  /* Flush to the bottom edge, exactly as the old hero's bar was. Not just for
     the extra width: styles.css offsets bot.js's chat launcher by
     --searchbar-h assuming the bar ends at the viewport bottom, so any gap
     underneath it puts the launcher back on top of the first field. */
  .cin-search {
    left: 0; right: 0;
    bottom: 0;
    /* The stage is 100lvh so the poster still covers when the toolbar retracts,
       but the visible viewport on load is svh — anchoring to the stage's bottom
       puts the submit button below the fold on every iPhone. Lift by exactly
       the toolbar band. .act__type solves the same problem by being 100svh
       tall; .cin-search is its sibling, not its child, so it cannot inherit
       that and needs its own answer. */
    bottom: calc(100lvh - 100svh);
    translate: none;
    width: auto;
    margin-top: 0;
  }
  body.cinema .cin-search .searchbar { border-radius: 0; border-inline: 0; border-bottom: 0; }
  .cin-hero__micro { padding-inline: clamp(20px, 6vw, 28px); }
  /* styles.css puts the bar on a 2-up grid below 1024px, which assumes the
     full-bleed width the old hero gave it. Inset by a side margin here, the
     date field — the widest of the three, since it carries both check-in and
     check-out — overflows its half column and gets clipped. One field per row
     also makes the tab order match the visual order. */
  body.cinema .cin-search .searchbar__inner { grid-template-columns: 1fr; }
  body.cinema .cin-search .searchbar__field,
  body.cinema .cin-search .searchbar__inner > .wh,
  body.cinema .cin-search .searchbar__inner > .dp,
  body.cinema .cin-search .searchbar__inner > .gs { border-right: 0; }
  /* Stacked, the desktop field padding makes the bar 322px — nearly 40% of a
     phone screen, which is not a hero any more. Tightened, it is ~220px and the
     film keeps the frame. Touch targets stay above 44px. */
  body.cinema .cin-search .searchbar__inner > .wh > .wh-trigger,
  body.cinema .cin-search .searchbar__inner > .dp > .dp-trigger,
  body.cinema .cin-search .searchbar__inner > .gs > .gs-trigger,
  body.cinema .cin-search .searchbar__field { padding: 13px 18px; }
  body.cinema .cin-search .searchbar__submit { padding: 17px; }

  /* Clear the bar and its micro line. --searchbar-h is the bar's measured
     offsetHeight, published on <html> by script.js (it already uses it to lift
     the chat launcher clear of the same bar). Reading it rather than hard-coding
     a number means the headline keeps its clearance when the bar's height moves
     — a fifth field, a longer label, a different font landing late. The 96px
     covers the micro line under the bar plus its bottom offset; the fallback
     matches the current stacked height for the moment before script.js runs. */
  .act--hero .act__type {
    padding-block: 96px calc(var(--searchbar-h, 278px) + 96px);
  }
  .act--hero .act__copy { align-self: end; }
  .cin-hero__copy { text-align: left; justify-items: start; }
  /* The micro line moves above the bar so the bar is the bottom-most thing on
     screen. styles.css lifts bot.js's chat launcher by --searchbar-h, which
     measures the bar alone — with the micro line underneath it, that offset
     lands the launcher on top of the Where field. Reordering keeps the site's
     existing collision maths correct instead of overriding it here, and the
     reassurance reads before the form rather than after it. */
  .cin-search { display: flex; flex-direction: column; }
  .cin-hero__micro { order: -1; justify-content: flex-start; margin: 0 0 14px; }
  .cin-cue { display: none; }
}


/* =========================================================
   9 · DEGRADATION
   ========================================================= */

/* Reveals on the solid sections are opt-IN, not opt-out: an element is visible
   by default and only becomes animatable once home.js — the file that also
   observes it — has actually run. The usual `.reveal { opacity: 0 }` pattern
   inverts that, so a single failed script request leaves half the page
   permanently invisible. Everything here is below the fold, so there is no
   flash to trade against. */
.has-js [data-reveal] {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .8s var(--ease), transform .8s var(--ease);
}
.has-js [data-reveal].is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .has-js [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* No JS: home.js never runs, so --o/--y are never written. Everything must
   already be at its resting position, and the videos (which only ever get a
   src from JS) stay invisible behind their posters. This block is the
   difference between "cinematic" and "a blank dark page". */
.no-js .act__copy > *,
.no-js .cin-search { opacity: 1; transform: none; filter: none; }
.no-js .act__stage { position: relative; }
.no-js .act { min-height: 100vh; min-height: 100lvh; }
.no-js .cin-rail, .no-js .cin-cue { display: none; }
.no-js .act__video { display: none; }
/* With JS, searchfields.js collapses the two date inputs into one trigger and
   the bar is exactly four cells wide. Without it there are five native
   controls, so the submit wraps onto a second row and lands in the first
   column looking like a mistake. Let it span instead. */
.no-js .cin-search .searchbar__submit { grid-column: 1 / -1; }

@media (prefers-reduced-motion: reduce) {
  /* Posters only, no scrubbing, no pinning, nothing in motion. The page
     stays fully readable and fully bookable with every video inert. */
  body.cinema { --act-scroll: 100vh; --act-scroll: 100lvh;
                --act-scroll-hero: 100vh; --act-scroll-hero: 100lvh; }
  .act__stage { position: relative; }
  .act__video { display: none; }
  .act__flare { display: none; }
  .act__copy > * { opacity: 1 !important; transform: none !important; filter: none !important; }
  .cin-search { opacity: 1 !important; transform: none !important; }
  /* home.js writes --v-scale unconditionally, so the poster's slow push has to
     be neutralised here rather than left to the engine. */
  .act__poster, .act__video { transform: none !important; }
  .cin-cue__line::after { animation: none; top: 16px; }
  .cin-card, .cin-card__media img { transition: none; }
  .cin-card:hover { transform: none; }
  .cin-card--skeleton .cin-sk { animation: none; }
  body.cinema .nav.is-away { transform: none; }
}

/* Windows high-contrast / forced colours: scrims and blurs carry no meaning
   there, and a forced background behind a video would hide it entirely. */
@media (forced-colors: active) {
  .act__scrim, .act__flare, .cin-band__bg, .cin-band__seal { display: none; }
  .cin-card { border: 1px solid CanvasText; }
}
