/**
 * Oceaneros - Cinematic Scroll Engine, structural layer
 *
 * Owns only what the engine needs to be correct: the pinned viewport box, the
 * adapter mount, and the two states the engine switches between. Responsive
 * refinement and the reduced-motion presentation pass are A3.
 *
 * Two states, driven by data-oe-cinematic-mode on the root:
 *
 *   static  - the default in the HTML, and the permanent state for
 *             reduced-motion visitors, for browsers that cannot run either
 *             adapter, and for anyone whose JS never arrives. The poster frame
 *             renders and every beat's copy is laid out in normal document
 *             flow, stacked and legible. Nothing is hidden behind an animation
 *             that may never start.
 *
 *   scroll  - set by the engine only after an adapter has actually painted a
 *             frame. The viewport becomes a full-height stage that GSAP pins,
 *             and the beats become overlays whose opacity the timeline owns.
 *
 * Spec: OCEANEROS-MASTER-BUILD-SPEC.md section 4, phase A2.
 */

/* Declared at EVERY level that consumes them, deliberately.
   Custom properties inherit downward only, and this bit twice:
     1. Declared on .oe-cine alone, .oe-cine-capture is its *sibling* — so the
        capture section resolved every var() to nothing: transparent background
        over the theme's cream body (white text on cream), no button blue, no
        coral focus ring.
     2. Then declared on .oe-cine-page, but <body> is its *ancestor* — so the
        body background resolved to transparent and the page showed a white band
        below the content.
   Redeclaring the same values at each level is free and removes the whole class
   of error; a var() that resolves to nothing fails silently, which is why both
   of these looked like styling that had simply not been written. */
.oe-cine-body,
.oe-cine-page,
.oe-cine,
.oe-cine-capture {
  /* Cinematic/photographic content carries the LOGO palette; interface and
     line-art content carries the --oe-* interface tokens. Per spec 3b these are
     two deliberate systems and must not be corrected into each other. This page
     is cinematic, so the accents are the logo blues and coral, mirroring the
     naming already used in oceaneros-homepage-experience.css.

     The ground is --oe-surface-abyss from the design system. An earlier draft
     used an invented #04121f, which is in neither palette. */
  --oe-cine-blue: #0027f2;
  --oe-cine-blue-light: #3464fd;
  --oe-cine-coral: #ff6e73;
  --oe-cine-muted: #7c87c0;
  --oe-cine-ground: var(--oe-surface-abyss, #071d31);
  --oe-cine-ground-rgb: 7, 29, 49;
}

/* The page ground itself, so nothing shows the theme's cream body through. */
.oe-cine-body,
.oe-cine-page {
  background: var(--oe-cine-ground);
  color: var(--oe-white, #fff);
}

.oe-cine {
  position: relative;
  background: var(--oe-cine-ground);
  color: var(--oe-white, #fff);
}

.oe-cine__viewport {
  position: relative;
}

.oe-cine__stage {
  position: relative;
  overflow: hidden;
  background: var(--oe-cine-ground);
}

/* The poster is a real frame from the trim (A1), so the still-to-motion
   handover has nothing to jump between. It sits under the adapter's own
   element, which is opaque once it paints. */
.oe-cine__poster,
.oe-cine__canvas,
.oe-cine__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.oe-cine__poster {
  position: absolute;
  inset: 0;
}

.oe-cine__canvas,
.oe-cine__video {
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* ---- Beats ---- */

.oe-cine__beat {
  max-width: 34rem;
}

.oe-cine__beat :is(h1, h2),
.oe-cine__beat p {
  margin: 0 0 0.6em;
  text-wrap: balance;
}

.oe-cine__beat :is(h1, h2) {
  /* Explicit, not inherited. oe-design-system.css sets
     `h1,h2,h3,h4,h5,h6 { color: var(--ce-navy) }` and Kadence adds its own
     inline h1/h2 colour — both outrank the white this section inherits, so the
     headings rendered navy-on-navy over the footage while the paragraphs stayed
     white. Every colour on this page is now stated outright rather than
     depending on the cascade reaching us intact. */
  color: var(--oe-white, #fff);
  font-family: var(--ce-font-display, var(--oe-font-display, serif));
  font-size: clamp(1.75rem, 4.4vw, 3.25rem);
  line-height: 1.06;
  letter-spacing: -0.01em;
}

.oe-cine__beat p {
  font-family: var(--ce-font-ui, var(--oe-font-body, system-ui, sans-serif));
  font-size: clamp(1rem, 1.6vw, 1.2rem);
  line-height: 1.55;
  max-width: 30rem;
}

/* ---- static: the state the page ships in ---- */

.oe-cine[data-oe-cinematic-mode='static'] .oe-cine__stage {
  /* Poster keeps the trim's own 1280x656 shape rather than a guessed height. */
  aspect-ratio: 1280 / 656;
  max-height: 70svh;
}

.oe-cine[data-oe-cinematic-mode='static'] .oe-cine__beats {
  display: grid;
  gap: clamp(2rem, 6vw, 4rem);
  padding: clamp(2.5rem, 7vw, 5rem) clamp(1.25rem, 5vw, 4.5rem);
}

.oe-cine[data-oe-cinematic-mode='static'] .oe-cine__beat {
  /* Explicit: GSAP has not run, so nothing else is guaranteeing visibility. */
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* ---- scroll: pinned stage, beats as overlay ---- */

.oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__viewport {
  height: 100svh;
}

.oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__stage {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  max-height: none;
}

.oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__beats {
  /* Padding goes through custom properties so the short-viewport override, the
     wide-screen column, and the safe-area rule can all participate in the
     cascade. Setting the longhands directly in a later rule silently beat the
     media query. Block and inline are separate because the wide-screen rule
     wants a large inline indent and must not also inflate the vertical
     padding - a 1800x600 window would otherwise lose 440px of its 600. */
  --oe-beat-pad-block: clamp(1.5rem, 5vw, 4.5rem);
  --oe-beat-pad-inline: clamp(1.5rem, 5vw, 4.5rem);
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  align-items: end;
  padding-block: var(--oe-beat-pad-block);
  padding-inline: var(--oe-beat-pad-inline);
  pointer-events: none;
}

.oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__beat {
  /* Every beat occupies the same cell. The timeline owns which one is visible;
     stacking them here is what makes that a crossfade rather than a reflow. */
  grid-area: 1 / 1;
  align-self: end;
  /* The beats fade with opacity alone so they stay in the accessibility tree
     (see buildBeats in the engine). An opacity:0 element still takes clicks, and
     all three occupy the same grid cell — so interaction is switched off here
     instead. There is nothing interactive inside a beat. */
  pointer-events: none;
  text-shadow: 0 1px 24px rgba(var(--oe-cine-ground-rgb), 0.55);
}

/* Scrim, not a flat overlay: copy sits low in the frame per the blueprint's
   never-over-the-subject rule, so only the lower third needs protecting. */
.oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__stage::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Depth measured, not eyeballed. Sampling the real frame pixels under the copy
     and compositing this gradient over them, the previous 0.72/0.28/55% left a
     worst case of 1.5:1 against white text — the whitewater in beat 2 is very
     bright. These values put the worst case at 7.4:1 across all three beats.
     The footage still reads; the darkening lands where the subject rarely is. */
  background: linear-gradient(
    to top,
    rgba(var(--oe-cine-ground-rgb), 0.96) 0%,
    rgba(var(--oe-cine-ground-rgb), 0.72) 42%,
    rgba(var(--oe-cine-ground-rgb), 0) 72%
  );
}

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

/* The engine already refuses to boot here, so this is belt-and-braces against a
   future caller that forgets - and it makes the static state the one the CSS
   asserts, not merely the one it inherits. */
@media (prefers-reduced-motion: reduce) {
  .oe-cine .oe-cine__viewport {
    height: auto;
  }

  .oe-cine .oe-cine__beat {
    opacity: 1 !important;
    visibility: visible !important;
    transform: none !important;
  }
}

/* ---- profiler overlay (opt-in, ?oe-cinematic-profile=1) ---- */

.oe-cine__hud {
  position: fixed;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 9999;
  margin: 0;
  padding: 0.7rem 0.9rem;
  border-radius: 6px;
  background: rgba(var(--oe-cine-ground-rgb), 0.88);
  color: #b9f0ff;
  font: 500 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre;
  pointer-events: none;
}

/* ============================================================
   A3 — Responsive and reduced-motion
   ============================================================ */

/* ---- Per-beat stills, declared but not yet fetched ----
 *
 * A url() sitting in a custom property is not a request. Nothing is fetched
 * until one of the rules below substitutes it into a real background-image, so
 * the motion path - which is most visits - costs zero extra bytes for imagery
 * it will never show. The breakpoint swap comes free with the declaration.
 *
 * Paths are relative to this stylesheet, which is what makes them safe: they
 * resolve wherever the theme directory happens to be called this deployment.
 */

.oe-cine__beat--1 { --oe-still: url('../cinematic/beat-1-720.jpg'); }
.oe-cine__beat--2 { --oe-still: url('../cinematic/beat-2-720.jpg'); }
.oe-cine__beat--3 { --oe-still: url('../cinematic/beat-3-720.jpg'); }

@media (min-width: 768px) {
  .oe-cine__beat--1 { --oe-still: url('../cinematic/beat-1-1280.jpg'); }
  .oe-cine__beat--2 { --oe-still: url('../cinematic/beat-2-1280.jpg'); }
  .oe-cine__beat--3 { --oe-still: url('../cinematic/beat-3-1280.jpg'); }
}

/* Free to apply always: without a background-image none of it paints. */
.oe-cine__beat {
  position: relative;
  isolation: isolate;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
}

.oe-cine__beat :is(h1, h2) {
  max-width: 22ch;
}

/* ---- When the stills are actually earned ----
 *
 * Three cases, and only three:
 *   1. JS never ran        - <html> never got data-oe-js
 *   2. Reduced motion      - the engine refuses to boot by design
 *   3. The engine gave up  - no variants, no stage, GSAP blocked, or neither
 *                            adapter could load; it sets the fallback attribute
 *
 * In all three the visitor would otherwise be looking at one opening frame and
 * three paragraphs. This gives them the sequence instead.
 *
 * The card treatment is deliberately tied to the *same* condition as the image
 * rather than to static mode. Static is also the state the page sits in during
 * the moment before the engine boots, and a tall empty card in that window
 * reads as a broken layout - on a slow connection, for several seconds.
 *
 * The reduced-motion case is duplicated rather than folded in because it must
 * not depend on JavaScript having run. That is the whole point of it.
 */

html:not([data-oe-js]) .oe-cine__beat,
.oe-cine[data-oe-cinematic-fallback='stills'] .oe-cine__beat {
  background-image: var(--oe-still);
  max-width: none;
  min-height: min(58svh, 27rem);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: clamp(1.25rem, 4vw, 2.5rem);
  border-radius: 3px;
  overflow: hidden;
}

html:not([data-oe-js]) .oe-cine__beat::before,
.oe-cine[data-oe-cinematic-fallback='stills'] .oe-cine__beat::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to top,
    rgba(var(--oe-cine-ground-rgb), 0.94) 0%,
    rgba(var(--oe-cine-ground-rgb), 0.58) 46%,
    rgba(var(--oe-cine-ground-rgb), 0.12) 80%
  );
}

@media (prefers-reduced-motion: reduce) {
  .oe-cine .oe-cine__beat {
    background-image: var(--oe-still);
    max-width: none;
    min-height: min(58svh, 27rem);
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding: clamp(1.25rem, 4vw, 2.5rem);
    border-radius: 3px;
    overflow: hidden;
  }

  .oe-cine .oe-cine__beat::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(
      to top,
      rgba(var(--oe-cine-ground-rgb), 0.94) 0%,
      rgba(var(--oe-cine-ground-rgb), 0.58) 46%,
      rgba(var(--oe-cine-ground-rgb), 0.12) 80%
    );
  }
}

/* In the stills path the three cards carry the story, so the poster steps back
   to a masthead. It opens on the same kite surfer as card 1, and at full height
   the two read as a stutter rather than a sequence. Shrinking it is better than
   hiding it: the <img> is in the HTML and has already been fetched, so
   display:none would spend the bytes and show nothing for them. */

html:not([data-oe-js]) .oe-cine__stage,
.oe-cine[data-oe-cinematic-fallback='stills'] .oe-cine__stage {
  max-height: min(34svh, 15rem);
}

@media (prefers-reduced-motion: reduce) {
  .oe-cine .oe-cine__stage {
    max-height: min(34svh, 15rem);
  }
}

/* ---- Scroll mode: viewport shape ----
 *
 * The pinned stage is the one place where viewport *height* matters more than
 * width. A landscape phone is a 100svh box about 340px tall, and the desktop
 * type scale does not fit in it alongside a paragraph.
 */

@media (max-height: 560px) and (orientation: landscape) {
  .oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__beats {
    --oe-beat-pad-block: clamp(0.75rem, 3vw, 1.5rem);
    --oe-beat-pad-inline: clamp(0.75rem, 3vw, 1.5rem);
  }

  .oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__beat :is(h1, h2) {
    font-size: clamp(1.3rem, 3.4vh, 1.9rem);
    margin-bottom: 0.35em;
  }

  .oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__beat p {
    font-size: clamp(0.85rem, 2.1vh, 1rem);
    line-height: 1.4;
    max-width: 42ch;
  }
}

/* Very short viewports in portrait too — small phones with a browser chrome
   that never collapses. */
@media (max-height: 640px) and (orientation: portrait) {
  .oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__beat :is(h1, h2) {
    font-size: clamp(1.5rem, 6.5vw, 2rem);
  }
}

/* Notch, home indicator, and rounded display corners. max() keeps the designed
   padding on every device that does not report an inset. */
@supports (padding: max(0px)) {
  .oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__beats {
    padding-left: max(var(--oe-beat-pad-inline), env(safe-area-inset-left));
    padding-right: max(var(--oe-beat-pad-inline), env(safe-area-inset-right));
    padding-bottom: max(var(--oe-beat-pad-block), env(safe-area-inset-bottom));
  }
}

/* Wide screens: hold the copy against a measured content column instead of
   letting it drift to the far edge of a 2560px display. The footage still
   fills the frame — that is the point of it. */
@media (min-width: 1500px) {
  .oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__beats {
    --oe-beat-pad-inline: max(4.5rem, calc((100% - 1360px) / 2));
  }
}

/* Narrow viewports crop the 1.95:1 frame hard, which brings more subject into
   the lower third where the copy sits. Deepen the scrim to compensate. */
@media (max-width: 767px) {
  .oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__stage::after {
    background: linear-gradient(
      to top,
      rgba(var(--oe-cine-ground-rgb), 0.97) 0%,
      rgba(var(--oe-cine-ground-rgb), 0.78) 46%,
      rgba(var(--oe-cine-ground-rgb), 0) 76%
    );
  }
}

/* ---- Static mode: stack ---- */

.oe-cine[data-oe-cinematic-mode='static'] .oe-cine__beats {
  gap: clamp(1rem, 2.5vw, 1.5rem);
  padding: clamp(1rem, 4vw, 2.5rem);
  max-width: 1360px;
  margin-inline: auto;
}

/* ---- Ending: capture and nav ----
 *
 * Layout only. A4 owns the Brevo integration and the form's behaviour; the
 * submit control stays disabled until it does.
 */

.oe-cine-capture {
  background: var(--oe-cine-ground);
  padding: clamp(3rem, 9vw, 6rem) clamp(1.25rem, 5vw, 4.5rem);
  max-width: 44rem;
  margin-inline: auto;
}

.oe-cine-capture h2 {
  color: var(--oe-white, #fff);
  font-family: var(--ce-font-display, var(--oe-font-display, serif));
  font-size: clamp(1.6rem, 3.6vw, 2.5rem);
  line-height: 1.1;
  margin: 0 0 0.5em;
  text-wrap: balance;
}

/* Direct child only. The form's fields are <p> elements too, and an unscoped
   rule here put a 38ch measure on them - which silently capped the email input
   at first-name width instead of letting it span the row. */
.oe-cine-capture > p {
  font-family: var(--ce-font-ui, var(--oe-font-body, system-ui, sans-serif));
  font-size: clamp(0.95rem, 1.5vw, 1.05rem);
  line-height: 1.6;
  margin: 0 0 1.75em;
  color: rgba(255, 255, 255, 0.82);
  max-width: 38ch;
}


.oe-cine-capture__form label {
  font: 600 0.75rem/1 var(--ce-font-ui, system-ui, sans-serif);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
}

.oe-cine-capture__form input {
  min-height: 44px; /* Non-negotiable: 44px targets. */
  padding: 0 0.9rem;
  border: 1px solid rgba(255, 255, 255, 0.34);
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.06);
  color: var(--oe-white, #fff);
  font: 400 1rem/1 var(--ce-font-ui, system-ui, sans-serif);
}

.oe-cine-capture__form input::placeholder {
  color: rgba(255, 255, 255, 0.45);
}

.oe-cine-capture__form button {
  min-height: 44px;
  padding: 0 1.5rem;
  border: 0;
  border-radius: 3px;
  background: var(--oe-cine-blue);
  color: var(--oe-white, #fff);
  font: 600 0.95rem/1 var(--ce-font-ui, system-ui, sans-serif);
  cursor: pointer;
}

/* Brevo's honeypot. Off-screen rather than display:none — it has to be
   submitted, and a display-hidden field is the pattern bots learn to skip.
   aria-hidden + tabindex="-1" in the markup keep it away from real people. */
.oe-cine-capture__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.oe-cine-page__nav {
  padding: 0 clamp(1.25rem, 5vw, 4.5rem) clamp(3rem, 8vw, 5rem);
  max-width: 44rem;
  margin-inline: auto;
}

.oe-cine-page__nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--oe-white, #fff);
  font: 600 0.95rem/1 var(--ce-font-ui, system-ui, sans-serif);
  text-underline-offset: 0.35em;
}

/* Visible focus everywhere, on the dark ground this page uses. */
.oe-cine a:focus-visible,
.oe-cine-capture :focus-visible,
.oe-cine-page__nav a:focus-visible {
  outline: 3px solid var(--oe-cine-coral);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---- Print ----
 * Nobody prints a landing page on purpose, but a pinned 100svh stage prints as
 * a blank sheet, which looks like a broken page rather than a skipped one. */
@media print {
  .oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__viewport { height: auto; }
  .oe-cine__canvas,
  .oe-cine__video,
  .oe-cine__hud { display: none; }
  .oe-cine,
  .oe-cine-capture { background: #fff; color: #000; }
  .oe-cine__beat { opacity: 1 !important; visibility: visible !important; transform: none !important; }
}


/* ============================================================
   Logo and capture form — added 2026-09-07 at Chad's direction
   ============================================================ */

/* The mark sits inside the pinned viewport, above the scrim, so it holds at
   every scroll depth — the spec's "persistent, always-visible" requirement,
   met by the brand mark rather than by a nav. */
.oe-cine__logo {
  position: absolute;
  top: clamp(1rem, 3vw, 2rem);
  left: clamp(1rem, 3vw, 2.5rem);
  z-index: 3;
  width: clamp(56px, 7vw, 88px);
  height: auto;
  pointer-events: none;
  filter: drop-shadow(0 2px 12px rgba(var(--oe-cine-ground-rgb), 0.5));
}

@supports (padding: max(0px)) {
  .oe-cine__logo {
    top: max(clamp(1rem, 3vw, 2rem), env(safe-area-inset-top));
    left: max(clamp(1rem, 3vw, 2.5rem), env(safe-area-inset-left));
  }
}

/* In the stills path the viewport is not pinned and the masthead is short, so
   the mark returns to normal flow above it rather than sitting on the poster. */
html:not([data-oe-js]) .oe-cine__logo,
.oe-cine[data-oe-cinematic-fallback='stills'] .oe-cine__logo {
  position: static;
  margin: clamp(1.25rem, 4vw, 2rem) clamp(1rem, 4vw, 2.5rem) clamp(0.75rem, 2vw, 1.25rem);
}

@media (prefers-reduced-motion: reduce) {
  .oe-cine .oe-cine__logo {
    position: static;
    margin: clamp(1.25rem, 4vw, 2rem) clamp(1rem, 4vw, 2.5rem) clamp(0.75rem, 2vw, 1.25rem);
  }
}

/* Three fields now: first name and last name share a row from 30rem up, email
   takes the full width. Below that everything stacks, because two 44px targets
   side by side on a 320px screen is not a form anyone completes. */
.oe-cine-capture__form {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.9rem;
  align-items: end;
}

@media (min-width: 30rem) {
  .oe-cine-capture__form {
    grid-template-columns: 1fr 1fr;
  }

  .oe-cine-capture__field:not(.oe-cine-capture__field--half),
  .oe-cine-capture__form button {
    grid-column: 1 / -1;
  }
}

.oe-cine-capture__field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin: 0;
}

.oe-cine-capture__form button {
  justify-self: start;
}

/* Interactive states for the live capture form (A4). */
.oe-cine-capture__form input:focus {
  border-color: var(--oe-cine-blue-light);
  background: rgba(255, 255, 255, 0.1);
}

/* :user-invalid, not :invalid. A required empty field is :invalid from the
   moment it renders, so the form greeted every visitor with three fields
   already marked as errors. The usual :not(:placeholder-shown) guard does
   nothing here because these inputs have no placeholder attribute — that
   selector only ever suppresses anything when one is present.
   :user-invalid matches only after the person has actually interacted or
   submitted. Browsers without it simply show no custom error styling; native
   validation still fires on submit, so the degradation is safe. */
.oe-cine-capture__form input:user-invalid {
  border-color: var(--oe-cine-coral);
}

.oe-cine-capture__form button:hover,
.oe-cine-capture__form button:focus-visible {
  background: var(--oe-cine-blue-light);
}

/* Windows High Contrast / forced-colors: the scrim and the canvas both drop out,
   which would leave copy on bare footage. Give the beats a real background in
   that mode so the words survive. */
@media (forced-colors: active) {
  .oe-cine[data-oe-cinematic-mode='scroll'] .oe-cine__beat {
    background: Canvas;
    color: CanvasText;
    padding: 0.75rem 1rem;
    forced-color-adjust: none;
  }

  .oe-cine__logo {
    forced-color-adjust: none;
  }
}
