/* about.css: page-specific styles for /about, loaded after site.css.
   Short "quiet" page: a hero with a generated far-plane image plus two
   SVG planes, then a bio band with a timeline-rail device and Ian's real
   portrait (Phase 4, task 1c -- ian-griffiths-portrait.webp, 400x400 1x
   plus ian-griffiths-portrait@2x.webp, 800x800 2x). Single column below
   1000px (copy first, portrait after -- the round-C phone rule); two
   columns at 1000px and up, portrait left at native size in a panel,
   copy right. No page.js needed -- site.js drives the parallax,
   reveal-line and nav; nothing here is bespoke behaviour. */

/* Skip link now lives in site.css (shared .skip / .skip-link rule) --
   this page no longer carries its own copy. */

/* This page has no in-page anchor links (the nav's #proof/#offer targets live
   on the homepage), so site.css's smooth scroll-behavior buys nothing here.
   Instant scroll is the safer default even though it did not turn out to be
   the cause of check-contrast.js's own scroll-behavior:auto override at
   375px (see the page report: the same instability reproduces on the
   already-shipped homepage, so it is a pre-existing tool/page interaction,
   not something introduced here). */
html { scroll-behavior: auto; }

/* --------------------------------------------------------------- hero */
.hero {
  position: relative; overflow: hidden;
  min-height: 560px;
  display: flex; align-items: center;
  padding: 48px 0 64px;
}
/* Far plane: the generated image, same recipe as every other hero on the
   site (agent-mot, headroom, etc.) -- a plain <img class="plane-far">,
   full-bleed behind the scrim. .plane-far's generic site.css rule
   (bottom-anchored, height:auto) is for the SVG arcs; this override wins
   on specificity (.hero .plane-far, 0,2,0) and gives it real cover
   sizing. Parallax rate 0.10 comes from the class itself via site.js's
   DEFAULT_RATE map -- no data-rate attribute needed. */
.hero .plane-far { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
/* Scrim: navy over the upper two-thirds only, where the H1/sub sit, so
   glyph-pixel contrast clears 4.5:1 even with the image forced pure white
   (check-contrast.js --white-media, the brightest case). Fades out over
   the lower third so the image reads clearly there. */
.hero-scrim {
  position: absolute; inset: 0; z-index: 1;
  /* Last stop re-darkens over the closing ~6% of the hero: the source
     photo's own bottom edge carries a row of point lights right where
     object-fit:cover crops it, which read as a torn/speckled seam
     without this. A soft navy wash over that band (not a hard line)
     hides the seam while the plane-near arc still overlaps it too. */
  background: linear-gradient(180deg,
    color-mix(in oklab, var(--navy) 94%, transparent) 0%,
    color-mix(in oklab, var(--navy) 90%, transparent) 45%,
    color-mix(in oklab, var(--navy) 84%, transparent) 68%,
    color-mix(in oklab, var(--navy) 35%, transparent) 88%,
    color-mix(in oklab, var(--navy) 0%, transparent) 94%,
    color-mix(in oklab, var(--navy) 55%, transparent) 100%);
}
.hero .plane-mid { z-index: 1; opacity: 0.7; }
/* Explicit height (was auto, inheriting a few px of inline-SVG phantom
   space): hero bottom minus this equals 72px above the hero's own
   bottom edge, which lands the arc's crest 8px above .hero-act's own
   bottom edge at 1440 (hero-act's 8px padding-bottom is the buffer) --
   behind the copy (z-index 1 vs hero-inner's 2), same recipe as the
   homepage hero and /agent-mot. */
.hero .plane-near { z-index: 1; height: 72px; }
.hero .plane-near svg { display: block; width: 100%; height: 100%; }
.hero-inner { position: relative; z-index: 2; width: min(1280px, 92%); margin: 0 auto; }
.hero h1 { max-width: 18ch; }
.hero .sub {
  margin: 24px 0 0; max-width: 56ch;
  font-size: 19px; line-height: 1.5; font-weight: 400;
  color: var(--text); opacity: 0.9; text-wrap: pretty;
}
.hero-act { margin-top: 32px; padding-bottom: 8px; }
.hero .support { display: block; margin-top: 14px; color: var(--text-dim); font-size: 14px; }

@media (max-width: 860px) {
  .hero { min-height: 460px; padding: 40px 0 56px; }
  .hero h1 { max-width: 14ch; }
}
@media (max-width: 600px) {
  .hero { min-height: 420px; padding: 32px 0 48px; }
  .hero .sub { font-size: 17px; }
  /* Lime cap (brandbook: under 2% of the first view at 390): site.css's
     shared mobile .btn-lime put this button at 2.72% of the 390 first
     view. Same fix the shipped homepage already uses on this exact CTA
     label (index.html's own inline style, same breakpoint): hide the
     "90-Second " word, not the whole button's type/padding, so the
     control keeps the standard size and only gets narrower. */
  .hero-act .btn-lime .long { display: none; }
}

/* ------------------------------------------------------- bio band --- */
/* Phase 4 task 1: Ian's real portrait replaces the round-C comment slot.
   .bio-grid is a CSS grid: one column by default, DOM order is copy then
   portrait (the round-C phone rule -- copy first, image after, no order
   trick needed for that case, it is just source order). At 1000px and up
   the grid gains a second, content-sized column and .portrait takes
   order:-1 so it renders left of the copy without moving in the DOM --
   align-items:start on the row top-aligns the panel with .bio-copy's
   first child, h2#team (margin-top:0), per the brief. .bio-copy keeps its
   own ~64ch measure inside its column so the two H2s, paragraph and
   timeline read at one comfortable width rather than stretching full
   width. Task 1c moved the breakpoint down from 1100px so 1024px (a
   common laptop/tablet width) already shows the two-column layout instead
   of stacking; at 1024 the copy column lands at ~430px (wrap ~942px minus
   the portrait's 448px minus the 64px gap), still comfortably readable. */
.bio-band { padding: 72px 0 96px; }
.bio-grid { display: grid; grid-template-columns: 1fr; gap: 48px; }
.bio-copy { max-width: 64ch; }

/* Portrait panel: var(--surface) (#11162a), 16px radius, 24px padding,
   no border, no shadow -- ruling is explicit on this. Sized by
   width:100% + max-width:448px (400px photo + 24px padding each side)
   so it hugs its own content and never stretches to the full column;
   justify-self:start left-aligns the resulting box once max-width caps
   it below the column's own width. The image itself is fluid
   (width:100%, aspect-ratio 1/1) below 1000px so nothing overflows a
   narrow phone, and switches to its true native 400x400 pixels at
   1000px and up -- never upscaled, never cropped, the file's own
   resolution. Task 1c: the image itself carries an 8px radius so it
   reads as a photo sitting inside the panel rather than a square block
   flush with the panel's own 16px corners; its vignette (elliptical,
   soft edge) is rendered to fade to this exact panel colour, #11162a,
   so there is no visible step between the photo's edge and the padding
   around it. */
.portrait {
  justify-self: start;
  width: 100%; max-width: 448px;
  background: var(--surface);
  border-radius: 16px;
  padding: 24px;
}
.portrait-img { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; border-radius: 8px; }
.portrait figcaption { margin-top: 16px; color: var(--text-dim); font-size: 14px; line-height: 1.5; }

/* .bio-side wraps the side column (today: the portrait only; the ocean
   still moved into the copy column as .ocean-band, see below). The 1000px
   grid rule sits on the wrapper so anything added under the portrait stacks
   at 448px. Below 1000px the wrapper follows the copy (copy first). */
.bio-side { display: grid; gap: 24px; justify-items: start; }

/* Task 1e (A): the Southern Ocean still is a band inside the copy column,
   directly under the purpose paragraph, with the story as its caption set
   over a navy gradient so the words sit on the picture with contrast. Ian:
   "the wave lacks context... what's the story?" The band answers it. */
.ocean-band { position: relative; margin: 20px 0 4px; border-radius: 16px; overflow: hidden; background: var(--surface); }
.ocean-band .ocean-img { display: block; width: 100%; height: auto; aspect-ratio: 32 / 15; object-fit: cover; }
.ocean-band::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(10,14,26,.14) 0%, rgba(10,14,26,.14) 26%, rgba(10,14,26,.88) 52%, rgba(10,14,26,.96) 100%); }
@media (min-width: 1000px) and (max-width: 1200px) { .ocean-band .ocean-img { aspect-ratio: 3 / 2; } }
.ocean-band figcaption { position: absolute; left: 24px; right: 24px; bottom: 20px; z-index: 1;
  color: var(--text); font-size: 16px; line-height: 1.5; text-wrap: pretty; }
@media (max-width: 599px) {
  .ocean-band .ocean-img { aspect-ratio: 4 / 3; }
  .ocean-band figcaption { left: 16px; right: 16px; bottom: 16px; font-size: 15px; }
}

@media (min-width: 1000px) {
  .bio-grid { grid-template-columns: max-content 1fr; align-items: start; gap: 64px; }
  .bio-side { order: -1; width: 448px; max-width: 448px; }
  .portrait { width: 448px; max-width: 448px; }
  .portrait-img { width: 400px; height: 400px; }
}

.bio-copy h2 { margin-top: 0; text-wrap: balance; }
.bio-copy h2:not(:first-child) { margin-top: 40px; }
.bio-copy p { margin-top: 12px; font-size: 17px; line-height: 1.55; color: var(--text-dim); text-wrap: pretty; }

/* Timeline rail: a thin cyan vertical rule with a dot per fact, the
   page's own existing sentences, revealing line by line via .reveal-line
   (site.js). Cyan, never lime -- the page's one lime control is the hero
   CTA. Two facts (the duplicate third was removed in round B -- it
   repeated the bio paragraph above it word for word). With the column no
   longer sharing the row with a portrait, give the rail real vertical
   presence: a taller gap before it, more line height and more padding
   between entries, bigger dots -- so two facts read as a deliberate
   device, not a thin list. */
/* Task 1f: the numbers. Ian's own figures from the 4 Sep interview, big to
   small, under the portrait in the side column (fills the column the checker
   flagged as empty). Numeral in JetBrains Mono (brand: numbers), driven by
   scroll through about.js (the ladder below); the line beside it in dim
   text. Hairline rows, no dots, no box-shadow. */
.stats { margin-top: 8px; width: 100%; max-width: 448px; }
.stats h2 { font-size: 22px; line-height: 1.25; margin: 0 0 12px; text-wrap: balance; }
.stat-list { list-style: none; margin: 0; padding: 0; }
/* The ladder answers the wheel (about.js): each row carries --p, 0 below
   the reading line and 1 above it, set continuously from scroll. The row
   lights, its number counts up and its rule draws in as it crosses the
   line, and all of it winds back if the reader scrolls up. The default
   --p is 1, so with no JS, or under reduced motion, the list is simply
   finished; a one-line head script adds html.ladder when motion is
   allowed, which starts the rows unlit BEFORE first paint, so a refresh
   mid-rail never flashes the finished list. */
.stat-list li {
  --p: 1;
  position: relative;
  display: grid; grid-template-columns: 92px 1fr; column-gap: 14px; align-items: baseline;
  padding: 10px 0;
}
/* Only the starting progress lives under the motion class; the layout above
   must hold for reduced motion and no-JS, which never get the class. */
html.ladder .stat-list li { --p: 0; }
.stat-list li::before, .stat-list li:last-child::after {
  content: ""; position: absolute; left: 0; right: 0; height: 1px;
  background: rgba(122, 133, 168, 0.22);
  transform: scaleX(var(--p)); transform-origin: left center;
}
.stat-list li::before { top: 0; }
.stat-list li:last-child::after { bottom: 0; }
.stat-list .n {
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-weight: 600; font-size: 26px;
  letter-spacing: -0.02em; color: var(--text); text-align: right; font-variant-numeric: tabular-nums;
  opacity: var(--p);
}
.stat-list .t { font-size: 15px; line-height: 1.45; color: var(--text-dim); text-wrap: pretty;
  opacity: calc(0.94 + 0.06 * var(--p)); }   /* the floor keeps the dim label above 4.5:1 (0.85 gave 4.1); the number and the rule carry the reveal */
@media (prefers-reduced-motion: reduce) {
  .stat-list li { --p: 1 !important; }
}
@media (max-width: 599px) {
  .stat-list li { grid-template-columns: 88px 1fr; }   /* 27,000 measures 79px at 22px; 80 left no room for a fallback face */
  .stat-list .n { font-size: 22px; }
}

@media (max-width: 600px) {
  .bio-band { padding: 56px 0 72px; }
}

/* ------------------------------------------------------ the deck (task 8)
   One 2:3 picture card per row of the rail, fanned like a hand of cards.
   about.js sets each card's --dx/--dy/--rot/--sc/--op/--z from a single
   progress value: it follows the ladder (the lit row's card on top) and can
   be dragged and flung. Shown only under html.ladder (JS on, motion
   allowed); the rail carries the facts, the deck is decoration. Desktop:
   the slot is the grid's second row in the copy column, sticky beside the
   rail. Phone: the slot sits inside .stats above the rows, sticky under
   the nav, and about.js moves the deck between the two slots on resize. */
.deck-slot { display: none; }
html.ladder .deck-slot { display: block; }
.deck { position: relative; width: 100%; height: 210px; overflow: hidden; touch-action: pan-y; cursor: grab; user-select: none; -webkit-user-select: none; }   /* clips the fan: without it the outer cards widened the document; the outer cards fade out before they reach the edge (about.js) */
.deck.is-dragging { cursor: grabbing; }
.deck__card {
  position: absolute; left: 50%; top: 50%; width: 112px; height: 168px; margin: -84px 0 0 -56px;
  border-radius: 14px; overflow: hidden; background: var(--surface);
  transform: translate3d(var(--dx, 0px), var(--dy, 0px), 0) rotate(var(--rot, 0deg)) scale(var(--sc, 1));
  opacity: var(--op, 1); z-index: var(--z, 1); will-change: transform;
}
.deck__card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.deck__card::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(10, 14, 26, .82), rgba(10, 14, 26, .15) 45%, transparent 70%); }
.deck__card.is-plain { background: #11162a url(/assets/img/hero-mid-960.webp) center / cover no-repeat; }
.deck__card.is-plain::before { content: ""; position: absolute; inset: 0; background: rgba(10, 14, 26, .32); }
.deck__card.is-plain[data-i="12"] { background-image: url(/assets/img/hero-far-960.webp); background-position: center 30%; }   /* the last card is not the one before it shown twice */
.deck__card figcaption { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 1; color: var(--text); opacity: var(--cap, 1); }   /* the caption shows on the upright card only, so no tilted card slices a word */
.deck__card b { display: block; font: 600 24px/1 'JetBrains Mono', ui-monospace, monospace; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.deck__card span { display: block; margin-top: 4px; font-size: 11px; line-height: 1.3; color: rgba(236, 240, 250, .78); }
.deck__card.is-plain b { font-size: 34px; }
.deck-slot--phone { position: sticky; top: 84px; z-index: 5; margin: 0 0 8px; background: var(--bg, #0a0e1a); }   /* an opaque slot: rows scrolling under it are hidden, not half-seen through the fan */
@media (min-width: 1000px) {
  /* Two rows. Row 1: the portrait (left) and the copy (right). Row 2: the
     deck (left, under the portrait, sticky) and the rail (right, under the
     copy), so the deck is level with the rail from its first row to its
     last. .bio-side gives up its box so the portrait and the rail can sit
     in different rows of the outer grid. */
  .bio-grid { grid-template-rows: auto 1fr; }
  .bio-side { display: contents; }
  .bio-side .portrait { grid-column: 1; grid-row: 1; }
  .bio-side .stats { grid-column: 2; grid-row: 2; max-width: 560px; margin-top: 0; }
  .bio-copy { grid-column: 2; grid-row: 1; }
  .deck-slot--desk { grid-column: 1; grid-row: 2; align-self: start; position: sticky; top: 100px; width: 448px; }
  .deck-slot--phone { display: none !important; }
  .deck { height: 360px; }
  .deck__card { width: 176px; height: 264px; margin: -132px 0 0 -88px; border-radius: 16px; }
  .deck__card figcaption { left: 18px; right: 18px; bottom: 18px; }
  .deck__card b { font-size: 34px; }
  .deck__card span { font-size: 13px; margin-top: 6px; }
  .deck__card.is-plain b { font-size: 48px; }
}
