/* ============================================================
   LW03 v0.6 · Component Styles
   ============================================================
   References tokens.css only. No raw values.
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  /* Do NOT add overflow-x:clip here — iOS Safari uses <html> as the
     scroll container and overflow on it breaks scrolling. Clip
     transient horizontal overflow at the offending child instead. */
}

body {
  margin: 0;
  background: var(--color-bg-light);
  color: var(--color-text-on-light);
  font-family: var(--font-body);
  font-weight: var(--weight-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "kern" 1, "liga" 1;
  /* Block synthetic weight/style. Foot bar at 400 falls through to Inter
     Regular instead of fake-bolding PP Radio Grotesk Ultralight. */
  font-synthesis: none;
}

a { color: inherit; text-decoration: none; }

/* Skip-link / a11y helper */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0);
  white-space: nowrap; border: 0;
}

/* ============================================================
   Page wrapper — light area
   12-col grid spanning full viewport with --pad-page L/R rail.
   All direct children (content-blocks, sizzle-reels) place themselves
   via grid-column. Vertical rhythm comes from row-gap.
   ============================================================ */
.page {
  margin-inline: auto;
  max-width: var(--design-cap);
  /* Top padding = gap-content-block. Mirrors the gap between content
     blocks inside .page, sitting between the X-Ray bar row (above) and
     the first content block (below). Total visible space from viewport
     top to "Hello, I'm" = gap-section + trigger height + gap-content-block,
     and every term scales with --u so the proportion holds from the
     widest breakpoint down through mobile. */
  padding:
    var(--gap-content-block)
    var(--pad-page)
    var(--pad-page);
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gutter);
  row-gap: var(--gap-section);
}

/* Default: every direct child of .page fills the full 12 cols.
   Content-blocks override this at tablet/desktop breakpoints to
   narrow to 10 / 8 cols. */
.page > * { grid-column: 1 / -1; }

/* The page top anchor — invisible target for Back-to-Top */
#top {
  display: block;
  height: 0;
  outline: none;
}

/* ============================================================
   CONTENT BLOCK (organism)
   Vertical stack: (Eyebrow) · Preroll · Anchor · Body · (CTA / Cite)
   8 cols wide (cols 3/11 = 864px @ 1440), centered within the 12-col
   page grid.

   Vertical rhythm:
     - Internal gap between Preroll / Anchor / Body / CTA = --gap-content-block (64 @ 1440)
     - Padding-bottom = --pad-content-block-bot (40 @ 1440)
     - Combined with .page row-gap (64 @ 1440), gap from content-block to
       the next sizzle reel is 40 + 64 = 104. Gap from sizzle reel back to
       the next content-block is row-gap alone = 64. Asymmetric on purpose:
       sections "exhale" into the image band below, then "inhale" tighter
       into the next section.
   ============================================================ */
.content-block {
  display: flex;
  flex-direction: column;
  text-align: center;
  gap: var(--gap-content-block);
}

/* Grid placement for content-blocks inside .page or .section__inner.
   Locked at 8 cols (cols 3/11) at every viewport — the Collins pattern.
   Combined with capped-vw type (--u), the text-to-block ratio stays
   constant across every breakpoint, so the line breaks are identical
   from 375vp through to the 1800px design cap. Sizzle-reels keep the
   universal-default 12 cols at every breakpoint so image bands run
   full grid width. */
.page > .content-block,
.section__inner > .content-block {
  grid-column: 3 / 11;
}

.content-block > * { margin: 0; }

/* Tighten the gap from anchor to body across every block (LEIGH/SCALE/TIME/
   VOUCH/HELLO). The flex `gap` on .content-block is uniform; this negative
   margin claws back the difference between the tightened anchor-to-body
   value and the default gap, so the anchor + sentence below it read as
   one optical group. */
.anchor + .body {
  margin-top: calc(var(--gap-anchor-to-body) - var(--gap-content-block));
}

/* ============================================================
   EYEBROW DIVIDE (atom)
   1088 × 64 frame with eyebrow centered.
   ============================================================ */
.eyebrow-divide {
  /* Fills its parent .content-block (which is the grid-column-placed
     element). The eyebrow stays centered above the content column at
     every viewport because the parent's width tracks the grid. */
  width: 100%;
  height: var(--eyebrow-divide-height);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-inline: auto;
}

.eyebrow {
  display: block;
  width: var(--eyebrow-bar-width);
  height: var(--eyebrow-bar-height);
  background: var(--color-accent);
}

/* Carousel variant: 6 dots, dot 1 active */
.eyebrow-divide--carousel {
  gap: var(--eyebrow-dot-gap);
}

.eyebrow-dot {
  display: block;
  width: var(--eyebrow-dot-size);
  height: var(--eyebrow-dot-size);
  background: currentColor;
  opacity: 0.85;
}

.eyebrow-dot--active {
  background: var(--color-accent);
  opacity: 1;
}

/* === Testimonial controls — inline row: prev arrow · dots · next arrow ===
   Sits at the bottom of the testimonial content block in place of the
   plain eyebrow-divide--carousel. Min-height matches the eyebrow rhythm;
   row grows naturally when the arrows are larger than the eyebrow band. */
.testi-controls {
  min-height: var(--eyebrow-divide-height);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(var(--u) * 2.778);
}

.testi-dots {
  display: flex;
  align-items: center;
  gap: var(--testi-dot-gap);
}

/* Override the square .eyebrow-dot inside the testimonial carousel only —
   wider rectangle, tighter gap (driven by the testi-dot tokens). The
   default square treatment is preserved for any non-carousel eyebrow. */
.testi-dots .eyebrow-dot {
  width: var(--testi-dot-width);
  height: var(--testi-dot-height);
}

.testi-arrow {
  font-family: var(--font-display);
  font-weight: var(--weight-body-emphasis); /* Safiro Medium */
  font-size: var(--type-testi-arrow);
  line-height: 1;
  letter-spacing: 0;
  color: var(--color-accent);
  background: none;
  border: 0;
  cursor: pointer;
  padding: 0 6px;
  transition: transform 220ms var(--ease-out), opacity var(--duration-fast) var(--ease-out);
}

/* Hover-only nudge: wrap in (hover: hover) so a touchscreen tap
   doesn't fire the :hover state and leave the arrow shifted ±4px
   until the user interacts elsewhere. */
@media (hover: hover) {
  .testi-arrow--prev:hover { transform: translateX(-4px); }
  .testi-arrow--next:hover { transform: translateX(4px); }
}

.testi-arrow:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .testi-arrow:hover { transform: none; }
}

/* Testimonial prev/next arrows bumped 40% on mobile — at narrow widths
   the vw-scaled size sat under a comfortable touch target. Placed
   after the base .testi-arrow rule so the mobile override wins the
   cascade (matches the foot-bar pattern). */
@media (max-width: 879px) {
  .testi-arrow {
    font-size: calc(var(--type-testi-arrow) * 1.4);
  }
}

/* ============================================================
   TYPOGRAPHY (atoms used inside content-block)
   ============================================================ */

/* Every anchor (LEIGH / SCALE / TIME / VOUCH / HELLO) is built the same:
   one size, one weight, one tracking, one centring mechanism. Flex centring
   is applied to all because per-letter spans + negative letter-spacing
   produce inline content wider than the content-block on the widest words.
   text-align: center left-aligns overflowing inline content (it only
   distributes positive leftover space); flex justify-content: center
   genuinely centres regardless of overflow.

   The legacy `.anchor--bookend` and `.anchor--mid` modifier classes remain
   on the HTML as semantic hooks but no longer carry visual difference. */
.anchor {
  font-family: var(--font-display);
  font-weight: var(--weight-anchor);
  font-size: var(--type-anchor);
  letter-spacing: var(--tracking-anchor);
  line-height: var(--leading-anchor);
  text-transform: none;
  font-style: normal;
  display: flex;
  justify-content: center;
  align-items: baseline;
}

/* LEIGH bespoke kerning — the only anchor with per-letter overrides.
   The 'I' is the narrowest letter and needs the tightest negative
   spacing to optically join the surrounding pair; 'G' + 'H' open
   slightly to let the curves breathe. Sequence is fixed (L-E-I-G-H),
   so nth-child keeps the spec close to the markup. */
.anchor--leigh span:nth-child(1),
.anchor--leigh span:nth-child(2) { letter-spacing: -0.07em; } /* L, E */
.anchor--leigh span:nth-child(3) { letter-spacing: -0.12em; } /* I */
.anchor--leigh span:nth-child(4),
.anchor--leigh span:nth-child(5) { letter-spacing: -0.10em; } /* G, H */

/* TIME bespoke kerning — open T→I by 20%, tighten I→M by 30%, M→E by 45%. */
.anchor--time span:nth-child(1) { letter-spacing: -0.064em; } /* T → I */
.anchor--time span:nth-child(2) { letter-spacing: -0.104em; } /* I → M */
.anchor--time span:nth-child(3) { letter-spacing: -0.116em; } /* M → E */

/* VOUCH bespoke kerning — tighten V→O by 30%, C→H by 20%. */
.anchor--vouch span:nth-child(1) { letter-spacing: -0.104em; } /* V → O */
.anchor--vouch span:nth-child(4) { letter-spacing: -0.096em; } /* C → H */

/* HELLO bespoke kerning — H→E -40%, E→L -10%, L→L +20%, L→O -10%. */
.anchor--hello span:nth-child(1) { letter-spacing: -0.112em; } /* H → E */
.anchor--hello span:nth-child(2) { letter-spacing: -0.088em; } /* E → L */
.anchor--hello span:nth-child(3) { letter-spacing: -0.064em; } /* L → L */
.anchor--hello span:nth-child(4) { letter-spacing: -0.088em; } /* L → O */

/* Preroll line — Radio Grotesk Ultralight 96 / -4% */
.preroll {
  font-family: var(--font-body);
  font-weight: var(--weight-body);
  font-size: var(--type-body);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-preroll);
}

/* Body paragraph — Radio Grotesk Ultralight 96 / -4% */
.body {
  font-family: var(--font-body);
  font-weight: var(--weight-body);
  font-size: var(--type-body);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-body);
}

/* Body sentence emphasis — Safiro Regular upright, -6% tracking.
   Lighter than the display-size em (CTA / cite / testi-arrow) so the
   sentence reads as a continuous voice with PP Radio Ultralight rather
   than as a callout. Underline is a pseudo-element (animatable) when
   present; the quote-emphasis variant removes it. */
.body em {
  font-family: var(--font-display);
  font-weight: var(--weight-body-em);
  font-style: normal;
  letter-spacing: var(--tracking-body-emphasis);
  position: relative;
}

/* CTA emphasis — Safiro Medium 40 / -4%, upright. Shares weight + tracking
   with .cite so both display-size Safiro roles read identically. */
.cta em {
  font-family: var(--font-display);
  font-weight: var(--weight-body-emphasis);
  font-style: normal;
  letter-spacing: var(--tracking-cta-emphasis);
  font-size: inherit;
  position: relative;
}

/* === Body em — hover-only underline, slides in from left, retracts to right === */
.body em::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--body-em-underline-bottom);
  height: var(--emphasis-underline-thickness);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform var(--duration-normal) var(--ease-out);
  pointer-events: none;
}

.body em:hover::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* Quote variant: no underline at all */
.content-block--quote-emphasis .body em::after {
  display: none;
}

/* === CTA em — persistent underline, swipe-redraw on hover === */
.cta em::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--cta-em-underline-bottom);
  height: var(--emphasis-underline-thickness);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--emphasis-underline-duration-load) var(--ease-out);
  pointer-events: none;
}

.cta em.is-revealed::after {
  transform: scaleX(1);
}

.cta a:hover em::after {
  animation: emphasis-redraw var(--emphasis-underline-duration-hover) var(--ease-out);
}

@keyframes emphasis-redraw {
  0%   { transform: scaleX(1); transform-origin: right; }
  49%  { transform: scaleX(0); transform-origin: right; }
  50%  { transform: scaleX(0); transform-origin: left; }
  100% { transform: scaleX(1); transform-origin: left; }
}

@media (prefers-reduced-motion: reduce) {
  .body em::after,
  .cta em::after {
    transition: none;
  }
  .cta em.is-revealed::after { transform: scaleX(1); }
  .cta a:hover em::after { animation: none; }
}

/* Quote-emphasis modifier — body em is typeface-only (no underline).
   Applied to every section in the static-prose v0.6 build (LEIGH / SCALE /
   TIME / VOUCH / HELLO) so the Safiro halves read as pure typographic
   contrast against PP Radio, not as CTAs. The CTA still underlines its
   target. */
.content-block--quote-emphasis .body em {
  text-decoration: none;
}

/* Testimonial sizing — dropped from 96px to 80px so the longest
   real testimonial still fits the 8-col block in 2 PP Radio + 2 Safiro
   lines (4 lines max). All six testimonials share this single size. */
.content-block--testimonial .body {
  font-size: var(--type-body-testi);
}

/* Curly quotes around every testimonial body, generated automatically.
   Lives on the body element so both the initial HTML render and every
   JS-rotated testimonial inherit the same treatment — quotes are never
   hard-coded into the testimonial copy. */
.content-block--testimonial .body::before { content: "\201C"; }
.content-block--testimonial .body::after  { content: "\201D"; }

/* CTA — Radio Grotesk Ultralight 32 / -2% */
.cta {
  font-family: var(--font-body);
  font-weight: var(--weight-body);
  font-size: var(--type-cta);
  letter-spacing: var(--tracking-cta);
  line-height: var(--leading-cta);
}

.cta a {
  display: inline-block;
}

.cta a:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 4px;
}

/* === Cite — Safiro Medium underlined citation (VOUCH testimonial credit) ===
   Visual peer of the CTA line. Persistent underline (static, not animated).
   Sits at the bottom of the testimonial content block above the pagination
   eyebrow. Used only for the testimonial citation line; the rest of the
   citation/CTA roles still use .cta. */
.cite {
  font-family: var(--font-display);
  font-weight: var(--weight-body-emphasis); /* Safiro Medium */
  font-size: var(--type-cta);
  letter-spacing: var(--tracking-cta-emphasis);
  line-height: var(--leading-cta);
}

/* Cite link — persistent underline that swipe-redraws on hover, but
   the text colour stays the standard off-black (no orange flip). Same
   pseudo-element underline + same `emphasis-redraw` keyframes as the
   .cta em pattern; only the colour rule differs by design — cite is
   editorial credit, not call-to-action, so it stays in the body
   colour palette. */
.cite a {
  position: relative;
  text-decoration: none;
  color: inherit;
}
.cite a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--cta-em-underline-bottom);
  height: var(--emphasis-underline-thickness);
  background: currentColor;
  transform: scaleX(1);
  transform-origin: left;
  pointer-events: none;
}
.cite a:hover::after {
  animation: emphasis-redraw var(--emphasis-underline-duration-hover) var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .cite a:hover::after { animation: none; }
}
.cite a:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 4px;
}

/* Fallback for testimonials with no LinkedIn URL (renders as a plain
   span, not a link). No underline — underlining a non-link would
   mislead. */
.cite .cite-name {
  font: inherit;
  color: inherit;
}

/* ============================================================
   VOUCH headshot reveal — single shared <img> repositioned per
   testimonial via JS. On cite-hover the image fades in above the
   name and lifts gently into place; on mouse-out it fades back.
   ============================================================ */
.content-block--testimonial {
  position: relative; /* anchor for the absolute-positioned headshot */
  /* Clip the slide animation (translateX ±40px on body+cite) so the
     transient overflow can't extend documentScrollWidth and trigger an
     iOS Safari viewport refit. Targeted here so we don't touch the
     <html> scroll container (which iOS uses for rubber-band paint). */
  overflow: clip;
}
.testi-headshot {
  position: absolute;
  width: 144px;
  height: 144px;
  border-radius: 50%;
  object-fit: cover;
  pointer-events: none; /* never block the link click */
  opacity: 0;
  transform: translate(-50%, 12px);
  transition:
    opacity 520ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 720ms cubic-bezier(0.22, 1, 0.36, 1);
  z-index: 3;
  /* `left` + `top` are set by JS per testimonial so the image
     centers on the cite name regardless of its rendered width. */
}
.testi-headshot.is-revealed {
  opacity: 1;
  transform: translate(-50%, 0);
}
@media (prefers-reduced-motion: reduce) {
  .testi-headshot { transition: opacity 200ms ease; transform: translate(-50%, 0); }
}

/* ============================================================
   SIZZLE REEL (organism, placeholder)
   1312 × 738 image band. Four instances on the page.
   Last one overlaps the dark section by 64px (z-index lifted).
   ============================================================ */
/* Suppress iOS Safari's long-press image preview / drag handle on the
   sizzle-reel media and the testimonial headshot. These aren't
   interactive surfaces; the iOS native context menu (Save Image, etc.)
   distorts the asset visually and serves no purpose here. */
.sizzle-reel__img,
.sizzle-reel__video,
.testi-headshot {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}

.sizzle-reel {
  /* Spans the full 12-col grid (set on .page > * default); visual size
     driven by transform: scale(). Default scale(0.6585) ≈ 8/12
     column-equivalent — uniform on both axes so the box holds visually
     as well as in the layout box. transform is GPU-accelerated
     (composite-only), so the animation runs smoothly even at slower
     durations. Aspect-ratio is 4:3 on mobile (narrow viewports need
     vertical real estate) and 16:9 from tablet up (cinematic widescreen
     once the band has horizontal breathing room). */
  margin: 0;
  aspect-ratio: 4 / 3;
  transform: scale(0.6585);
  transform-origin: center;
  transition: transform 1.2s var(--ease-out); /* fast start, decelerating to a soft stop */
  background:
    repeating-linear-gradient(
      45deg,
      rgba(0,0,0,0.04) 0,
      rgba(0,0,0,0.04) 2px,
      transparent 2px,
      transparent 12px
    ),
    #DEDEDC;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
}

/* Metric intercut — typographic impact statement that overlays the video
   between loops. Absolutely positioned inside .sizzle-reel; opacity-only
   transition keeps it compositor-cheap. Counters inside .metric__value
   are driven by JS (count-up animation in xray.js). */
.sizzle-reel__metric {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--gap-content-block);
  background: var(--color-sizzle-bg);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-normal) var(--ease-out);
  z-index: 1;
}
.sizzle-reel__metric.is-revealed { opacity: 1; }

.metric__value {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600; /* Safiro SemiBold */
  font-size: var(--type-metric-value);
  line-height: 1;
  letter-spacing: var(--tracking-anchor);
  color: var(--color-metric-value);
  white-space: nowrap;
}

.metric__label {
  margin: 0;
  font-family: var(--font-body);
  font-weight: var(--weight-body); /* PP Radio Grotesk Ultralight — the only PPR weight licensed for v1.0 */
  font-size: var(--type-metric-label);
  line-height: var(--leading-foot);
  color: var(--color-text-on-light);
}

.sizzle-reel__label {
  font-family: var(--font-body);
  font-weight: var(--weight-foot);
  font-size: var(--type-foot);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(0,0,0,0.45);
}

/* Desktop+ widens the sizzle reel from 4:3 portrait-leaning to a 16:9
   cinematic band. Threshold matches the 880px mobile/desktop split
   used by the mobile-mode rule below. */
@media (min-width: 880px) {
  .sizzle-reel { aspect-ratio: 16 / 9; }
}

/* Last sizzle reel sits on top of the dark section's top 64px */
.sizzle-reel--overlap {
  position: relative;
  z-index: 1;
}

/* IntersectionObserver toggle: expands reel to full 12-col size when in viewport */
.sizzle-reel.is-expanded {
  transform: scale(1);
}

@media (prefers-reduced-motion: reduce) {
  .sizzle-reel { transform: none; transition: none; }
}

/* Video sizzle variant: source video sits inside the band, pillarboxed
   or letterboxed against a clean background colour so an off-aspect
   source (e.g. ~4:3 product UI capture) lands cleanly in the 16:9
   desktop / 4:3 mobile container without crop or stretch. */
.sizzle-reel--video {
  background: var(--color-sizzle-bg);
  overflow: hidden;
}

.sizzle-reel__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
}

/* Cropped sizzle videos (no baked-in pillarbox / letterbox padding) sit
   at 80% × 80% of the container, centred. The remaining 10% margin on
   each side renders as the parent's CSS bg (--color-sizzle-bg) — single
   source of truth, no colour seam possible. Applied to Bands 2 + 3 via
   the --cropped modifier since hotfix #7. Band 1 (mixed-canvas, padding
   baked in for the portrait mobile UI segment) keeps the default 100% ×
   100% sizing.

   Absolute positioning with inset: 10% gives a deterministic 80% × 80%
   box. Width/height auto so the video element fills the inset box; the
   inherited object-fit: contain handles aspect-ratio inside that box. */
.sizzle-reel--video:has(> .sizzle-reel__video--cropped) {
  position: relative;
}
.sizzle-reel__video--cropped {
  position: absolute;
  top: 10%;
  left: 10%;
  width: 80%;
  height: 80%;
}

/* Image sizzle variant: a single still image (e.g. closing portrait
   in band 4). Shares the same pillarbox/letterbox treatment as the
   video variant — same background, object-fit: contain so off-aspect
   sources don't crop or stretch. */
.sizzle-reel--image {
  background: var(--color-sizzle-bg);
  overflow: hidden;
}

.sizzle-reel__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
}

@media (prefers-reduced-motion: reduce) {
  .sizzle-reel__video { animation-play-state: paused; }
}

/* ============================================================
   Mobile mode (≤ 879px — phones, all tablets in portrait)
   The content-block widens from 8 cols to the full 12 cols, AND the
   visual unit --u is bumped by 1.5× so the type, vertical rhythm, and
   components grow proportionally. The grid-cell width tokens (pad-page,
   col-width, gutter) override that bump back to the raw 1vw rate so the
   12-col grid math still fits the viewport exactly (12 cols + 11 gutters
   + 2 L/R paddings = 100vw).

   Why 1.5: a 12-col block is 1.5× wider than an 8-col block at the same
   viewport, so bumping the type by 1.5× preserves the desktop type-to-
   block ratio (≈ 9.3%). Line breaks land at the same words on mobile
   as on desktop.

   880px chosen so all phones (incl. iPhone 14 landscape at 852) and
   all tablets in portrait (iPad Mini 768, Air 820, Pro 11" 834) read
   in mobile mode, while tablets in landscape (1024+) and any normal
   desktop window cross to the 8-col layout. No real-world device sits
   exactly on the boundary. At the 879→880px boundary there is a small
   visual jump as the layout switches mode in one step — invisible to
   anyone who isn't drag-resizing across it. */
@media (max-width: 879px) {
  :root {
    --u: min(1.5vw, calc(var(--design-cap) / 100));

    --pad-page:  4.444vw;
    --col-width: 5.556vw;
    --gutter:    2.222vw;

    /* Orange circular buttons (X-Ray trigger at top + BTT at bottom)
       were too prominent on mobile relative to body text. 64px desktop
       → 48px mobile (75% of desktop) — meaningful de-emphasis without
       crashing into the 44px touch-target minimum, and the in-between
       sweet spot Leigh asked for after 32 felt too small.
       --btt-icon-size derives from --btt-size via calc so the inner
       sparkle (X-Ray) and arrow (BTT) icons scale in lockstep with the
       circle at every breakpoint, preserving the 50% icon-to-container
       ratio. No further mobile overrides needed for icons. */
    --btt-size: 48px;
  }

  .page > .content-block,
  .section__inner > .content-block {
    grid-column: 1 / -1;
  }

  /* Trigger-to-preroll gap opened up by 80% on mobile — at narrow
     widths the trigger reads tight against the preroll, so the gap
     gets extra breathing room here. Desktop keeps the original
     gap-content-block. */
  .page {
    padding-top: calc(var(--gap-content-block) * 1.8);
  }

  /* Mobile sizzle reels: the container narrows to 4:3 (more vertical
     real estate) but the videos are encoded 16:9. Switch to cover so
     the video FILLS the 4:3 container instead of letterboxing — accepts
     a small L/R crop in exchange for no pale-blue gaps top/bottom.
     Opt-out via .sizzle-reel__video--contain on a per-video basis when
     the source fills the frame edge-to-edge and side-crop would eat
     content (e.g. the Figma pattern library video in band 3). */
  .sizzle-reel__video,
  .sizzle-reel__img {
    object-fit: cover;
  }
  .sizzle-reel__video.sizzle-reel__video--contain {
    object-fit: contain;
  }

  /* Gap from CTA to BTT button opened 50% on mobile — base gap is
     row-gap: var(--gap-section); this margin-top adds half of that
     again, taking the total visible gap to 1.5×. */
  .btt-wrap {
    margin-top: calc(var(--gap-section) * 0.5);
  }

  /* Testimonial arrow row needs more vertical breathing room on mobile —
     the arrows were sitting too close to the citation name above and the
     headshot/sizzle image below, raising tap-target collision risk on
     touchscreens. Adds margin-block separating it from neighbours. */
  .testi-controls {
    margin-block: calc(var(--u) * 4);
  }
}

/* ============================================================
   DARK SECTION — full-width
   Sits below .page. Negative margin-top pulls it up so the
   previous SIZZLE REEL (with z-index:1) overlaps it by 64.
   Inner container matches .page's max-width + padding.
   ============================================================ */
.section--dark {
  background: var(--color-bg-dark);
  color: var(--color-text-on-dark);
  width: 100%;
  /* Pull up by .page's padding-bottom AND the overlap distance
     so the dark visibly extends 64px UNDER the previous SIZZLE REEL. */
  margin-top: calc(-1 * (var(--pad-page) + var(--image-overlap)));
  position: relative;
  z-index: 0;
}

.section__inner {
  /* 12-col grid mirroring .page so dark-section content-blocks place
     themselves via the same grid-column rules. Dark BACKGROUND is
     full-bleed via .section--dark. Foot-bar is a sibling of
     section__inner and handles its own top padding. Capped at
     --design-cap to match .page so the grid stops growing on
     ultra-wide / 4K displays. */
  margin-inline: auto;
  max-width: var(--design-cap);
  padding:
    calc(var(--image-overlap) + var(--gap-section))
    var(--pad-page)
    0;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gutter);
  row-gap: var(--gap-section);
}

/* Default: every direct child of .section__inner spans the full 12 cols.
   Content-blocks override at tablet/desktop via the rules above. */
.section__inner > * { grid-column: 1 / -1; }

/* CONTENT BLOCK on dark inverts text colour token by inheritance.
   No structural difference. */
.content-block--dark { color: var(--color-text-on-dark); }

/* ============================================================
   BACK-TO-TOP BUTTON (atom)
   64×64 fully rounded. On dark section: accent fill, white arrow.
   ============================================================ */
.btt-wrap {
  display: flex;
  justify-content: center;
}

.btt {
  width: var(--btt-size);
  height: var(--btt-size);
  border-radius: 50%;
  background: var(--color-accent);
  color: var(--color-text-on-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--type-cta);
  line-height: 1;
}

/* The button stays put on hover — moving the whole button causes
   layout shift and breaks the click target. The arrow inside is
   the hover signal: it can't rotate (it'd stop pointing up) so it
   enlarges slightly and nudges upward to beckon the action.
   Static at rest, animated only on hover/focus. */
.btt__arrow {
  display: inline-block;
  width: var(--btt-icon-size);
  height: var(--btt-icon-size);
  transform-origin: 50% 50%;
  transition: transform var(--duration-btt) var(--ease-out);
}
/* :hover wrapped in (hover: hover) so iPhone taps don't trigger the
   raised+scaled state and leave the arrow stuck mid-animation until
   the user interacts elsewhere. :focus-visible stays unconditional
   (keyboard a11y; tap doesn't activate it per spec). */
@media (hover: hover) {
  .btt:hover .btt__arrow {
    transform: translateY(-3px) scale(1.12);
  }
}
.btt:focus-visible .btt__arrow {
  transform: translateY(-3px) scale(1.12);
}

@media (prefers-reduced-motion: reduce) {
  .btt__arrow { transition: none; }
  .btt:hover .btt__arrow,
  .btt:focus-visible .btt__arrow { transform: none; }
}
.btt:focus-visible {
  outline: 2px solid var(--color-text-on-dark);
  outline-offset: 4px;
}

/* ============================================================
   FOOT BAR (molecule)
   1312-wide ends-aligned row. Theme-aware via inherited color.
   ============================================================ */
.foot-bar {
  /* Caps at --design-cap to match .page / .section__inner so the foot
     row's left + right groups stay anchored to the same vertical lines
     as the masthead and the grid above. L/R padding of var(--pad-page)
     is the same token that drives the grid's L/R rail, so the foot-bar's
     left and right groups align to grid column 1 left and column 12 right
     by construction. Top padding = gap-section so the dark section ends
     with consistent rhythm above the foot text. */
  margin-inline: auto;
  max-width: var(--design-cap);
  padding: var(--gap-section) var(--pad-page) var(--pad-page);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--gutter);
  font-family: var(--font-body);
  font-weight: var(--weight-foot);
  font-size: var(--type-foot);
  letter-spacing: var(--tracking-foot);
  line-height: var(--leading-foot);
}

.foot-bar__left,
.foot-bar__right {
  display: flex;
  align-items: baseline;
  gap: calc(var(--u) * 0.667);
}

/* Foot-bar text bumped 20% on mobile — at narrow widths the vw-scaled
   --type-foot read too small. Placed after the base .foot-bar rule so
   the mobile override wins the cascade. */
@media (max-width: 879px) {
  .foot-bar {
    font-size: calc(var(--type-foot) * 1.2);
  }
}

/* ============================================================
   X-RAY PANEL & LAYERS
   ============================================================
   The X-Ray system is the Easter egg for designers and front-end devs
   reading the site closely. A single pill in the top-right expands
   into a stack of 14 layer toggles — each independently visualises
   one slice of the design system (tokens, layout, type, etc).

   The panel is built as a pure CSS state machine. Every layer is a
   body class — body.xray-{name} — and the corresponding CSS rules
   light up overlays in #xray-stage. JS only manages state (which
   layers are on) and dynamic content injection (live readouts,
   token-sourced labels).

   Conventions:
   - All visual layers live inside #xray-stage (fixed, inset:0,
     pointer-events:none). Layers never block page interaction.
   - .xray-panel and its children DO accept pointer events.
   - Color: accent orange (#FE3702) marks any X-Ray annotation so
     it never gets confused with real design tokens.
   - The kbd hints are real shortcuts — see xray.js.
   ============================================================ */

/* --- The panel itself ----------------------------------------- */
.xray-panel {
  /* In normal flow as its own row at the top of the body, so it takes
     real vertical space and never overlaps page content — at any
     viewport. Earlier we tried position: absolute with a fixed top
     offset, but the trigger's fixed 64px height didn't shrink with
     viewport while the page-padding-top that cleared it did, producing
     overlap on every viewport narrower than ~1150px. Static is simpler
     and universally correct.
     The bar lays out as [Dark pill | Trigger circle | X-Ray pill] in a
     centered flex row; pills pop out symmetrically from behind the
     central trigger. Scrolls away with the masthead naturally because
     normal-flow content scrolls. */
  position: static;
  /* gap-section above the trigger; no padding below — the gap between
     the trigger and the first content-block is owned by .page (see its
     padding-top: var(--gap-content-block)). Total from viewport top to
     preroll: gap-section + trigger height + gap-content-block, which
     matches the previous absolute-positioned layout exactly. */
  padding: var(--gap-section) 0 0;
  z-index: 200;
  display: flex;
  justify-content: center;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Shared pill style — handle + the standalone theme toggle + the
   AI-version compare link, all sit next to the trigger circle.
   Asymmetric padding (16L / 10R) keeps the icon comfortably inset
   from the left edge while pulling the label tight to the right —
   "LIGHT" (the longest of Dark/Light/X-Ray) lands close to the
   right edge with minimal slack. */
.xray-panel__handle,
.theme-toggle,
.compare-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 40px;
  padding: 0 var(--xray-pill-padding-right) 0 var(--xray-pill-padding-left);
  background: var(--color-bg-dark);
  color: var(--color-text-on-dark);
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out),
    transform var(--duration-fast) var(--ease-out);
}

.xray-panel__handle:hover,
.theme-toggle:hover,
.compare-toggle:hover {
  background: var(--color-accent);
  color: #fff;
}

/* X-Ray pill: when on, holds the brand-orange state to signal "X-Ray active". */
.xray-panel__handle[aria-pressed="true"] {
  background: var(--color-accent);
  color: #fff;
}

.xray-panel__handle:focus-visible,
.theme-toggle:focus-visible,
.compare-toggle:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
/* Anchor-based compare-toggle: kill default link underline + inherit color. */
.compare-toggle { text-decoration: none; }

/* Bar inside .xray-panel — three children in a centered row:
   [theme-toggle | trigger | xray-handle]. Gap is 12px so each popout
   pill has visible breathing room from the central trigger circle
   (4px snap-tight read as one chip; 12px reads as a deliberate
   triplet). */
.xray-panel__bar {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--xray-bar-gap);
  position: relative;
}

/* Both pop-out pills share the retracted state (sit behind the trigger,
   slightly scaled down) and the open state (slide out to their side).
   The left/right modifier picks the direction of slide.
   min-width keeps both pills identical in width regardless of their
   label ("Dark" / "Light" / "X-Ray") — so the cluster reads as a
   true symmetric mirror around the central trigger circle. */
.xray-panel__pill {
  /* Fixed width so Dark/Light and X-Ray pills are the same size on
     both sides of the trigger, regardless of label content. Width
     is determined by "LIGHT" — the longest of the three possible
     labels (Dark / Light / X-Ray). With asymmetric 16L/10R padding
     this lands tight to the right edge for every state, plus 4px
     of breathing room past the label. */
  width: var(--xray-pill-width);
  flex-shrink: 0;
  justify-content: flex-start;
  opacity: 0;
  pointer-events: none;
  transition:
    opacity   220ms ease,
    transform 600ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.xray-panel__pill--left {
  /* Retracted: slide right behind the trigger. */
  transform: translateX(var(--xray-pill-offset)) scale(0.7);
}
.xray-panel__pill--right {
  /* Retracted: slide left behind the trigger. */
  transform: translateX(calc(var(--xray-pill-offset) * -1)) scale(0.7);
}
/* Open: both pills land at their resting position on either side. */
.xray-panel[data-cluster="open"] .xray-panel__pill {
  opacity: 1;
  transform: translateX(0) scale(1);
  pointer-events: auto;
}

/* === The trigger circle — always visible, the entry point.
   Uses the AI sparkle icon (pulsing) as the easter-egg invite. === */
.xray-panel__trigger {
  /* Shares --btt-size with the back-to-top button so the two circular
     orange buttons at top and bottom of the page are exactly the same
     size at every viewport. Despite the token's name (history: was BTT-
     specific), it's now the shared "circle button" size. */
  width: var(--btt-size);
  height: var(--btt-size);
  border: 0;
  border-radius: 50%;
  /* Trigger is the brand entry-point: always orange bg + white star,
     in every theme, in every state. No state-driven bg flip. */
  background: var(--color-accent);
  color: #fff;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  flex-shrink: 0;
  /* Sits on top of the retracting pills so they disappear under it. */
  position: relative;
  z-index: 2;
}
.xray-panel__trigger:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Trigger icon — center it inside the circle (the global .pill-icon
   has a negative margin-right to tighten against pill labels; the
   trigger has no label, so reset it). Sized to ~20px so it scales
   proportionally with the 40px trigger circle. */
/* Trigger icon is the only standalone pill-icon (no label next to it),
   so it doesn't need the right-margin tightening AND it sizes larger
   than the pill-row icons to match the bigger trigger circle. */
.pill-icon.trigger-icon {
  margin-right: 0;
  width: var(--btt-icon-size);
  height: var(--btt-icon-size);
}
.pill-icon.trigger-icon svg {
  display: block;
  width: var(--btt-icon-size);
  height: var(--btt-icon-size);
  transform-origin: 50% 50%;
}

/* Every pill uses JetBrains Mono so the whole panel reads as one
   consistent technical voice. */
.theme-toggle,
.xray-panel__handle,
.compare-toggle {
  font-family: 'JetBrains Mono', ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.06em;
}
/* Version labels keep their lowercase "v" (standard versioning convention)
   even though the surrounding pill is uppercase-transformed. */
.compare-toggle__label {
  text-transform: none;
}

/* === Pill icons — SVGs to the left of each label === */
.pill-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin-right: -2px;       /* tighten against the label (pill gap handles spacing) */
  position: relative;
  color: inherit;
}
.pill-icon svg {
  display: block;
  width: 18px;
  height: 18px;
}

/* === Theme toggle icon: moon ↔ sun crossfade + rotation on state change === */
.theme-toggle__icon { width: 18px; height: 18px; }
.theme-toggle__icon-moon,
.theme-toggle__icon-sun {
  position: absolute;
  inset: 0;
  transition:
    transform 400ms cubic-bezier(.22, 1, .36, 1),
    opacity   200ms ease;
}
/* Light theme (default): moon shows ("click to go dark") */
.theme-toggle__icon-sun {
  opacity: 0;
  transform: rotate(-90deg) scale(0.6);
}
/* Dark theme: sun shows ("click to go light") */
body.theme-dark .theme-toggle__icon-moon {
  opacity: 0;
  transform: rotate(90deg) scale(0.6);
}
body.theme-dark .theme-toggle__icon-sun {
  opacity: 1;
  transform: rotate(0) scale(1);
}
/* Hover: enlarge to acknowledge the click target — no rotation. */
.theme-toggle:hover .theme-toggle__icon-moon,
.theme-toggle:hover .theme-toggle__icon-sun {
  transform: scale(1.08);
}
body.theme-dark .theme-toggle:hover .theme-toggle__icon-moon {
  opacity: 0;
  transform: rotate(90deg) scale(0.6);
}
body.theme-dark .theme-toggle:hover .theme-toggle__icon-sun {
  transform: scale(1.08);
}

/* === X-Ray icon: EyeSlash when off, Eye when on. The two SVGs are
   stacked at the same position inside .pill-icon and crossfade based
   on the pill's aria-pressed state. Matches the moon↔sun pattern on
   the theme-toggle so the panel has one consistent state-swap idiom. === */
.xray-icon { position: relative; }
.xray-icon svg { overflow: visible; }
.xray-icon__off,
.xray-icon__on {
  position: absolute;
  inset: 0;
  margin: auto;
  transition:
    opacity   200ms ease,
    transform 400ms cubic-bezier(.22, 1, .36, 1);
}
/* X-Ray off (default): EyeSlash shows. */
.xray-icon__on {
  opacity: 0;
  transform: rotate(-90deg) scale(0.6);
}
/* X-Ray on: Eye shows, EyeSlash rotates out. */
.xray-panel__handle[aria-pressed="true"] .xray-icon__off {
  opacity: 0;
  transform: rotate(90deg) scale(0.6);
}
.xray-panel__handle[aria-pressed="true"] .xray-icon__on {
  opacity: 1;
  transform: rotate(0) scale(1);
}
/* Hover: enlarge to acknowledge the click target — no rotation. */
.xray-panel__handle:hover .xray-icon__off,
.xray-panel__handle:hover .xray-icon__on {
  transform: scale(1.08);
}
.xray-panel__handle[aria-pressed="true"]:hover .xray-icon__off {
  transform: rotate(90deg) scale(0.6);
}
.xray-panel__handle[aria-pressed="true"]:hover .xray-icon__on {
  transform: scale(1.08);
}

/* === Trigger icon — static at rest, enlarge-on-hover matching the
   design-system pattern shared with the theme moon/sun and X-Ray
   eye. Enlarge only, no rotation. === */
.trigger-icon svg {
  transition: transform 400ms cubic-bezier(.22, 1, .36, 1);
  transform-origin: 50% 50%;
}
.xray-panel__trigger:hover .trigger-icon svg {
  transform: scale(1.08);
}

/* === Version glyph — circular arrow with V. The cyclic arrow shape
   tells you the button toggles between versions; the spin on render
   + hover reinforces "click to swap". === */
.ai-icon svg {
  transform-origin: 50% 50%;
  animation: version-spin 0.9s cubic-bezier(0.4, 0, 0.2, 1) 1 both;
}
@keyframes version-spin {
  0%   { transform: rotate(0); }
  100% { transform: rotate(360deg); }
}
.compare-toggle:hover .ai-icon svg {
  animation: version-spin 0.7s cubic-bezier(0.4, 0, 0.2, 1) 1;
}
/* Re-spin when the cluster opens so the version pill matches the
   stagger entrance. */
.xray-panel[data-cluster="open"] .ai-icon svg {
  animation: version-spin 0.9s cubic-bezier(0.4, 0, 0.2, 1) 1 both;
}

@media (prefers-reduced-motion: reduce) {
  .ai-icon svg { animation: none; }
  .theme-toggle__icon-moon,
  .theme-toggle__icon-sun,
  .xray-icon__off,
  .xray-icon__on,
  .trigger-icon svg { transition: none; }
}

.xray-panel__label {
  display: inline-block;
}

@media (prefers-reduced-motion: reduce) {
  .xray-panel__handle,
  .xray-panel__pill {
    transition: none;
  }
}

/* --- The stage: container for every overlay layer ------------- */
.xray-stage {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 150;
  /* Clip off-viewport overlay children (breakpoint marker at left:880px,
     etc.) so they never expand documentScrollWidth — which would
     trigger an iOS Safari viewport refit and visibly nudge layout on
     X-Ray toggle. */
  overflow: hidden;
}
.xray-stage > *:not(.xray-aeo-notice):not(.xray-layout-overlay) { display: none; }

/* ============================================================
   LAYER 1 · GRID — 12-col overlay mirroring .page
   ============================================================ */
.xray-stage > .xray-grid {
  width: 100%;
  height: 100%;
  padding: 0 var(--pad-page);
  margin-inline: auto;
  max-width: var(--design-cap);
  display: none;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gutter);
  box-sizing: border-box;
}
body.xray-grid .xray-stage > .xray-grid { display: grid; }
.xray-stage > .xray-grid > span {
  background: rgba(254, 55, 2, 0.06);
  border-left: 1px dashed rgba(254, 55, 2, 0.45);
  border-right: 1px dashed rgba(254, 55, 2, 0.45);
}

/* ============================================================
   LAYER 2 · OUTLINES — every block element gets a 1px boundary
   ============================================================ */
body.xray-outlines * {
  outline: 1px dashed rgba(0, 100, 255, 0.35);
  outline-offset: -1px;
}
body.xray-outlines main,
body.xray-outlines section,
body.xray-outlines article,
body.xray-outlines aside,
body.xray-outlines nav,
body.xray-outlines footer,
body.xray-outlines header {
  outline: 2px solid rgba(0, 100, 255, 0.6);
  outline-offset: -2px;
}
body.xray-outlines .xray-panel,
body.xray-outlines .xray-panel *,
body.xray-outlines .xray-stage,
body.xray-outlines .xray-stage * {
  outline: none;
}

/* ============================================================
   LAYER 3 · BASELINE — horizontal lines at body line-height
   intervals so the type rhythm is visible.
   ============================================================ */
.xray-stage > .xray-baseline {
  position: absolute;
  inset: 0;
  /* Baseline at the body line-height (--type-body * 1.14 ≈ 91px @ 1440).
     Repeat as a 1px line every line-height step. */
  --baseline: calc(var(--type-body) * var(--leading-body));
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(254, 55, 2, 0) 0,
    rgba(254, 55, 2, 0) calc(var(--baseline) - 1px),
    rgba(254, 55, 2, 0.18) calc(var(--baseline) - 1px),
    rgba(254, 55, 2, 0.18) var(--baseline)
  );
}
body.xray-baseline .xray-stage > .xray-baseline { display: block; }

/* ============================================================
   LAYER 4 · TYPE — label every typographic atom with its token
   name + computed size. Labels float just above the element.
   ============================================================ */
body.xray-type .preroll,
body.xray-type .anchor,
body.xray-type .body,
body.xray-type .body em,
body.xray-type .cta,
body.xray-type .cta em,
body.xray-type .cite,
body.xray-type .foot-bar,
body.xray-type .sizzle-reel__label {
  position: relative;
}
body.xray-type .preroll::before,
body.xray-type .anchor::before,
body.xray-type .body::before,
body.xray-type .body em::before,
body.xray-type .cta::before,
body.xray-type .cta em::before,
body.xray-type .cite::before,
body.xray-type .foot-bar::before,
body.xray-type .sizzle-reel__label::before {
  content: attr(data-xray-type);
  position: absolute;
  top: -1.8em;
  left: 50%;
  transform: translateX(-50%);
  display: block;
  padding: 2px 6px;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0;
  font-style: normal;
  text-transform: none;
  color: #fff;
  background: var(--color-accent);
  border-radius: 3px;
  white-space: nowrap;
  z-index: 1;
  pointer-events: none;
}
/* On dark sections, keep the badge readable. */
body.xray-type .content-block--dark .preroll::before,
body.xray-type .content-block--dark .anchor::before,
body.xray-type .content-block--dark .body::before,
body.xray-type .content-block--dark .body em::before,
body.xray-type .content-block--dark .cta::before,
body.xray-type .foot-bar::before {
  color: #fff;
}

/* ============================================================
   LAYER 6 · SPACING — in-situ. Floats a small mono badge at the top
   edge of each element that's preceded by a meaningful spacing token,
   labelled with the token name + live px value. The badge sits inside
   the gap itself so the spec reads where it lives.
   ============================================================ */
body.xray-spacing [data-xray-spacing] { position: relative; }
body.xray-spacing [data-xray-spacing]::before {
  content: attr(data-xray-spacing);
  position: absolute;
  top: -1.6em;
  left: 50%;
  transform: translateX(-50%);
  padding: 2px 6px;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  letter-spacing: 0;
  background: var(--color-accent);
  color: #fff;
  border-radius: 3px;
  white-space: nowrap;
  z-index: 5;
  pointer-events: none;
}

/* Radius layer removed in S22 (binary X-Ray refactor) — no in-situ
   radius badges. Shadow layer was removed earlier (no shadow tokens
   in the system by editorial choice). */

/* ============================================================
   LAYER 9 · MOTION — in-situ. Tags every transition-using element
   with a small mono badge showing easing curve + duration tokens.
   Static elements get no label.
   ============================================================ */
body.xray-motion [data-xray-motion] { position: relative; }
body.xray-motion [data-xray-motion]::before {
  content: attr(data-xray-motion);
  position: absolute;
  top: 4px;
  left: 50%;
  transform: translateX(-50%);
  padding: 2px 6px;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  letter-spacing: 0;
  background: var(--color-accent);
  color: #fff;
  border-radius: 3px;
  white-space: nowrap;
  z-index: 5;
  pointer-events: none;
}

/* ============================================================
   LAYER 10 · LAYOUT — in-situ. Cap line at --design-cap + a small
   live readout pinned to the top edge of the viewport showing
   vp / --u / mode / cap. Lives on the canvas, not in a corner panel.
   ============================================================ */
.xray-stage .xray-layout-overlay {
  position: absolute;
  inset: 0;
  display: none;
  pointer-events: none;
}
body.xray-layout .xray-stage .xray-layout-overlay { display: block; }
.xray-layout-overlay__cap {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: var(--design-cap);
  max-width: 100%;
  border-left:  1px dashed rgba(254, 55, 2, 0.35);
  border-right: 1px dashed rgba(254, 55, 2, 0.35);
}
.xray-layout-overlay__readout {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 14px;
  padding: 4px 10px;
  background: var(--color-bg-dark);
  color: #fff;
  border-radius: 3px;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.xray-layout-overlay__readout strong { color: var(--color-accent); font-weight: 400; }

/* ============================================================
   LAYER 11 · BREAKPOINTS — pulsing vertical line at the 880px
   mobile/desktop boundary, plus the current mode at top-center.
   ============================================================ */
.xray-stage > .xray-breakpoint-marker {
  position: absolute;
  top: 0;
  bottom: 0;
  /* Pin to the boundary in viewport pixels. */
  left: 880px;
  width: 1px;
  background: linear-gradient(to bottom, rgba(254,55,2,0) 0%, rgba(254,55,2,0.5) 8%, rgba(254,55,2,0.5) 92%, rgba(254,55,2,0) 100%);
  display: none;
}
body.xray-breakpoints .xray-stage > .xray-breakpoint-marker { display: block; }
/* On viewports narrower than the breakpoint itself, the marker would be
   drawn off-screen to the right — but its absolute position at left:880px
   still extends documentScrollWidth, which iOS Safari treats as a refit
   trigger. Hide it entirely below the breakpoint; the marker is only
   meaningful when the viewport reaches the boundary anyway. */
@media (max-width: 879px) {
  body.xray-breakpoints .xray-stage > .xray-breakpoint-marker { display: none; }
}
.xray-breakpoint-marker::before {
  content: '880 — mobile / desktop boundary';
  position: absolute;
  top: 14px;
  left: 8px;
  background: var(--color-accent);
  color: #fff;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  letter-spacing: 0.04em;
  padding: 2px 6px;
  border-radius: 3px;
  white-space: nowrap;
}

/* ============================================================
   LAYER 12 · THEME — flip the whole page to dark mode, independent
   of the user's system preference.
   ============================================================ */
body.theme-dark {
  /* Dark mode body bg = dark (matches dark page surface for the iOS
     top rubber-band reveal). */
  background: var(--color-bg-dark);
  color: var(--color-text-on-dark);
}
body.theme-dark .page,
body.theme-dark .section {
  color: inherit;
}
body.theme-dark .anchor,
body.theme-dark .preroll,
body.theme-dark .body,
body.theme-dark .cta,
body.theme-dark .cite {
  color: inherit;
}

/* In dark mode the HELLO section reverses to light so the page still
   ends on a contrast change (was: light body / dark footer; becomes:
   dark body / light footer). Force text reversal across every text
   role inside the section, including the foot-bar's children, since
   `color: inherit` on the body-class overrides earlier in this file
   doesn't fully cascade to nested elements that have their own color
   inheritance chain. */
body.theme-dark .section--dark,
body.theme-dark .section--dark .anchor,
body.theme-dark .section--dark .preroll,
body.theme-dark .section--dark .body,
body.theme-dark .section--dark .body em,
body.theme-dark .section--dark .cta,
body.theme-dark .section--dark .cta em,
body.theme-dark .section--dark .cite,
body.theme-dark .section--dark .foot-bar,
body.theme-dark .section--dark .foot-bar a,
body.theme-dark .section--dark .foot-bar span {
  color: var(--color-text-on-light);
}
body.theme-dark .section--dark { background: var(--color-bg-light); }

/* The X-Ray UI inverts in dark mode so every part stays readable
   against the dark body — pills + AEO corner panel swap their
   resting background. Hover stays brand orange in both themes;
   only the resting state flips. In-situ labels remain accent-orange
   on white text — the accent is the same in both themes so labels
   read identically. */
body.theme-dark .xray-panel__handle,
body.theme-dark .theme-toggle,
body.theme-dark .compare-toggle,
body.theme-dark .xray-aeo-notice {
  background: var(--color-bg-light);
  color: var(--color-text-on-light);
}
body.theme-dark .xray-panel__handle:hover,
body.theme-dark .theme-toggle:hover,
body.theme-dark .compare-toggle:hover {
  background: var(--color-accent);
  color: #fff;
}
/* X-Ray on, dark theme: keep the brand-orange "active" state — same
   as light mode. */
body.theme-dark .xray-panel__handle[aria-pressed="true"] {
  background: var(--color-accent);
  color: #fff;
}

/* ============================================================
   LAYER 13 · A11Y — touch-target rings + focus-state visualization
   on every interactive element.
   ============================================================ */
body.xray-a11y a:not([aria-hidden="true"]),
body.xray-a11y button {
  position: relative;
}
body.xray-a11y a:not([aria-hidden="true"])::after,
body.xray-a11y button::after {
  content: '';
  position: absolute;
  inset: 50% auto auto 50%;
  transform: translate(-50%, -50%);
  width: max(100%, 44px);
  height: max(100%, 44px);
  border: 1px dashed var(--color-accent);
  pointer-events: none;
  border-radius: 4px;
}

/* A11Y notice — bottom-right summary so the layer has payoff
   even when there's nothing interactive above the fold. */
/* A11Y size label — next to the dashed touch-target ring on each
   interactive element. Shows actual width × height + a ✓ if it
   meets the 44×44 minimum. */
body.xray-a11y [data-xray-a11y] { position: relative; }
body.xray-a11y [data-xray-a11y]::before {
  content: attr(data-xray-a11y);
  position: absolute;
  top: -1.4em;
  left: 50%;
  transform: translateX(-50%);
  padding: 1px 5px;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  letter-spacing: 0;
  background: var(--color-accent);
  color: #fff;
  border-radius: 3px;
  white-space: nowrap;
  z-index: 6;
  pointer-events: none;
}
/* Exclude the X-ray panel + invisible scroll anchors from a11y rings. */
body.xray-a11y .xray-panel button::after,
body.xray-a11y .xray-panel a::after { display: none; }

/* ============================================================
   LAYER 14 · SEO — outline + label every landmark + heading so
   the semantic structure is visible.
   ============================================================ */
body.xray-seo main,
body.xray-seo header,
body.xray-seo footer,
body.xray-seo nav,
body.xray-seo aside,
body.xray-seo section,
body.xray-seo article,
body.xray-seo h1,
body.xray-seo h2,
body.xray-seo h3 {
  position: relative;
  outline: 1px solid rgba(254, 55, 2, 0.45);
  outline-offset: -1px;
}
body.xray-seo main::before,
body.xray-seo header::before,
body.xray-seo footer::before,
body.xray-seo nav::before,
body.xray-seo aside::before,
body.xray-seo section::before,
body.xray-seo article::before,
body.xray-seo h1::before,
body.xray-seo h2::before,
body.xray-seo h3::before {
  content: attr(data-xray-seo);
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  padding: 2px 6px;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  letter-spacing: 0.04em;
  font-style: normal;
  text-transform: none;
  font-weight: 400;
  background: var(--color-accent);
  color: #fff;
  border-radius: 0 0 4px 0;
  z-index: 1;
  pointer-events: none;
  white-space: nowrap;
}
body.xray-seo .xray-panel,
body.xray-seo .xray-panel *,
body.xray-seo .xray-stage,
body.xray-seo .xray-stage * { outline: none; }
body.xray-seo .xray-panel::before,
body.xray-seo .xray-panel *::before,
body.xray-seo .xray-stage *::before { display: none; }

/* SEO notice — bottom-right summary of the semantic landmark
   structure, so the layer has above-the-fold payoff. */
/* SEO notice removed — the layer's landmark outlines + element labels
   above ARE the in-situ treatment. No separate corner panel needed. */

/* ============================================================
   LAYER 1 · THEME — in-situ. Small mono badge in the top-right of
   each surface tagged with its theme token. Bg containers, text
   containers, and emphasis spans all get a label.
   ============================================================ */
body.xray-theme [data-xray-theme] { position: relative; }
body.xray-theme [data-xray-theme]::before {
  content: attr(data-xray-theme);
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 2px 6px;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  letter-spacing: 0;
  background: var(--color-accent);
  color: #fff;
  border-radius: 3px;
  white-space: nowrap;
  z-index: 5;
  pointer-events: none;
}
/* Emphasis spans are small — float the label above them instead of
   in-corner so it doesn't overlap the word itself. Reset left/transform
   from the type rule so the badge anchors strictly to right:0;
   otherwise the combined left:50%/right:0 stretches the box to half the
   em's width and the nowrap content overflows rightward past the
   viewport (this was the source of the iPhone X-Ray jump). */
body.xray-theme .body em::before,
body.xray-theme .cta em::before {
  top: -1.4em;
  left: auto;
  right: 0;
  transform: none;
}

/* ============================================================
   LAYER 14 · AEO — in-situ badges + the ONE corner panel.
   In-situ side: badges on elements that the schema/meta tags
   reference, so a designer can see what each visible element
   contributes to AI discovery.
   Corner side: live check status for the invisible artefacts
   (llms.txt, robots.txt allowlist, JSON-LD validity).
   ============================================================ */
body.xray-aeo [data-xray-aeo] { position: relative; }
body.xray-aeo [data-xray-aeo]::after {
  content: attr(data-xray-aeo);
  position: absolute;
  bottom: -1.4em;
  left: 50%;
  transform: translateX(-50%);
  padding: 2px 6px;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  letter-spacing: 0;
  background: var(--color-accent);
  color: #fff;
  border-radius: 3px;
  white-space: nowrap;
  z-index: 5;
  pointer-events: none;
}

/* AEO notice — the corner panel. Fixed bottom-right. Visible when
   AEO layer is on AND the X-Ray panel is open. */
.xray-aeo-notice {
  position: fixed;
  bottom: 12px;
  right: 12px;
  background: var(--color-bg-dark);
  color: #fff;
  border-radius: 8px;
  padding: 10px 14px;
  font-family: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
  font-size: 10px;
  letter-spacing: 0.04em;
  line-height: 1.6;
  max-width: 320px;
  display: none;
  z-index: 150;
}
body.xray-aeo .xray-aeo-notice { display: block; }
.xray-aeo-notice strong { color: var(--color-accent); font-weight: 400; }
.xray-aeo-notice ul { list-style: none; margin: 6px 0 0; padding: 0; }
.xray-aeo-notice li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.xray-aeo-notice .ok   { color: #66e09a; }
.xray-aeo-notice .bad  { color: var(--color-accent); }
.xray-aeo-notice .warn { color: #f0c674; }

/* ============================================================
   RESPONSIVE — clamps in tokens.css handle floor/ceiling sizes;
   these media queries handle layout-level shifts (proportions,
   not just sizes).
   ============================================================ */

/* Inner-icon scaling for the orange buttons used to live here as a
   second mobile media query (when icon sizes were hardcoded). The icon
   sizes now derive from --btt-icon-size = calc(var(--btt-size) * 0.5),
   so they scale automatically at every breakpoint. The mobile-only
   block is no longer needed. */

