/* Band scaffold — full-bleed dark art sections alternating with plain ground.
   v1 is static: one flat image per band, no parallax layers and no motion.
   Placeholder gradients stand in for the art; swap each .plate for an <img>.
   (Layered parallax is a later option; see assets/ART-BRIEF.md.) */

.bands-page {
  --page-gutter: max(2rem, calc((100vw - 1160px) / 2));
  --deep-900: #0b161d;   /* matched to the hero photo's darkest water */
  --deep-800: #0b1f2c;
  --deep-700: #123043;
  --deep-600: #1b4459;
  --surface-light: #7fb7c9; /* light through the waterline */
  --foam: #cfe4e8;
  background: var(--color-bg);
}

.band-note {
  padding: 12px var(--page-gutter);
  background: var(--color-accent-100);
  border-bottom: 1px solid var(--color-accent-300);
  font-size: 13px;
}
.band-note a { color: var(--color-accent-700); }

/* One flat art plate per band. Becomes <img> once the real art lands. */
.plate { position: absolute; inset: 0; background-size: cover; background-position: center; }

/* Labels on the placeholder slots. Delete with the placeholders. */
.slot-tag {
  position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 3; padding: 6px 12px; white-space: nowrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase;
  color: #cfe4e8; background: rgb(6 19 28 / 72%); border: 1px solid rgb(207 228 232 / 25%);
}

/* ── SITE HEADER (over the dark hero) ─────────────────────────────────── */
.site-head { position: relative; z-index: 5; background: var(--deep-900); }
.site-head .nav { color: #eef6ff; }
.site-head .nav a { color: #cfe4e8; }
.site-head .nav a:hover { color: #ffffff; }
.site-head .nav a.btn-primary { border-color: transparent; }
.site-head .brand-name { color: #f4fbff; }

/* ── BAND 1 · HERO ────────────────────────────────────────────────────── */
.hero-band {
  position: relative; isolation: isolate;
  display: grid; align-items: center;
  min-height: min(92svh, max(40rem, 40vw));
  padding: 4rem 0 4.5rem;
  background: var(--deep-900); color: #eef6ff;
  overflow: hidden;
}
/* The inner .wrap carries the gutter; the band stays full bleed. */
.hero-inner { position: relative; z-index: 2; width: 100%; }
.hero-copy { width: min(52%, 560px); }
.hero-note { font-size: 13px; margin-top: 18px; color: #8fadbd; }
.hero-band h1 {
  font-size: clamp(38px, 5.2vw, 62px); text-transform: uppercase;
  letter-spacing: 0.01em; margin: 0 0 20px; text-wrap: balance; color: #f4fbff;
}
.hero-band .lede { font-size: 19px; line-height: 1.5; max-width: 44ch; margin-bottom: 28px; color: #bcd6e4; }
.hero-band .tag-outline { border-color: var(--surface-light); color: var(--surface-light); }
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
/* Light-on-dark secondary button; the stock .btn-secondary is tuned for the light ground. */
.btn-ghost-light { border: 1px solid rgb(207 228 232 / 45%); color: #eef6ff; }
.btn-ghost-light:hover { background: rgb(207 228 232 / 12%); color: #ffffff; }

.hero-art {
  position: absolute; z-index: 1; inset: 0 0 0 auto;
  /* Wider than the placeholder era: the sub sits right of center in the photo,
     so the art needs room before the mask starts eating the left edge. */
  width: min(74%, 1180px);
  /* Feather the left edge so the art dissolves under the copy instead of
     ending on a hard line, and soften the bottom into the next band. */
  mask-image: linear-gradient(90deg, transparent 0%, rgb(0 0 0 / 55%) 22%, #000 46%),
              linear-gradient(#000 78%, transparent 99%);
  mask-composite: intersect;
  -webkit-mask-composite: source-in;
}
/* object-position right: keeps the conning tower in frame when the band is
   taller or narrower than the image's own 16:9. */
.hero-art img { width: 100%; height: 100%; object-fit: cover; object-position: 72% center; }

/* The hero photograph carries its own sky, so the band ground is matched to
   the image's darkest water rather than the brighter placeholder palette. */

/* ── BAND 2 / 4 · PLAIN GROUND ────────────────────────────────────────── */
.band-plain { padding: clamp(3.5rem, 7vw, 6.5rem) var(--page-gutter); background: var(--color-bg); }
.band-plain .inner, .band-interior .inner { max-width: 1160px; margin: 0 auto; }
.band-title { font-size: clamp(30px, 3.6vw, 42px); text-transform: uppercase; max-width: 24ch; margin: 0 0 40px; }
.band-title.light { color: #f4fbff; }
.kicker.light { color: var(--surface-light); }
.band-plain .cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 40px 48px; }
.band-plain h3, .band-interior h3 { font-size: 20px; margin-bottom: 8px; }
.band-plain p { max-width: 42ch; }

/* ── BAND 3 · INTERIOR ────────────────────────────────────────────────── */
.band-interior {
  position: relative; isolation: isolate; overflow: hidden;
  padding: clamp(3.5rem, 7vw, 6rem) 0;
  background: var(--deep-800); color: #c3d8e4;
}
/* Inherited from site.css, but the dark ground needs its own values. */
.band-interior .section-title.light { color: #f4fbff; }
.band-interior .label.text-muted { color: #8fadbd; }
.band-interior .bubble-stamp { color: #8fadbd; }
/* The phone frames are light objects sitting on the dark band: reset the
   inherited light-on-dark text back to the page's ink, or the customer
   message washes out against its own pale bubble. */
.band-interior .phone,
.band-interior .phone p,
.band-interior .phone strong { color: var(--color-text); }
.band-interior .phone .text-muted,
.band-interior .phone p.text-muted { color: color-mix(in srgb, var(--color-text) 65%, transparent); }
.band-interior .phone .phone-from,
.band-interior .phone .bubble-stamp { color: var(--color-neutral-700); }
.interior-art { position: absolute; inset: 0; z-index: 0; }
.interior-art img { width: 100%; height: 100%; object-fit: cover; object-position: center; }
/* The hull photo is a neutral near-black (#212527); the hero's water is
   blue-teal. This tint pulls the texture into the hero's family without
   flattening the metal — `color` keeps the rivets' own light and shade.
   Retune the color here rather than re-exporting the image. */
.interior-art::after {
  content: ""; position: absolute; inset: 0;
  background: #123043; mix-blend-mode: color;
}
/* Hold the texture behind the text rather than competing with it. At full
   strength the rivets read straight through the body copy, so the whole
   layer is dimmed and a scrim sits over it. */
.interior-art { opacity: .95; z-index: 0; }
/* Scrim sits above the art (z-index 0) and below the copy (z-index 2). */
.band-interior::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(100deg, var(--deep-800) 10%, rgb(11 31 44 / 88%) 44%, rgb(11 31 44 / 62%));
}
.band-interior .inner { position: relative; z-index: 2; max-width: 1120px; }
.band-interior p { color: #a9c4d4; max-width: 44ch; }
.feature-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: clamp(2rem, 5vw, 4.5rem); }

/* ── HOW IT WORKS · scroll-driven beam ────────────────────────────────────
   A hairline track runs down the centreline. As the section moves through
   the viewport an amber beam grows down the track, scrubbed to scroll, and
   each stop's dot ignites as the beam reaches it. Native CSS scroll-driven
   animation, no script. Browsers without support, and reduced-motion users,
   get the finished state: track fully lit, every dot on. */
.how-band {
  /* `clip`, not the inherited `hidden`: overflow:hidden makes the band a
     scroll container, and a view timeline measures against the nearest
     scroll container. This band's own scroll position never moves, so the
     beam froze at a fixed 46%. clip clips the art the same way without
     creating a scroller, so the timeline tracks the viewport as intended. */
  overflow: clip;
  --rail: 2px;
  --track: rgb(207 228 232 / 22%);
  --beam: var(--color-signal);
  --beam-hot: #ffc27a;
  --beam-glow: rgb(217 122 43 / 55%);
}
.how-band .band-title { margin-bottom: clamp(2rem, 4vw, 3rem); }
/* The band is full-bleed, so its contents carry the page gutter themselves.
   box-sizing keeps the 1120px measure from growing by the padding. */
.how-band .inner { padding-inline: var(--page-gutter); max-width: 1120px; box-sizing: content-box; }
/* This band uses the portrait plate (816×1456) rather than the landscape one.
   Anchored to the top: the band is taller than it is wide, so a top-anchored
   cover crop keeps the plate seams evenly spaced down the run instead of
   centring one seam and cropping the other two away. */
.how-band .interior-art img { object-position: center top; }
/* The shared interior scrim is angled for left-weighted copy. This band has
   copy on both sides, so it needs an even vertical wash instead: darkest at
   the edges where the text sits, lighter through the middle so the rivets
   still read behind the beam. */
.how-band::before {
  background:
    linear-gradient(90deg, rgb(11 31 44 / 92%) 0%, rgb(11 31 44 / 62%) 34%, rgb(11 31 44 / 62%) 66%, rgb(11 31 44 / 92%) 100%),
    linear-gradient(rgb(11 31 44 / 86%) 0%, rgb(11 31 44 / 46%) 22%, rgb(11 31 44 / 46%) 80%, rgb(11 31 44 / 88%) 100%);
}
/* The hull reads stronger here than behind the demo form, so let more of
   the texture through than the shared .interior-art dimming allows. */
.how-band .interior-art { opacity: 1; }

.run { list-style: none; margin: 0; padding: 0; position: relative; }
/* The run owns the timeline. The beam and the dots reference it by name, so
   the beam's own scaling can't feed back into the progress it's driven by. */
.run { view-timeline: --run block; view-timeline-inset: 50% 40%; }

/* Track: faint, and masked to nothing at both ends so it has no hard start
   or stop. */
.run::before {
  content: ""; position: absolute; z-index: 0;
  left: calc(50% - var(--rail) / 2); top: 0; bottom: 0; width: var(--rail);
  background: linear-gradient(to bottom, transparent, var(--track) 12%, var(--track) 88%, transparent);
}
/* Beam: same geometry, grows from the top. Brightest at its leading edge and
   fading back along its length, so it reads as a head with a tail rather
   than a solid fill. */
.run::after {
  content: ""; position: absolute; z-index: 0;
  left: calc(50% - var(--rail) / 2); top: 0; bottom: 0; width: var(--rail);
  background: linear-gradient(to top, var(--beam-hot) 0%, var(--beam) 14%, rgb(217 122 43 / 0) 100%);
  filter: drop-shadow(0 0 6px var(--beam-glow));
  transform-origin: center top; transform: scaleY(1);
}

/* Each stop is copy on one side of the rail; flipped rows put it on the
   other, so the copy alternates down the run. Generous vertical padding
   gives the beam scroll distance to travel between stops. */
/* The padding also sets how much of a turn each stop gets at the centre of
   the screen: too tight and all three sit in view at once, so the focus
   state never resolves to a single block. */
.stop {
  --stop-pad: clamp(2.5rem, 6vw, 5.5rem);
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 1fr) clamp(3rem, 6vw, 5rem) minmax(0, 1fr);
  align-items: start; padding: var(--stop-pad) 0;
}
/* Both cells pin to row 1: on flipped rows the copy is placed in column 3
   first, and without the explicit row the art asking for column 1 gets
   auto-placed on the row below. */
.stop-copy { grid-column: 1; grid-row: 1; text-align: right; padding-right: clamp(1rem, 2.5vw, 2rem); }
.stop-flip .stop-copy { grid-column: 3; text-align: left; padding-right: 0; padding-left: clamp(1rem, 2.5vw, 2rem); }

/* The art sits opposite the copy: column 3 on a normal row, column 1 on a
   flipped one. Small and quiet — a single scene on the plate, not a full
   illustration — so it reads as an instrument reading next to the copy
   rather than competing with the painted hull behind it. */
.stop-art { grid-column: 3; grid-row: 1; display: flex; align-items: center; }
.stop-flip .stop-art { grid-column: 1; justify-content: flex-end; }
.scene { width: 100%; max-width: 150px; height: auto; display: block; overflow: visible; }

.scene-plate { fill: rgb(11 31 44 / 55%); stroke: rgb(207 228 232 / 22%); stroke-width: 1; }
.scene-bars { fill: var(--beam); }
.scene-label {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 8px;
  letter-spacing: 0.14em; fill: rgb(207 228 232 / 55%);
}
/* Each bar breathes on its own beat, so the row reads as a live waveform
   rather than a static EQ icon. */
@keyframes bar-pulse { 0%, 100% { transform: scaleY(0.45); } 50% { transform: scaleY(1); } }
.scene-bars rect { transform-box: fill-box; transform-origin: center; }

/* Steps 02 and 03 carry the full phone frames (component in site.css),
   scaled to the slot with zoom so their layout shrinks with them (a
   transform would leave the full-size box in the flow). */
.stop-art .phone { zoom: 0.8; }

@media (prefers-reduced-motion: no-preference) {
  .scene-bars rect:nth-child(1) { animation: bar-pulse 1.1s ease-in-out infinite; animation-delay: -0.9s; }
  .scene-bars rect:nth-child(2) { animation: bar-pulse 1.1s ease-in-out infinite; animation-delay: -0.6s; }
  .scene-bars rect:nth-child(3) { animation: bar-pulse 1.1s ease-in-out infinite; animation-delay: -0.3s; }
  .scene-bars rect:nth-child(4) { animation: bar-pulse 1.1s ease-in-out infinite; animation-delay: -0.75s; }
  .scene-bars rect:nth-child(5) { animation: bar-pulse 1.1s ease-in-out infinite; animation-delay: 0s; }
  .scene-bars rect:nth-child(6) { animation: bar-pulse 1.1s ease-in-out infinite; animation-delay: -0.45s; }
  .scene-bars rect:nth-child(7) { animation: bar-pulse 1.1s ease-in-out infinite; animation-delay: -0.15s; }
  .scene-bars rect:nth-child(8) { animation: bar-pulse 1.1s ease-in-out infinite; animation-delay: -1s; }
}

.stop-num {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 34px; line-height: 1; color: var(--surface-light);
  margin: 0 0 6px; letter-spacing: 0.02em;
}
.band-interior .stop h3 { font-size: clamp(20px, 2.2vw, 25px); color: #f4fbff; margin-bottom: 10px; }
.band-interior .stop p { margin: 0; max-width: 38ch; }
/* Right-aligned copy has to hug the rail, so push its measure to the edge. */
.stop:not(.stop-flip) .stop-copy p { margin-left: auto; }

/* The dot: on the rail, level with the step number. Drawn lit; the
   scroll-driven rule below starts it dim and lights it as the beam arrives. */
.stop::before {
  content: ""; position: absolute; z-index: 2;
  left: 50%; top: calc(var(--stop-pad) + 17px);
  width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: 50%;
  background: var(--beam); border: 2px solid var(--beam-hot);
  box-shadow: 0 0 0 4px rgb(217 122 43 / 18%), 0 0 14px var(--beam-glow);
}

/* Focus: only the stop nearest the middle of the screen reads at full
   strength; the others sit back. Dimming the copy alone left the numbers
   shouting, so the whole block fades and lifts very slightly. */
@keyframes stop-focus {
  0%, 100% { opacity: 0.22; filter: blur(0.7px); }
  44%, 56% { opacity: 1; filter: blur(0); }
}

@keyframes beam-grow { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes dot-ignite {
  from { background: var(--deep-800); border-color: rgb(207 228 232 / 35%); box-shadow: 0 0 0 0 transparent, 0 0 0 transparent; }
  to   { background: var(--beam); border-color: var(--beam-hot); box-shadow: 0 0 0 4px rgb(217 122 43 / 18%), 0 0 14px var(--beam-glow); }
}
/* The timeline window is the band between 50% and 60% of the viewport
   height (insets of 50% from the top, 40% from the bottom). The beam starts
   growing when the run's top crosses the 60% line and finishes when its
   bottom crosses the 50% line, so its leading edge always sits inside that
   band. The dots watch the same window; each one's range is offset to where
   it sits down the run, so it ignites as the edge reaches it. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .run::after {
      animation: beam-grow linear both;
      animation-timeline: --run;
      animation-range: cover 0% cover 100%;
    }
    .stop::before {
      animation: dot-ignite linear both;
      animation-timeline: --run;
    }
    /* Each dot lights over a short window around its own position down the
       run, so it ignites as the beam's leading edge reaches it. Positions
       measured at 1440px with the phones in place: 6.3%, 25.5%, 65.9% of the
       run's height. They drift a point or two as copy rewraps at other
       widths, which the 3% windows absorb. If a stop is added, the copy or
       art changes height, or --stop-pad changes, re-measure. */
    .stop:nth-child(1)::before { animation-range: cover 5% cover 8%; }
    .stop:nth-child(2)::before { animation-range: cover 24% cover 27%; }
    .stop:nth-child(3)::before { animation-range: cover 64.5% cover 67.5%; }

    /* Each stop's copy watches its OWN trip through the viewport rather than
       the shared run timeline, so the focus point needs no measuring and
       survives copy changes. The keyframes hold full strength through the
       middle of that trip, which is when the stop sits mid-screen. The dot
       is excluded: it stays lit once the beam has passed it. */
    .stop-copy, .stop-art {
      animation: stop-focus linear both;
      animation-timeline: view(block);
      animation-range: cover 0% cover 100%;
      will-change: opacity;
    }
  }
}

@media (max-width: 860px) {
  /* One column: rail and dots move to the left edge, all copy left-aligned. */
  .run::before, .run::after { left: calc(14px - var(--rail) / 2); }
  .stop, .stop-flip { grid-template-columns: minmax(0, 1fr); padding-left: 44px; }
  .stop-copy, .stop-flip .stop-copy { grid-column: 1; text-align: left; padding: 0; }
  .stop:not(.stop-flip) .stop-copy p { margin-left: 0; }
  .stop::before { left: 14px; }
  .band-interior .stop p { max-width: none; }
  /* The art drops below the copy rather than sitting beside it — there's no
     second column left to put it in — and stays small and left-aligned so
     it reads as a detail, not a hero image. */
  .stop-art, .stop-flip .stop-art { grid-column: 1; grid-row: 2; justify-content: flex-start; margin-top: 18px; }
  .scene { max-width: 108px; }
  .stop-art .phone { zoom: 0.66; }
}

/* ── BOOK A DEMO · hull band ──────────────────────────────────────────── */
.demo-band { padding: clamp(4rem, 8vw, 6.5rem) 0; }
.demo-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.demo-band .band-title { margin-bottom: 16px; }
/* Light card for the form: the inputs, labels and button keep the page's
   own light-ground styling instead of needing a dark variant. */
.demo-card {
  padding: 28px 26px; background: var(--color-bg); color: var(--color-text);
  border-radius: var(--radius-lg);
  box-shadow: 0 0 0 1px rgb(255 255 255 / 8%), 0 30px 60px -20px rgb(0 0 0 / 70%);
}
.demo-card p { color: var(--color-text); max-width: none; }
.demo-card p.text-muted { color: color-mix(in srgb, var(--color-text) 65%, transparent); }
@media (max-width: 860px) {
  .demo-grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 900px) {
  .hero-band { min-height: 0; padding-top: 3.5rem; }
  .hero-copy { width: 100%; }
  /* Art moves behind the copy. A scrim over the photo keeps the headline
     readable; dimming the image alone left it competing with the text. */
  .hero-art {
    width: 100%; opacity: 0.55;
    mask-image: linear-gradient(#000 70%, transparent 98%);
  }
  .hero-band::after {
    content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background: linear-gradient(rgb(11 22 29 / 72%), rgb(11 22 29 / 40%) 60%, var(--deep-900));
  }
  /* The lede's 44ch runs past a phone's width; cap it to the column instead. */
  .hero-band .lede { max-width: none; }
  /* Let the placeholder label wrap rather than overflow the viewport. */
  .slot-tag { max-width: calc(100% - 2rem); white-space: normal; text-align: center; }
}
