/* ==========================================================================
   Shoreline Design — scroll-page system (redesign)
   --------------------------------------------------------------------------
   Shared by every scroll-driven page (feasibility-scroll, subdivision-scroll,
   about-scroll, gallery-scroll, project-scroll). Colour and font tokens come
   from site.css; this file adds the scene mechanics and the shared pieces:
   header states, progress rail, interludes, buttons, labels.

   How a scene works: a tall wrapper (.scene--N = N screens of scroll) holds a
   sticky full-screen .stage. scroll.js writes the wrapper's scroll progress
   (0 → 1) into the CSS variable --p on it; everything that moves is plain CSS
   reading --p. No animation library.
   ========================================================================== */

:root { --paper: #FAF8F3; --gutter: clamp(20px, 4vw, 64px); }
body { overflow-x: clip; }     /* not "hidden": that would break position: sticky */
main { display: block; }

.mono   { font-family: var(--body); font-size: 13px; }
.kicker { font-family: var(--body); font-size: 13px; color: var(--muted); }
.fx h1, .fx h2, .fx h3 { font-family: var(--heading); font-weight: 400; margin: 0; letter-spacing: -0.01em; text-wrap: pretty; }
.fx p { margin: 0; }

.cta { display: inline-flex; align-items: center; gap: 10px; height: 48px; padding: 0 22px; font-size: 12px; letter-spacing: .14em; text-transform: uppercase; font-weight: 500; border-radius: 2px; border: 1px solid transparent; text-decoration: none; cursor: pointer; font-family: var(--body); transition: background .2s, color .2s, border-color .2s; }
/* A button that pins its OWN background must pin its own text colour too.
   --ink is a scoped token: sd.css:111 flips it to limestone inside any
   [data-dark] subtree, which left this button rendering limestone text on its
   own paper fill - 1.03:1, invisible - the moment the feasibility hero entered
   the dark scope. --navy is a palette constant and cannot be re-scoped, which is
   what .cta--flag and .cta--paper already do. Same reasoning for :hover. */
.cta--light { background: var(--paper); color: var(--navy); }
.cta--light:hover { background: var(--accent); color: var(--navy); }
.cta--ghost { color: var(--paper); border-color: rgba(243,240,232,.55); background: transparent; }
.cta--ghost:hover { border-color: var(--paper); color: var(--paper); }
.cta--ink { background: var(--ink); color: var(--paper); }
.cta--ink:hover { background: var(--accent); color: var(--ink); }
.cta--line { color: var(--ink); border-color: var(--line); background: transparent; }
.cta--line:hover { border-color: var(--ink); color: var(--ink); }

/* ---- Header: the site's nav, transparent over a hero, solid afterwards ---- */
.site-nav { transition: background .3s, border-color .3s; }
.site-nav .site-nav__logo, .site-nav--overhero .site-nav__phone { opacity: 1; }
.site-nav__logo img { width: clamp(150px, 16vw, 210px); }
.site-nav .logo-dark { display: none; }
body.is-scrolled .site-nav--overhero { background: var(--bg); border-bottom: 1px solid var(--line); }
body.is-scrolled .site-nav .logo-light { display: none; }
body.is-scrolled .site-nav .logo-dark { display: block; }
body.is-scrolled.is-dark .site-nav--overhero { background: var(--ink); border-bottom-color: rgba(243,240,232,.14); }
body.is-scrolled.is-dark .site-nav .logo-light { display: block; }
body.is-scrolled.is-dark .site-nav .logo-dark { display: none; }
/* Pages that open on a light hero (no photo) start in the "scrolled" look. */
body.nav-solid .site-nav--overhero { background: var(--bg); border-bottom: 1px solid var(--line); }
body.nav-solid .site-nav .logo-light { display: none; }
body.nav-solid .site-nav .logo-dark { display: block; }

/* ---- Progress rail (built by scroll.js from the page's scenes) ---------- */
.rail { position: fixed; right: calc(var(--gutter) - 6px); top: 50%; transform: translateY(-50%); z-index: 40; display: flex; flex-direction: column; align-items: center; gap: 12px; pointer-events: none; }
.rail__n { font-family: var(--body); font-size: 12px; writing-mode: vertical-rl; color: var(--ink); opacity: .7; margin-bottom: 4px; }
.rail i { display: block; width: 1px; height: 12px; background: var(--ink); opacity: .3; transition: height .3s, opacity .3s; }
.rail i.on { height: 28px; opacity: 1; }
body.is-dark .rail__n, body.is-dark .rail i { background: var(--paper); color: var(--paper); }
@media (max-width: 800px) { .rail { display: none; } }

/* ---- Scene mechanics ----------------------------------------------------- */
.scene { position: relative; }
.scene--2 { height: 200vh; } .scene--3 { height: 300vh; } .scene--4 { height: 400vh; } .scene--5 { height: 500vh; }
/* svh, not vh, and deliberately not dvh.
   On a phone `100vh` is the TOOLBAR-COLLAPSED height, so while the toolbar is
   showing -- which it is on landing and whenever you scroll up -- a pinned stage
   is about 110px taller than the visible area and `overflow: hidden` deletes
   whatever falls in that band. Measured at iPhone SE metrics, six sections had
   under 60px of headroom and feasibility-review was already slicing a sentence
   mid-line. `svh` is the toolbar-SHOWING height, so the box is the smaller,
   stable size and the content is laid out in the space that actually exists.

   NOT `dvh`: that tracks the live viewport, so a pinned stage would change height
   mid-scroll as the toolbar collapses -- a worse problem than the one it solves.

   And svh does NOT fix clipping. It makes the box honest. Anything that still
   does not fit is handled by the short-viewport rule at the bottom of this file,
   which unpins the section instead of cutting it off.
   The plain `vh` line stays first as the fallback for anything without svh. */
.stage { position: sticky; top: 0; height: 100vh; height: 100svh; overflow: hidden; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; }
.stage.grid2 { height: 100vh; height: 100svh; }
@media (max-width: 800px) { .grid2 { grid-template-columns: 1fr; grid-template-rows: 46vh 1fr; } }

/* Static sections between scenes; .rise children fade up once on arrival. */
.interlude { padding: clamp(80px, 12vh, 140px) var(--gutter); }
.interlude__inner { max-width: 1120px; margin: 0 auto; }
.rise { opacity: 0; transform: translateY(18px); transition: opacity .5s ease-out, transform .5s ease-out; }
.rise.in { opacity: 1; transform: none; }

/* Full-bleed photographic hero with parallax and a copy block low-left. */
.fx-hero { height: 100vh; height: 100svh; position: relative; overflow: hidden; background: var(--ink); color: var(--paper); }
.fx-hero__img { position: absolute; inset: -12% 0 0 0; height: 124%; width: 100%; object-fit: cover; transform: translateY(calc(var(--p, 0) * 12%)); will-change: transform; }
/* This default gradient has NEVER been contrast-fitted and must not be relied on.
   Both pages that use .fx-hero override it with a profile measured against where
   their own copy actually lands - subdivision-delivery.html and
   feasibility-review.html, each with the sweep written next to it. Measured on
   this default, every element of the feasibility hero failed its WCAG bar
   somewhere across six widths, because the .05 trough at 35% sits directly above
   a copy block that starts at 42%. A new .fx-hero page must fit its own profile
   the same way; do not assume this one passes. */
.fx-hero__wash { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(24,41,37,.38) 0%, rgba(24,41,37,.05) 35%, rgba(24,41,37,.74) 100%); }
.fx-hero__copy { position: absolute; left: var(--gutter); right: var(--gutter); bottom: clamp(40px, 8vh, 80px); max-width: 780px; display: flex; flex-direction: column; gap: 22px; opacity: calc(1 - var(--p, 0) * 1.4); transform: translateY(calc(var(--p, 0) * -40px)); }
.fx-hero h1 { font-size: clamp(42px, 6vw, 88px); line-height: .98; color: var(--paper); }
.fx-hero p { max-width: 520px; font-size: 16px; line-height: 1.6; color: rgba(243,240,232,.86); }
.fx-hero__cta { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 4px; }
.fx-hero__hint { position: absolute; right: calc(var(--gutter) + 60px); bottom: clamp(40px, 8vh, 80px); display: flex; flex-direction: column; align-items: center; gap: 10px; color: rgba(243,240,232,.8); opacity: calc(1 - var(--p, 0) * 3); }
.fx-hero__hint i { display: block; width: 1px; height: 56px; background: currentColor; animation: hint 2.4s ease-in-out infinite; transform-origin: top; }
@keyframes hint { 0%, 100% { transform: scaleY(.4); } 50% { transform: scaleY(1); } }
@media (max-width: 800px) { .fx-hero__hint { display: none; } }

/* Small mono label pinned over an image. */
.lbl { position: absolute; font-family: var(--body); font-size: 13px; color: var(--paper); background: rgba(24,41,37,.85); padding: 6px 10px; white-space: nowrap; z-index: 3; }
@media (max-width: 800px) { .lbl { font-size: 12px; padding: 5px 8px; max-width: 70%; white-space: normal; } }

/* Numbered list rows that light up one at a time (data-step). */
.steplist { display: flex; flex-direction: column; }
.step-row { display: grid; grid-template-columns: 48px 1fr; gap: 16px; padding: 18px 0; border-top: 1px solid var(--line); transition: border-color .4s; }
.step-row .n { font-family: var(--body); font-size: 13px; color: var(--muted); padding-top: 6px; transition: color .4s; }
.step-row h3 { font-size: clamp(22px, 2.1vw, 30px); line-height: 1.1; color: var(--muted); transition: color .4s; }   /* "not yet" in muted (4.6:1); the current and done steps go to full ink */
.step-row p { font-size: 15px; line-height: 1.6; color: var(--muted); max-width: 420px; max-height: 0; overflow: hidden; opacity: 0; transition: max-height .5s ease, opacity .4s ease, margin .4s; }
.step-row.done .n, .step-row.done h3 { color: var(--ink); }
.step-row.on { border-top-color: var(--ink); }
.step-row.on .n { color: var(--flag); }
.step-row.on h3 { color: var(--ink); }
.step-row.on p { max-height: 120px; opacity: 1; margin-top: 8px; }
@media (max-width: 800px) { .step-row { padding: 12px 0; } .step-row h3 { font-size: 20px; } .step-row.on p { max-height: 90px; font-size: 14px; } }

/* Layers over an image that switch on with the steps (data-layer). */
.layer { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; transition: opacity .5s ease; z-index: 2; }
.layer.on { opacity: 1; }
.lblgrp { position: absolute; inset: 0; opacity: 0; transition: opacity .5s ease; z-index: 3; pointer-events: none; }
.lblgrp.on { opacity: 1; }

/* Dark "payoff" scene. */
.scene--dark .stage { background: var(--ink); color: var(--paper); }
.scene--dark h2, .scene--dark h3 { color: var(--paper); }

/* Booking / enquiry block shared by service pages (site.js drives the form). */
.book { min-height: 100vh; display: grid; grid-template-columns: 52fr 48fr; border-top: 1px solid var(--line); }
@media (max-width: 900px) { .book { grid-template-columns: 1fr; } }
.book__side { display: flex; flex-direction: column; justify-content: center; gap: 24px; padding: clamp(80px, 12vh, 140px) clamp(20px, 6vw, 96px) 80px; background: var(--panel); border-right: 1px solid var(--line); }
.book h2 { font-size: clamp(34px, 4vw, 56px); line-height: 1.02; }
.book__side > p { font-size: 16px; line-height: 1.6; color: var(--muted); max-width: 460px; }
.book .form { margin-top: 8px; position: relative; }
.book .form .btn { align-self: flex-start; margin-top: 4px; }
.book__note { font-size: 13px; color: var(--muted); }
.book__faq { display: flex; flex-direction: column; justify-content: center; padding: clamp(80px, 12vh, 140px) clamp(20px, 8vw, 150px) 80px clamp(20px, 5vw, 80px); }
.book__faq .kicker { margin-bottom: 18px; }

/* Interactive controls used by the explorers. */
.ctl { display: flex; flex-direction: column; gap: 10px; }
.ctl label { display: flex; justify-content: space-between; font-family: var(--body); font-size: 13px; color: var(--muted); }
.ctl label output { color: var(--ink); }
.ctl input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 22px; background: transparent; margin: 0; cursor: pointer; }
.ctl input[type="range"]::-webkit-slider-runnable-track { height: 1px; background: var(--ink); }
.ctl input[type="range"]::-moz-range-track { height: 1px; background: var(--ink); }
.ctl input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--paper); border: 1px solid var(--ink); margin-top: -8.5px; }
.ctl input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--paper); border: 1px solid var(--ink); }
.seg { display: flex; flex-wrap: wrap; gap: 8px; }
.seg button { font-family: var(--body); font-size: 13px; padding: 10px 14px; border: 1px solid var(--line); background: transparent; color: var(--muted); border-radius: 2px; cursor: pointer; transition: border-color .2s, color .2s, background .2s; }
.seg button[aria-pressed="true"] { border-color: var(--ink); color: var(--ink); background: var(--panel); }
.seg button:hover { border-color: var(--ink); }

/* ---- Reduced motion: no pinning, everything visible ---------------------- */
@media (prefers-reduced-motion: reduce), (max-width: 560px) and (max-height: 740px) {
  .scene--2, .scene--3, .scene--4, .scene--5 { height: auto; }
  .stage { position: relative; height: auto; min-height: 100vh; min-height: 100svh; }
  /* .stage.grid2 sets height:100vh and outranks the rule above on specificity, so
     without this a two-column stage stayed exactly one screen tall with overflow
     hidden — on a 360x640 phone that cut the last two of the four checks off the
     page. Same specificity, later in the file, so this wins. */
  .stage.grid2 { height: auto; }
  .fx-hero__img, .fx-hero__copy, .rise { transform: none !important; opacity: 1 !important; }
  /* The hint's opacity is driven by calc(1 - var(--p) * 3), and scroll.js pins
     --p to 1 under reduced motion, so without this the 'Scroll' affordance was
     the one piece of the hero that vanished in this mode - on a page that still
     scrolls. Its bar is stilled on the line below rather than hidden. */
  .fx-hero__hint { opacity: 1 !important; }
  .fx-hero__hint i { animation: none; }
  .step-row p { max-height: none; opacity: 1; margin-top: 8px; }
  .step-row .n, .step-row h3 { color: var(--ink); }
  .layer, .lblgrp { opacity: 1; }
  /* A box marked data-steps-box keeps stepping in this mode (scroll.js), so its
     layers follow the steps rather than all appearing at once. The .5s fades stay
     switched off — the reveal is a state change, not a movement. */
  [data-steps-box] .layer, [data-steps-box] .lblgrp { opacity: 0; transition: none; }
  [data-steps-box] .layer.on, [data-steps-box] .lblgrp.on { opacity: 1; }
}
