/* The landing funnel.
   One column, one decision at a time: the drop zone is the whole page until a
   file is chosen, and each later panel replaces it rather than appearing
   below it. Nothing competes with the step the visitor is actually on. */

body.landing {
  min-height: 100vh;
}

.masthead {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 18px 24px;
}

.masthead .quiet {
  margin-left: auto;
  color: var(--muted);
  text-decoration: none;
  font-size: 15px;
}

.masthead .quiet:hover {
  color: var(--text);
}

/* ── the hero backdrop ────────────────────────────────────────────────────
 * The render bleeding in from the right edge, beside a centred 44rem column.
 *
 * The width is DERIVED, not guessed: it is exactly the gap between the
 * column's right edge and the viewport, so the image cannot reach the text at
 * any width or zoom level. An earlier version used a fixed 46vw strip plus a
 * `max-width: 1279px` kill switch, which meant anyone with a non-maximised
 * window — or browser zoom above 100%, where 1512 becomes 1210 — saw no hero
 * at all. A magic breakpoint was the wrong tool for a layout question that
 * has an exact answer.
 *
 * Fixed, so scrolling slides the content out from under it. Behind everything
 * via a negative z-index; the body's own background paints below that, so the
 * ground colour still shows through the masked region.
 */
body.landing::before,
.hero-motion {
  position: fixed;
  top: 0;
  right: 0;
  /* the space to the right of the column, plus the bleed it is allowed to
     take out of the column's own gutter */
  width: max(0px, calc((100vw - 43rem) / 2 + 4vw));
  height: min(84vh, 840px);
  z-index: -1;
  pointer-events: none;
  background: var(--s-hero, none) right top / cover no-repeat;
  opacity: var(--s-hero-opacity, 0.85);
  /* One mask layer per axis. If a browser does not support mask-composite the
     layers fall back to the initial `add`, i.e. the union — which shows MORE
     of the image, never less, so the hero degrades to a harder edge instead
     of disappearing. */
  -webkit-mask-image:
    linear-gradient(to right, transparent 0%, rgba(0, 0, 0, 0.55) 34%, #000 78%),
    linear-gradient(to bottom, transparent 0%, transparent 7%, #000 21%, #000 54%, transparent 96%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right, transparent 0%, rgba(0, 0, 0, 0.55) 34%, #000 78%),
    linear-gradient(to bottom, transparent 0%, transparent 7%, #000 21%, #000 54%, transparent 96%);
  mask-composite: intersect;
}

/* The still is the resting state and the floor: it sits BELOW the clip, so a
   video that never loads, never decodes or is blocked from autoplaying leaves
   the page looking exactly as it did before the orbit existed. */
body.landing::before { content: ""; z-index: -2; }

.hero-motion { z-index: -1; }
.hero-motion video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: right top;
  opacity: var(--s-hero-opacity, 0.85);
}

/* Below 900px the derived width collapses to a sliver, so the desktop
 * geometry stops working — but the answer was `display: none`, which meant a
 * phone visitor got the whole page as text on flat black with no imagery
 * anywhere on it. On the device most of them arrive on, the brand was
 * invisible.
 *
 * So the render moves rather than leaving: full width, anchored to the top,
 * behind the column. It keeps its right-weighted composition and its
 * horizontal mask, which is what holds it off the left-aligned headline — the
 * text sits over the dark side of the frame, not over the lit bone. The
 * vertical fade lands well above the drop zone, so the one thing on this page
 * that has to be unmissable never has a picture behind it.
 */
@media (max-width: 900px) {
  body.landing::before,
  .hero-motion {
    width: 100%;
    /* Deliberately short. A taller band looked better in isolation and put
       lit rib over the lede, which is `--muted` grey and the one paragraph on
       this page that has to explain what the product is. The headline is
       white, bold and 2rem — it reads over the render and gains from it; body
       copy does not. So the band ends where the headline does, and the
       vertical fade is finished before the lede's first line at every phone
       height (a `vh` band shrinks on short screens, while the text below it
       is laid out by content and stays put — so the cap, not the vh, is the
       number that matters on a tall phone). */
    height: min(30vh, 265px);
    opacity: calc(var(--s-hero-opacity, 0.85) * 0.7);
    -webkit-mask-image:
      linear-gradient(to right, transparent 0%, rgba(0, 0, 0, 0.5) 30%, #000 72%),
      linear-gradient(to bottom, transparent 0%, #000 24%, #000 62%, transparent 95%);
    mask-image:
      linear-gradient(to right, transparent 0%, rgba(0, 0, 0, 0.5) 30%, #000 72%),
      linear-gradient(to bottom, transparent 0%, #000 24%, #000 62%, transparent 95%);
  }

  .hero-motion video { opacity: 1; }
}

body.landing main {
  max-width: 44rem;
  margin: 0 auto;
  padding: 3vh 24px 64px;
}

.hero h1 {
  font-size: clamp(2rem, 6vw, 3rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0 0 0.6rem;
  /* Two lines of roughly equal length rather than a full line and one orphan
     word. Ignored where unsupported, which costs nothing. */
  text-wrap: balance;
}

.nowrap { white-space: nowrap; }

.hero .lede {
  color: var(--muted);
  font-size: 1.05rem;
  margin: 0 0 1.8rem;
}

/* The drop zone. Big, obviously a target, and the only thing to do here. */
.drop {
  border: 2px dashed #33333d;
  border-radius: 18px;
  padding: 44px 24px;
  text-align: center;
  transition: border-color 0.15s, background 0.15s;
}

.drop.hot {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.drop-big {
  font-size: 1.25rem;
  font-weight: 600;
}

.drop-small {
  color: var(--muted);
  margin-top: 6px;
}

.drop-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 22px;
}

/* Each later step. */
.panel {
  border: 1px solid var(--line, #26262e);
  border-radius: 18px;
  padding: 24px;
  text-align: center;
}

.panel h2 {
  margin: 0 0 0.4rem;
  font-size: 1.3rem;
}

.chosen-row {
  display: flex;
  align-items: baseline;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px 14px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--line, #26262e);
  margin-bottom: 20px;
}

.chosen-summary {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.link {
  background: none;
  border: 0;
  padding: 0;
  color: var(--accent);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  text-decoration: underline;
}

/* The address ask. Appears only once a file is held — see landing.php. */
.ask {
  text-align: left;
}

.ask label {
  display: block;
  font-weight: 600;
  margin-bottom: 10px;
}

.ask-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.ask-row input {
  flex: 1 1 16rem;
  min-width: 0;
}

.ask-note {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.5;
  margin: 12px 0 0;
}

.ask-note a {
  color: var(--accent);
}

/* Waiting for the link. */
.spinner {
  width: 26px;
  height: 26px;
  margin: 4px auto 16px;
  border: 3px solid #33333d;
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}

/*
 * The link is now the fallback behind the code, so its spinner stops being a
 * centred "please wait" and becomes a small sign that the poll is still live
 * on the line that explains it.
 */
.spinner.inline {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-width: 2px;
  margin: 0 6px 0 0;
  vertical-align: -1px;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; }
}

/* ── the assembly (§13.7) ─────────────────────────────────────────────────
   Their own slices, decoded locally, dealing into a tilted 3D stack while
   the upload and ingest run. The geometry is the iOS build demo's: tilt
   from above, slow yaw drift, translucent slices accumulating into a
   proto-volume on the black stage. JS only appends canvases and toggles
   .placed — every motion lives here.

   NOT the iOS scene's screen-blend: `mix-blend-mode` is a grouping
   property, and on a 3D-transformed child it forces flattening — all 14
   slices collapse into one plane and the stack reads as a single bright
   sheet (browser-verified; CALayer composits blend+3D freely, CSS does
   not). Translucent normal blending keeps the 3D fan AND lets deeper
   slices glow through, which is the same proto-volume read.

   The stage is a fixed-size black box like every thumbnail surface; the
   slices are absolutely-positioned canvases fanned along the stack axis
   with translate3d, dealt in from above via the .placed transition. */
.assembly {
  margin: 0 auto 18px;
}

.assembly-stage {
  position: relative;
  width: 240px;
  height: 240px;
  margin: 0 auto;
  background: #000;
  border-radius: 14px;
  overflow: hidden;
  perspective: 700px;
}

.assembly-spin {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  animation: assembly-yaw 14s ease-in-out infinite alternate;
}

@keyframes assembly-yaw {
  from { transform: rotateX(56deg) rotateZ(-20deg); }
  to   { transform: rotateX(56deg) rotateZ(24deg); }
}

.assembly-spin canvas {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 150px;
  height: 150px;
  object-fit: contain;
  margin: -75px 0 0 -75px;
  opacity: 0;
  /* Dealt in from above the stack, along its axis. --z is the slice's
     settled position, set by JS; the un-.placed state hovers 130px higher
     and transparent. */
  transform: translate3d(0, 0, calc(var(--z, 0px) + 130px));
  transition: transform 0.55s cubic-bezier(0.22, 0.8, 0.3, 1), opacity 0.4s ease-out;
}

.assembly-spin canvas.placed {
  /* Low enough that a 14-deep stack stays structured rather than washing
     to white; the overlap glow is what makes it read as a volume. */
  opacity: 0.55;
  transform: translate3d(0, 0, var(--z, 0px));
}

/* The reveal handoff: the settled stack fades before the panel switches. */
.assembly.leaving {
  opacity: 0;
  transition: opacity 0.35s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  /* landing.js never builds the scene under reduced motion; this is the
     belt to that suspender. */
  .assembly { display: none; }
}

/* Upload progress. The bar is the sunk cost made visible — a multi-minute
   transfer with nothing moving is where people give up. */
.bar {
  height: 8px;
  border-radius: 999px;
  background: #23232b;
  overflow: hidden;
  margin: 16px 0 4px;
}

.bar i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.2s linear;
}

/* ── the case for uploading ───────────────────────────────────────────────
 * Everything in this group carries `data-idle` and is hidden by landing.js
 * the moment a file is chosen (see its `show()`). It exists to answer "will
 * this work, and can I trust it" — questions that stop being live the instant
 * the visitor answers them by acting.
 *
 * The type scale steps DOWN from the drop zone deliberately. This is material
 * to be skimmed by someone deciding, not the decision itself, and a proof
 * block that shouts as loudly as the call to action just makes two things
 * competing for the same eye.
 */

/* The eyebrow. Small, tracked-out caps above the h1, holding the words a
   visitor may have searched for so the headline can hold the words a visitor
   actually says. */
.eyebrow {
  margin: 0 0 0.7rem;
  color: var(--s-dim, #5c564f);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* On a phone this runs to two lines and the tracking makes it break after
     "NOTHING TO", stranding INSTALL alone. Balancing splits it at the middle
     separator instead. Unsupported browsers get the plain greedy wrap, which
     is what shipped before — never a layout that depends on it. */
  text-wrap: balance;
}

.hero .lede code {
  font-size: 0.92em;
  color: var(--text);
}

/* Under the drop zone's buttons: the three objections, in one line. Sits
   inside the dashed box so it is read as part of the offer, not as a footnote
   about it. */
.drop-note {
  margin: 18px 0 0;
  color: var(--muted);
  font-size: 13px;
}

/* The scope list. Centred and wide-set — it is scanned for one's own modality,
   never read left to right. */
.formats {
  margin: 26px auto 0;
  max-width: 38rem;
  text-align: center;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.7;
}

.formats b {
  display: inline-block;
  margin-bottom: 4px;
  color: var(--text);
  font-weight: 600;
  letter-spacing: 0.01em;
}

.formats i {
  font-style: normal;
  color: var(--s-warn, #d9a05b);
}

/* The horizontal page's way onward to the three profession pages. Set as
   quiet inline links rather than as buttons: this is a signpost under the
   scope claim, not a second call to action competing with the drop zone six
   inches above it. Only `/` renders it — see _formats.php. */
.formats.routes {
  margin-top: 14px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 22px;
}

.formats.routes a {
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 1px;
}

.formats.routes a:hover {
  color: var(--text);
  border-bottom-color: var(--accent-line);
}

/* Shared heading for the two proof sections. Quieter than the trust/FAQ h2s
   below, which are section headings for a reader who has already scrolled;
   these interrupt someone still deciding. */
.proof-h {
  margin: 0 0 1.1rem;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--s-dim, #5c564f);
  text-align: center;
}

.proof {
  margin-top: 54px;
}

/* The three figures, at a size that matches how load-bearing they are.
 *
 * They were bold spans inside 13.5px body copy — the only numbers on the page
 * a competitor cannot also claim, set smaller than the FAQ. A reader who scans
 * rather than reads now gets the whole argument from three glyphs.
 *
 * `ch` on the figure, not px: these are numerals, the widest is five glyphs
 * ("1,347"), and tabular figures make the row settle at the same rhythm in
 * Archivo as in whatever fallback a browser without webfonts lands on.
 */
.stats {
  display: grid;
  gap: 20px 24px;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  margin: 0 0 34px;
  padding: 0 0 30px;
  border-bottom: 1px solid var(--line, #26262e);
}

.stat dt {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-stretch: var(--display-stretch);
  letter-spacing: var(--display-track);
  font-size: clamp(2.1rem, 5.4vw, 2.9rem);
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* The denominator is context, not the claim — 53 is the number, /62 is what
   makes it honest. Same face, dimmed and stepped down so it reads as a unit
   with the figure rather than as a second figure. */
.stat .stat-of {
  color: var(--s-dim, #5c564f);
  font-size: 0.5em;
  letter-spacing: 0;
}

.stat dd {
  margin: 10px 0 0;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.5;
}

/* Three columns at full width, not two-plus-an-orphan. The trust grid below
   holds four cards so its 15rem minimum lands on a tidy 2×2; three cards at
   the same minimum wrap to 2+1 and leave a hole the size of a card, which
   reads as something failing to load. 12rem is the widest minimum that still
   fits three across the 44rem column. */
.proof-grid {
  display: grid;
  gap: 22px 24px;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
}

.proof-grid h3 {
  margin: 0 0 0.4rem;
  font-size: 0.95rem;
}

.proof-grid p {
  margin: 0;
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.65;
}

.proof-grid b { color: var(--text); font-weight: 600; }
.proof-grid a { color: var(--accent); }

/* ── the product band ─────────────────────────────────────────────────────
 * The one section that leaves the 44rem column.
 *
 * Everything else on this page is the same width on the same ground at the
 * same type scale from the masthead to the footer, which is what made a page
 * of genuinely strong claims read as a wall. One full-bleed band with its own
 * ground is enough to turn the column into a chosen measure.
 *
 * The breakout is the standard centred-column escape. `100vw` counts the
 * classic scrollbar on Windows and Linux, which would otherwise buy a
 * horizontal scrollbar on the whole page, so the body clips the overflow —
 * `clip`, never `hidden`: `hidden` makes the body a scroll container and
 * kills `position: sticky` and scroll anchoring with it. Fixed-position
 * children (the hero render) take the viewport as their containing block and
 * are not clipped by this.
 */
body.landing { overflow-x: clip; }

.showcase {
  width: 100vw;
  margin: 56px 0 0 calc(50% - 50vw);
  padding: 48px 24px 44px;
  /* Pure black, DARKER than the page — the opposite of the usual "lift the
     feature band" instinct, and the render is the reason. It is a lit object
     photographed against black, so its own ground has to BE the band's ground
     or the picture reads as a dark rectangle pasted onto a lighter panel,
     with a visible seam exactly where the composition wants infinite space.
     On `--panel` the seam was plainly visible. This is a stage, not a card. */
  background: #000;
  border-top: 1px solid var(--line-soft, #131211);
  border-bottom: 1px solid var(--line-soft, #131211);
}

/* Full-bleed ground, but the CONTENT still has a measure — a 2,000px-wide row
   of three scans is a screensaver, not an argument. */
.showcase-inner {
  max-width: 62rem;
  margin: 0 auto;
}

/* The lead render. No frame, no border, no panel — the render is already a
 * lit object floating in black, and the band's ground is black, so any box
 * around it would draw a rectangle where the picture ends and reveal it as a
 * pasted-in asset. It bleeds instead — which only works because the band's
 * ground is pure black too; see .showcase. */
.hero-render {
  margin: 0;
}

.hero-render img {
  display: block;
  width: 100%;
  height: auto;
  max-width: 54rem;
  margin: 0 auto;
}

.showcase-lead {
  max-width: 34rem;
  margin: 6px auto 0;
  text-align: center;
  color: var(--text);
  font-size: 15.5px;
  line-height: 1.6;
}

/* The planes are now the supporting act, so the heading that introduces them
   is quieter than the band's own and the panes themselves are smaller. */
.showcase-sub {
  margin: 42px 0 14px;
  text-align: center;
  color: var(--s-dim, #5c564f);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.showcase-panes {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  max-width: 46rem;
  margin: 0 auto;
}

/* Black box, image letterboxed inside it — the same rule the iOS library
   cards follow, and for the same reason: these are three different physical
   shapes (a coronal is portrait, an axial is landscape) and cropping them to
   a common frame would cut anatomy off. The black is the viewer's own ground,
   so the panes read as panes rather than as photographs. */
.pane {
  margin: 0;
  background: #000;
  border: 1px solid var(--line, #26262e);
  border-radius: var(--s-radius, 14px);
  overflow: hidden;
  position: relative;
}

.pane img {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
}

/* The pane label sits UNDER its pane, not over it.
 *
 * The viewer overlays these because a radiologist needs the label welded to
 * the image it names, and has a whole screen of black to put it on. These
 * panes are now a third of that size and the contained image fills nearly all
 * of one, so the label landed on the anatomy — muted grey over lit bowel and
 * spine, which is unreadable and is the one kind of sloppiness a medical page
 * cannot afford. Below the pane it always has its own ground.
 *
 * That moves the frame from the figure to the image, or the caption would sit
 * inside the box it labels. */
.pane {
  border: 0;
  background: none;
  overflow: visible;
}

.pane img {
  background: #000;
  border: 1px solid var(--line, #26262e);
  border-radius: var(--s-radius, 14px);
}

.pane figcaption {
  margin-top: 8px;
  text-align: center;
  color: var(--s-dim, #5c564f);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.showcase-note {
  max-width: 40rem;
  margin: 34px auto 0;
  text-align: center;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.65;
}

.showcase-note b { color: var(--text); font-weight: 600; }

.showcase-credit {
  max-width: 40rem;
  margin: 12px auto 0;
  text-align: center;
  color: var(--s-dim, #5c564f);
  font-size: 12px;
  line-height: 1.55;
}

.showcase-credit a { color: inherit; }

/* "See it work first." Two real targets rather than a sentence with two links
   in it: for a stranger who will not upload a medical file to a site they have
   never heard of, this is the primary path, not the consolation prize. */
.seeit {
  margin-top: 48px;
}

.seeit-grid {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

.seeit-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px;
  border: 1px solid var(--line, #26262e);
  border-radius: var(--s-radius, 14px);
  background: var(--panel, #0e0d0c);
  text-decoration: none;
  transition: border-color 0.15s, background 0.15s;
}

.seeit-card:hover {
  border-color: var(--s-accent-line, rgba(227, 155, 114, 0.42));
  background: var(--s-accent-soft, rgba(227, 155, 114, 0.12));
}

.seeit-text {
  display: block;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.55;
}

.seeit-text b {
  display: block;
  margin-bottom: 3px;
  color: var(--accent);
  font-size: 14.5px;
  font-weight: 600;
}

.seeit-note {
  margin: 14px 0 0;
  text-align: center;
  color: var(--s-dim, #5c564f);
  font-size: 12.5px;
  line-height: 1.6;
}

/* Quotes. Three across, no photos, no star rows, no company logos — the
   corroboration is the sentence, and the ornament that usually surrounds a
   testimonial is what makes people read it as advertising. Deliberately the
   quietest block on the page: it sits below the two proof sections that make
   checkable claims, because a stranger weighing a medical upload is moved
   further by "verified against pydicom" than by a compliment. */
.voices {
  margin-top: 48px;
}

.voices-grid {
  display: grid;
  gap: 18px 24px;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

.voice {
  margin: 0;
}

.voice blockquote {
  margin: 0 0 8px;
  color: var(--text);
  font-size: 14px;
  line-height: 1.6;
}

.voice figcaption {
  color: var(--s-dim, #5c564f);
  font-size: 12.5px;
}

/* The section only renders for a visitor who asked for the design controls
   (?brandtest=1) — but the person judging the layout is also the person who
   could forget which quotes are real. Loud on purpose, and the only warm
   thing in this block, so it cannot be mistaken for part of the design being
   evaluated. Goes when real quotes land, with the gate. */
.voices-flag {
  display: inline-block;
  margin-left: 10px;
  padding: 2px 8px;
  border: 1px solid var(--s-warn, #d9a05b);
  border-radius: 999px;
  color: var(--s-warn, #d9a05b);
  font-size: 10px;
  letter-spacing: 0.14em;
  vertical-align: 2px;
}

/* Social proof. Rendered only above the floor, so this styling is for a real
   number or for nothing at all — there is no placeholder state. */
.social {
  text-align: center;
  color: var(--muted);
  font-size: 14px;
  margin-top: 10px;
}

.social strong {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* Trust and FAQ. Below the fold by design: the funnel is the page, and these
   answer the questions somebody has only once they are considering it. */
.trust,
.faq {
  max-width: 44rem;
  margin: 72px auto 0;
}

.trust h2,
.faq h2 {
  font-size: 1.35rem;
  margin: 0 0 1.4rem;
}

.trust-grid {
  display: grid;
  gap: 22px 28px;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.trust-grid h3 {
  font-size: 0.98rem;
  margin: 0 0 0.4rem;
}

.trust-grid p {
  color: var(--muted);
  font-size: 14px;
  line-height: 1.6;
  margin: 0;
}

.trust-more {
  margin-top: 26px;
  font-size: 14px;
}

.trust-more a,
.faq a {
  color: var(--accent);
}

.faq details {
  border-top: 1px solid var(--line, #26262e);
  padding: 14px 0;
}

.faq details:last-of-type {
  border-bottom: 1px solid var(--line, #26262e);
}

.faq summary {
  cursor: pointer;
  font-weight: 600;
  list-style: none;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::before {
  content: "+";
  color: var(--muted);
  display: inline-block;
  width: 18px;
}

.faq details[open] summary::before {
  content: "–";
}

.faq details p {
  color: var(--muted);
  font-size: 14px;
  line-height: 1.65;
  margin: 10px 0 2px 18px;
}

.faq code {
  background: #1c1c22;
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 13px;
}

/* .foot lives in app.css — every view carries the same footer, not just this
   page. See _foot.php. */

/* The reveal. Their scan, read back to them, before anything is asked for. */
.tick {
  width: 44px;
  height: 44px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 22px;
  line-height: 44px;
}

.reveal-facts {
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0 0 16px;
}

/* Their own volume, orbiting. The worker already wrote this as the animated
   thumbnail and it is free-tier readable, so the most persuasive thing on the
   screen costs nothing extra. Letterboxed in a fixed box — the iOS rule; a
   tall study sized to its own image would blow the panel open. */
.reveal-thumb {
  display: block;
  width: 200px;
  height: 200px;
  margin: 0 auto 16px;
  object-fit: contain;
  background: #000;
  border-radius: 12px;
}

/* ── the local tease (§13.1) ──────────────────────────────────────────────
   One real slice, rendered on the visitor's machine by assets/tease.js. The
   canvas letterboxes in a fixed dark box, like every thumbnail surface in
   the product — sizing the box to the image would let a tall CR blow the
   panel open. The panel is complete without it: this whole block stays
   hidden until the parse lands. */
.tease {
  margin: 16px 0 4px;
}

.tease-canvas {
  width: 220px;
  height: 220px;
  margin: 0 auto 12px;
  background: #000;
  border-radius: 12px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.tease-canvas canvas {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  /* Medical grays dither badly when the browser smooths a big slice down;
     crisp edges read as "real scan", smoothing reads as a stock photo. */
  image-rendering: auto;
}

.tease-line {
  font-size: 0.95rem;
  color: var(--muted, #9aa3af);
  margin: 0 0 14px;
}

.tease-line strong {
  color: var(--fg, #e6e9ee);
}

/* The reveal's honest exit. Quiet, but a real button at readable size — a
   6px gray whisper would be the dark pattern this page refuses to be. */
.link.danger {
  color: #e07a7a;
  margin-top: 18px;
}

/* The reveal's honest exit, at the same weight as the email submit it sits
   opposite. Outlined rather than filled so it reads as the alternative and
   not the recommendation — but full size, full width and full contrast,
   because §13.2's whole argument is that a person at the ask can see two
   real choices. Anything quieter is the thumb on the scale. */
.button.danger-outline {
  margin-top: 16px;
  border: 1px solid #6b3a3a;
  background: transparent;
  box-shadow: none;
  color: #e58f8f;
}

.button.danger-outline:hover {
  border-color: #8f4a4a;
  background: rgba(224, 122, 122, 0.08);
  color: #f0a8a8;
}

/* ── the sample orbit (§13.4) ─────────────────────────────────────────────
   3D is the product's main feature; this is the one place the landing page
   SHOWS it rather than describing it. Reduced motion swaps the gif for the
   still poster — two imgs, CSS picks one, no script. */
/* The orbiting MIP, now the thumbnail of the demo card rather than a 56px
   ornament in a sentence. `flex: none` matters: it is the flex item beside a
   text block that will happily squash it at narrow widths, and a squashed
   volume render reads as a broken image. */
.sample-orbit {
  flex: none;
  display: block;
  width: 76px;
  height: 76px;
  border-radius: 10px;
  overflow: hidden;
  background: #000;
}

.sample-orbit img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.sample-orbit .if-still { display: none; }

@media (prefers-reduced-motion: reduce) {
  .sample-orbit .if-motion { display: none; }
  .sample-orbit .if-still { display: block; }
}
