/* ============================================================================
   LogosPilot — EXPERIENCE LAYER
   Amendment A-15 (Dynamic Visual Experience Standard)
   Amendment A-16 (Visual Identity Motion System)
   Amendment A-17 (logo-derived primitives)

   SCOPE: the homepage. Loaded by `/` and by nothing else, so the thirty other
   routes carry none of this weight and remain under the zero-motion doctrine
   until the executive authorises propagation.

   THREE RULES, IN ORDER OF PRECEDENCE
     1. Motion explains or it is deleted. Every animation in this file has a
        sentence above it naming what a reader learns from it. An animation
        whose sentence would be "it looks alive" is not written.
     2. Motion is never the carrier. Every diagram states its meaning in text
        and in resting geometry. A reader who sees no frame — reduced motion,
        no JavaScript, a printout — loses no information whatsoever.
     3. The geometry is the logo's. Lines that carry signal are drawn at
        --lp-slant and round-capped, because the mark is. Nothing else is.

   Components consume TIER 2 semantic tokens only, as everywhere else. The one
   deliberate exception is documented at --e-slant below.
   ========================================================================= */

/* ── LOCAL BINDINGS · TIER 3 ───────────────────────────────────────────────
   --lp-slant is a Tier 1 primitive with no Tier 2 role, because it is not
   colour, space or type — the three families the semantic layer is defined
   over. Adding a fourth family to hold one value would be the larger change.
   Design Token §A.2 permits a Tier 3 binding; it forbids a component reading
   Tier 1 directly, which is why this exists.                               */
.lp-x {
  --e-slant: var(--lp-slant);
  --e-slant-tan: var(--lp-slant-tan);
}

/* ══ 1 · SIGNAL FIELD — the hero illustration ══════════════════════════════
   ILLUSTRATION I-01. Abstract, conceptual, depicts no product interface.
   EXPLAINS: the whole product in one figure, read left to right — speech
   arrives, is routed, resolves on a display. The ladder runs azure→white
   exactly once, because that is the L→P hand-off and also what the software
   does.                                                                    */
.lp-signalfield {
  position: relative;
  width: 100%;
  /* Reserved by aspect-ratio, so the figure occupies its final box on the
     first frame and contributes exactly zero to cumulative layout shift. */
  aspect-ratio: 4 / 3;
  background:
    radial-gradient(120% 90% at 78% 32%, rgb(0 82 210 / 0.16), transparent 62%),
    radial-gradient(90% 70% at 18% 68%, rgb(0 168 252 / 0.10), transparent 60%),
    var(--color-background-raised);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-overlay);
  overflow: hidden;
}
.lp-signalfield > svg { width: 100%; height: 100%; display: block; }
.lp-herofig { display: grid; gap: var(--space-stack-default); }

/* The field's ruled ground: a faint lattice at the mark's own angle. It is
   the only background texture on the site, and it is the logo's geometry
   rather than a pattern chosen for effect. */
.lp-signalfield::before {
  content: "";
  position: absolute; inset: -20%;
  background-image: repeating-linear-gradient(
    calc(90deg - var(--e-slant)),
    var(--border-subtle) 0 1px,
    transparent 1px 88px);
  opacity: 0.5;
  pointer-events: none;
}

/* ══ 2 · DIAGRAM PRIMITIVES ════════════════════════════════════════════════
   Every SVG figure on the page inherits these, so a diagram cannot be drawn
   in a language other than the mark's. */
.lp-dia { display: block; width: 100%; height: auto; overflow: visible; }
.lp-dia * { vector-effect: non-scaling-stroke; }

.lp-dia__rail,
.lp-dia__path {
  fill: none;
  stroke-linecap: var(--lp-terminal);
  stroke-linejoin: var(--lp-terminal);
}
.lp-dia__rail { stroke: var(--color-signal-idle); stroke-width: 2; }
.lp-dia__path { stroke: var(--color-signal-transit); stroke-width: 3; }

.lp-dia__node {
  fill: var(--color-surface-default);
  stroke: var(--border-default);
  stroke-width: 2;
}
.lp-dia__node--live { stroke: var(--color-signal-lead); }
/* A route or node that is DOWN. Carried by shape — a dashed border and a
   strike-through — with colour only reinforcing, because no four states stay
   distinguishable under every colour-vision condition (ECS §E.8). */
.lp-dia__node--down { stroke: var(--color-content-disabled); stroke-dasharray: 5 6; }
.lp-dia__sever {
  fill: none; stroke: var(--color-content-disabled); stroke-width: 2.5;
  stroke-linecap: var(--lp-terminal);
}
.lp-dia__fill    { fill: var(--color-signal-trail); }
.lp-dia__lead    { fill: var(--color-signal-lead); }
.lp-dia__resolved{ fill: var(--color-signal-resolved); }
.lp-dia__label {
  fill: var(--color-content-tertiary);
  font-family: var(--type-family-sans);
  font-size: 13px;
  font-weight: var(--lp-weight-medium);
}
.lp-dia__label--strong { fill: var(--color-content-primary); }
.lp-dia__label--mono {
  font-family: var(--type-family-mono);
  font-size: 12px;
  fill: var(--color-content-tertiary);
}

/* THE SIGNAL TOKEN — primitive P4. The play triangle from the bowl of the P
   is the only non-letterform element in the mark, so it is the shape this
   site uses to mean "this is the thing that is moving". A dot would have
   been arbitrary; this is not. */
.lp-dia__token { fill: var(--color-signal-lead); }

/* ══ 3 · AMBIENT MOTION ════════════════════════════════════════════════════
   Each animation below states what it teaches. All are paused until the
   figure is on screen (experience.js sets data-run), so nothing animates in
   a tab nobody is looking at and nothing animates before it can be seen. */

/* TEACHES: that the signal travels, and which way it runs.
   A plain translate rather than `offset-path`: every route in this language is
   a STRAIGHT line at --e-slant, and two numbers cannot desynchronise from the
   path they follow the way a duplicated `path()` string can. Runs are declared
   once, in §13. */
@keyframes lp-travel {
  0%   { transform: translate(0, 0); opacity: 0; }
  8%   { opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: translate(var(--e-dx), var(--e-dy)); opacity: 0; }
}
[data-run="true"] .lp-anim-travel {
  animation: lp-travel var(--motion-signal-travel) var(--motion-ease-signal) infinite;
  animation-delay: var(--e-delay, 0ms);
}
/* RESTING STATE. Visible, at the origin, waiting. Not invisible: a reader with
   no JavaScript, or a figure that has scrolled out of view, must still see the
   thing the diagram is about. The keyframe's own 0% takes it to zero for the
   duration of each pass, which is where the fade belongs. */
.lp-anim-travel { opacity: 1; }

/* TEACHES: that a path is being used rather than merely existing — the drawn
   stroke advances along the route the signal takes. */
@keyframes lp-trace {
  0%   { stroke-dashoffset: var(--e-len); }
  55%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: 0; }
}
[data-run="true"] .lp-anim-trace {
  stroke-dasharray: var(--e-len);
  animation: lp-trace var(--motion-signal-cycle) var(--motion-ease-signal) infinite;
  animation-delay: var(--e-delay, 0ms);
}

/* TEACHES: that a stage has ACKNOWLEDGED something — the node brightens as
   the signal reaches it and settles once it has passed. */
@keyframes lp-ack {
  0%, 70%, 100% { stroke: var(--color-signal-idle); }
  12%, 40%      { stroke: var(--color-signal-lead); }
}
[data-run="true"] .lp-anim-ack {
  animation: lp-ack var(--motion-signal-cycle) var(--motion-ease-standard) infinite;
  animation-delay: var(--e-delay, 0ms);
}

/* TEACHES: the lag. The ONLY animation on the page whose subject is a defect
   rather than a capability, and it is the reason the product exists — the
   lower bar starts later than the upper one. The delay is not decorative
   timing; it IS the message.

   An earlier version travelled two markers along two tracks and measured the
   gap with a bracket floating between them. It was wrong: the bracket was
   anchored to nothing, and at most instants one marker was mid-fade, so the
   figure read as a mistake. Growing bars fixed both — the offset is between
   two FIXED origins the bracket is drawn from, and a bar that is partly grown
   still says what it means. */
@keyframes lp-lag-grow {
  0%,  4%  { transform: scaleX(0); }
  34%      { transform: scaleX(1); }
  92%      { transform: scaleX(1); }
  100%     { transform: scaleX(0); }
}
.lp-anim-bar {
  /* fill-box + left origin, so a bar grows FROM ITS OWN ORIGIN — the point
     the bracket measures — rather than from the SVG's. */
  transform-box: fill-box;
  transform-origin: left center;
  transform: scaleX(1);            /* RESTING: full, and the offset is visible */
}
[data-run="true"] .lp-anim-bar {
  animation: lp-lag-grow var(--motion-signal-cycle) var(--motion-ease-signal) infinite;
  animation-delay: var(--e-delay, 0ms);
}

/* TEACHES: that a passage has RESOLVED — the moment blue becomes white is the
   moment the software has committed to putting something on a screen.

   THE FLOOR IS 0.55, NOT 0. An earlier version faded the resolved text to
   nothing between arrivals, which meant that for roughly two fifths of every
   cycle — and in any screenshot taken during them — the display frame was
   simply empty. A figure that reads as broken at a randomly chosen instant is
   a broken figure. The passage is therefore always on the screen; what the
   animation carries is the ARRIVAL, not the existence.                      */
@keyframes lp-resolve {
  0%, 40%  { opacity: 0.55; }
  56%, 88% { opacity: 1; }
  100%     { opacity: 0.55; }
}
[data-run="true"] .lp-anim-resolve {
  animation: lp-resolve var(--motion-signal-cycle) var(--motion-ease-enter) infinite;
  animation-delay: var(--e-delay, 0ms);
}
.lp-anim-resolve { opacity: 1; }

/* ══ 4 · SECTION REVEAL ════════════════════════════════════════════════════
   TEACHES: nothing. The one piece of non-explanatory motion in the file, held
   to 240ms and 8px — below the threshold at which motion reads as an effect
   rather than as the page settling. Opt-in per element, and it never gates
   content: experience.js fails open three separate ways (see §2 there). */
.lp-reveal { opacity: 1; }
[data-motion="on"] .lp-reveal {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--motion-reveal) var(--motion-ease-enter),
              transform var(--motion-reveal) var(--motion-ease-enter);
  transition-delay: var(--e-delay, 0ms);
}
[data-motion="on"] .lp-reveal[data-shown="true"] { opacity: 1; transform: none; }

/* ══ 5 · HERO ══════════════════════════════════════════════════════════════ */
.lp-hero-grid {
  display: grid;
  gap: var(--space-inline-group);
  align-items: center;
  padding-block: var(--space-stack-subsection) var(--space-stack-group);
}
@media (min-width: 1024px) {
  .lp-hero-grid {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    gap: var(--space-900);
    padding-block: var(--space-900) var(--space-stack-subsection);
  }
}
.lp-hero-grid > * { min-width: 0; }

/* The eyebrow: small, slant-ruled, and the first thing that says what
   category of product this is before the headline says what it does. */
.lp-eyebrow {
  display: inline-flex; align-items: center; gap: var(--space-inline-tight);
  font-size: var(--lp-size-12); line-height: var(--lp-lead-16);
  font-weight: var(--lp-weight-strong);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--color-content-tertiary);
  margin-block-end: var(--space-stack-related);
}
.lp-eyebrow::before {
  content: ""; inline-size: 2px; block-size: 14px;
  background: var(--color-accent-default);
  /* The one rule that makes a 2px line belong to this brand rather than any
     other: it leans at the mark's angle. */
  transform: skewX(calc(-1 * var(--e-slant)));
}

/* ══ 6 · INTERACTIVE WORKFLOW ══════════════════════════════════════════════
   Replaces four static explanation cards. Without JavaScript every stage is
   present and readable as a plain ordered sequence; with it, the same markup
   becomes a tab set. Nothing is reachable only through the enhanced form. */
.lp-flow { display: grid; gap: var(--space-stack-group); }

.lp-flow__rail {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
  gap: var(--space-inline-tight);
}
.lp-flow__stage {
  position: relative;
  display: grid; gap: var(--space-100);
  align-content: start;
  text-align: start;
  padding: var(--space-300) var(--space-400);
  min-height: var(--size-target-comfortable);
  background: none;
  border: var(--border-width-default) solid transparent;
  border-radius: var(--radius-control);
  color: var(--color-content-secondary);
  cursor: pointer;
  transition: background-color var(--motion-micro) var(--motion-ease-standard),
              color var(--motion-micro) var(--motion-ease-standard);
}
/* The stage rule: a slanted bar, idle grey, azure when this stage is the one
   being read. Selection is carried by the bar AND by the label weight AND by
   aria-selected — never by colour alone. */
.lp-flow__stage::before {
  content: "";
  position: absolute; inset-block: var(--space-200); inset-inline-start: 0;
  inline-size: 2px;
  background: var(--color-signal-idle);
  transform: skewX(calc(-1 * var(--e-slant)));
  transition: background-color var(--motion-transition) var(--motion-ease-standard);
}
.lp-flow__stage:hover { background: var(--color-surface-default); color: var(--color-content-primary); }
.lp-flow__stage[aria-selected="true"] {
  background: var(--color-surface-default);
  color: var(--color-content-primary);
}
.lp-flow__stage[aria-selected="true"]::before { background: var(--color-signal-lead); }
.lp-flow__stage[aria-selected="true"] .lp-flow__name { font-weight: var(--lp-weight-strong); }

.lp-flow__ord {
  font-size: var(--lp-size-12); line-height: var(--lp-lead-16);
  color: var(--color-content-tertiary);
  font-variant-numeric: tabular-nums;
}
.lp-flow__name { font-size: var(--lp-size-16); line-height: var(--lp-lead-24); font-weight: var(--lp-weight-medium); }

.lp-flow__panel {
  display: grid; gap: var(--space-inline-group);
  padding: var(--space-inset-comfortable);
  background: var(--color-surface-documentation);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-container);
}
@media (min-width: 768px) {
  .lp-flow__panel { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); align-items: center; }
}
.lp-flow__panel > * { min-width: 0; }
.lp-flow__figure {
  background: var(--color-background-base);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-control);
  padding: var(--space-400);
  /* Reserved: every stage figure occupies the same box, so switching stages
     moves nothing on the page. */
  aspect-ratio: 16 / 10;
  display: grid; place-items: center;
}
.lp-flow__figure > svg { width: 100%; height: auto; max-height: 100%; }

/* `display: grid` above outranks the UA's `[hidden] { display: none }`, so the
   attribute has to be honoured explicitly or a hidden panel would still show.
   Only JavaScript ever sets it; without JavaScript nothing is hidden and every
   stage renders in sequence. */
.lp-flow__panel[hidden] { display: none; }

/* The abstain control. This is the most important interaction on the page:
   it lets a visitor watch the product REFUSE. Pressed state is carried by
   the label, the border and aria-pressed together. */
.lp-flow__confidence {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-inline-default);
  padding-block-start: var(--space-stack-related);
  border-top: var(--border-width-hairline) solid var(--border-subtle);
}
.lp-flow__toggle {
  display: inline-flex; align-items: center; gap: var(--space-inline-tight);
  min-height: var(--size-target-comfortable);
  padding: var(--space-200) var(--space-400);
  border: var(--border-width-default) solid var(--border-interactive);
  border-radius: var(--radius-control);
  background: var(--color-surface-default);
  color: var(--color-content-primary);
  font-size: var(--lp-size-14); line-height: var(--lp-lead-20);
  font-weight: var(--lp-weight-medium);
  cursor: pointer;
  transition: background-color var(--motion-micro) var(--motion-ease-standard),
              border-color var(--motion-micro) var(--motion-ease-standard);
}
.lp-flow__toggle[aria-pressed="true"] {
  border-color: var(--color-feedback-warning);
  background: var(--color-surface-raised);
}
.lp-flow__toggle::before {
  content: ""; inline-size: 10px; block-size: 10px; flex: none;
  border-radius: var(--radius-full);
  background: var(--color-signal-lead);
}
.lp-flow__toggle[aria-pressed="true"]::before { background: var(--color-feedback-warning); }

/* The two states of the Display stage. BOTH are authored in the document;
   this only chooses which is shown, so a reader with no JavaScript gets the
   confident state complete and the abstain state described in the prose
   beneath it rather than hidden behind a control they cannot operate. */
.lp-state--abstain { display: none; }
[data-abstain-state="on"] .lp-state--confident { display: none; }
[data-abstain-state="on"] .lp-state--abstain   { display: inline; }

/* In the abstain state the display frame is EMPTY and the frame itself says
   so. A blank screen is the product's correct answer; the figure must not
   soften it into a warning triangle over a passage that is still showing. */
.lp-dia__screen--blank { fill: var(--color-background-base); stroke: var(--color-feedback-warning); }
.lp-dia__hold {
  fill: var(--color-feedback-warning);
  font-family: var(--type-family-sans);
  font-size: 12px; font-weight: var(--lp-weight-strong);
}

/* ══ 7 · ROUTING FIGURE — integrations ═════════════════════════════════════
   ILLUSTRATION I-04. The two certified transports as two routes out of one
   node. Versions are stated in the table beside it; the figure never carries
   a fact the table does not. */
.lp-routing {
  background: var(--color-background-raised);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-container);
  padding: var(--space-inset-comfortable);
}

/* ══ 8 · MEDIA SLOT ════════════════════════════════════════════════════════
   Where an authentic founder capture will go, built now at final size so that
   dropping the file in later changes nothing about the layout. The
   capture-brief id travels in data-capture, so the asset and its brief cannot
   be separated.

   Not the dashed engineering placeholder it replaces. It states a fact in the
   site's own voice and refuses to substitute a mock interface. The refusal is
   the content. */
.lp-mediaslot {
  position: relative;
  border: var(--border-width-hairline) solid var(--border-default);
  border-radius: var(--radius-container);
  background:
    linear-gradient(180deg, var(--color-surface-documentation), var(--color-background-raised));
  overflow: hidden;
}
.lp-mediaslot__frame {
  aspect-ratio: 16 / 10;
  display: grid; place-items: center;
  padding: var(--space-inset-comfortable);
  text-align: center;
}
.lp-mediaslot__frame > img,
.lp-mediaslot__frame > video { width: 100%; height: 100%; object-fit: contain; }
.lp-mediaslot__inner { display: grid; gap: var(--space-stack-default); justify-items: center; max-width: 46ch; }
.lp-mediaslot__state {
  display: inline-flex; align-items: center; gap: var(--space-inline-tight);
  font-size: var(--lp-size-14); line-height: var(--lp-lead-20);
  font-weight: var(--lp-weight-medium);
  color: var(--color-content-secondary);
}
.lp-mediaslot__state::before {
  content: ""; inline-size: 2px; block-size: 16px;
  background: var(--color-content-disabled);
  transform: skewX(calc(-1 * var(--e-slant)));
}
.lp-mediaslot__bar {
  display: flex; flex-wrap: wrap; gap: var(--space-inline-default);
  justify-content: space-between; align-items: center;
  padding: var(--space-300) var(--space-400);
  border-top: var(--border-width-hairline) solid var(--border-subtle);
  background: var(--color-surface-documentation);
  font-size: var(--lp-size-12); line-height: var(--lp-lead-16);
  color: var(--color-content-tertiary);
}
.lp-mediaslot__bar code { font-family: var(--type-family-mono); }

/* ══ 9 · FIGURE CAPTION ════════════════════════════════════════════════════
   A-15 requires every conceptual illustration to say that it is one. This is
   the component that says it, and it is not optional: a figure on this page
   without one is a governance defect, not a styling omission. */
.lp-figcap {
  margin-block-start: var(--space-stack-default);
  font-size: var(--lp-size-12); line-height: var(--lp-lead-16);
  color: var(--color-content-tertiary);
  max-width: var(--type-measure-narrow);
}
.lp-figcap b { color: var(--color-content-secondary); font-weight: var(--lp-weight-medium); }

/* ══ 10 · MICRO-INTERACTIONS ═══════════════════════════════════════════════
   Restrained by construction: on a near-black ground a shadow is invisible, so
   every hover state below is a SURFACE LUMINANCE change — the mechanism the
   elevation system already uses. No scale, no lift, no glow. */
.lp-x a.lp-card {
  position: relative;
  transition: background-color var(--motion-transition) var(--motion-ease-standard),
              border-color var(--motion-transition) var(--motion-ease-standard);
}
.lp-x a.lp-card::after {
  /* The card's slanted edge marker: idle at the subtle border, azure on
     hover and on focus. It is the same 2px slanted rule as the eyebrow and
     the stage rail, which is what makes these read as one system. */
  content: "";
  position: absolute; inset-block: var(--space-500); inset-inline-start: 0;
  inline-size: 2px;
  background: var(--color-signal-idle);
  transform: skewX(calc(-1 * var(--e-slant)));
  transition: background-color var(--motion-transition) var(--motion-ease-standard);
}
.lp-x a.lp-card:hover { border-color: var(--border-default); }
.lp-x a.lp-card:hover::after,
.lp-x a.lp-card:focus-visible::after { background: var(--color-signal-lead); }

.lp-x .lp-btn--primary {
  transition: background-color var(--motion-micro) var(--motion-ease-standard),
              color var(--motion-micro) var(--motion-ease-standard);
}
.lp-x .lp-nav__link { position: relative; }

/* ══ 11 · SUPPORTING LAYOUT ════════════════════════════════════════════════ */
.lp-splitfig {
  display: grid; gap: var(--space-inline-group); align-items: center;
}
@media (min-width: 900px) {
  .lp-splitfig { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .lp-splitfig--wide { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
}
.lp-splitfig > * { min-width: 0; }

/* A row of short factual statements. Not a "stat" component — there are no
   metrics here to inflate. Each cell is a fact the site can defend. */
.lp-facts {
  display: grid; gap: var(--space-inline-group);
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
  padding-block: var(--space-stack-group);
  border-block: var(--border-width-hairline) solid var(--border-subtle);
}
.lp-facts > * { min-width: 0; }
.lp-fact { display: grid; gap: var(--space-100); align-content: start; }
.lp-fact__val {
  font-size: var(--lp-size-24); line-height: var(--lp-lead-32);
  font-weight: var(--lp-weight-strong);
  color: var(--color-content-primary);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.lp-fact__key {
  font-size: var(--lp-size-14); line-height: var(--lp-lead-20);
  color: var(--color-content-tertiary);
}

/* ══ 12 · REDUCED MOTION — THE HARD STOP ═══════════════════════════════════
   Zeroing a duration is not enough: a 0ms infinite loop still runs. Every
   animation is CANCELLED, and each figure's resting state is the state it is
   authored in — complete, legible, and identical to what a reader with no
   JavaScript sees. Focus indication and reveal-completion survive, because a
   stiller interface must never be a less informative one.                   */
@media (prefers-reduced-motion: reduce) {
  .lp-anim-travel,
  .lp-anim-trace,
  .lp-anim-ack,
  .lp-anim-bar,
  .lp-anim-resolve {
    animation: none !important;
    offset-path: none !important;
  }
  /* The tokens that were carrying the signal must still be VISIBLE, parked at
     their resting position — a diagram missing its token would be a diagram
     with a hole in it. */
  .lp-anim-travel  { opacity: 1; transform: translate(var(--e-dx), var(--e-dy)); }
  .lp-anim-resolve { opacity: 1; }
  .lp-anim-trace   { stroke-dasharray: none; }
  .lp-anim-bar     { transform: scaleX(1); }
  [data-motion="on"] .lp-reveal {
    opacity: 1; transform: none; transition: none;
  }
}

/* Print, and any renderer that lays the page out without scrolling it. Nothing
   on this page may depend on a scroll event to become visible. */
@media print {
  [data-motion="on"] .lp-reveal { opacity: 1; transform: none; }
}

/* ══ 13 · RUN DECLARATIONS ═════════════════════════════════════════════════
   Every travelling token's run, in the user units of its figure, collected so
   the set is auditable at a glance. The `'self'`-only CSP forbids a style
   attribute, which is a constraint worth having: the geometry stays in the
   stylesheet where it can be reviewed rather than scattered through markup.

   For a SIGNAL route dy/dx = --lp-slant-tan, because a signal route is drawn
   at the mark's angle. The lag tracks are deliberately axis-true — they
   represent TIME, not signal — and that difference is the point. */

/* I-01 · hero signal field — three converging routes into the display */
#i01-t1 { --e-dx: 122px; --e-dy: 37.6px; --e-delay:    0ms; }
#i01-t2 { --e-dx: 122px; --e-dy: 37.6px; --e-delay: 1700ms; }
#i01-t3 { --e-dx: 122px; --e-dy: 37.6px; --e-delay: 3400ms; }
#i01-out { --e-delay: 600ms; }

/* I-02 · the lag. Axis-true: this figure measures time, not signal. The only
   value that matters here is the DELAY between the two bars — it is the gap
   the bracket is drawn across, expressed in time instead of in x. */
#i02-spoken { --e-delay:    0ms; }
#i02-shown  { --e-delay: 1400ms; }

/* I-03 · workflow stage figures */
#i03-listen-t   { --e-dx: 150px; --e-dy: 46px; }
#i03-recognize-t{ --e-dx: 150px; --e-dy: 46px; }
#i03-prepare-t  { --e-dx: 150px; --e-dy: 46px; }
#i03-display-t  { --e-dx: 150px; --e-dy: 46px; }

/* I-04 · certified transports — two routes out of one node */
#i04-obs  { --e-dx: 150px; --e-dy: -46.2px; --e-delay:   0ms; }
#i04-vmix { --e-dx: 150px; --e-dy:  46.2px; --e-delay: 900ms; }

/* I-05 · offline. The local route runs; the network route does not. */
#i05-local { --e-dx: 134px; --e-dy: 41.3px; --e-delay: 0ms; }

/* I-06 · abstention, /product. The CONFIDENT route completes; the uncertain
   route is severed and carries no token, which is the whole point of the
   figure — the absence is the message. 196 × 0.3079 = 60.35. */
#i06-confident { --e-dx: 196px; --e-dy: -60.3px; --e-delay: 0ms; }

/* I-07 · the hand-off chain, /product/how-it-works. One token per hop, staggered,
   because the stages hand off in sequence rather than firing at once. Each hop is
   40 × 12.3 — the mark's angle. */
#i07-a { --e-dx: 40px; --e-dy: 12.3px; --e-delay:    0ms; }
#i07-b { --e-dx: 40px; --e-dy: 12.3px; --e-delay: 1700ms; }
#i07-c { --e-dx: 40px; --e-dy: 12.3px; --e-delay: 3400ms; }
