/* ============================================================================
   THE FIGURE SECTIONS — science, methodology, network (below, "section 2,
   3, 4" are these three, in that order, as in layers.js).
   One figure, pinned on the right, carries all three, so they read as one
   band-canvas (§11.2, one mechanism: a scroll-driven figure). It starts as
   the Radar canvas, turns into the hub's core, then gains its rays and its
   orbits.
   Markup: js/render/figure.js, science.js, method.js, network.js.
   Behaviour: js/behaviour/layers.js.

   Two modes:
   - Static (default): prefers-reduced-motion, or below 980px. Every
     element is in its final state; nothing depends on scroll (§8.3). The
     canvas sits beside section 2's text and the whole hub under it.
     Below 980px the whole hub is hidden and section 3 shows its own hub,
     core and rays only, with the four points in its corners.
   - Live (.layers.is-live, set by layers.js): the figure is pinned, and the
     scroll position drives it.

   Recorded exceptions, decided by the client:
   - Above the {motion.slow} cap: counter count-up (1200ms), the section-4
     ring draw on first load (1.6s, the hub component's own entrance),
     the section-3 ray settle (900ms), shot (3s) and sparkle (1.8s), the
     section-4 ring draw (800ms) and label unroll (500ms).
   - Hard stops (layers.js step 8): entering a pinned beat glides the page to
     its rest point and holds it for 0.9s, ignoring wheel, touch and scroll
     keys. Section 4 holds as long, or until its labels have unrolled if
     that takes longer (at most about 1.6s). In-page links are exempt.
   - Section 3 fires its rays at each point in turn until the reader picks
     one. The shots only sparkle; the text changes on a pick alone.
   ========================================================================= */

/* ============================================================================
   LAYOUT — text column left, figure column right, both inside the 1200px
   container.
   ========================================================================= */
.layers {
  /* tier-3, scoped */
  --layers-text: 610px;   /* the hero's text column, so the page keeps one measure */
  --layers-fig:  calc(var(--layout-container-max) - 2 * var(--space-6) - var(--space-12) - var(--layers-text));
  --layers-accent: color-mix(in srgb, var(--purple-700) 75%, var(--purple-300));   /* 9.0:1 on white */

  /* The spinning rays' box pokes past the screen edge (the beams are
     transparent there). clip, not hidden: hidden would break position: sticky. */
  overflow-x: clip;
  display: grid;
  /* the left gutter keeps room for the conduit's lane (conduit.css) */
  grid-template-columns:
    minmax(var(--conduit-lane, var(--space-6)), 1fr)
    [text-start] minmax(0, var(--layers-text)) [text-end]
    var(--space-12)
    [fig-start] minmax(0, var(--layers-fig)) [fig-end]
    minmax(var(--space-6), 1fr);
}

/* Static: the hub has a row of its own, under section 2 (whose own figure
   is the canvas). Live mode pins it beside all three (LIVE MODE below). */
.layer,
.stage          { grid-column: text-start / fig-end; }
.layer--science { grid-row: 1; }
.stage          { grid-row: 2; }
.layer--method  { grid-row: 3; }
.layer--network { grid-row: 4; }

/* ============================================================================
   THE FIGURE — hub and Radar canvas share one box
   ========================================================================= */
.stage__pin {
  height: 100%;
  display: grid;
  place-items: center;
}
.stage__fig {
  position: relative;
  width: min(100%, 640px);
}
.stage__canvas { display: none; }   /* static mode shows the canvas in section 2 */

/* Two layers of the hub fade with the scroll (--o-*, layers.js). */
.stage {
  --o-rays: 1;
  --o-orbits: 1;
}
.stage .hub__svg    { opacity: var(--o-rays); }
.stage .hub__orbits { opacity: var(--o-orbits); }

/* Faded out by the scroll: out of hover too. */
.stage[data-rays-off] .hub__svg { visibility: hidden; }

/* The rays turn under the pointer, so the component's own hover lift would
   flicker as a ray slides away: switched off. */
.stage .hub__band { pointer-events: none; }
.stage .hub__sector:hover { transform: none; }

/* The layer a section introduces grows a little while that section is on
   screen (data-accent, scroll). The stage stops clipping so the outer ring
   has room to grow. */
.stage .hub__stage { overflow: visible; }
.stage .hub__radar,
.stage .hub__svg,
.stage .hub__orbits { transition: scale var(--motion-slow); }
.stage[data-accent="core"] .hub__radar { scale: 1.12; }
.stage[data-accent="rays"] .hub__svg   { scale: 1.05; }

/* A community label that is hovered, focused or selected spotlights its ring,
   as the hub's own labels did. */
.layers:has(.community__btn:is(:hover, :focus-visible, [aria-pressed="true"])) .hub__orbit { opacity: 0.3; }
.layers:has(.community li:nth-child(1) .community__btn:is(:hover, :focus-visible, [aria-pressed="true"])) .hub__orbit:nth-of-type(1),
.layers:has(.community li:nth-child(2) .community__btn:is(:hover, :focus-visible, [aria-pressed="true"])) .hub__orbit:nth-of-type(2),
.layers:has(.community li:nth-child(3) .community__btn:is(:hover, :focus-visible, [aria-pressed="true"])) .hub__orbit:nth-of-type(3),
.layers:has(.community li:nth-child(4) .community__btn:is(:hover, :focus-visible, [aria-pressed="true"])) .hub__orbit:nth-of-type(4) {
  opacity: 1; filter: saturate(1.3) brightness(1.05);
}

/* ============================================================================
   2–4 · THE LAYER SECTIONS
   ========================================================================= */
.layer {
  padding-block: var(--space-section);
}
.layer__pin {
  display: grid;
  grid-template-columns: minmax(0, var(--layers-text)) minmax(0, var(--layers-fig));
  column-gap: var(--space-12);
  align-items: center;
}

.layer__title {
  margin: 0;
  font-size: var(--type-h1);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--color-ink);
}
.layer__subhead {
  margin: var(--space-4) 0 0;
  font-size: var(--type-h3);
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--layers-accent);
}
.layer__body {
  margin: var(--space-6) 0 0;
  max-width: 60ch;
  font-size: var(--type-body);
  line-height: 1.6;
  color: var(--color-body);
}
/* §10.1 link: in running text, underlined at rest. */
.layer__body a {
  color: var(--color-action-primary);
  text-decoration: underline; text-underline-offset: 3px;
  border-radius: var(--radius-sm);
  transition: color var(--motion-fast);
}
.layer__body a:hover  { color: var(--color-action-primary-hover); }
.layer__body a:active { color: var(--color-action-primary-active); }
.layer__body a:focus-visible { outline: 3px solid var(--color-focus-ring); outline-offset: 2px; }
/* Bullets under the text, or in its place. */
.layer__list { margin-top: var(--space-3); padding-left: 1.25em; }
.layer__list li + li { margin-top: var(--space-2); }

/* --- section 3: the four points of the method ---------------------------
   Static: a two-by-two list under the abstract. Live: see LIVE MODE below. */
.points {
  margin: var(--space-8) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6);
}
.points {
  --points-trace: color-mix(in srgb, var(--graphic-violet) 45%, var(--purple-300));   /* the orbit rings' violet */
}
.points__item { margin: 0; }

/* Section 3's own hub, core and rays only: shown below 980px alone
   (RESPONSIVE below), where the points sit in its corners. */
.method-fig { display: none; }

/* Each point is a button that swaps the sub-headline and text above it.
   The negative margin gives it a padded hit area without moving the text. */
.points__btn {
  --point-color: var(--purple-300);   /* live: layers.js sets the colour of the ray aimed at it */
  position: relative;
  display: inline-block;
  margin: calc(-1 * var(--space-2));
  padding: var(--space-2);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  font: 600 var(--type-h4) / 1.4 var(--font-body);
  color: var(--color-ink);
  text-align: inherit;
  cursor: pointer;
  transform-origin: left center;
  transition: color var(--motion-fast), scale var(--motion-medium);
}
/* the short stroke over each point */
.points__btn::before {
  content: "";
  display: block;
  width: var(--space-6);
  height: 2px;
  margin-bottom: var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--point-color);
  transition: width var(--motion-medium), height var(--motion-medium), background-color var(--motion-slow);
}
.points__btn:hover  { color: var(--color-action-primary-hover); }
.points__btn:active { color: var(--color-action-primary-active); }
.points__btn:focus-visible { outline: 3px solid var(--color-focus-ring); outline-offset: 2px; }
.points__btn:disabled { color: var(--color-muted); cursor: not-allowed; }
/* Hover and focus hint that the point is a button: a longer stroke and a
   title 3% larger. */
.points__btn:is(:hover, :focus-visible) { scale: 1.03; }
.points__btn:is(:hover, :focus-visible)::before { width: calc(var(--space-6) + var(--space-3)); }
/* Picked by the reader (never by the automatic shots): its stroke grows, the
   title grows a little, and the other points step back. */
.points__btn[aria-pressed="true"] { scale: 1.08; }
.points__btn[aria-pressed="true"]::before { width: var(--space-12); height: 3px; }
.points:has([aria-pressed="true"]) .points__btn:not([aria-pressed="true"]) { color: var(--color-muted); }

/* --- section 4: community labels ----------------------------------------
   Static: a row of pills above the abstract. Live: see LIVE MODE below. */
.community {
  --community-fill: color-mix(in srgb, var(--purple-100) 50%, var(--purple-050));   /* the hub's pill tone */
  --community-line: var(--purple-300);                                               /* live: the line at rest */
  --community-ring: color-mix(in srgb, var(--graphic-violet) 45%, var(--purple-300)); /* the orbit rings' violet */
  --points-trace: var(--community-ring);                                              /* the sparkle, as in section 3 */
  margin: var(--space-6) 0 0;
  padding: 0;
  list-style: none;
  display: flex; flex-wrap: wrap; gap: var(--space-2);
}
.community__btn {
  display: inline-flex; align-items: center;
  min-height: 44px;
  padding: 0 var(--space-4);
  border: 0; border-radius: var(--radius-pill);
  background: var(--community-fill);
  color: var(--color-action-primary);
  font: 600 var(--type-label) / 1 var(--font-body);
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: background-color var(--motion-fast), color var(--motion-fast), translate var(--motion-medium);
}
.community__btn:hover  { background: var(--purple-100); }
.community__btn:active { background: var(--color-canvas-pressed); color: var(--color-action-primary-active); }
.community__btn:focus-visible { outline: 3px solid var(--color-focus-ring); outline-offset: 2px; }
/* Selected: ringed in the orbits' violet, name in ink. */
.community__btn[aria-pressed="true"] {
  background: var(--purple-100);
  color: var(--color-ink);
  box-shadow: inset 0 0 0 2px var(--community-ring);
}
.community__btn:disabled { background: var(--purple-050); color: var(--color-muted); cursor: not-allowed; }
/* Labels only for now: not clickable, not hoverable, out of the tab order
   (tabindex in render/network.js). */
.community__btn { pointer-events: none; cursor: default; }
/* the name: the sparkle runs round it; padded by negative margins so the
   text itself does not move */
.community__name {
  position: relative;
  display: inline-block;
  margin: calc(-1 * var(--space-1)) calc(-1 * var(--space-2));
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  transform-origin: left center;
  transition: scale var(--motion-medium);
}

/* The abstracts share one grid cell, so the cell keeps the tallest height.
   Section 3 stacks sub-headline + text pairs, section 4 stacks text only. */
.abstracts { display: grid; margin-top: var(--space-6); }
.abstracts--method { margin-top: 0; }
.abstracts--subhead { margin-top: var(--space-4); }   /* the sub-headline's own gap under the title */
.abstracts > * { grid-area: 1 / 1; margin: 0; visibility: hidden; }
.abstracts > .is-on { visibility: visible; animation: layers-fade-in var(--motion-medium); }
@keyframes layers-fade-in { from { opacity: 0; } }

/* --- the three counters (sections 2 and 4) ------------------------------ */
.counters {
  margin: var(--space-12) 0 0;
  padding-top: var(--space-6);
  border-top: 1px solid var(--color-hairline);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  transition: opacity var(--motion-slow), transform var(--motion-slow);
}
.counters__item {
  padding: 0 var(--space-6);
  border-left: 1px solid var(--color-hairline-soft);
  display: flex;
  flex-direction: column;
}
.counters__item:first-child { padding-left: 0; border-left: 0; }

.counters dt {
  order: 1;   /* figure above label, figures level even when a label wraps; dt stays first in the DOM */
  margin-top: var(--space-2);
  font-size: var(--type-body-sm);
  line-height: 1.5;
  color: var(--color-muted);
}
/* A small line naming the period the counters cover (section 4); it takes
   the counters' top margin so it sits just above their rule. */
.counters__note {
  margin: var(--space-12) 0 var(--space-2);
  font-size: var(--type-caption);
  line-height: 1.4;
  color: var(--color-muted);
}
.counters__note + .counters { margin-top: 0; }

/* Numerals in data: Space Grotesk's defined home (§4.2 data-label, §4.3). */
.counters__figure {
  margin: 0;
  font-family: var(--font-accent);
  font-size: var(--type-h1);
  font-weight: 600;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--color-ink);
}

/* ============================================================================
   LIVE MODE — the figure pins, the scroll drives it
   ========================================================================= */
/* While layers.js switches mode, nothing transitions: hidden things (the
   section-3 points, the section-4 labels, the counters) start hidden rather
   than fading out over the figure on page load. */
.layers.is-switching *,
.layers.is-switching *::before,
.layers.is-switching *::after { transition: none !important; }

/* The figure beside all three sections, pinned. */
.layers.is-live .stage { grid-column: fig; grid-row: 1 / span 3; }   /* inside the container: its right edge meets the topbar button's */
.layers.is-live .layer--method  { grid-row: 2; }
.layers.is-live .layer--network { grid-row: 3; }
.layers.is-live .stage__pin {
  position: sticky;
  top: 0;
  height: 100vh;
}
/* Room for the topbar above and below, so it never covers the figure. */
.layers.is-live .stage__fig {
  width: min(100%, calc(100vh - 2 * (var(--layout-topbar) + var(--space-4))), 720px);
}

.layers.is-live .layer {
  grid-column: text;
  padding-block: 0;
  min-height: 100vh;
}
.layers.is-live .layer__pin {
  display: flex;
  align-items: center;
  min-height: 100vh;
}
.layers.is-live .layer__fig { display: none; }

/* Section 2 is two screens tall and its text stays put for both beats:
   the core alone, then the Radar canvas. */
.layers.is-live .layer--science { min-height: 200vh; }
.layers.is-live .layer--science .layer__pin { position: sticky; top: 0; }

/* Counters wait until their section lands. */
.layers.is-live .counters.is-waiting { opacity: 0; transform: translateY(var(--space-2)); }

/* Section 4 is two screens tall and its text stays put while the labels are
   out, the same hard stop as sections 2 and 3. Its text starts below the
   lowest label; layers.js measures --labels-bottom. */
.layers.is-live .layer--network { min-height: 200vh; }
.layers.is-live .layer--network .layer__pin {
  position: sticky; top: 0;
  box-sizing: border-box;
  align-items: flex-start;
  padding-top: calc(var(--labels-bottom, 30vh) + var(--space-8));
}

/* --- section 4: the orbit rings draw on, as in the hub's first entrance ----
   Undrawn (data-orbits="off") until section 4 arrives. A transition on
   --hub-draw (registered by the component) makes the draw reversible. */
.layers.is-live .stage[data-orbits] .hub__orbit {
  animation: none;
  transition:
    --hub-draw 0.4s cubic-bezier(0.65, 0, 0.35, 1) calc(var(--i) * 0.1s),
    opacity var(--motion-slow), filter var(--motion-slow);
}
.layers.is-live .stage[data-orbits="off"] .hub__orbit {
  --hub-draw: 0deg;
  transition-duration: 0.5s, var(--motion-slow), var(--motion-slow);
  transition-delay: 0s;
}
.layers.is-live .stage[data-orbits="on"] .hub__orbit { --hub-draw: 360deg; }
.layers.is-live .stage .hub__halo { transition: opacity var(--motion-slow); }
.layers.is-live .stage[data-orbits="off"] .hub__halo { opacity: 0; }

/* --- section 3: the points sit in the figure's four corners, around the
   rays. layers.js lays the list over the figure (--px, --py, --ps). They
   come in from the corners once the rays are back. */
/* Section 3 is two screens tall and its text stays put while the points are
   up, the same hard stop as section 2. */
.layers.is-live .layer--method { min-height: 200vh; }
.layers.is-live .layer--method .layer__pin { position: sticky; top: 0; }
.layers.is-live .points {
  position: absolute;
  left: var(--px); top: var(--py);
  width: var(--ps); height: var(--ps);
  margin: 0;
  display: block;
  pointer-events: none;
}
.layers.is-live .points__item {
  position: absolute;
  max-width: 36%;
  opacity: 0;
  visibility: hidden;   /* out of the tab order while hidden */
  translate: var(--dx) var(--dy);
  /* leaving: fast, so a quick scroll cannot drag them away half-faded */
  transition: opacity var(--motion-fast), translate var(--motion-fast), visibility 0s 160ms;
}
.layers.is-live .points__item:nth-child(1) { top: 0;    left: 0;  --dx: 8px;  --dy: 8px; }
.layers.is-live .points__item:nth-child(2) { top: 0;    right: 0; --dx: -8px; --dy: 8px; }
.layers.is-live .points__item:nth-child(3) { bottom: 0; left: 0;  --dx: 8px;  --dy: -8px; }
.layers.is-live .points__item:nth-child(4) { bottom: 0; right: 0; --dx: -8px; --dy: -8px; }
.layers.is-live .points__item:nth-child(even) { text-align: right; }
.layers.is-live .points__item:nth-child(even) .points__btn { transform-origin: right center; }
.layers.is-live .points__item:nth-child(even) .points__btn::before { margin-left: auto; }
.layers.is-live .points.is-shown .points__item {
  opacity: 1;
  visibility: visible;
  translate: 0 0;
  /* arriving: slower, one after another ({motion.stagger}) */
  transition:
    opacity var(--motion-slow) calc(var(--i) * 80ms),
    translate var(--motion-slow) calc(var(--i) * 80ms),
    visibility 0s calc(var(--i) * 80ms);
}
.layers.is-live .points.is-shown .points__btn { pointer-events: auto; }

/* --- section 3: the rays hold still in an X, one aimed at each point
   (layers.js turns .hub__aim). Their regular sweep flashes stop; layers.js
   fires one slow shot at a time instead. --------------------------------- */
.layers.is-live .stage[data-aim] .hub__flash { animation: none; opacity: 0; }
.layers.is-live .stage[data-aim] .hub__sector.is-shot .hub__flash { animation: hub-flash 3s linear 1; }

/* --- section 4: the labels. Each is a thin violet line that leaves its ring
   at the top band and runs left, with its name at the far end. layers.js
   sets --x, --y, --w, --h and the line weight --t, and moves the whole set
   with --oy so it stays on the rings while the text scrolls in. The rings
   draw as the section arrives; the lines unroll right to left once it has
   landed and its text holds still, each only after its own ring is drawn. */
.layers.is-live .community { margin: 0; }
.layers.is-live .community__btn {
  position: absolute;
  left: var(--x); top: calc(var(--y) + var(--oy, 0px));
  width: var(--w); height: var(--h);
  min-height: 0;
  padding: 0;
  gap: var(--space-3);
  border-radius: var(--radius-sm);
  background: none;
  clip-path: inset(-6px -6px -6px 100%);   /* the 6px leaves room for the focus ring */
  visibility: hidden;
  /* leaving: very fast, so a quick scroll cannot drag them away half-rolled */
  transition:
    clip-path 0.12s ease-in, visibility 0s 0.12s,
    color var(--motion-fast);
}
.layers.is-live .community__btn::after {
  content: "";
  flex: 1;
  height: var(--t);
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg,
    var(--community-line) 0%, var(--community-line) 82%,
    color-mix(in srgb, var(--community-line) 0%, transparent) 100%);   /* dissolves into the ring */
  transition: height var(--motion-fast);
}
.layers.is-live .community.is-shown .community__btn {
  clip-path: inset(-6px);
  visibility: visible;
  /* --wait (layers.js) is what the first ring still needs to finish drawing;
     each next ring ends 0.1s later, and so does its label */
  transition:
    clip-path 0.3s cubic-bezier(0.22, 1, 0.36, 1) calc(var(--wait, 0ms) + var(--i) * 0.1s),
    visibility 0s calc(var(--wait, 0ms) + var(--i) * 0.1s),
    color var(--motion-fast);
}
/* Names set like the section-3 points, and they answer the same way: hover
   and focus grow the name 3% and run the sparkle; a pick grows it 8%, the
   other names step back, and the line takes its ring's colour and weight.
   Only the name scales, never the long line. */
.layers.is-live .community__btn {
  font: 600 var(--type-h4) / 1.2 var(--font-body);
  letter-spacing: 0;
  color: var(--color-ink);
}
.layers.is-live .community__btn:hover  { background: none; color: var(--color-action-primary-hover); --community-line: var(--community-ring); }
.layers.is-live .community__btn:active { background: none; color: var(--color-action-primary-active); }
.layers.is-live .community__btn:is(:hover, :focus-visible) .community__name { scale: 1.03; }
.layers.is-live .community__btn[aria-pressed="true"] {
  background: none;
  box-shadow: none;
  color: var(--color-ink);
  --community-line: var(--community-ring);
}
.layers.is-live .community__btn[aria-pressed="true"] .community__name { scale: 1.08; }
.layers.is-live .community__btn[aria-pressed="true"]::after { height: calc(var(--t) * 1.6); }
.layers.is-live .community:has([aria-pressed="true"]) .community__btn:not([aria-pressed="true"]) { color: var(--color-muted); }

/* Each ring's light streak finishes its lap at the ring's top, where the
   label's line leaves it, and layers.js then sparkles that label. The head
   of the streak sits 6deg into its gradient, so a lap ends at -6deg. It
   starts 40% of a lap in, so the first arrival comes soon after landing. */
.layers.is-live .stage[data-orbits="on"] .hub__comet {
  --lap: calc((4s + var(--i) * 0.9s) * var(--speed));
  animation: hub-comet-home var(--lap) cubic-bezier(0.4, 0, 0.6, 1) calc(var(--lap) * -0.4) infinite;
}
@keyframes hub-comet-home {
  0%   { transform: rotate(354deg); opacity: 0; }
  8%   { opacity: 0.9; }
  96%  { opacity: 0.9; }
  100% { transform: rotate(-6deg); opacity: 0; }
}

/* --- the swap: the hub rolls in to its centre, then the canvas rolls out.
   Each side's delays are set on the state it is going to, so the order
   reverses by itself when scrolling back up. ------------------------------ */
.layers.is-live .stage__canvas {
  display: block;
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  aspect-ratio: 1;
}
.layers.is-live .stage__hub,
.layers.is-live .stage__canvas { will-change: transform, opacity; }

/* at rest on the hub: the hub grows back once the canvas has started to fold */
.layers.is-live .stage__hub {
  transition:
    transform var(--motion-slow) 200ms,
    opacity var(--motion-slow) 200ms,
    visibility 0s 200ms;
}
.layers.is-live .stage__canvas {
  opacity: 0;
  visibility: hidden;
  transform: scale(0.3) rotate(120deg);
  transition:
    transform var(--motion-slow),
    opacity var(--motion-slow),
    visibility 0s 400ms;
}

/* on the canvas: the hub folds first, the canvas opens behind it */
.layers.is-live .stage[data-view="canvas"] .stage__hub {
  opacity: 0;
  visibility: hidden;
  transform: scale(0.15) rotate(-180deg);
  transition:
    transform var(--motion-slow),
    opacity var(--motion-slow),
    visibility 0s 400ms;
}
.layers.is-live .stage[data-view="canvas"] .stage__canvas {
  opacity: 1;
  visibility: visible;
  transform: scale(1.2);   /* 20% over the hub's box, so the labels read */
  transition:
    transform var(--motion-slow) 200ms,
    opacity var(--motion-slow) 200ms,
    visibility 0s 200ms;
}

/* ============================================================================
   RESPONSIVE (§13.1) — review at 1440, 1180, 980, 720, 480, 390.
   Below 980px the page is always static: copy first, figure under it.
   ========================================================================= */
@media (max-width: 979px) {
  .layers {
    display: block;
    padding-inline: var(--space-6);
  }
  .stage__fig { width: min(100%, 560px); margin-inline: auto; }
  .layer__pin { display: block; }
  .layer__fig { margin: var(--space-8) auto 0; max-width: 560px; }

  /* Section 2's canvas is the figure here: the full hub under it goes. */
  .stage { display: none; }

  /* Section 3: its own hub, core and rays only, with the four points in its
     corners, as in live mode. The rays hold still in the X that aims each
     at its point, and each point's stroke takes its ray's colour (know
     orange, learn green, cultivate violet, apply blue; see layers.js
     step 6). The hub and the list share one grid cell, so the list is the
     hub's size. */
  .layer--method .layer__text { display: grid; }
  .layer--method .method-fig,
  .layer--method .points {
    grid-area: 3 / 1;
    justify-self: center;
    width: min(100%, 480px);
    margin: var(--space-8) 0 0;
  }
  .method-fig { display: block; }
  .method-fig .hub__band { pointer-events: none; }
  .method-fig .hub__sector:hover { transform: none; }
  .method-fig .hub__rotor { animation: none; }
  .method-fig .hub__aim { transform: rotate(-45deg); }

  .layer--method .points { position: relative; display: block; }
  .layer--method .points__item { position: absolute; max-width: 38%; }
  .layer--method .points__item:nth-child(1) { top: 0;    left: 0;  --point-color: var(--radar-cpf); }
  .layer--method .points__item:nth-child(2) { top: 0;    right: 0; --point-color: var(--radar-ebs); }
  .layer--method .points__item:nth-child(3) { bottom: 0; left: 0;  --point-color: var(--radar-dmr); }
  .layer--method .points__item:nth-child(4) { bottom: 0; right: 0; --point-color: var(--radar-ahp); }
  .layer--method .points__item:nth-child(even) { text-align: right; }
  .layer--method .points__item:nth-child(even) .points__btn { transform-origin: right center; }
  .layer--method .points__item:nth-child(even) .points__btn::before { margin-left: auto; }
  .layer--method .points__btn { --point-color: inherit; font-size: var(--type-body-sm); line-height: 1.3; }
  .layer--method .points__btn::before { margin-bottom: var(--space-2); }
}

@media (max-width: 719px) {
  .layers { padding-inline: var(--space-4); }
  /* The three counters stay in one row: figures and labels set smaller. */
  .counters, .counters__note { margin-top: var(--space-8); }
  .counters__item { padding: 0 var(--space-3); }
  .counters__figure { font-size: var(--type-h2); }
  .counters dt { font-size: var(--type-caption); line-height: 1.4; }
}
