/* ---------------------------------------------------------------------------------------
   Engagement pass — interaction and motion for the marketing pages.
   Loaded on all seven pages: item 06 (nav reaction, sticky CTA) is site-wide, and the
   scaffolding below is too. Item-specific blocks are scoped by their own selectors.

   Motion tokens (--ease-out, --dur-*) live in style.css's local-only :root, not here and
   not in assets/design-system/ — that folder is a mirror and HOW-TO-UPDATE.md says so.
   They are marked pending-upstream there.
   --------------------------------------------------------------------------------------- */

/* Every animation collapses to its end state rather than being removed, so an element whose
   start state is hidden still finishes visible. Duration, not `animation: none` — the two
   are not the same, and `none` would strand anything that animates INTO position.
   Files that already handle their own reduced-motion case (how-it-works-anim.css) set
   `animation: none`, which nulls the name, so this rule has nothing to act on there. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* The hidden-then-revealed pattern, authored the safe way round: visible by default, hidden
   only once JS has confirmed it can reveal them again. With JS off or failed, every one of
   these is simply on the page.

   RULE: never put data-reveal on a full-bleed coloured band. Put it on the content inside.
   The translateY below assumes the element sits ON the page background. When the element IS
   the background, the transform vacates that 18px and the page shows through the gap it
   leaves — on .belief, a dark teal band directly under the hero, that was a white line at
   the seam, shrinking and fading as the band settled. It reads as a flash while scrolling
   and it is not a compositing artefact: the gap measured exactly 18px at device pixel
   ratios 1, 1.25, 1.5, 1.75, 2 and 2.5, integer at every one, and sat in layout rather than
   in paint. The fix is to move the attribute inward, never to overlap the seam. */
[data-reveal] { opacity: 1; transform: none; }
.js [data-reveal] {
  opacity: 0; transform: translateY(18px);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

/* ---------- 06 · nav reaction ---------- */
/* Surface, not size. The original spec compressed the row from 72px to 52px; tools/sweep.mjs
   asserts 72px at every width and its README records the bug that assertion came from, so the
   reaction is carried entirely by shadow and border colour. The background does not change:
   both states are opaque white (see style.css), because a translucent nav let hero paragraph
   text show through the gaps between the pills as it scrolled under.
   border-bottom-color, NOT border-bottom: none — removing the border would take a pixel off
   the header box and is exactly the kind of thing the assertion exists to catch. */
header.is-scrolled {
  border-bottom-color: transparent;
  box-shadow: var(--shadow-card-marketing);
}

/* ---------- 06 · sticky CTA bar ---------- */
/* Hidden by default and shown by JS — the one item here that does NOT reveal on failure. With
   JS off there is no bar and the hero CTAs carry the page, which the spec accepts. It is not a
   [data-reveal] element and must not become one: a permanently visible bar over every page
   would be worse than no bar. */
/* The teal is full-bleed; the contents are not. They sit in the site's own
   .container, so the message begins on the same gutter as every other section
   and the pills end where the content column ends -- at 1920 that is 400px in
   from the viewport, which is the point: the pills belong next to the message
   they act on, not stranded at the screen edge. */
.cta-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background: var(--teal);
  transform: translateY(110%); opacity: 0;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) ease;
}
.cta-bar-inner {
  display: flex; align-items: center; justify-content: space-between; gap: 28px;
  /* vertical only: the horizontal gutter stays .container's own 24px */
  padding-top: 16px; padding-bottom: 16px;
}
.cta-bar.is-shown { transform: translateY(0); opacity: 1; }
/* The message is the bar, not a caption on it. Poppins semibold at 21px on
   desktop, against the 13px it used to whisper at. */
.cta-bar-label {
  margin: 0;
  font-family: 'Poppins', sans-serif; font-weight: 600;
  /* 1.35vw landed 19.4px at 1440, under the 20-22px this needs to read as
     the bar rather than a caption on it. 1.46vw puts 1440 at 21px and 1920 at
     the 22px cap, and the 16px floor holds from about 1100 down, where the two
     pills start needing the width. */
  font-size: clamp(1rem, 1.46vw, 1.375rem);
  line-height: 1.3;
  color: var(--white);
}
.cta-bar-actions { display: flex; gap: 10px; flex-shrink: 0; }
/* Both bar buttons are .btn .btn-sm, so they inherit the pill radius and the 2px border that
   keeps every pill on this site the same cap height. Only the colours differ. */
.cta-bar .btn-bar-primary { background: var(--white); color: var(--dark-teal); border: 2px solid transparent; }
.cta-bar .btn-bar-secondary { background: rgba(255,255,255,.18); color: var(--white); border: 2px solid transparent; }
.cta-bar .btn-bar-primary:hover { background: var(--grey); }
.cta-bar .btn-bar-secondary:hover { background: rgba(255,255,255,.28); }
/* On a teal surface the page focus ring would disappear, so it inverts here. */
.cta-bar .btn:focus-visible { outline: 2px solid var(--dark-teal); outline-offset: 2px; }
/* The label is the first thing to go: below 480px the two buttons need the width,
   and 21px type will not sit beside two pills there. The centring moves to
   .cta-bar-inner with the flex context -- left on .cta-bar it would have been a
   dead property on a block element. */
@media (max-width: 480px) {
  .cta-bar-inner { justify-content: center; }
  .cta-bar-label { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .cta-bar { transition: none; }
  .cta-bar:not(.is-shown) { transform: translateY(110%); }
}

/* ── 04 · Product cards get a second state ─────────────────────────────────
   Scoped to .product-card, never .card. .card is used 35 times across seven
   pages -- story cards, contact cards, the activate page -- so a lift hung on
   .card:hover would raise every one of them. */
.product-card {
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.product-card:hover,
.product-card:focus-within {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}

/* The media box keeps the 130px height and 8px radius the cards already
   shipped with, lifted out of the inline styles that carried them. */
.product-media {
  position: relative; overflow: hidden;
  height: 130px; border-radius: 8px;
}
/* Both frames are photographs of the same object, shot at the same aspect, so
   one treatment covers both and the swap has no framing jump. The grey plate
   and the contain override that used to sit here are gone with the catalogue
   art that needed them: there is no longer a mismatch to reconcile. */
.product-media img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  /* The new photography is near-square (1.09:1 and 1.19:1) in a 2.19:1 box, so
     cover discards half the frame's height. Centred, that cut lands on the
     faces: the coach lost his head entirely and the librarian scene lost the
     girl's eyes, leaving three handovers with nobody in them.
     One value for all six frames, not per-image tuning. 38% is where the faces
     come back without the labelled object dropping out of the bottom -- at 28%
     the laptop and its sticker start leaving the frame, which is the other
     thing these cards exist to show. */
  object-position: 50% 38%;
}

/* The reunion frame, revealed on hover or focus. */
.product-media-find {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity 500ms var(--ease-out), transform 6s linear;
}
.product-card:hover .product-media-find,
.product-card:focus-within .product-media-find {
  opacity: 1;
  /* Barely perceptible drift -- do not speed this up. */
  transform: scale(1.08);
}

/* No hover to give and nothing to lose: touch devices present both states at
   once rather than hiding half the card behind a gesture that never comes. */
@media (hover: none) {
  .product-media-find { opacity: 1; transition: none; transform: none; }
  .product-card:hover .product-media-find,
  .product-card:focus-within .product-media-find { transform: none; }
}

/* The swap still happens, instantly; the drift does not. The global collapse
   above would otherwise snap the scale to 1.08, which is the motion this
   preference exists to remove. */
@media (prefers-reduced-motion: reduce) {
  .product-card:hover .product-media-find,
  .product-card:focus-within .product-media-find { transform: none; }
}

/* 04's acceptance asks for a visible focus ring on the card's link, and .btn
   turned out to have no focus-visible rule at all -- every button on every
   page was falling back to the UA default hairline. The ring is not new: it
   is the outline .nav-toggle and .carousel-track already use, applied where
   it was missing rather than invented for this one card.
   .btn-primary inverts for the same reason .cta-bar .btn does above: a teal
   ring separated from a teal button by a 2px gap is not a ring. */
.btn:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.btn-primary:focus-visible { outline-color: var(--dark-teal); }

/* ── 05 · Recovery stories become a rail ──────────────────────────────────
   Native overflow-x does the scrolling. Everything below is presentation;
   the drag layer in engagement.js is an enhancement on top of a scroller
   that already works with no JS at all. */
.rail {
  display: flex; gap: 24px;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  /* The rail runs off the right edge of the viewport, not the content
     column: a card cut by the screen edge is what says "there is more".
     50% is half the container's content box, 50vw half the viewport, so
     the difference is exactly the gutter at every width -- 24px below
     1120px where there is only padding, 184px at 1440. */
  margin-right: calc(50% - 50vw);
  padding-bottom: 4px;
}
.rail::-webkit-scrollbar { height: 8px; }
.rail::-webkit-scrollbar-thumb { background: rgba(255,255,255,.25); border-radius: var(--radius-full); }

.story-card {
  flex: 0 0 300px;
  scroll-snap-align: start;
  padding: 0; overflow: hidden;
}
/* .card carries 28px of padding for every other card on the site. The story
   media has to reach the card edges, so the padding moves inward to the text
   instead of being cancelled per-side around the image. */
.story-card-text {
  display: flex; flex-direction: column; flex: 1;
  padding: 20px 24px 24px;
}
.story-media {
  display: block; width: 100%; height: 170px;
  object-fit: cover;
  pointer-events: none;
}
/* The last card is a conversion, not a dead stop. Yellow is the surface here
   because the band is already dark teal and a dark-teal card would vanish
   into it -- the one place yellow-as-surface is sanctioned. */
.story-card-cta {
  background: var(--yellow); color: var(--dark-teal);
  text-decoration: none;
  justify-content: center;
  padding: 28px;
  gap: 10px;
}
.story-cta-head {
  font-family: 'Poppins', sans-serif; font-weight: 700; font-size: 1.25rem;
  color: var(--dark-teal);
}
.story-cta-body { font-size: .93rem; color: var(--charcoal); }
.story-cta-pill {
  align-self: flex-start; margin-top: 6px;
  background: var(--white); color: var(--dark-teal);
  font-family: 'Poppins', sans-serif; font-weight: 600; font-size: 0.85rem;
  padding: 9px 20px; border-radius: 999px;
}
.story-card-cta:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }

/* Pointer-drag. cursor stays default until JS confirms it can honour it. */
.js .rail { cursor: grab; }
.js .rail.is-dragging { cursor: grabbing; user-select: none; scroll-snap-type: none; }
.js .rail.is-dragging .story-card { pointer-events: none; }

/* Above 1280 there are not enough stories to fill a rail. Three cards and the
   CTA come to 1272px, and because the bleed widens the rail towards the
   viewport rather than the column, the wider the screen the less there is to
   reveal: 96px of travel at 1280, 16px at 1440, none at all from 1472 up. A row
   that merely looks clipped is worse than a plain row, so past 1280 the
   degradation is made deliberate rather than left to emerge -- the bleed stops,
   the grab cursor stops, and the four cards shrink from their 300px basis to
   sit exactly across the content column. The drag layer stays bound and goes
   inert on its own: a rail with nothing to scroll has nothing to drag.
   This reverses itself the moment a fourth real story exists. It is a
   consequence of having three, not a design decision. */
@media (min-width: 1281px) {
  .rail { margin-right: 0; scroll-snap-type: none; }
  .story-card { flex: 0 1 300px; }
  .js .rail, .js .rail.is-dragging { cursor: auto; }
}

/* ── 07 · A plain closing CTA ─────────────────────────────────────────────
   index.html ended on an argument with nothing to do about it. The signature
   that first stood here is gone -- Patrick Hand read informal against Poppins,
   and it was the tagline a third time on one page -- but the page still needs
   somewhere to act. Site fonts, no divider, no stagger; the section fades in
   with the shared [data-reveal] observer exactly as the belief band does. */
.closing { text-align: center; padding: var(--section-y) 0; }
/* Semibold, not the site h2's 700: this closes a page rather than opening a
   section, and the lighter weight sits better above a single button.
   Deliberately "your things", not the "your lost items" of the registered
   tagline #4 on faq.html and how-it-works.html. Those keep the registered
   wording; this slot wants the shorter, warmer variant. Not a typo to fix. */
.closing-head {
  font-family: 'Poppins', sans-serif; font-weight: 600;
  font-size: clamp(1.375rem, 3vw, 2rem); line-height: 1.25;
  color: var(--dark-teal); margin: 0; text-wrap: balance;
}
/* The 32x2px yellow rule, standing alone. Returns with the headline: it is the
   motif from .section-head h2::after, which cannot be reused because that
   selector needs a .section-head wrapper this section does not have. Same four
   values, no new ones. */
.accent-divider {
  width: 32px; height: 2px; border-radius: 999px;
  background: var(--yellow); margin: 22px auto 26px;
}
.closing-note { font-size: 12px; color: rgba(31, 41, 51, .5); margin-top: 14px; }

/* ── 03 · The four steps become something you operate ─────────────────────
   The base state below is the layout that shipped: four cards across, no
   media, no tabs. The stepper is layered on top by .is-live, which JS adds
   only once it has decided it can honour it -- so no-JS and reduced-motion
   share one path and neither gets a half-built control. */
.steps-media, .steps-tabs { display: none; }
.steps-copy { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }

.steps.is-live {
  display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: center;
}
.steps.is-live .steps-copy { display: block; }
.steps.is-live .steps-media { display: block; position: relative; min-height: 280px; }

/* Every panel occupies the same cell, and all four keep their space: the cell
   is therefore always as tall as the longest step, and the row cannot change
   height as the steps advance. Hiding the inactive ones with [hidden] does not
   work here -- it takes them out of layout, so the cell sizes to whichever step
   is showing and the block jumped 35px between step 1 and the rest.
   visibility, unlike opacity, still removes them from the accessibility tree,
   so exactly one panel is readable at a time; and unlike a cross-fade it never
   paints two paragraphs over each other. */
.steps.is-live .steps-item {
  grid-area: 1 / 1;
  padding: 0; box-shadow: none; background: none;
  visibility: hidden;
}
.steps.is-live .steps-item.is-on { visibility: visible; }
.steps.is-live .steps-copy { display: grid; }
.steps.is-live .steps-tabs { grid-area: 2 / 1; }
.steps.is-live .step-num { display: none; }
.steps.is-live .steps-item h3 {
  font-family: 'Poppins', sans-serif; font-weight: 600; font-size: 22px;
  color: var(--dark-teal); margin: 0 0 8px;
}
/* The body sets the floor for the tallest of the four, so the tab row below it
   does not move as the steps advance. */
.steps.is-live .steps-item p {
  font-size: 15px; line-height: 1.7; color: rgba(31, 41, 51, .8);
  min-height: 76px; margin: 0;
}

.steps.is-live .steps-tabs { display: flex; gap: 16px; margin-top: 24px; }
.steps-tab {
  flex: 1; position: relative; text-align: left;
  background: none; border: 0; border-top: 2px solid var(--rmp-border);
  padding: 12px 0 0; cursor: pointer;
  font-family: 'Poppins', sans-serif; font-weight: 600; font-size: 12px;
  color: rgba(31, 41, 51, .4);
  transition: color var(--dur-fast) var(--ease-out);
}
.steps-tab.is-on { color: var(--dark-teal); }
.steps-tab:focus-visible { outline: 2px solid var(--teal); outline-offset: 3px; }
/* The fill sits on the 2px border the tab already draws, not beside it. */
.steps-fill {
  position: absolute; left: 0; top: -2px; height: 2px; width: 0;
  background: var(--teal);
  transition: width var(--dur-slow) ease;
}
.steps-tab.is-on .steps-fill { width: 100%; }

.steps-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; border-radius: var(--radius);
  opacity: 0; transition: opacity var(--dur-slow) var(--ease-out);
}
.steps-img.is-on { opacity: 1; }

@media (max-width: 860px) {
  .steps-copy { grid-template-columns: repeat(2, 1fr); }
  .steps.is-live { grid-template-columns: 1fr; }
  .steps.is-live .steps-media { min-height: 220px; order: -1; }
  .steps.is-live .steps-tabs { gap: 10px; }
  .steps-tab { font-size: 11px; }
}
@media (max-width: 560px) {
  .steps-copy { grid-template-columns: 1fr; }
}

/* ── 01 · The hero goes full-bleed ────────────────────────────────────────
   The photograph is the hero now; the copy sits on it rather than beside it.
   Everything the carousel already did -- scroll-position derivation, dots,
   arrows, the permanent lock -- is untouched. Only the frame changed. */
.hero-full {
  position: relative;
  /* 100vh minus the header, not 100vh. The header is position:sticky, so it occupies
     73px of normal flow above this section -- a 100vh hero therefore ends 73px below the
     fold and takes its own dots with it. Found by hit-testing the dots, which came back
     off-screen at exactly the viewport heights where 100vh won over the 900px cap. */
  height: min(calc(100vh - 73px), 900px);
  padding: 0;
  overflow: hidden;
  display: flex; align-items: flex-end;
}
.hero-full .carousel {
  position: absolute; inset: 0;
  height: auto; border-radius: 0; background: var(--dark-teal);
}

/* One layer for all five. Dark teal rather than black: on these photographs
   black reads as a grade, and the band already belongs to the brand. */
.hero-scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  /* Measured, not chosen, three times over.
     The geometry first: a gradient fading out by 60% left the H1 above any scrim at all and
     white came back at 1.66:1. The hold has to reach the top of the H1, which sits at 48%
     of the frame at 1440 and 61% at 390, so it holds to 66% and fades out by 90%.
     Then the alpha. At .84 the stills cleared by 0.04, which is not headroom, so .86.
     Then the carousel took video, and that produced the rule worth keeping:

       SAMPLE MOVING FOOTAGE, NEVER THE POSTER ALONE.

     The scan clip passed on its poster at 5.38:1 and fell to 4.48:1 by t=4.2s as the shot
     brightens. A still is one measurement where a five-second clip at 24fps is a hundred
     and twenty, and every check this project had — the sweep, the scrim measurement, the
     contrast gate — looked only at the frame the poster froze. That would have shipped a
     contrast failure none of them could see.
     Sampled at nine points across both clips at both viewports, .88 clears by 0.18 and .90
     by 0.39. .90, because nine samples prove nine frames and not the ones between them, and
     a scrim is not worth optimising to two decimal places.
     Worst case now: 4.89:1 at 1440 and 4.95:1 at 390 against a 4.5 gate, across eight
     slides and both clips, on the brightest 2% of pixels behind the H1 and the sub. */
  background: linear-gradient(to top,
    rgba(15, 110, 105, .90) 0%,
    rgba(15, 110, 105, .90) 66%,
    rgba(15, 110, 105, 0) 90%);
}

/* Above the scrim, and above the copy, so the controls stay reachable. */
.hero-full .carousel-btn { z-index: 3; }
.hero-full .carousel-dots { z-index: 3; }

.hero-copy {
  position: relative; z-index: 2;
  width: 100%;
  padding-bottom: 76px;   /* clears the dots */
  /* The block spans the column, but only its links take the pointer: a
     transparent panel over the photograph would swallow drags on the track. */
  pointer-events: none;
}
.hero-copy a, .hero-copy .btn { pointer-events: auto; }
.hero-full .kicker { color: rgba(255, 255, 255, .82); }
.hero-full h1 { color: var(--white); max-width: 15em; }
.hero-full .sub { color: rgba(255, 255, 255, .92); max-width: 34em; }
/* .btn-secondary is dark teal on transparent, which disappears on a photograph.
   The hero variant inverts rather than adding a colour: white on white. */
.btn-hero-secondary {
  background: transparent; color: var(--white); border: 2px solid var(--white);
}
.btn-hero-secondary:hover { background: var(--white); color: var(--dark-teal); }
.hero-full .btn:focus-visible { outline: 2px solid var(--white); outline-offset: 3px; }

/* The dots move off centre: centred they would sit under the copy. */
.hero-full .carousel-dots { bottom: 28px; }

@media (max-width: 860px) {
  .hero-copy { padding-bottom: 64px; }
  .hero-full h1 { max-width: none; }
}

/* ── The hero stops being full-bleed on a phone ───────────────────────────
   Measured, and the reason is geometry rather than taste. The hero is
   min(100vh - 73px, 900px) -- a tall portrait box -- and every slide is 16:9
   landscape (the clips are 1916x1080). object-fit: cover fills a portrait box by
   discarding the sides, so a phone was showing 29% of each frame and a desktop 98%:

     desktop 1440      1440 x 827   98% of the frame
     iPhone SE          375 x 594   36%
     Galaxy S8          360 x 667   30%
     iPhone 14/15       390 x 771   29%
     Pixel 7 / 15 PM    412-430 x 842-859   28%

   Re-aiming object-position was tried first and rejected on measurement: four of
   the six slides already sit within a percent or two of their best focal point, so
   it moved three slides by 5-8% and bought almost nothing. It also could not touch
   the real defect. The scan clip ends on the recovery page filling the frame, and
   that page is TEXT: its ink spans 0%-85% of the frame width, so no 29% window
   contains it at any focal point. "You've found a tagged item" rendered as an
   unreadable middle slice on exactly the devices finders actually scan with.

   So below 600 the image stops carrying the copy. The carousel takes the footage's
   own 16:9 and the copy sits beneath it on dark teal -- the scrim colour, so the
   band still reads as one object. 100% of every frame, and the recovery page legible.
   The scrim goes with it: it exists to hold text off a photograph, and there is no
   longer text on the photograph. Contrast stops depending on what the footage is
   doing and becomes flat white-on-dark-teal.
   600px is the site-wide stack breakpoint (.grid-2 and the rest), not a new one. */
@media (max-width: 600px) {
  .hero-full {
    height: auto;
    display: block;
    background: var(--dark-teal);
  }
  /* Out of the absolute fill and back into flow, sized by the source's own ratio.
     aspect-ratio needs height:auto to have anything to resolve against -- .carousel's
     base rule is a flat 420px, which would otherwise win and re-crop the frame. */
  .hero-full .carousel {
    position: relative;
    inset: auto;
    aspect-ratio: 16 / 9;
    height: auto;
  }
  /* The scrim stays, doing a different job. On desktop it holds text off a photograph;
     here there is no text on the photograph, and what it closes instead is the seam where
     the frame meets the copy band. So it runs bottom-up only, and the bottom stop is alpha
     1 rather than the desktop .90 -- at .90 the last row of the gradient is still 10% image,
     which puts a visible line exactly where the band starts and moves the seam rather than
     removing it. Same colour as the band it lands on, so the two meet with nothing between.
     30% of the frame, expressed as a proportion so the fade keeps its depth from a 203px
     frame on a Galaxy S8 to 242px on a 15 Pro Max. The subjects sit mid-frame -- the bottom
     third is desk, wall and coat -- so nothing the story needs is under it. */
  .hero-scrim {
    background: linear-gradient(to top,
      rgba(15, 110, 105, 1) 0%,
      rgba(15, 110, 105, 0) 30%);
  }
  /* Nothing sits under the copy now, so it no longer has to pass drags through to
     the track, and no longer has to clear the dots -- those stay on the image. */
  .hero-copy {
    pointer-events: auto;
    padding-top: var(--section-y-sm);
    padding-bottom: var(--section-y-sm);
  }
  .hero-full .carousel-dots { bottom: 12px; }
}

/* The reveal starts an element 18px low and transparent, which assumes it sits ON
   the page background. A full-bleed coloured band IS the background, so revealing
   the band itself vacates 18px directly under the hero and the white body shows
   through it -- a white line that shrinks and fades as the band settles, which
   reads as a flash at the seam while scrolling. Measured 18px at every device
   pixel ratio from 1 to 2.5, so it was never subpixel or compositing.
   Fixed in the markup, not here: data-reveal moved from .belief to the sentence
   inside it, so the band never moves and nothing is vacated. The rule it leaves
   behind is to reveal a band's contents, never the band -- there is no CSS for
   that, which is why this is a note rather than a declaration doing no work. */
