/* Angler — 10 · hero.
   The closing CTA's scene (css/90-cta.css), inverted for morning and used as the
   title card: four planes over one section — sky · lake-back (+0.22) · copy ·
   lake-fore (-0.55, climbs past it). The one literal colour on this page is
   #ffffff, and it is deliberate: tokens.css tops out at --paper #fdfbf6, and the
   brief is specifically that the scene fades to WHITE at the top rather than to
   --sky-top tan (the old hero) or --lake-deep (the closer). Everything else is a
   token. `black`/`transparent` appear only inside alpha masks — channel values,
   not colour. */

/* ---------------------------------------------------------------- the scene */

.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* One screen. It is a title card now — the product lives in section 15. */
  min-height: clamp(640px, 100vh, 1080px);
  min-height: clamp(640px, 100svh, 1080px);
  /* AgentPeek parks the stack HIGH — its headline starts about a quarter of the way
     down, not on the optical centre. The lopsided rows do that.
     The first number was 0.35fr when a 68px app icon sat above the headline. Losing
     the icon and its 18px margin gave 86px of free space back to the fr distribution
     and dropped the headline to 15.2% of the viewport; 0.92fr puts it back to 23.2%
     at 1440x900 and holds 23–26% across the whole 940–2560 range.
     Row 3 holds the agent strip, so its floor is min-content rather than 0 — on a
     short window the hero grows instead of crushing the strip into the copy. */
  display: grid;
  grid-template-rows: minmax(0, 0.92fr) auto minmax(min-content, 2.4fr);
  /* tracks --nav-top + --nav-h in 05-nav.css; the bar floats now, so the inset counts */
  padding-block: calc(12px + 84px) 0;
  background: #ffffff;            /* ground under every plane; no gaps, ever */
}

/* plane 0 — morning sky. White overhead, warming only as far as --vellum by the
   waterline; the plate supplies every colour below that. */
.hero__sky {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* White only long enough to tuck under the 66px nav — it used to hold to 30%,
     which read as a white band with a painting stuck to the bottom. */
  /* The plate is opaque over almost all of this now, so the sky only has to cover
     the strip under the nav before the painting takes over. */
  background: linear-gradient(to bottom,
    #ffffff 0%,
    var(--paper) 5%,
    var(--vellum) 14%,
    var(--vellum) 100%);
}

.hero__plate {
  position: absolute;
  left: 0;
  right: 0;
  pointer-events: none;
}
/* height:auto is load-bearing — the width/height attributes on the <img> are a
   presentational hint that would otherwise pin the plate to its natural pixels. */
.hero__plate img { width: 100%; height: auto; }

/* plane 1 — the lake, bottom-anchored. Its top is masked away entirely, so what
   arrives under the copy is white, then paper, then halftone. */
.hero__plate--back {
  z-index: 1;
  bottom: -70px;
  /* Taller and arriving sooner: the painting now starts just under the nav rather
     than a third of the way down the screen. */
  height: 92%;
  mask-image: linear-gradient(to bottom, transparent 0%, black 14%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 14%);
}
.hero__plate--back .hero__art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center bottom;
}
/* Pass 1 — the whole hero. Range collapsed almost flat, so hue, light and the
   shapes of the mountains survive but nothing has enough contrast to fight type. */
/* contrast .055 composited to 0.10L + 224 — a 224-250 range, i.e. white with a
   rumour of a painting in it. That read as MORE white, not less. .26/1.5 gives
   0.39L + 142, a 142-241 range: the mountains, the treeline and the water are all
   actually visible, and the copy still clears 6:1 because it is near-black serif. */
.hero__art--calm {
  /* 0.46, was 0.26. Brightness sets the midpoint and contrast sets the spread — at an
     input midtone the output is b/2 regardless of c — so raising c alone widens the
     range without shifting overall lightness. Measured on the rendered plate, the copy
     band goes from an 80-level spread to 140: the mountains, the treeline and the far
     shore read as a painting rather than a rumour of one.
     0.46 is the ceiling, not a preference. Above it the highlights clip (out_max =
     b*(0.484c + 0.5) exceeds 1.0 past c≈0.34 and the sky starts flattening to white),
     and the requirements line below the button loses its footing faster than the
     headline does. */
  filter: saturate(1.1) contrast(0.46) brightness(1.5);
  opacity: 0.95;
}
/* Pass 2 — the painting at real strength, faded in only below the copy. */
.hero__art--scene {
  filter: saturate(0.78) contrast(0.95) brightness(1.04);
  /* Held off until 70% — the copy block bottoms out near 68% of the stage, and any
     earlier and the tree mass climbs back behind the button. */
  mask-image: linear-gradient(to bottom, transparent 70%, rgb(0 0 0 / .5) 84%, black 96%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 70%, rgb(0 0 0 / .5) 84%, black 96%);
}
/* The white the brief asked for. Inside the masked element, so it only ever
   lightens the plate — it never paints over the sky above it. */
.hero__plate--back::after {
  content: "";
  position: absolute;
  inset: 0;
  /* A lens over the copy band only, not a blanket. Light where the icon, the name
     and the button sit; released above them so the sky reads as painting, and
     released below them so the shoreline keeps its texture. */
  /* Only a whisper now — the calm pass does the work, so this just softens the
     handoff at the very top and keeps the shoreline from going muddy. */
  /* Only the very top, to soften the handoff under the nav. Everything lower is
     the painting itself now. */
  background: linear-gradient(to bottom,
    color-mix(in srgb, #ffffff 34%, transparent) 0%,
    transparent 18%,
    transparent 100%);
}

/* plane 3 — the near shoreline, above the copy, climbing. Masked to its left
   portion for the same reason as the closer: both plates are crops of one
   painting and both carry the bear at ~63% across, so the front one is faded out
   before it reaches him. Stops are in plate coordinates and hold at every width. */
.hero__plate--fore {
  z-index: 3;
  bottom: -170px;
  width: 210%;
  left: -22%;
  right: auto;
  /* Two masks, intersected. The horizontal one fades the plate out before it reaches
     the bear (both plates carry him). The vertical one is new and is what stops the
     tree mass climbing into the copy block — without it the left third of the
     headline and button sit on dark foliage at luminance ~149. */
  mask-image:
    linear-gradient(to right, black 0 40%, transparent 58%),
    linear-gradient(to bottom, transparent 64%, rgb(0 0 0 / .6) 78%, black 90%);
  -webkit-mask-image:
    linear-gradient(to right, black 0 40%, transparent 58%),
    linear-gradient(to bottom, transparent 64%, rgb(0 0 0 / .6) 78%, black 90%);
  mask-composite: intersect;
  -webkit-mask-composite: source-in;
}
.hero__plate--fore img { filter: saturate(0.72) brightness(1.06); }
/* the same morning wash, masked by the plate's own alpha so the sky beside it
   stays sky */
.hero__plate--fore::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom,
    color-mix(in srgb, #ffffff 46%, transparent) 0%,
    color-mix(in srgb, var(--paper) 10%, transparent) 100%);
  mask-image: url("../assets/img/lake-fore.webp");
  -webkit-mask-image: url("../assets/img/lake-fore.webp");
  mask-size: 100% 100%;
  -webkit-mask-size: 100% 100%;
  mask-mode: alpha;
}

/* the handoff into section 15, which is --paper ground */
.hero::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 130px;
  z-index: 4;
  pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--paper) 86%);
}

/* ---------------------------------------------------------------- the stack */

/* plane 2 — no parallax. Base sets .section > .wrap to z-index 1; it needs 2. */
.hero .hero__copy {
  grid-row: 2;
  max-width: 1160px;
  position: relative;
  z-index: 2;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* AgentPeek's hero is carried by the headline, not by a wordmark — two tight lines
   that own the screen. The <br> in the markup fixes the break so it never wraps
   somewhere accidental. */
.hero__name {
  font-family: var(--serif);
  font-weight: 600;
  /* Their headline is 19 characters a line; ours is 29, so the column has to be
     wider than the 960 body column or line one wraps and we get three lines. */
  max-width: 1120px;
  font-size: clamp(2.2rem, 5.15vw, 4.5rem);
  line-height: 1.04;
  letter-spacing: -0.028em;
  text-wrap: balance;
  color: var(--lake-deep);
}

.hero__line {
  margin-top: 20px;
  max-width: 52ch;
  font-size: clamp(1.06rem, 1.35vw, 1.22rem);
  line-height: 1.5;
  color: var(--ink-soft);
}

.hero__act { margin-top: 32px; }

/* ---- the glass pill (AgentPeek's construction, Angler's colour) ----
   Their rim and glow are blue; ours are --lake. Geometry is theirs: 68px tall,
   18px/500, -0.18px tracking, pill radius, 1.75em side padding. */
.btn-glass {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 68px;
  padding: 0 1.75em;
  border: 0;
  border-radius: var(--r-pill);
  /* DARK glass, not light. At 62% paper this was a pale object on a pale hero and it
     disappeared into the plate — the whole button read as a watermark. Flipping the
     fill keeps every bit of the material (blurred backdrop, specular rim, the sheen
     sweep on ::before) and simply makes it the figure instead of the ground.
     --lake-deep, not --lake: against the hero plate (mean L 0.456) a --lake fill
     measured 2.33:1 and --lake-dark 2.79:1, both under the 3:1 a control's own
     boundary has to clear. This is the footer's colour, so it stays in family.
     92%, not 100%: enough of the lake plate still moves behind it to read as glass. */
  background: color-mix(in srgb, var(--lake-deep) 92%, transparent);
  -webkit-backdrop-filter: blur(6px) saturate(140%);
  backdrop-filter: blur(6px) saturate(140%);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 1.125rem;
  font-weight: 500;
  letter-spacing: -0.18px;
  line-height: 1;
  will-change: transform;
  -webkit-font-smoothing: antialiased;
  /* Inset highlights inverted with the fill: the light catch moves to the TOP edge,
     where a lit dark object would carry it, and the bottom takes the shadow. */
  box-shadow:
    inset 0 1.5px 1px rgb(255 255 255 / .30),
    inset 0 -2px 3px rgb(0 0 0 / .18),
    inset 0 0 0 1px rgb(255 255 255 / .12),
    0 4px 2px -2px rgb(14 54 62 / .26),
    0 10px 20px -6px rgb(14 54 62 / .34),
    0 18px 32px -10px rgb(14 54 62 / .22);
  transition:
    transform .4s var(--ease-house),
    box-shadow .4s var(--ease-house),
    backdrop-filter .4s var(--ease-house);
}
.btn-glass__content {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.btn-glass__apple { height: 18px; width: auto; flex: none; margin-top: -2px; }

/* the rotating rim, drawn as a padded conic gradient masked to a hairline */
.btn-glass::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  padding: clamp(1px, 0.0625em, 4px);
  pointer-events: none;
  background: conic-gradient(from var(--rim, 0deg) at 50% 50%,
    rgb(25 92 106 / .5),
    rgb(150 205 215 / .1) 5% 40%,
    rgb(25 92 106 / .5) 50%,
    rgb(150 205 215 / .1) 60% 95%,
    rgb(25 92 106 / .5));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}
/* the diagonal sheen — parked off-frame, swept across on hover */
.btn-glass::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(-45deg,
    rgb(255 255 255 / 0) 0,
    rgb(255 255 255 / .5) 40%,
    rgb(255 255 255 / .5) 50%,
    rgb(255 255 255 / 0) 55%) 200% 50% / 200% 200% no-repeat;
  transition: background-position .55s var(--ease-house);
}
/* Why 200%: the sheen layer is 200% of the button wide, so with
   background-position p the image spans [-Wp, -Wp + 2W]. At the old 140% that is
   [-1.4W, +0.6W] — the band sat across the left 60% of the button at REST, which is
   the glare that was washing the label. Fully off-frame is p >= 200% (parked left)
   or p <= -100% (parked right). Rest is now 200%, hover runs to -40%, so the band
   crosses the face during the sweep and both end states are clean. */
/* Gated: on touch the sheen swept once on tap and then stayed swept, and the button
   stayed lifted, for the rest of the visit. See the note in css/base.css. */
@media (hover: hover) and (pointer: fine) {
  /* Lifts and deepens rather than lightens — the light-fill highlights this used to
     carry would have washed the dark pill back out on hover. */
  .btn-glass:hover {
    transform: translateY(-2px);
    -webkit-backdrop-filter: blur(10px) saturate(140%);
    backdrop-filter: blur(10px) saturate(140%);
    background: color-mix(in srgb, var(--lake-deep) 97%, transparent);
    box-shadow:
      inset 0 1.5px 1px rgb(255 255 255 / .38),
      inset 0 -2px 3px rgb(0 0 0 / .20),
      inset 0 0 0 1px rgb(255 255 255 / .16),
      0 6px 3px -2px rgb(14 54 62 / .28),
      0 16px 28px -8px rgb(14 54 62 / .38),
      0 26px 44px -12px rgb(14 54 62 / .26);
  }
  .btn-glass:hover::before { background-position: -40% 50%; }
}
.btn-glass:active { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .btn-glass, .btn-glass::before { transition: none; }
  .btn-glass:hover { transform: none; }
}

@media (max-width: 520px) {
  .btn-glass { height: 58px; font-size: 1.03rem; padding: 0 1.4em; }
}

/* --ink, not the .fine default of --muted. This line sits below the button on the
   darkest part of the calm plate, and --muted measured 2.63:1 there BEFORE the contrast
   change — already under the 4.5:1 its 13px demands. Opening the plate up drops that
   backdrop further, so the fix is the type, not the picture: --ink holds 5.6:1 on the
   same pixels. At this size the hierarchy is carried by the size, not the value — the
   same call the closing CTA's fine print makes on its dark plate. */
.hero__req {
  margin-top: 16px;
  font-size: 13px;
  /* 500 and a halo, because the problem here is not the ratio. --ink on this backdrop
     measures 5.24:1, comfortably past the gate, but the plate under it swings 98 levels
     (sd 21.7) and at 13px/400 the strokes are about as wide as the halftone cells they
     sit on — the ground and the glyph are the same spatial frequency and the eye cannot
     separate them. A heavier stroke spans more cells and averages the texture out; the
     halo gives the glyph its own local ground without drawing a box. */
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink);
  text-shadow:
    0 0 3px color-mix(in srgb, var(--paper) 88%, transparent),
    0 0 9px color-mix(in srgb, var(--paper) 62%, transparent);
}

/* --------------------------------------------------------- the agent strip */

/* AgentPeek's own strip, measured off the live page: 72px tiles, `border-radius:22%`
   and a per-tile ground applied by CSS, `margin-left:-22px` so the tiles overlap into a
   50px pitch, no rest shadow, and `transform .18s ease` lifting to
   `translateY(-6px) scale(1.25)` on hover. Narrow they drop to 42px.
   Kept: the size (68 here, 56 narrow), the .18s lift, the shadow-on-hover only.
   Dropped: the overlap — a stacking device for 25 tiles that just reads as a mistake
   with four, so these sit on a real 14px gap.
   NOT REPRODUCED, deliberately: the radius and the ground. These four files are
   finished app tiles whose corners live in their alpha channel, so the tile chrome is
   IN the artwork. Any background, border-radius or box-shadow declared here would
   paint a square behind a rounded icon. The rest shadow is therefore a drop-shadow()
   filter, which reads the same alpha — the treatment the old .hero__icon used on the
   app icon PNG for exactly this reason. */

.hero__agents {
  grid-row: 3;
  align-self: end;
  justify-self: center;
  position: relative;
  /* Above .hero::after (z-index 4). The 130px fade into section 15's --paper runs
     right through this band; under it the tiles would be washed halfway to cream. */
  z-index: 5;
  margin-bottom: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.hero__agents-row {
  display: flex;
  justify-content: center;
  gap: 14px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.hero__agent {
  width: 68px;
  height: 68px;
  /* drop-shadow, not box-shadow: it is taken from the icon's own alpha, so it follows
     the squircle instead of boxing it. */
  filter: drop-shadow(0 1px 1px rgb(14 54 62 / .16))
          drop-shadow(0 8px 18px rgb(14 54 62 / .18));
  /* An entrance, not a loop: each tile rises once and stops. The delays continue the
     copy stack's own rhythm (.07/.14/.21/.28 on the four .reveal lines above) rather
     than starting a second, competing one. Plain CSS, no observer — the strip is part
     of the first screen, so "enters the viewport" and "loads" are the same moment, and
     the shared .reveal observer's -12% root margin actually misses it on a 1080-tall
     window. */
  animation: hero-agent-in .5s var(--ease-open) backwards;
  transition:
    transform .18s var(--ease-nav),
    filter .18s var(--ease-nav);
}
.hero__agent:nth-child(1) { animation-delay: .34s; }
.hero__agent:nth-child(2) { animation-delay: .40s; }
.hero__agent:nth-child(3) { animation-delay: .46s; }
.hero__agent:nth-child(4) { animation-delay: .52s; }

@keyframes hero-agent-in {
  from { opacity: 0; transform: translateY(14px); }
}

.hero__agent img { width: 100%; height: 100%; display: block; }

@media (hover: hover) and (pointer: fine) {
  .hero__agent:hover {
    transform: translateY(-6px) scale(1.05);
    filter: drop-shadow(0 2px 2px rgb(14 54 62 / .18))
            drop-shadow(0 14px 24px rgb(14 54 62 / .28));
  }
}

/* the note — AgentPeek's `.lp-cta-note`: absolutely placed off the side of the thing
   it points at, flex column, 2px gap, arrow under the text and nudged in a couple of
   pixels, pointer-events off, and the text itself rotated a degree or two off true.
   Theirs is 15px Schibsted Grotesk *italic*; we have no italic file for either face
   and will not add one, so this is Fraunces synthesised oblique — which on a serif at
   16px is closer to a margin note than a real italic would be anyway. */
.hero__agents-note {
  position: absolute;
  bottom: 74px;                 /* 68px tile + 6px clearance above the row */
  right: calc(50% + 118px);     /* mirrored: the tip now lands over the FIRST tile */
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  /* --ink, not --lake-deep. The note lands over the shoreline: the darkest composited
     pixel under it at 1440x900 is 0.247 luminance, where --lake-deep measured 4.2:1 —
     under the 4.5 bar for 16px text. Ink measures 5.10:1 on the same pixel. It is also
     the colour AgentPeek's own note uses. */
  color: var(--ink);
  pointer-events: none;
  animation: hero-agent-in .5s var(--ease-open) .30s backwards;
}
.hero__agents-say {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1rem;
  /* 600, not 500 — Fraunces only ships 400/600/700 here. Same reasoning as .hero__req,
     and worse in this spot: the backdrop under this line swings 136 levels because the
     full-strength scene pass and the foreground plate both reach it. At 400 this
     measured 3.63:1 on its darkest pixel, under the 4.5:1 a 16px line needs. */
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  transform: rotate(2.2deg);
  /* Denser than .hero__req's: solid --paper at a tight radius, because this line is
     over the foreground tree mass and a 66% outer halo only lifted its worst ground
     from 3.64:1 to 4.20:1 — still short of the 4.5 a 16px line owes. */
  text-shadow:
    0 0 4px var(--paper),
    0 0 13px color-mix(in srgb, var(--paper) 80%, transparent);
}
/* scaleX(-1) mirrors the drawn curve so it points down-RIGHT at the row instead of
   down-left. Mirroring the path beats re-authoring it — the hand-drawn wobble is in
   the control points and would have to be re-tuned by hand otherwise. */
.hero__agents-arrow {
  width: 56px;
  height: 48px;
  margin-right: 6px;
  transform: scaleX(-1);
}

@media (prefers-reduced-motion: reduce) {
  /* No stagger and no transform. They are simply there. */
  .hero__agent, .hero__agents-note { animation: none; }
  .hero__agent:hover { transform: none; }
}

/* ---------------------------------------------------------------- narrow */

@media (min-width: 1000px) {
  /* capped: past ~1400px a percentage width makes the shoreline tall enough to
     climb over the button instead of stopping under it */
  .hero__plate--fore { width: min(150%, 2100px); left: -240px; bottom: -120px; }
}

/* The note is 118px right of centre plus ~285px of nowrap text. Below this the run
   would push past the gutter, so it stops being an aside and becomes a caption: above
   the row, centred, arrow gone. AgentPeek does the same thing — `.lp-cta-note` is
   `display:none` under 760px on their page. */
@media (max-width: 940px) {
  .hero__agents-note {
    position: static;
    order: -1;
    align-items: center;
    animation-delay: .28s;
  }
  .hero__agents-say {
    white-space: normal;
    text-align: center;
    transform: none;
    font-size: .95rem;
  }
  .hero__agents-arrow { display: none; }

  /* 130px -> 190px. In the wide layout the note is off to the side, over open water,
     and measures 5.5-5.8:1. Centred above the row it lands on the shoreline instead,
     and --ink on the darkest composited pixel there came out at 4.30:1 (390x844) —
     under the 4.5 bar for 15px text. Lengthening the handoff puts ~40% paper under
     the caption band and takes it to 8:1, without touching the plate or the mask
     stack. The tiles are on z-index 5, above this, so they do not wash with it. */
  .hero::after { height: 190px; }
}

@media (max-width: 720px) {
  /* Narrower type means a shorter stack, and the caption stacks above the row down
     here, so row 3 needs more of the free space. 0.40fr holds the headline between
     22.6% (320x640) and 25.3% (700x813) across the whole range. */
  .hero { grid-template-rows: minmax(0, 0.40fr) auto minmax(min-content, 1.05fr); }
  .hero__plate--back { height: 66%; bottom: -40px; }
  .hero__plate--fore { bottom: -110px; }
  .hero__agents { margin-bottom: 32px; }
}

@media (max-width: 520px) {
  .hero__agent { width: 56px; height: 56px; }
  .hero__agents-row { gap: 10px; }
}

@media (max-width: 420px) {
  .hero__line { font-size: 1rem; max-width: 30ch; }
}
