/* ==========================================================================
   layout.css — containers, rhythm, header/footer chrome, section scaffolds
   Deliberately plain: the visual design pass replaces the section rules
   below. The container / section / grid primitives are meant to survive it.
   ========================================================================== */

/* --- Primitives ---------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--w-page);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-width: 820px; }

.section { padding-block: var(--section-y); }
.section--subtle { background: var(--c-bg-subtle); }
/* Dark-only: "inverse" now means a deeper panel, not a light one. Flipping to
   paper here would drop a white slab into the middle of the page. Text colour
   is inherited deliberately — it does not need to change. */
.section--inverse { background: var(--c-bg-deep); }

.section__head { display: grid; gap: var(--s-3); margin-bottom: var(--s-6); max-width: var(--w-prose); }

.stack     { display: grid; gap: var(--s-4); }
.stack--lg { display: grid; gap: var(--s-6); }
.row       { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; }

.grid { display: grid; gap: var(--s-5); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); }

/* auto-fit means the column count follows the available width, which is right
   for a list of unknown length and wrong for a set of exactly four: at the full
   1200px container, .grid--2 fits three 20rem tracks and leaves the fourth card
   alone on its own row. --pair pins it to two columns once there is room for
   two, so four cards read as 2x2 and stack to one column below that. */
.grid--pair { grid-template-columns: 1fr; }
@media (min-width: 46rem) {
  .grid--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Any wide block (tables, timelines) scrolls inside itself, never the page. */
.scroll-x { overflow-x: auto; overscroll-behavior-x: contain; }

/* --- Header -------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  /* Heavier scrim than the light theme carried: at 88% the hero lockup was
     legible straight through the blurred bar as it scrolled under. */
  background: color-mix(in srgb, var(--c-bg) 94%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur) var(--ease);
}
.site-header[data-scrolled="true"] { border-bottom-color: var(--c-divider); }

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  min-height: 4.25rem;
}

.brand {
  display: inline-flex;
  align-items: center;
}

/* The Futurewave wordmark is 309x41 — roughly 7.5:1, against the 3:1 of the
   two-line lockup this replaced. Held at 1.5rem it would run ~180px and crowd
   the nav, so the height comes down and the rendered width lands near 140px. */
.brand__logo {
  height: 1.125rem;
  width: auto;
  color: var(--c-ink);
}

.site-nav ul { display: flex; gap: var(--s-5); }
.site-nav a {
  font-size: var(--t-small);
  color: var(--c-ink-muted);
  text-decoration: none;
}
.site-nav a:hover, .site-nav a[aria-current="true"] { color: var(--c-ink); }

.nav-toggle { display: none; }

@media (max-width: 860px) {
  .site-nav { display: none; }
  .site-nav[data-open="true"] {
    display: block;
    position: absolute;
    inset: 100% 0 auto 0;
    padding: var(--s-5) var(--gutter);
    background: var(--c-bg);
    border-bottom: 1px solid var(--c-border);
  }
  .site-nav[data-open="true"] ul { flex-direction: column; gap: var(--s-4); }
  .nav-toggle { display: inline-flex; }
  .site-header__cta { display: none; }
}

/* --- Section scaffolds (placeholder styling — replaced in the design pass) */
/* --- Hero ----------------------------------------------------------------
   Centred stack: logo, eyebrow, paragraph, one CTA, dates. The logo is the
   dominant element on first load; the whole stack is sized to clear a 900px
   viewport without scrolling on desktop (mobile is allowed to scroll).
   min-height subtracts the sticky header so "one screen" means one screen.
   svh, not vh, so mobile browser chrome does not push the CTA out of view. */
.hero {
  position: relative;
  isolation: isolate;   /* keeps the backdrop's z-index local to the hero */
  overflow: hidden;     /* the globe is wider than the page on small screens */
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100svh - var(--h-header));
  padding-block: var(--s-7);
  text-align: center;
}

/* --- Hero backdrop: LED-panel globe ---------------------------------------
   Three stacked layers, all centred in one grid cell:
     bloom   - unmasked, blurred copy; the glow a panel throws past its dots
     globe   - the dot matrix itself
     scrim   - darkens the middle so the hero copy stays legible

   The source GIF is 128x128, upscaled with image-rendering: pixelated, so one
   source pixel == one LED. --led-cell is DERIVED from the rendered size
   (size / 128), so the mask tile and the image's pixel grid are the same
   measurement and stay aligned at any width - no moire, no breakpoint maths.

   Both light layers blend with `screen`, which is what makes this read as an
   emissive panel rather than a picture of one: black contributes nothing, so
   the GIF's black background disappears instead of stamping a dark square on
   the page, and only the lit dots add light. --c-bg on the backdrop gives
   `screen` a known ground to blend against. */
.hero__backdrop {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: grid;
  place-items: center;
  background: var(--c-bg);
  pointer-events: none;
}
.hero__backdrop > * { grid-area: 1 / 1; }

.hero__globe {
  --globe-size: clamp(18rem, 96vmin, 52rem);
  --led-cell: calc(var(--globe-size) / 128);

  width: var(--globe-size);
  height: var(--globe-size);
  background-image: url("../img/globe-led.gif");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  image-rendering: pixelated;
  mix-blend-mode: screen;

  /* Two mask layers intersected: one dot per LED, then a horizontal scanline
     gutter. Scanlines ride inside the mask rather than sitting on top as their
     own rectangle, so they only ever darken the panel, never the page.

     Dot coverage is generous (44%/50% of the cell, 3-of-4 scanlines) because
     coverage is the one dial that does NOT move the contrast numbers: AA is
     measured on the brightest pixel, so lighting more area at the same peak
     makes the panel read solid without eating into the ceiling above. */
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000 0 44%, transparent 50%),
                      repeating-linear-gradient(to bottom, #000 0 3px, transparent 3px 4px),
                      radial-gradient(circle at 50% 50%, rgb(0 0 0 / 0.175) 0 60%, rgb(0 0 0 / 0.34) 78%, #000 95%);
          mask-image: radial-gradient(circle at 50% 50%, #000 0 44%, transparent 50%),
                      repeating-linear-gradient(to bottom, #000 0 3px, transparent 3px 4px),
                      radial-gradient(circle at 50% 50%, rgb(0 0 0 / 0.175) 0 60%, rgb(0 0 0 / 0.34) 78%, #000 95%);
  -webkit-mask-size: var(--led-cell) var(--led-cell), 100% 4px, 100% 100%;
          mask-size: var(--led-cell) var(--led-cell), 100% 4px, 100% 100%;
  -webkit-mask-repeat: repeat, repeat, no-repeat;
          mask-repeat: repeat, repeat, no-repeat;
  -webkit-mask-composite: source-in;
          mask-composite: intersect;

  /* Not a taste value - a contrast ceiling. The copy sits directly on the
     globe, and per-pixel contrast is what WCAG measures, so a single bright
     LED under a glyph is a real failure even when the grid reads as grey to
     the eye. The binding element is .lead (#B5B2AC, 18px), which needs its
     background at or below #454545.

     That ceiling only applies where text actually is. At 96vmin the globe is
     wider than the copy, so the third mask layer above holds the centre down
     to ~30% alpha (keeping the text band inside the ceiling) and releases the
     limb to full - which is why this can sit at 0.46 rather than 0.14. The
     bright edge is free contrast-wise and reads as a planet's limb.

     Raising either the base opacity or the mask's centre stop eats into the
     text band. Re-measure before touching either. */
  opacity: 0.90;
}

.hero__globe--bloom {
  -webkit-mask-image: radial-gradient(circle at 50% 50%, rgb(0 0 0 / 0.10) 0 60%, rgb(0 0 0 / 0.30) 78%, #000 95%);
          mask-image: radial-gradient(circle at 50% 50%, rgb(0 0 0 / 0.10) 0 60%, rgb(0 0 0 / 0.30) 78%, #000 95%);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  filter: blur(calc(var(--led-cell) * 2.4));
  mix-blend-mode: screen;
  opacity: 0.16;
}

/* Sized off the hero, not the globe: the copy needs cover wherever it sits. */
.hero__scrim {
  width: 100%;
  height: 100%;
  /* Depth, not legibility - the opacity ceiling above does the real work.
     A scrim alone cannot: the copy spans ~60% of the hero height, so any
     gradient tight enough to look deliberate leaves the outer lines
     uncovered. This one only deepens the centre. */
  background: radial-gradient(
    ellipse 70% 62% at 50% 50%,
    rgb(23 22 26 / 0.24) 0%,
    rgb(23 22 26 / 0.18) 55%,
    rgb(23 22 26 / 0.06) 100%
  );
}

/* An animated background is exactly what this preference is for. Swap to a
   single frame of the same treatment - the CSS optics are unchanged. */
@media (prefers-reduced-motion: reduce) {
  .hero__globe { background-image: url("../img/globe-led-static.png"); }
}

/* --- Hero content (sits above .hero__backdrop) ---------------------------- */
.hero__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-5);
  max-width: 52rem;
}

/* The logo is the h1 - the accessible name lives in a visually-hidden span, so
   the page keeps exactly one h1 and the heading order stays intact. */
.hero__logo {
  margin: 0 0 var(--s-2);
  line-height: 0;
}

/* Height-driven, not width-driven: on a short viewport the logo is the first
   thing that must give, or it pushes the CTA below the fold. At a 900px
   viewport 21vh ~= 189px tall ~= 580px wide - dominant without crowding.
   Overrides the `svg { max-width:100%; height:auto }` default in base.css. */
.hero__logo svg {
  height: clamp(3.25rem, 21vh, 11.5rem);
  width: auto;
  max-width: min(88vw, 44rem);
  margin-inline: auto;
  color: var(--c-ink); /* the lockup is drawn with fill="currentColor" */
}

.hero__eyebrow { color: var(--c-accent); }

.hero__lead {
  max-width: 38rem; /* ~2 lines on desktop */
  margin-inline: auto;
  color: var(--c-ink-muted);
  font-size: var(--t-lead);
  line-height: 1.5;
}

.hero__cta { margin-top: var(--s-2); }

/* Carries .eyebrow in the markup, so the treatment (mono, uppercase,
   wide-tracked, accent) comes from there and is not restated here - the two
   lines stay identical by construction. This rule is spacing only. */
.hero__dates { margin-top: var(--s-3); }

@media (max-width: 640px) {
  /* Let the stack breathe out of a fixed screen on small viewports. */
  .hero { min-height: 0; padding-block: var(--s-8) var(--s-7); }
}

.itinerary { display: grid; gap: var(--s-4); }
.itinerary__day {
  display: grid;
  grid-template-columns: 6.5rem 1fr;
  gap: var(--s-5);
  padding-block: var(--s-5);
  border-top: 1px solid var(--c-divider); /* structural row rule, 2.14:1 */
}
/* Mono + accent: the day numeral is the one place the copper earns a repeat,
   and --c-ink-faint alone was too quiet to anchor a row on dark. */
.itinerary__day > .day-label {
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  font-size: var(--t-caption);
  color: var(--c-accent);
  padding-top: 0.25em;
}
@media (max-width: 640px) {
  .itinerary__day { grid-template-columns: 1fr; gap: var(--s-2); }
}

/* Accordions are the element most likely to go muddy in a straight recolour:
   on dark, a 1px rule plus a text-colour shift is not enough feedback. Hover
   and open both get a surface change as well, and the rule is --c-divider. */
.faq__item { border-top: 1px solid var(--c-divider); }
.faq__item:last-child { border-bottom: 1px solid var(--c-divider); }

.faq__item summary {
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-4);
  margin-inline: calc(-1 * var(--s-4));
  border-radius: var(--r-md);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary:hover { background: var(--c-bg-hover); color: var(--c-ink); }
.faq__item[open] > summary { color: var(--c-accent); }
.faq__list { max-width: 820px; } /* keeps the measure; left edge stays on the page grid */
.faq__item summary::after { content: "+"; color: var(--c-accent); font-size: 1.25em; line-height: 1; }
.faq__item[open] summary::after { content: "–"; }
.faq__item > div { padding-bottom: var(--s-5); max-width: var(--w-prose); color: var(--c-ink-muted); }

/* --- Fact rows (#practical "Key facts") -----------------------------------
   The page has no <table>; this card is where tabular reading happens. On dark
   the stacked .stack blocks ran together, so each row after the first gets a
   --c-divider hairline and the label keeps the mono eyebrow treatment. */
.fact-rows { display: grid; gap: 0; }
.fact-rows > .stack {
  gap: var(--s-2);
  padding-block: var(--s-5);
}
.fact-rows > .stack + .stack { border-top: 1px solid var(--c-divider); }
.fact-rows > .stack:first-child { padding-top: var(--s-4); }
.fact-rows > .stack:last-child { padding-bottom: 0; }

/* --- Apply form ---------------------------------------------------------- */
.apply { }
.apply__grid { display: grid; gap: var(--s-7); align-items: start; }
@media (min-width: 900px) { .apply__grid { grid-template-columns: 1fr 1.15fr; } }

.form { display: grid; gap: var(--s-5); }
.form__row { display: grid; gap: var(--s-4); }
@media (min-width: 560px) { .form__row--2 { grid-template-columns: 1fr 1fr; } }

.form__status { font-size: var(--t-small); }
.form__status[data-state="error"]   { color: var(--c-error); }
.form__status[data-state="success"] { color: var(--c-success); }
.form__status:focus { outline: none; }

/* After a successful send the form folds away; only the confirmation and the
   contact line remain. */
.form.is-submitted > *:not(.form__status):not(.form__contact) { display: none; }
.form.is-submitted .form__status {
  font-size: var(--t-lead, 1.25rem);
  animation: form-confirm-in 0.3s ease-out;
}
@keyframes form-confirm-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* --- Footer -------------------------------------------------------------- */
.site-footer {
  padding-block: var(--s-7) var(--s-6);
  border-top: 1px solid var(--c-divider);
  font-size: var(--t-small);
  color: var(--c-ink-muted);
}
.site-footer__about {
  display: grid;
  gap: var(--s-3);
  margin-bottom: var(--s-7);
}
.site-footer__about > p:not(.eyebrow) { max-width: var(--w-prose); }
.site-footer__logo { justify-self: start; display: inline-flex; }
.site-footer__logo img { display: block; height: 1.75rem; width: auto; }
.site-footer__inner { display: flex; flex-wrap: wrap; gap: var(--s-5); justify-content: space-between; }

/* --- Card content alignment ----------------------------------------------
   .card is a grid, so by default its rows stretch to fill whatever height the
   tallest card in the row forces. That pushed the body copy of the SHORT cards
   down the card, and a row of cards then read with its paragraphs at four
   different heights. Pinning the content to the start is what keeps headings
   and first lines on one baseline across a row.
   ------------------------------------------------------------------------- */
.card.stack,
.card > .stack,
.card__body.stack { align-content: start; }

/* --- Bulleted lists inside cards -----------------------------------------
   The global reset strips list markers (ul { list-style: none }) because most
   lists on this page are layout, not prose. The qualification lists ARE prose,
   so they opt the markers back in as a drawn glyph rather than a UA bullet —
   a UA disc cannot be coloured or positioned against a hanging indent.
   ------------------------------------------------------------------------- */
.bullets > li {
  position: relative;
  padding-left: 1.6em;
  color: var(--c-ink-muted);
}
.bullets > li::before {
  content: "";
  position: absolute;
  left: 0.15em;
  top: 0.62em;
  width: 0.45em;
  height: 0.45em;
  border-radius: var(--r-pill);
  background: var(--c-accent);
}
/* The disqualification list reads as exclusion, so its marker is an open ring
   rather than a filled dot. Same weight on the page, opposite sign. */
.bullets--out > li::before {
  background: none;
  border: 1.5px solid var(--c-ink-faint);
}

/* --- Proposition ---------------------------------------------------------
   One paragraph, one full-bleed screen. The value proposition stated in full,
   directly below the fold, before the page starts arguing for it. It is the
   only thing on its screen on purpose: nothing competes with it and nothing
   scrolls past it.

   No .section padding here — the screen IS the padding, and the copy is
   optically centred with the corner markers pinned to the frame.
   ------------------------------------------------------------------------- */
.proposition {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-items: center;
  padding-block: var(--s-8);
  border-block: 1px solid var(--c-divider);
}
.proposition__inner { position: relative; width: 100%; }

.proposition__mark {
  position: absolute;
  top: calc(var(--s-8) * -1);
  font-family: var(--font-mono);
  font-size: var(--t-caption);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-ink-faint);
}
/* Offset by the container gutter: absolute left/right:0 resolves to the padding
   box, outside the gutter, which left the markers wider than the copy and every
   other section. */
.proposition__mark--index { left: var(--gutter); }
.proposition__mark--file  { right: var(--gutter); color: var(--c-accent); }

/* The same two markers on every other section: a row above the section head,
   index left, name right. Identical classes and type to the proposition
   screens; only the positioning changes (in the flow, not pinned to a frame). */
.section__marks {
  grid-column: 1 / -1; /* spans the two-column .apply__grid */
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  margin-bottom: var(--s-6);
}
.section__marks .proposition__mark { position: static; }

.proposition__copy {
  font-family: var(--font-serif);
  font-size: var(--t-h2); /* same size as every H2 */
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--c-ink);
  max-width: min(100%, 26ch);
  text-wrap: pretty;
}

/* The five claims under the statement. Small, mono, ruled: they read as a
   specification list against the serif sentence rather than as more prose,
   which is the whole reason they can sit this close to it. */
.proposition__usps {
  display: grid;
  gap: var(--s-2);
  margin-top: var(--s-6);
  max-width: min(100%, 34ch);
}
.proposition__usps > li {
  position: relative;
  padding-left: var(--s-5);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-wide);
  color: var(--c-ink-muted);
}
.proposition__usps > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: var(--s-3);
  height: 1px;
  background: var(--c-accent);
}
/* The measure has to give way before the type does — at tablet width a tight
   measure on display type leaves a column two words wide. */
@media (max-width: 64rem) {
  .proposition__copy { max-width: min(100%, 30ch); }
}
@media (max-width: 40rem) {
  .proposition {
    min-height: 0;
    padding-block: var(--s-9);
  }
  .proposition__inner > .proposition__mark { position: static; display: block; margin-bottom: var(--s-5); }
  .proposition__inner > .proposition__mark--file { display: none; }
  .proposition__copy { max-width: none; line-height: 1.2; }
  .proposition__usps { max-width: none; margin-top: var(--s-5); }
}

/* --- Who's taking you ----------------------------------------------------
   A dossier, not a card row: three numbered blocks across, then the trust line
   as the section's closing panel. Cards were the wrong container — they made
   Futurewave read as one more feature of the trip rather than the reason the
   trip exists.
   ------------------------------------------------------------------------- */
.studio__proof {
  display: grid;
  gap: var(--s-6);
  margin-top: var(--s-7);
}
@media (min-width: 56rem) {
  .studio__proof { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6); }
}
.studio__block {
  display: grid;
  gap: var(--s-3);
  align-content: start;
  padding-top: var(--s-4);
  border-top: 2px solid var(--c-divider);
}
.studio__num {
  font-family: var(--font-mono);
  font-size: var(--t-caption);
  font-weight: 500;
  letter-spacing: var(--ls-wide);
  color: var(--c-accent);
}


/* --- Itinerary decision line ---------------------------------------------
   The second register: what the day lets you decide, as against where it takes
   you. Set apart from the body so a scanner can read only these five lines and
   still get the offer.
   ------------------------------------------------------------------------- */
.itinerary__decision {
  padding-left: var(--s-4);
  border-left: 2px solid var(--c-accent);
  font-size: var(--t-small);
  font-style: italic;
  color: var(--c-ink);
}

/* Arrival and departure are travel, not programme. They carry a name where the
   five working days carry a number, and they sit back a step so the count the
   rest of the page promises — five on the ground — survives a glance. */
/* Specificity has to clear .itinerary__day > .day-label, which sets the accent
   colour the numbered days rely on. */
.itinerary__day > .day-label--bookend {
  color: var(--c-ink-faint);
  letter-spacing: var(--ls-wide);
}
.itinerary__day--bookend { border-top-style: dashed; }

/* --- Draft-only helpers --------------------------------------------------
   Everything below this line is scaffolding for the draft, not part of the
   design. It comes out before launch.
   ------------------------------------------------------------------------- */

/* Parked sections: hidden but not deleted. The bare `hidden` attribute is not
   enough on its own — these sections set their own `display`, and any explicit
   display wins over the UA's `[hidden] { display: none }`. The class carries the
   !important that makes the attribute stick.

   Both attribute and class sit on the markup on purpose: the attribute keeps the
   section out of the accessibility tree and out of in-page find, the class wins
   the cascade. Restore by removing both. */
.is-parked { display: none !important; }

/* Unwritten copy that is blocked on a fact rather than on a decision. Loud by
   design — these blocks must not reach a prospect as they stand. */
.placeholder {
  padding: var(--s-4);
  border: 1px dashed var(--c-warning);
  border-radius: var(--r-md);
  font-family: var(--font-mono);
  font-size: var(--t-caption);
  line-height: 1.5;
  color: var(--c-warning);
}
[data-todo] {
  outline: 1px dashed var(--c-border-strong);
  outline-offset: 4px;
}
[data-todo]::after {
  content: " TODO";
  font-size: var(--t-caption);
  font-weight: 600;
  letter-spacing: var(--ls-wide);
  color: var(--c-accent);
  vertical-align: super;
}
body[data-hide-todo="true"] [data-todo] { outline: none; }
body[data-hide-todo="true"] [data-todo]::after { content: none; }
