/* ==========================================================================
   ShipOra home — the scroll film and the dock that never leaves.

   The whole point of this page: the film scrubs behind, the rate search stays
   on screen the entire way down. Two stacked layers in one grid cell — the
   acts give the section its height, the dock rail overlays it and holds a
   sticky card. No JS positions anything, so the dock cannot end up detached
   from the viewport if a script fails.
   ========================================================================== */

.film {
  display: grid;
  position: relative;
  background: var(--ink);
}
.film > * { grid-column: 1; grid-row: 1; }

/* ---------- the acts ---------- */
.act { position: relative; }
.act-sticky {
  position: sticky; top: 0; height: 100vh; height: 100svh;
  overflow: hidden; background: var(--ink);
}
.act-film { display: block; width: 100%; height: 100%; }

/* Legibility veil. The footage is a grey overcast palette, so text needs a
   floor under it; the gradient keeps the middle of the frame clear. */
.act-veil {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(90deg, rgba(10,13,18,.86) 0%, rgba(10,13,18,.55) 34%, rgba(10,13,18,.12) 58%, rgba(10,13,18,.42) 100%),
    linear-gradient(180deg, rgba(10,13,18,.55) 0%, rgba(10,13,18,0) 26%, rgba(10,13,18,0) 62%, rgba(10,13,18,.62) 100%);
}
.hud {
  position: absolute; left: clamp(18px, 4vw, 44px); bottom: 22px;
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: rgba(255,255,255,.5); display: flex; gap: 8px; align-items: baseline;
}
.hud b { font-weight: 600; color: rgba(255,255,255,.78); font-variant-numeric: tabular-nums; }
.hud em { font-style: normal; }

/* ---------- per-act copy ---------- */
.act-copy {
  /* Pulled back over the sticky canvas: both are sticky siblings, so without
     the negative margin the copy sits a full screen BELOW its own footage. */
  position: sticky; top: 0; height: 100vh; height: 100svh; margin-top: -100vh;
  display: flex; flex-direction: column; justify-content: center;
  padding: 0 clamp(18px, 5vw, 60px);
  width: min(430px, 42vw);
  color: #fff; pointer-events: none;
  opacity: 0; transform: translateY(14px);
  transition: opacity .5s ease, transform .5s ease;
}
.act-copy.is-live { opacity: 1; transform: none; }
.act-copy .eyebrow {
  font-size: 11px; letter-spacing: .2em; text-transform: uppercase;
  color: rgba(255,255,255,.6); margin: 0 0 14px;
}
.act-copy h1, .act-copy h2 {
  font-size: clamp(26px, 3.2vw, 40px); line-height: 1.08; margin: 0 0 14px;
  color: #fff; letter-spacing: -.02em; max-width: 12ch;
}
.act-copy p { margin: 0 0 20px; color: rgba(255,255,255,.78); font-size: 15px; line-height: 1.6; max-width: 38ch; }
.spec { list-style: none; margin: 0; padding: 0; border-top: 1px solid rgba(255,255,255,.16); }
.spec li {
  display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: baseline;
  padding: 9px 0; border-bottom: 1px solid rgba(255,255,255,.11);
  font-size: 13.5px; color: rgba(255,255,255,.72);
}
.spec li b { color: #fff; font-weight: 600; font-variant-numeric: tabular-nums; }

/* ---------- the dock ---------- */
.dock-rail { pointer-events: none; z-index: 5; }
.dock-hold {
  position: sticky; top: var(--header-h);
  height: calc(100vh - var(--header-h)); height: calc(100svh - var(--header-h));
  /* Right middle: hard over to the viewport's right edge rather than the
     1200px wrap, and vertically centred with the padding weighted top-heavy
     so the card settles a touch below the middle. */
  display: flex; align-items: center; justify-content: flex-end;
  width: 100%; margin: 0;
  padding: 80px clamp(20px, 3.2vw, 46px) 32px;
}
.dock-card {
  pointer-events: auto;
  width: min(452px, 100%);
  background: rgba(255,255,255,.90);
  backdrop-filter: blur(26px) saturate(180%);
  -webkit-backdrop-filter: blur(26px) saturate(180%);
  border: 1px solid rgba(255,255,255,.6);
  border-radius: var(--r-lg);              /* rounded again, 2026-09-02 */
  box-shadow: 0 24px 70px rgba(0,0,0,.42), 0 2px 10px rgba(0,0,0,.22);
  overflow: hidden;
}
@supports not (backdrop-filter: blur(4px)) { .dock-card { background: #fff; } }

.dock-head { padding: 15px 18px 0; }
.dock-head .dock-kicker {
  font-size: 10.5px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 3px;
}
.dock-head .dock-title { font-size: 16px; font-weight: 650; color: var(--ink); margin: 0 0 12px; letter-spacing: -.01em; }

/* The markup is the site's standard #qm-search, so app.js drives it unchanged.
   Only the layout is restated here: a column, not the wide hero bar. */
.dock .search-tabs { border-bottom: 1px solid var(--line); background: transparent; }
.dock .search-tab {
  /* Four equal cells, label centred in each. The base rule is a flex row with
     no justify-content, so the text sat hard against the left of its cell and
     "Parcel" looked pinned to the card edge while "LCL" left a hole on the
     right. The sliding ink is sized to the cell, so centring the label centres
     it under the ink too. */
  flex: 1 1 0; min-width: 0; gap: 0;
  justify-content: center; text-align: center;
  padding: 11px 4px; font-size: 12px; letter-spacing: -.01em;
  white-space: nowrap;
}
.dock .search-tab svg { display: none; }
.dock .search-body {
  grid-template-columns: 1fr 1fr;
  padding: 12px; gap: 8px;
}
.dock .search-body > .field:nth-child(1),
.dock .search-body > .field:nth-child(2) { grid-column: span 2; }
.dock .field { min-height: 62px; }
.dock .field-sub { display: none; }
.dock .search-submit { grid-column: span 2; }
.dock .search-submit .btn { width: 100%; height: 46px; }
.dock-foot {
  padding: 0 12px 12px; margin: 0;
  font-size: 11.5px; color: var(--muted); text-align: center;
}

/* ---------- fail open ---------- */
.film-note {
  position: relative; z-index: 8; margin: 0;
  background: var(--warn-soft); color: var(--warn);
  padding: 10px 16px; font-size: 13px; text-align: center;
}
.film.no-film .act-sticky { display: none; }
.film.no-film { background: var(--ink); }
.film.no-film .act { padding: 40px 0; }
.film.no-film .act-copy {
  position: static; height: auto; margin-top: 0; opacity: 1; transform: none;
  width: min(560px, 88vw); margin: 0 auto; pointer-events: auto;
}
.film.no-film .dock-hold { position: static; height: auto; padding: 30px 16px; }

/* ---------- the collapse toggle ----------
   Visually hidden rather than display:none so it stays keyboard reachable on a
   phone; the label is the tap target. Desktop never consults it. */
.dock-toggle {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  opacity: 0; clip-path: inset(50%); pointer-events: none;
}
.dock-peek { display: none; }

/* ---------- phones: the dock collapses so the film is not buried ----------
   It used to sit at the bottom fully expanded: 374px of a 844px screen, 44%,
   and 52% once the header is counted. The four mode tabs stay on screen the
   whole way down (that was the brief), everything else folds behind one row. */
@media (max-width: 1023px) {
  .act-copy {
    width: auto; max-width: 560px;
    justify-content: flex-start;
    padding-top: calc(var(--header-h) + 18px);
  }
  .act-copy p { max-width: none; }
  .dock-hold { align-items: flex-end; justify-content: center; padding: 14px 16px 16px; max-width: none; }
  .dock-card { width: 100%; }
  .dock-head { display: none; }
  .dock .search-body { grid-template-columns: 1fr 1fr; }

  /* collapsed is the default on a phone */
  .dock .search-body,
  .dock .dock-foot { display: none; }
  .dock-peek {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 0 14px; min-height: 48px; cursor: pointer;
    font-size: 13.5px; color: var(--muted);
    -webkit-tap-highlight-color: transparent;
  }
  /* The dock sits at the bottom and opens upward, so collapsed points UP
     (the SVG's own direction) and flips down to mean close. */
  .dock-peek-chev { width: 18px; height: 18px; color: var(--ink); transition: transform .22s ease; }
  .dock-toggle:focus-visible ~ .dock-peek { outline: 2px solid var(--ink); outline-offset: -2px; }

  /* expanded */
  .dock-toggle:checked ~ .search-body { display: grid; }
  .dock-toggle:checked ~ .dock-foot { display: block; }
  .dock-toggle:checked ~ .dock-peek { color: var(--ink); font-weight: 600; }
  .dock-toggle:checked ~ .dock-peek .dock-peek-chev { transform: rotate(180deg); }

  /* The dock no longer covers the lower frame, so stop crushing it to near
     black down there or the reclaimed footage arrives as a dark band. */
  .act-veil {
    background:
      linear-gradient(180deg, rgba(10,13,18,.80) 0%, rgba(10,13,18,.30) 38%, rgba(10,13,18,.34) 72%, rgba(10,13,18,.62) 100%);
  }
}
@media (max-width: 560px) {
  .act-copy h1, .act-copy h2 { font-size: 25px; max-width: none; }
  /* four labels in 390px: "Full container" runs into "LCL" at 12px */
  .dock .search-tab { font-size: 11px; padding: 11px 2px; }
  .act-copy p { font-size: 14px; margin-bottom: 14px; }
  .spec li { font-size: 12.5px; padding: 7px 0; }
  /* The old rule here hid every spec row past the fourth on ANY narrow screen,
     which silently lost "13.6 m curtainsider · 33 Euro pallets" from the Road
     act even on phones with room to spare. The real constraint was never width,
     it is HEIGHT: the act dots sit at a fixed y and the phone dock rises to meet
     them. Measured on the rebuilt stage — 390x844 leaves a 191px gap and
     414x896 leaves 243px, but 360x640 overlaps by 13px and the dock's tab strip
     then swallows every click on the dots. So the row is dropped on SHORT
     screens only, and tall phones get the full table. */
}
@media (max-width: 560px) and (max-height: 740px) {
  .spec li:nth-child(n+5) { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .dock-peek-chev { transition: none; }
}

@media (prefers-reduced-motion: reduce) {
  .act-copy { transition: none; }
}

/* ==========================================================================
   AUTOPLAY STAGE (2026-09-15) — replaces the scroll scrubber.

   Four acts now live in ONE 100vh stage, stacked in the same grid cell, and
   the film plays itself: van -> truck -> ship -> plane, crossfading, on a
   loop. Copy on the left swaps with the footage.

   Everything below is additive and beats the old .act rules by being later in
   the file. The old rules are kept because .act-copy, .spec and .hud are all
   reused verbatim; only the layout that made the page four screens tall goes.
   ========================================================================== */

.film-stage .film-track {
  position: relative;
  height: 100vh; height: 100svh;
  min-height: 560px;
  overflow: hidden;
}

/* ---- the stacked canvases ---- */
.film-stage .stage { position: absolute; inset: 0; background: var(--ink); }
.film-stage .stage .act-film {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  transition: opacity 900ms ease;
  will-change: opacity;
}
.film-stage .stage .act-film.is-live { opacity: 1; }
.film-stage .stage .act-veil,
.film-stage .stage .hud { z-index: 2; }

/* ---- copy column ---- */
.film-stage .stage-copy {
  position: absolute; inset: 0; z-index: 3;
  display: flex; flex-direction: column; justify-content: center;
  padding: 0 clamp(18px, 5vw, 60px);
  width: min(460px, 46vw);
  color: #fff; pointer-events: none;
}
/* The visible headline is the act's own h2, which changes with the footage.
   This h1 stays for document structure only. */
.film-stage .sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.film-stage .copy-deck h2 {
  font-size: clamp(27px, 3.1vw, 40px); line-height: 1.06;
  letter-spacing: -.025em; margin: 0 0 13px; max-width: 13ch; color: #fff;
}
.film-stage .copy-deck p { margin: 0 0 16px; font-size: 14.5px; max-width: 36ch; }
.film-stage .copy-deck .spec li { padding: 7px 0; font-size: 13px; }

/* The deck holds all four copy blocks in the same box. Only the live one is
   painted; the others are taken out of flow so the deck's height is the tallest
   block and the headline above it never jumps between acts. */
/* All four blocks share ONE grid cell, so the deck is exactly as tall as its
   tallest act and the dots below can never be overrun by the longest spec list.
   A min-height here would be a magic number that breaks the day the copy
   changes; this measures itself. */
.film-stage .copy-deck { display: grid; }
.film-stage .copy-deck .act-copy {
  grid-area: 1 / 1;
  width: 100%; margin: 0; padding: 0; height: auto;
  display: block;
  opacity: 0; transform: translateY(10px);
  transition: opacity 520ms ease, transform 520ms ease;
}
/* Hidden acts must not eat clicks on the live one. */
.film-stage .copy-deck .act-copy { visibility: hidden; }
.film-stage .copy-deck .act-copy.is-live { visibility: visible; }
.film-stage .copy-deck .act-copy.is-live { opacity: 1; transform: none; }

/* ---- act dots ---- */
.film-stage .acts-nav {
  display: flex; gap: 9px; margin-top: 26px; pointer-events: auto;
}
.film-stage .acts-nav .dot {
  appearance: none; border: 0; background: none; padding: 7px 0 0;
  width: 42px; cursor: pointer;
}
.film-stage .acts-nav .dot i {
  display: block; height: 2px; border-radius: 2px;
  background: rgba(255,255,255,.26); transition: background .3s ease;
}
.film-stage .acts-nav .dot[aria-selected="true"] i { background: var(--accent); }
.film-stage .acts-nav .dot:hover i { background: rgba(255,255,255,.55); }
.film-stage .acts-nav .dot:focus-visible i { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---- dock sits in the same 100vh box ---- */
.film-stage .dock-rail { z-index: 5; }
.film-stage .dock-hold { position: absolute; inset: 0; height: auto; }

/* ---- no film: the stage must not collapse to nothing ---- */
.film-stage.no-film .stage { background: var(--ink); }
.film-stage.no-film .act-veil { background: none; }

/* Reduced motion: no crossfade, no autoplay (film.js holds act 1). */
@media (prefers-reduced-motion: reduce) {
  .film-stage .stage .act-film,
  .film-stage .copy-deck .act-copy { transition: none; }
}

@media (max-width: 900px) {
  .film-stage .stage-copy { width: auto; max-width: 560px; padding-top: 86px; justify-content: flex-start; }
  .film-stage .film-track { height: auto; min-height: 100svh; }
}

/* Short phones, last word. This has to sit after the max-width:900px block
   above: same specificity, and the later rule wins. Putting it earlier is why
   360x640 still cleared the dock by a single pixel on the first attempt. */
@media (max-width: 560px) and (max-height: 740px) {
  .film-stage .stage-copy { padding-top: 60px; }
  .film-stage .copy-deck h2 { font-size: 23px; }
  .film-stage .acts-nav { margin-top: 18px; }
}
