/*
  XCP landing.

  The page is not a container for the animation. It is meant to look generated
  by the same machine: the hero's material, flow lines, activation states and
  cold silver light become the language of every section below it.

  Palette note. The approved animation carries its own accents -- cold silver,
  a restrained cyan and a teal -- and those are adopted here as landing tokens.
  The shared core tokens in tokens.css are untouched, so the drift gate against
  the control center still holds. Series X green appears only as a state signal:
  admitted, verified, active. Never as decoration.
*/

:root {
  /* Matched to the animation's own ground so the hero has no visible seam. */
  --xcp-ink: #050708;
  --xcp-ink-deep: #030405;
  --xcp-silver: #DCE2E3;
  --xcp-cyan: #BFF5F2;
  --xcp-teal: #78BBB5;

  --xcp-line: rgba(220, 226, 227, 0.10);
  --xcp-line-strong: rgba(220, 226, 227, 0.20);
  --xcp-dim: rgba(220, 226, 227, 0.52);
  --xcp-faint: rgba(220, 226, 227, 0.34);

  /*
    State is asserted in Series X green, taken from the tokens the control
    center already uses -- not from the animation's own cool accent, which
    belongs to movement. The rule is the one that was set for the identity:
    green means admitted, verified, enforced, active. Cold silver and cyan are
    structure and motion, and never claim anything on their own.

    Three steps because one green cannot do all three jobs on near-black: the
    brand value fills, the lifted value draws, the luminous value lights.
  */
  --xcp-state-fill: var(--xcp-green-brand);   /* #107C10 */
  --xcp-state: var(--xcp-green-core);         /* #1CAE1C */
  --xcp-state-lit: var(--xcp-accent);         /* #35D435 */

  --xcp-shell: clamp(20px, 5vw, 64px);
  --xcp-column: 1240px;
}

html { background: var(--xcp-ink); }

body {
  background: var(--xcp-ink);
  color: var(--xcp-silver);
  font-family: var(--xcp-font-sans);
  -webkit-font-smoothing: antialiased;
  /* A guard, not the fix. Decorative motion is kept inside the viewport by its
     own geometry; this only stops a future overflow from producing a light
     scrollbar across the bottom of a dark page. `clip` rather than `hidden`
     because it does not turn the body into a scroll container. */
  overflow-x: clip;
}

.shell { max-width: var(--xcp-column); margin: 0 auto; padding: 0 var(--xcp-shell); }

/* A single machined grid, faint, behind everything. The hero uses the same one
   internally, so the page reads as one continuous surface. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.30;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.016) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.013) 1px, transparent 1px);
  background-size: 88px 88px;
  mask-image: radial-gradient(120% 90% at 50% 0%, #000 0%, transparent 78%);
}

main, .site-head { position: relative; z-index: 1; }

/* ----------------------------------------------------------------- type */

.eyebrow {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.34em;
  /* Same tail removal. Invisible where the eyebrow is left aligned, necessary
     where it is centred under the stage. */
  margin-right: -0.34em;
  text-transform: uppercase;
  color: var(--xcp-faint);
}

.section-title {
  font-size: clamp(30px, 4.4vw, 56px);
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 1.04;
  text-wrap: balance;
  margin: 0;
}

.lede {
  font-size: clamp(16px, 1.6vw, 19px);
  line-height: 1.55;
  color: var(--xcp-dim);
  max-width: 58ch;
  margin: 0;
}

.micro {
  font-family: var(--xcp-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--xcp-state);
}

/* --------------------------------------------------------------- chrome */

.site-head {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  backdrop-filter: blur(16px) saturate(120%);
  background: linear-gradient(180deg, rgba(5, 7, 8, 0.86), rgba(5, 7, 8, 0.42));
  border-bottom: 1px solid var(--xcp-line);
}

.site-head .bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: 60px;
}

/*
  The lockup carries the machined mark, not a set word. Plain letter-spaced
  "XCP" read as generic; the graphic lettermark is the same silver-etched form
  the overture reveals, so the header echoes the hero instead of restating it.
  A thin lit rule sits between the symbol and the letters, the same device the
  animation flanks the wordmark with.
*/
.site-head .mark-lockup { display: flex; align-items: center; gap: 12px; }

.site-head .mark-symbol {
  width: 24px;
  height: 24px;
  display: block;
  opacity: 0.94;
}

.site-head .mark-rule {
  width: 1px;
  height: 18px;
  background: linear-gradient(180deg, transparent, var(--xcp-hairline-strong) 30%, var(--xcp-hairline-strong) 70%, transparent);
}

.site-head .mark-letters {
  height: 18px;
  width: auto;
  display: block;
  opacity: 0.95;
  /* The lettermark PNG is bright silver; a touch of contrast keeps it crisp on
     the blurred header without lighting it up like the hero reveal. */
  filter: saturate(0) brightness(1.02);
}

.site-head nav { display: flex; align-items: center; gap: 28px; }

.site-head nav a {
  font-size: 13px;
  color: var(--xcp-dim);
  text-decoration: none;
  transition: color 160ms;
}

.site-head nav a:hover { color: var(--xcp-silver); }

/*
  On a phone the page links stay and the call to action goes. Hiding the links
  instead would leave a three-page site with no way to reach two of its pages,
  and would remove them from the crawlable navigation on the viewport most
  likely to be crawled first.
*/
@media (max-width: 760px) {
  .site-head .bar { height: auto; min-height: 60px; padding-top: 9px; padding-bottom: 9px; flex-wrap: wrap; gap: 8px 18px; }
  .site-head nav { gap: 16px; flex-wrap: wrap; }
  .site-head nav a { font-size: 12px; }
  .site-head nav a.btn { display: none; }
}

/* --------------------------------------------------------------- buttons */

.btn {
  --btn-edge: var(--xcp-line-strong);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 46px;
  padding: 0 24px;
  border: 1px solid var(--btn-edge);
  border-radius: 3px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--xcp-silver);
  text-decoration: none;
  background: linear-gradient(180deg, rgba(220, 226, 227, 0.07), rgba(220, 226, 227, 0.02));
  transition: transform 110ms cubic-bezier(.2,.7,.3,1), border-color 180ms, color 180ms;
  overflow: hidden;
}

.btn::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 1px;
  width: 100%;
  background: linear-gradient(90deg, transparent, var(--xcp-cyan), transparent);
  opacity: 0;
  transition: opacity 220ms;
}

.btn:hover { border-color: rgba(191, 245, 242, 0.42); color: #FFFFFF; }
.btn:hover::after { opacity: 0.8; }
.btn:active { transform: scale(0.978); }

.btn.small { height: 36px; padding: 0 16px; font-size: 12.5px; }

.btn.primary {
  border-color: rgba(191, 245, 242, 0.34);
  background: linear-gradient(180deg, rgba(191, 245, 242, 0.12), rgba(120, 187, 181, 0.05));
  color: #F2FBFA;
}

.btn.primary::after { opacity: 0.55; }

/* ------------------------------------------------------------- 00 · overture */

/*
  The site opens on the sequence alone and then settles into the page. There is
  one component instance throughout: its host collapses from the full viewport
  to the hero height, so the symbol is continuous and never reloads.

  Everything that is not the stage is held back and arrives afterwards, in the
  order a control surface would come up: chrome, claim, support, actions.
*/
html[data-overture],
html[data-overture] body { overflow: hidden; }

html[data-overture] .hero xcp-hero-animation { --xcp-hero-height: 100svh; }

/* The collapse itself. The host's own min-height is driven by the variable, so
   the transition is declared from outside on the element. */
.hero xcp-hero-animation { transition: min-height 1100ms cubic-bezier(.16, .84, .24, 1); }

html[data-overture] .site-head {
  opacity: 0;
  transform: translateY(-100%);
  pointer-events: none;
}

.site-head {
  transition: opacity 600ms ease-out 140ms, transform 700ms cubic-bezier(.16, .84, .24, 1) 140ms;
}

html[data-overture] main > *:not(.hero) { opacity: 0; }
main > *:not(.hero) { transition: opacity 700ms ease-out 380ms; }

/* The skip affordance. Quiet, and it never covers the object. */
.overture-hint {
  position: fixed;
  left: 50%;
  bottom: clamp(22px, 4svh, 44px);
  transform: translateX(-50%);
  z-index: 30;
  font-family: var(--xcp-font-mono);
  font-size: 10px;
  letter-spacing: 0.28em;
  margin-right: -0.28em;
  text-transform: uppercase;
  color: rgba(220, 226, 227, 0.3);
  pointer-events: none;
  opacity: 0;
  transition: opacity 500ms ease-out;
}

html[data-overture] .overture-hint { opacity: 1; transition-delay: 2.4s; }

/* -------------------------------------------------------- 01 · system wake */

/*
  The animation keeps the top of the first screen and the copy sits under it,
  in flow. An earlier version laid the copy over the component and bottom-
  aligned it, which collided with the wordmark: the component centres its stage
  vertically and the `— X C P —` reveal hangs just below that centre, so the
  lower half of the host is not free space.

  Only the three phase captions stay overlaid, high above the symbol, because
  they have to read as part of the machine rather than as a caption under it.
*/
.hero {
  display: grid;
  grid-template-rows: auto auto auto;
  align-content: start;
  min-height: 100svh;
}

.hero-stage { position: relative; }

/*
  The host height must contain the whole stage, or the animation clips.

  The component sizes its symbol against `100svh` -- it reserves `100svh - 150px`
  for the mark and the remaining band for the `— X C P —` wordmark below it --
  and the host has `overflow: hidden`. An earlier override capped the host at
  `66svh`, well under what the internally 100svh-sized stage needs, so the host
  clipped its own bottom: the wordmark was cut off by up to ~90px on a tall wide
  desktop. Matching the component's own model (100svh, capped) lets the mark and
  wordmark both sit inside the box at every viewport, while the 900px ceiling
  still keeps the hero from swallowing a very tall screen.
*/
.hero xcp-hero-animation {
  --xcp-hero-height: clamp(520px, 100svh, 900px);
  display: block;
}

/*
  The phases sit under the stage, in one row, in reading order. Flanking the
  symbol put them in competition with it; underneath they annotate it, and the
  connectors between them make the row a sequence rather than three separate
  words.
*/
/*
  The captions clear the animation before they start. With no gap they sat on
  the wordmark's ambient glow, which is bright at the bottom of the stage, and
  the row read as washed out. The extra top padding lifts them out of that
  bleed so the sequence is legible on its own.
*/
.hero-phases {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 2.2vw, 26px);
  padding: clamp(40px, 6svh, 72px) var(--xcp-shell) 0;
  pointer-events: none;
}

/* The link between two stages, lit as the flow reaches it. */
.hero-phases .sep {
  display: block;
  width: clamp(18px, 4vw, 54px);
  height: 1px;
  background: var(--xcp-line-strong);
  opacity: 0;
  animation: sep-arrive 700ms ease-out forwards;
}

.hero-phases .s1 { animation-delay: 2.30s; }
.hero-phases .s2 { animation-delay: 4.65s; }

@keyframes sep-arrive {
  from { opacity: 0; transform: scaleX(0.2); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 620px) { .hero-phases .sep { display: none; } }

/*
  Three states, arriving on the animation's own schedule: the links establish
  first, the core spins up next, the verification lock closes last. They are
  captions for what the machine is doing at that instant, not a headline.
*/
/*
  Letter spacing is added after the last character as well as between them, so a
  tracked label carries an invisible tail. Centred, that tail pushes the glyphs
  off axis by half its width; in a row it also makes every gap wider on the left
  of a separator than on the right. The negative right margin removes the tail
  so the box ends where the last glyph does.
*/
.hero-phases span {
  display: block;
  font-family: var(--xcp-font-mono);
  font-size: clamp(11px, 1.35vw, 16px);
  letter-spacing: 0.24em;
  margin-right: -0.24em;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgba(220, 226, 227, 0);
  animation: phase-arrive 900ms ease-out forwards;
}

/*
  Only the middle phase is green. It is the one that names what happens inside
  the boundary, and green on this page means a state asserted on the console.
  What the PC directs and what the protocol verifies both happen outside it, so
  they stay metal.
*/
.hero-phases .p1 { animation-delay: 1.25s; }
.hero-phases .p2 { animation-delay: 3.40s; --phase-tint: var(--xcp-state); }
.hero-phases .p3 { animation-delay: 5.95s; }

/* The tail compensation tracks the spacing through the arrival, otherwise the
   row drifts sideways while the labels settle. */
@keyframes phase-arrive {
  from { color: rgba(220, 226, 227, 0); letter-spacing: 0.44em; margin-right: -0.44em; }
  to   { color: var(--phase-tint, rgba(220, 226, 227, 0.66)); letter-spacing: 0.24em; margin-right: -0.24em; }
}

@media (max-width: 620px) {
  .hero-phases { gap: 8px 16px; }
  .hero-phases span { font-size: 10.5px; letter-spacing: 0.18em; margin-right: -0.18em; }
  @keyframes phase-arrive {
    from { color: rgba(220, 226, 227, 0); letter-spacing: 0.34em; margin-right: -0.34em; }
    to   { color: var(--phase-tint, rgba(220, 226, 227, 0.66)); letter-spacing: 0.18em; margin-right: -0.18em; }
  }
}

.hero-copy {
  display: grid;
  gap: 20px;
  justify-items: center;
  text-align: center;
  padding-top: clamp(18px, 3svh, 40px);
  padding-bottom: clamp(64px, 9svh, 120px);
}

/* The hero eyebrow sits directly under the animation, over the darkest part of
   the page, so it needs more presence than the faint default used deeper down
   where the surrounding contrast is higher. */
.hero-copy .eyebrow { color: var(--xcp-text-secondary); }

/*
  The headline is machined, not printed. It carries the same brushed material as
  the wordmark the animation reveals just above it, with a slow specular pass on
  the same easing the component uses -- a quick sweep, then a long stillness.
  Two grades of metal keep the hierarchy: the claim is lit, the qualifier is the
  same alloy in shadow.
*/
.hero-copy h1 {
  margin: 0;
  font-size: clamp(30px, 4.6vw, 60px);
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1.03;
  max-width: 17ch;
  opacity: 0;
  background:
    linear-gradient(100deg,
      transparent 36%,
      rgba(255, 255, 255, 0.92) 47%,
      transparent 58%) 0 0 / 280% 100% no-repeat,
    linear-gradient(180deg, #FFFFFF 0%, #B4BEC1 42%, #EFF3F3 54%, #77838A 100%) 0 0 / 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  opacity: 1;
  animation: headline-sheen 9.8s 1.2s cubic-bezier(0.2, 0.7, 0.2, 1) infinite;
}

@keyframes headline-sheen {
  0%   { background-position: -180% 0, 0 0; }
  100% { background-position: 180% 0, 0 0; }
}

/* Its own material, so it reads as the quieter half of the same part rather
   than as the tail of the lit one. It also stops the specular pass here. */
.hero-copy h1 em {
  font-style: normal;
  display: block;
  background: linear-gradient(180deg, #C5CDD0 0%, #949FA3 46%, #C8D0D2 56%, #6B767B 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero-copy .support {
  max-width: 62ch;
  font-size: clamp(14px, 1.4vw, 16.5px);
  line-height: 1.6;
  color: var(--xcp-faint);
}

.hero-actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  justify-content: center;
}

/*
  The copy arrives on the handoff rather than on a clock started at page load.
  Timed from load it would drift out of step the moment the overture is skipped,
  or play to an empty screen when there is no overture at all.
*/
.hero-copy > * {
  transition:
    opacity 620ms ease-out,
    transform 760ms cubic-bezier(.16, .84, .24, 1);
}

html[data-overture] .hero-copy > * {
  opacity: 0;
  transform: translateY(18px);
  transition: none;
}

.hero-copy > *:nth-child(1) { transition-delay: 300ms; }
.hero-copy > *:nth-child(2) { transition-delay: 380ms; }
.hero-copy > *:nth-child(3) { transition-delay: 470ms; }
.hero-copy > *:nth-child(4) { transition-delay: 620ms; }
.hero-copy > *:nth-child(5) { transition-delay: 720ms; }

/*
  The thesis arrives last and alone, after a longer beat than the rest. It is
  the one sentence on the page that is an argument rather than a description,
  so it is given the pause and the only rule on this screen.
*/
.hero-copy .thesis {
  margin-top: clamp(6px, 1.4svh, 16px);
  padding-top: clamp(14px, 2svh, 22px);
  border-top: 1px solid var(--xcp-line);
  max-width: 46ch;
  font-size: clamp(15px, 1.6vw, 19px);
  line-height: 1.45;
  letter-spacing: -0.005em;
  color: var(--xcp-silver);
}

/* ---------------------------------------------------------------- sections */

.band { padding: clamp(88px, 12vw, 168px) 0; position: relative; }

.band-head { display: grid; gap: 18px; margin-bottom: clamp(44px, 6vw, 80px); }

/* Every band is separated by the same flow line the hero uses internally, with
   a packet travelling it. The page is stitched by the machine's own device. */
.flow-seam {
  position: relative;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--xcp-line-strong) 22%, var(--xcp-line-strong) 78%, transparent);
  overflow: visible;
}

/*
  The travelling packet is positioned with `left` and stops one packet-width
  short of the right edge. An earlier version translated to 88vw from left: 0,
  which on narrow viewports put the element past the viewport and produced an
  intermittent horizontal scrollbar -- appearing and vanishing with the loop.
  A seam is 1px tall, so it cannot clip its own overflow without also clipping
  the packet vertically; the geometry has to stay inside instead.
*/
.flow-seam::after {
  content: "";
  position: absolute;
  top: -1.5px;
  left: 0;
  width: 74px;
  height: 4px;
  border-radius: 4px;
  background: linear-gradient(90deg, transparent, var(--xcp-cyan), transparent);
  opacity: 0;
  filter: blur(0.6px);
  animation: seam-travel 9s linear infinite;
}

@keyframes seam-travel {
  0%   { left: 0; opacity: 0; }
  12%  { opacity: 0.55; }
  88%  { opacity: 0.55; }
  100% { left: calc(100% - 74px); opacity: 0; }
}

/*
  Seams alternate direction down the page. A single direction repeated at every
  boundary reads as one conveyor running under the whole site; alternating makes
  each seam belong to the pair of sections it separates.
*/
.flow-seam[data-flow="left"]::after { animation-name: seam-travel-left; }

@keyframes seam-travel-left {
  0%   { left: calc(100% - 74px); opacity: 0; }
  12%  { opacity: 0.55; }
  88%  { opacity: 0.55; }
  100% { left: 0; opacity: 0; }
}

/* -------------------------------------------------- 05 · not a jailbreak */

.boundary-figure {
  display: grid;
  grid-template-columns: 1fr 2px 1fr;
  align-items: stretch;
  border: 1px solid var(--xcp-line);
  border-radius: 4px;
  overflow: hidden;
  background: linear-gradient(180deg, rgba(220, 226, 227, 0.022), transparent);
}

@media (max-width: 860px) {
  .boundary-figure { grid-template-columns: 1fr; }
  .boundary-membrane { height: 2px; width: 100%; }
}

.domain { padding: clamp(30px, 4vw, 54px); display: grid; gap: 14px; align-content: start; }
.domain.protected { background: rgba(3, 4, 5, 0.6); }

.domain .tag { display: grid; gap: 5px; }
.domain .tag strong { font-size: 13px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; }
.domain .tag span { font-family: var(--xcp-font-mono); font-size: 11px; color: var(--xcp-faint); letter-spacing: 0.14em; }
.domain p { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--xcp-dim); }

/* The membrane. The only place on the page where a single element is the
   subject, because the boundary is the product. */
.boundary-membrane {
  position: relative;
  background: linear-gradient(180deg, transparent, var(--xcp-line-strong) 14%, var(--xcp-line-strong) 86%, transparent);
}

.boundary-membrane::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 8px;
  height: 92px;
  transform: translate(-50%, -50%);
  border-radius: 4px;
  background: linear-gradient(180deg, transparent, var(--xcp-state), transparent);
  opacity: 0.75;
  filter: blur(0.4px);
}

/* One compact workload crossing, on a slow loop. Nothing else crosses. */
.boundary-membrane::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--xcp-cyan);
  box-shadow: 0 0 14px 3px rgba(191, 245, 242, 0.5);
  animation: cross 6s cubic-bezier(.5,0,.5,1) infinite;
}

@keyframes cross {
  0%, 12%  { transform: translateX(-190px) scale(0.6); opacity: 0; }
  26%      { opacity: 1; }
  46%      { transform: translateX(0) scale(1); opacity: 1; }
  62%      { transform: translateX(0) scale(1); opacity: 1; }
  86%      { transform: translateX(190px) scale(0.6); opacity: 0; }
  100%     { transform: translateX(190px) scale(0.6); opacity: 0; }
}

@media (max-width: 860px) {
  .boundary-membrane::before { width: 92px; height: 8px; }
  .boundary-membrane::after { animation: none; opacity: 0.9; }
}

/* ------------------------------------------------- 03 · what you can do */

.proofs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }

/* The three lifecycle cards carry busy animated diagrams, so they need real
   separation from the section heading above them -- pressed close, the green
   figures read as an extension of the lede rather than as their own row. Set on
   the head's margin rather than the grid's, so it does not collapse against the
   head's existing bottom margin and vanish. */
#modes .band-head { margin-bottom: clamp(60px, 8vw, 116px); }

@media (max-width: 980px) { .proofs { grid-template-columns: 1fr; } }

.proof {
  border: 1px solid var(--xcp-line);
  border-radius: 4px;
  background: linear-gradient(180deg, rgba(220, 226, 227, 0.028), transparent 60%);
  display: grid;
  grid-template-rows: auto 1fr;
  overflow: hidden;
}

.proof figure {
  margin: 0;
  aspect-ratio: 16 / 10;
  border-bottom: 1px solid var(--xcp-line);
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(16, 124, 16, 0.09), transparent 62%),
    var(--xcp-ink-deep);
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
}

/* A faint instrument grid behind every figure, the same device the hero uses,
   so the three panels read as three readouts of one machine. */
.proof figure::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.022) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.018) 1px, transparent 1px);
  background-size: 22px 22px;
  mask-image: radial-gradient(90% 80% at 50% 45%, #000, transparent 82%);
  pointer-events: none;
}

/*
  No fabricated screenshots. Each figure is an instrument reading of the thing
  it names, drawn from the same geometry and lit with the same material as the
  hero: cold silver structure, cyan for movement, teal only where a state is
  actually being asserted.
*/
.diagram { position: relative; width: 100%; height: 100%; display: block; }

/* A readout strip: real parameters of the capability, in the instrument face. */
.readout {
  position: absolute;
  left: 14px;
  bottom: 12px;
  z-index: 2;
  font-family: var(--xcp-font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(220, 226, 227, 0.42);
}

.readout b { color: var(--xcp-state); font-weight: 400; }

/*
  The three panels run on deliberately different cycles. Synchronised loops
  read as one decorative pulse; three instruments each keeping their own time
  read as three things being observed.
*/

/* ------------------------------------------------------- figure 1 · create */

/*
  The frame is drawn before anything is in it, because that is the actual
  order of operations: you declare what you need, and only the declared is
  admitted. The modules then land inside it, one at a time, dimming as they
  go so the panel does not end as a solid green block.
*/
.cr-frame rect {
  stroke-dasharray: 708;
  stroke-dashoffset: 708;
  animation: cr-draw 7.2s cubic-bezier(.4,0,.3,1) infinite;
}

@keyframes cr-draw {
  0%       { stroke-dashoffset: 708; }
  22%      { stroke-dashoffset: 0; }
  92%      { stroke-dashoffset: 0; }
  100%     { stroke-dashoffset: 708; }
}

.cr-mods rect {
  transform-box: fill-box;
  transform-origin: left center;
  opacity: 0;
  animation: cr-land 7.2s cubic-bezier(.24,.9,.3,1) infinite;
}

@keyframes cr-land {
  0%, 24%  { transform: scaleX(0.06); opacity: 0; }
  34%      { transform: scaleX(1); opacity: 1; }
  90%      { transform: scaleX(1); opacity: 1; }
  97%, 100% { transform: scaleX(1); opacity: 0; }
}

.cr-mods .m1 { animation-delay: 0s; }
.cr-mods .m2 { animation-delay: 0.34s; }
.cr-mods .m3 { animation-delay: 0.62s; }
.cr-mods .m4 { animation-delay: 0.88s; }
.cr-mods .m5 { animation-delay: 1.12s; }

/* -------------------------------------------------------- figure 2 · adapt */

/*
  A foreign shape, a crossing, and a frame that fills only as far as the frame
  goes. The offcut is the honest half of the operation, so it is drawn in the
  same weight as everything else and it is amber -- seeing amber on this site
  means something did not make it.
*/
.ad-source { opacity: 0; animation: ad-source-in 6.8s ease-out infinite; }

@keyframes ad-source-in {
  0%       { opacity: 0; }
  10%      { opacity: 1; }
  88%      { opacity: 1; }
  100%     { opacity: 0; }
}

.ad-travel rect {
  transform-box: fill-box;
  animation: ad-cross 6.8s cubic-bezier(.5,0,.4,1) infinite;
}

/* 162 clears the source shape, which ends at 158; 182 stops at the frame edge
   at 196 without touching it. */
@keyframes ad-cross {
  0%, 14%  { transform: translateX(162px); opacity: 0; }
  20%      { transform: translateX(162px); opacity: 0.9; }
  44%      { transform: translateX(182px); opacity: 0.9; }
  50%      { transform: translateX(182px); opacity: 0; }
  100%     { transform: translateX(182px); opacity: 0; }
}

.ad-frame {
  stroke-dasharray: 456;
  stroke-dashoffset: 456;
  animation: ad-frame-draw 6.8s cubic-bezier(.4,0,.3,1) infinite;
}

@keyframes ad-frame-draw {
  0%, 8%   { stroke-dashoffset: 456; }
  30%      { stroke-dashoffset: 0; }
  90%      { stroke-dashoffset: 0; }
  100%     { stroke-dashoffset: 456; }
}

.ad-fill {
  transform-box: fill-box;
  transform-origin: left center;
  animation: ad-fill-in 6.8s cubic-bezier(.3,.85,.3,1) infinite;
}

@keyframes ad-fill-in {
  0%, 42%  { transform: scaleX(0); }
  62%      { transform: scaleX(1); }
  90%      { transform: scaleX(1); }
  100%     { transform: scaleX(0); }
}

.ad-offcut { opacity: 0; animation: ad-offcut-mark 6.8s linear infinite; }

@keyframes ad-offcut-mark {
  0%, 62%  { opacity: 0; }
  70%      { opacity: 1; }
  90%      { opacity: 1; }
  100%     { opacity: 0; }
}

/* ------------------------------------------------------- figure 3 · evolve */

/*
  Revision N is already there. N+1 arrives, most of it is reused, some of it is
  rebuilt, and the two things you changed by hand are carried across last --
  and they are the only elements that stay lit, because they are the reason the
  panel exists.
*/
.ev-reuse path {
  stroke-dasharray: 84;
  stroke-dashoffset: 84;
  animation: ev-line 7.6s cubic-bezier(.4,0,.3,1) infinite;
}

.ev-reuse path:nth-child(1) { animation-delay: 0.10s; }
.ev-reuse path:nth-child(2) { animation-delay: 0.20s; }
.ev-reuse path:nth-child(3) { animation-delay: 0.30s; }
.ev-reuse path:nth-child(4) { animation-delay: 0.40s; }

.ev-rebuild path {
  stroke-dasharray: 84;
  stroke-dashoffset: 84;
  animation: ev-line 7.6s cubic-bezier(.4,0,.3,1) infinite;
  animation-delay: 1.1s;
}

@keyframes ev-line {
  0%, 6%   { stroke-dashoffset: 84; }
  26%      { stroke-dashoffset: 0; }
  92%      { stroke-dashoffset: 0; }
  100%     { stroke-dashoffset: 84; }
}

.ev-overlay { opacity: 0; animation: ev-overlay-carry 7.6s cubic-bezier(.24,.9,.3,1) infinite; }

@keyframes ev-overlay-carry {
  0%, 44%  { opacity: 0; transform: translateX(-14px); }
  58%      { opacity: 1; transform: translateX(0); }
  92%      { opacity: 1; transform: translateX(0); }
  100%     { opacity: 0; transform: translateX(0); }
}

/* =========================================================================
   Shared components
   Used across index.html, studio.html, architecture.html and availability.html.
   Everything below is built from tokens.css plus the landing aliases above; no
   new hue is introduced here.
   ========================================================================= */

/* Keyboard first. Every interactive element must show where focus is, and the
   ring is drawn in cyan because that is this system's colour for movement --
   green would read as an assertion about state. */
a:focus-visible,
button:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--xcp-cyan);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 12px 18px;
  background: var(--xcp-ink-deep);
  border: 1px solid var(--xcp-line-strong);
  color: var(--xcp-silver);
  font-size: 13px;
  text-decoration: none;
}

.skip-link:focus { left: 12px; top: 12px; }

/* The current page in the primary navigation. A lit underline rather than a
   colour change, so it reads as position rather than as status. */
.site-head nav a[aria-current="page"] {
  color: var(--xcp-silver);
  box-shadow: inset 0 -1px 0 0 var(--xcp-state);
}

code {
  font-family: var(--xcp-font-mono);
  font-size: 0.92em;
  color: var(--xcp-silver);
  background: rgba(220, 226, 227, 0.05);
  border: 1px solid var(--xcp-line);
  border-radius: 3px;
  padding: 0.08em 0.36em;
}

.hash {
  font-family: var(--xcp-font-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--xcp-silver);
  overflow-wrap: anywhere;
}

/* ------------------------------------------------- inner-page hero */

/*
  The two inner pages do not carry the animation: it belongs to the home page,
  where it is the first thing that happens. Here the top of the page is
  typographic, and it starts below the fixed chrome rather than under it.
*/
.page-hero {
  padding: calc(60px + clamp(56px, 9vw, 128px)) 0 clamp(40px, 6vw, 72px);
}

.page-hero h1 {
  margin: 14px 0 0;
  font-size: clamp(34px, 5.4vw, 66px);
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1.03;
  text-wrap: balance;
  max-width: 20ch;
}

.page-sub {
  margin: 20px 0 0;
  font-size: clamp(17px, 2vw, 22px);
  color: var(--xcp-silver);
  max-width: 46ch;
  line-height: 1.4;
}

.page-lede {
  margin: 18px 0 0;
  font-size: clamp(15px, 1.5vw, 17px);
  line-height: 1.6;
  color: var(--xcp-dim);
  max-width: 62ch;
}

.page-toc {
  margin-top: clamp(30px, 4vw, 46px);
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  padding-top: 20px;
  border-top: 1px solid var(--xcp-line);
}

.page-toc a {
  font-family: var(--xcp-font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--xcp-faint);
  text-decoration: none;
}

.page-toc a:hover { color: var(--xcp-state); }

.sub-title {
  margin: clamp(44px, 6vw, 72px) 0 18px;
  font-size: clamp(18px, 2vw, 22px);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.prose { margin: 0; font-size: 15px; line-height: 1.65; color: var(--xcp-dim); max-width: 68ch; }

/* --------------------------------------------------------- download CTA */

/*
  A prominent, deliberately disabled download control. It looks like the primary
  action it will become, so a visitor sees exactly where the build will appear,
  but it carries a "coming soon" tag and cannot be actuated -- the release is not
  packaged, and a live-looking button to nothing would break the page's one
  promise, that what it says is checkable.
*/
.download-cta {
  margin-top: clamp(26px, 4vw, 44px);
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}

.download-btn {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  height: 54px;
  padding: 0 26px;
  border: 1px solid rgba(28, 174, 28, 0.5);
  border-radius: 4px;
  background: linear-gradient(180deg, rgba(16, 124, 16, 0.22), rgba(16, 124, 16, 0.06));
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--xcp-silver);
  cursor: not-allowed;
  user-select: none;
}

.download-btn .soon {
  font-family: var(--xcp-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--xcp-state-lit);
  border: 1px solid rgba(53, 212, 53, 0.42);
  border-radius: 999px;
  padding: 3px 10px;
}

.download-cta p { margin: 0; font-size: 13px; color: var(--xcp-faint); line-height: 1.5; max-width: 34ch; }

/* ---------------------------------------------------------- platform note */

/*
  A quiet, factual badge: the Studio is a WinUI 3 desktop app, so it runs on
  Windows only today. The Windows mark sits in the site's own metal, not the
  vendor blue -- it is a statement of scope, not a logo endorsement. "for now"
  is deliberate: the toolchain underneath is not Windows-locked by design, but
  nothing else is measured yet, so the badge claims exactly Windows and no more.
*/
.platform-note {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  padding: 11px 16px;
  border: 1px solid var(--xcp-line-strong);
  border-radius: 4px;
  background: rgba(220, 226, 227, 0.03);
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--xcp-dim);
}

.platform-note .win-glyph {
  flex: none;
  width: 16px;
  height: 16px;
  fill: var(--xcp-metal);
}

.platform-note b { color: var(--xcp-silver); font-weight: 600; }
.platform-note .soft { color: var(--xcp-faint); }

/* --------------------------------------------------------- the loop figure */

/*
  A loop, not a pipeline. The spine descends and the return column climbs back
  to the top, which is the only upward movement on the page -- evidence coming
  back is the thing that makes the next iteration informed rather than a guess,
  so it gets the emphasis and the only travelling light.
*/
.loopfig {
  display: grid;
  grid-template-columns: 1fr 148px;
  gap: 0;
  border-top: 1px solid var(--xcp-line);
  border-bottom: 1px solid var(--xcp-line);
}

.loopfig-spine {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: none;
}

.loopfig-spine li {
  display: grid;
  grid-template-columns: 58px 1fr;
  align-items: baseline;
  gap: 18px;
  padding: 18px 2px;
  border-bottom: 1px solid var(--xcp-line);
}

.loopfig-spine li:last-child { border-bottom: 0; }

.loopfig-spine .n {
  font-family: var(--xcp-font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--xcp-faint);
}

.loopfig-spine .t {
  font-size: clamp(16px, 1.9vw, 21px);
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--xcp-silver);
}

/* The two stages that happen on the console. */
.loopfig-spine li[data-accent] .t { color: #FFFFFF; }
.loopfig-spine li[data-accent] .n { color: var(--xcp-state); }

.loopfig-return {
  position: relative;
  border-left: 1px solid var(--xcp-line);
}

/* The return path itself: a rail up the right edge, an arrowhead at the top. */
.loopfig-return::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 26px;
  bottom: 26px;
  width: 1px;
  background: linear-gradient(180deg, var(--xcp-state), rgba(28, 174, 28, 0.18));
}

.loopfig-return::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 20px;
  width: 7px;
  height: 7px;
  margin-left: -3.5px;
  border-left: 1px solid var(--xcp-state-lit);
  border-top: 1px solid var(--xcp-state-lit);
  transform: rotate(45deg);
}

.loopfig-return .label {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) rotate(180deg);
  writing-mode: vertical-rl;
  font-family: var(--xcp-font-mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--xcp-state);
  background: var(--xcp-ink);
  padding: 12px 0;
}

/* The travelling charge, climbing. */
.loopfig-return .label::before {
  content: "";
  position: absolute;
  left: 50%;
  margin-left: -2px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--xcp-state-lit);
  box-shadow: 0 0 10px 2px rgba(53, 212, 53, 0.55);
  animation: loop-return 4.6s cubic-bezier(.5,0,.5,1) infinite;
}

@keyframes loop-return {
  0%   { top: 100%; opacity: 0; }
  8%   { opacity: 1; }
  86%  { opacity: 1; }
  100% { top: -8%; opacity: 0; }
}

.loopfig-note {
  margin: 26px 0 0;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--xcp-dim);
  max-width: 66ch;
}

@media (max-width: 760px) {
  .loopfig { grid-template-columns: 1fr 62px; }
  .loopfig-return .label { font-size: 9px; letter-spacing: 0.14em; }
}

/* --------------------------------------------------------------- shots */

/*
  A real screenshot, shown at a size where it can be read. It is set on a bevel
  edge rather than in a floating card with a shadow: the product is a piece of
  machined software, not a photograph of one, and a mockup frame would be a
  decoration standing between the reader and the thing.

  No perspective, no rounded device bezel, no gradient overlay. Nothing that
  changes what the pixels say.
*/
.shot {
  margin: 0;
  display: grid;
  gap: 14px;
}

.shot picture { display: block; }

.shot img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--xcp-line-strong);
  border-radius: 4px;
  background: var(--xcp-ink-deep);
}

.shot figcaption {
  font-family: var(--xcp-font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--xcp-faint);
  line-height: 1.6;
  max-width: 74ch;
}

.shot-support {
  margin: 26px 0 0;
  font-size: clamp(15px, 1.6vw, 17px);
  line-height: 1.6;
  color: var(--xcp-dim);
  max-width: 62ch;
}

/* ---------------------------------------------------------- zoomable shots */

/*
  A shot that can be enlarged says so. The cursor changes, a small corner
  affordance appears, and the edge lifts to cyan on hover -- the same colour the
  page uses for "this can move", never green, because opening a viewer asserts
  nothing about the product.
*/
.shot[data-zoom] { cursor: zoom-in; }

.shot[data-zoom] picture {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 4px;
}

.shot[data-zoom] img { transition: transform 400ms cubic-bezier(.2,.7,.3,1), border-color 200ms; }

.shot[data-zoom]:hover img,
.shot[data-zoom]:focus-visible img {
  transform: scale(1.012);
  border-color: rgba(191, 245, 242, 0.5);
}

.shot[data-zoom]:focus-visible { outline: none; }
.shot[data-zoom]:focus-visible img { outline: 2px solid var(--xcp-cyan); outline-offset: 2px; }

/* The corner glyph: a small expand mark, lit on hover, that tells a reader the
   image opens rather than links away. */
.shot[data-zoom] picture::after {
  content: "";
  position: absolute;
  top: 12px;
  right: 12px;
  width: 30px;
  height: 30px;
  border-radius: 4px;
  background:
    linear-gradient(180deg, rgba(6, 8, 9, 0.82), rgba(6, 8, 9, 0.62));
  border: 1px solid rgba(191, 245, 242, 0.28);
  opacity: 0;
  transition: opacity 220ms;
  /* a plus/expand mark drawn with two bars */
  background-image:
    linear-gradient(var(--xcp-cyan), var(--xcp-cyan)),
    linear-gradient(var(--xcp-cyan), var(--xcp-cyan)),
    linear-gradient(180deg, rgba(6, 8, 9, 0.82), rgba(6, 8, 9, 0.62));
  background-size: 12px 1.5px, 1.5px 12px, 100% 100%;
  background-position: center, center, center;
  background-repeat: no-repeat;
}

.shot[data-zoom]:hover picture::after,
.shot[data-zoom]:focus-visible picture::after { opacity: 0.9; }

@media (max-width: 620px) {
  /* Touch has no hover, so the affordance is always on. */
  .shot[data-zoom] picture::after { opacity: 0.72; }
}

/* --------------------------------------------------------------- lightbox */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: clamp(16px, 4vw, 56px);
  background: rgba(3, 4, 5, 0.86);
  backdrop-filter: blur(8px);
  opacity: 0;
  transition: opacity 260ms ease-out;
  cursor: zoom-out;
}

/*
  `display: grid` above outranks the user-agent `[hidden] { display: none }`
  rule, so without this the closed overlay stays laid out -- invisible at
  opacity 0 but full-screen and on top, swallowing every subsequent click so no
  image could be opened a second time. This restores the hide.
*/
.lightbox[hidden] { display: none; }

.lightbox[data-open] { opacity: 1; }

.lightbox-frame {
  position: relative;
  display: grid;
  gap: 14px;
  justify-items: center;
  max-width: min(1600px, 100%);
  cursor: default;
  transform: scale(0.985);
  transition: transform 260ms cubic-bezier(.2,.7,.3,1);
}

.lightbox[data-open] .lightbox-frame { transform: none; }

.lightbox-img {
  display: block;
  max-width: 100%;
  max-height: 82vh;
  width: auto;
  height: auto;
  border: 1px solid var(--xcp-line-strong);
  border-radius: 5px;
  background: var(--xcp-ink-deep);
  box-shadow: 0 40px 120px rgba(0, 0, 0, 0.6);
}

.lightbox-cap {
  margin: 0;
  font-family: var(--xcp-font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--xcp-faint);
  text-align: center;
  max-width: 74ch;
}

.lightbox-close {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 1px solid var(--xcp-line-strong);
  border-radius: 50%;
  background: var(--xcp-ink-deep);
  color: var(--xcp-silver);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 180ms, color 180ms;
}

.lightbox-close:hover { border-color: rgba(191, 245, 242, 0.5); color: #FFFFFF; }

@media (max-width: 620px) {
  .lightbox-close { top: -8px; right: 0; }
  .lightbox-img { max-height: 74vh; }
}

@media (prefers-reduced-motion: reduce) {
  .lightbox, .lightbox-frame, .shot[data-zoom] img { transition: none; }
  .lightbox-frame { transform: none; }
}

/* --------------------------------------------------------------- routes */

/*
  The two entry routes, stated before either case is shown, so the cases below
  read as instances of a rule rather than as two products.
*/
.routes {
  list-style: none;
  margin: 0 0 clamp(34px, 4vw, 52px);
  padding: 0;
  display: grid;
  gap: 10px;
}

.routes li {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 14px 18px;
  border: 1px solid var(--xcp-line);
  border-radius: 3px;
  font-family: var(--xcp-font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.routes .from { color: var(--xcp-dim); min-width: 7ch; }
.routes .via { color: var(--xcp-state); min-width: 7ch; }
.routes .to { color: var(--xcp-silver); }

.routes i {
  width: 26px;
  height: 1px;
  background: var(--xcp-line-strong);
  position: relative;
}

.routes i::after {
  content: "";
  position: absolute;
  right: 0;
  top: -2px;
  width: 5px;
  height: 5px;
  border-top: 1px solid var(--xcp-line-strong);
  border-right: 1px solid var(--xcp-line-strong);
  transform: rotate(45deg);
}

/* ---------------------------------------------------------------- cases */

.cases {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(28px, 4vw, 56px);
}

@media (max-width: 900px) { .cases { grid-template-columns: 1fr; } }

.case { display: grid; gap: 14px; align-content: start; }

.case h3 {
  margin: 0;
  font-size: clamp(20px, 2.2vw, 26px);
  font-weight: 600;
  letter-spacing: -0.025em;
}

.case > p { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--xcp-dim); }

.case .shot { margin-top: 6px; }

.case-note {
  font-size: 13px;
  line-height: 1.6;
  color: var(--xcp-faint);
  border-top: 1px solid var(--xcp-line);
  padding-top: 12px;
}

/* The qualification is not a footnote in smaller type than the claim it
   qualifies. Same size, one lit edge, so it is read rather than skipped. */
.case-note[data-qualified] {
  border-top-color: rgba(224, 166, 58, 0.42);
  color: var(--xcp-dim);
}

.cases-bridge {
  margin: clamp(34px, 4vw, 52px) 0 0;
  padding-top: 26px;
  border-top: 1px solid var(--xcp-line);
  font-size: clamp(15px, 1.6vw, 17px);
  line-height: 1.6;
  color: var(--xcp-dim);
  max-width: 68ch;
}

.cases-bridge a { color: var(--xcp-state); }

/* ------------------------------------------------------------- proof cards */

.proofline {
  font-family: var(--xcp-font-mono);
  font-size: 11.5px;
  line-height: 1.5;
  letter-spacing: 0.04em;
  color: var(--xcp-state);
}

/* ---------------------------------------------------------- categories row */

.categories {
  margin-top: clamp(40px, 5vw, 64px);
  padding-top: 26px;
  border-top: 1px solid var(--xcp-line);
}

.categories-lead { margin: 0 0 16px; font-size: 14px; color: var(--xcp-faint); }

.categories ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.categories li {
  font-size: 13.5px;
  color: var(--xcp-dim);
  border: 1px solid var(--xcp-line-strong);
  border-radius: 3px;
  padding: 7px 14px;
}

/* The first chip is the one everybody assumes is the whole product. */
.categories li:first-child { color: var(--xcp-silver); border-color: rgba(28, 174, 28, 0.42); }

/* -------------------------------------------------------------- proof points */

/*
  Seven points do not divide evenly into a responsive column count, so the last
  row is short. The separators are therefore drawn on the cells themselves
  rather than as a background showing through 1px gaps: with the background
  technique the leftover grid area renders as a filled block and reads as an
  empty card that failed to load.
*/
.points {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1px;
}

.points li {
  background: var(--xcp-ink);
  box-shadow: 0 0 0 1px var(--xcp-line);
}

.points a {
  display: grid;
  gap: 8px;
  padding: 26px 24px;
  text-decoration: none;
  height: 100%;
  align-content: start;
  transition: background 180ms;
}

.points a:hover { background: rgba(220, 226, 227, 0.03); }

.points .k {
  font-size: clamp(20px, 2.4vw, 28px);
  font-weight: 600;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--xcp-silver);
}

.points .v { font-size: 13.5px; line-height: 1.5; color: var(--xcp-faint); }

/* --------------------------------------------------------- role convergence */

/*
  Three roles arriving at one file. Drawn as a convergence because the claim is
  that there is no second path -- three parallel cards would say the opposite.
*/
.converge {
  display: grid;
  grid-template-columns: 1fr 90px 1fr;
  align-items: center;
  gap: 0;
  border: 1px solid var(--xcp-line);
}

.converge-roles { list-style: none; margin: 0; padding: 0; }

.converge-roles li {
  display: grid;
  gap: 5px;
  padding: 22px 24px;
  border-bottom: 1px solid var(--xcp-line);
}

.converge-roles li:last-child { border-bottom: 0; }

.converge-roles .role {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--xcp-silver);
}

.converge-roles .desc { font-size: 13px; line-height: 1.5; color: var(--xcp-faint); }

/* Three lines meeting at one point on the right. */
.converge-arrows {
  position: relative;
  align-self: stretch;
  background:
    linear-gradient(to right, transparent calc(50% - 0.5px), var(--xcp-line-strong) calc(50% - 0.5px), var(--xcp-line-strong) calc(50% + 0.5px), transparent calc(50% + 0.5px));
}

.converge-arrows::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 50%;
  height: 1px;
  background: var(--xcp-state);
}

.converge-target {
  display: grid;
  gap: 8px;
  padding: 26px 24px;
  background: var(--xcp-ink-deep);
  border-left: 1px solid var(--xcp-line);
  align-content: center;
  height: 100%;
}

.converge-target .file {
  font-family: var(--xcp-font-mono);
  font-size: clamp(14px, 1.6vw, 17px);
  color: var(--xcp-state-lit);
  overflow-wrap: anywhere;
}

.converge-target .note { font-size: 12.5px; color: var(--xcp-faint); }

.converge-note { margin: 22px 0 0; font-size: 14.5px; color: var(--xcp-dim); }

@media (max-width: 820px) {
  .converge { grid-template-columns: 1fr; }
  .converge-arrows { height: 34px; background: linear-gradient(to bottom, transparent calc(50% - 0.5px), var(--xcp-line-strong) calc(50% - 0.5px), var(--xcp-line-strong) calc(50% + 0.5px), transparent calc(50% + 0.5px)); }
  .converge-arrows::after { display: none; }
  .converge-target { border-left: 0; border-top: 1px solid var(--xcp-line); }
}

.split-note,
.activate-support {
  margin: 26px auto 0;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--xcp-faint);
  max-width: 64ch;
}

.activate-support { text-align: center; }

/* ------------------------------------------------------------------ chains */

/*
  A pipeline as real text, not an image of one: it has to be readable by a
  crawler and by a screen reader, and it has to survive being copied into a
  message. The accent marks the stages where the interesting work happens.
*/
.chain {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 10px;
  counter-reset: chain;
}

.chain li {
  position: relative;
  display: flex;
  align-items: center;
  padding: 14px 18px;
  border: 1px solid var(--xcp-line-strong);
  border-radius: 3px;
  background: linear-gradient(180deg, rgba(220, 226, 227, 0.035), transparent);
  font-size: 14px;
  color: var(--xcp-dim);
}

.chain li[data-accent] {
  border-color: rgba(28, 174, 28, 0.45);
  color: var(--xcp-silver);
  background: linear-gradient(180deg, rgba(16, 124, 16, 0.12), transparent);
}

/* The connector. Drawn between items rather than after the last one, so the
   chain never ends in an arrow pointing at nothing. */
.chain li + li::before {
  content: "";
  position: absolute;
  left: -10px;
  top: 50%;
  width: 10px;
  height: 1px;
  background: var(--xcp-line-strong);
}

.chain.vertical { flex-direction: column; align-items: stretch; gap: 12px; }

.chain.vertical li + li::before {
  left: 26px;
  top: -12px;
  width: 1px;
  height: 12px;
}

.chain-note {
  margin: 26px 0 0;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--xcp-dim);
  max-width: 66ch;
}

/* ------------------------------------------------------------- stage list */

.stages { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--xcp-line); }

.stages li {
  display: grid;
  grid-template-columns: 160px 1fr;
  gap: clamp(14px, 3vw, 36px);
  align-items: baseline;
  padding: 15px 2px;
  border-bottom: 1px solid var(--xcp-line);
}

.stages .s {
  font-family: var(--xcp-font-mono);
  font-size: 13px;
  color: var(--xcp-state);
  letter-spacing: 0.04em;
}

.stages .d { font-size: 14px; line-height: 1.5; color: var(--xcp-dim); }

@media (max-width: 700px) {
  .stages li { grid-template-columns: 1fr; gap: 5px; }
}

/* --------------------------------------------------------- evidence layers */

.layers { list-style: none; margin: 0; padding: 0; counter-reset: layer; }

.layers li {
  display: grid;
  grid-template-columns: 48px 260px 1fr;
  gap: clamp(14px, 2.6vw, 32px);
  align-items: baseline;
  padding: 17px 2px;
  border-bottom: 1px solid var(--xcp-line);
}

.layers li:first-child { border-top: 1px solid var(--xcp-line); }

.layers .n {
  font-family: var(--xcp-font-mono);
  font-size: 11px;
  color: var(--xcp-faint);
}

.layers .t { font-size: 15px; font-weight: 600; color: var(--xcp-silver); }
.layers .d { font-size: 13.5px; line-height: 1.5; color: var(--xcp-faint); }

@media (max-width: 860px) {
  .layers li { grid-template-columns: 40px 1fr; }
  .layers .d { grid-column: 2; }
}

/* ------------------------------------------------------------- card grids */

/* Same reason as .points: the card count rarely fills the last row, and a
   background showing through the gaps turns the leftover area into a blank
   card. The hairline belongs to the cell. */
.grid-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1px;
}

.grid-cards > div {
  background: var(--xcp-ink);
  box-shadow: 0 0 0 1px var(--xcp-line);
  padding: 24px 22px;
  display: grid;
  gap: 9px;
  align-content: start;
}

.grid-cards h3 { margin: 0; font-size: 15.5px; font-weight: 600; letter-spacing: -0.01em; color: var(--xcp-silver); }
.grid-cards p { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--xcp-faint); }

.grid-cards.dense > div { padding: 22px 20px; }

/* ------------------------------------------------------------ split columns */

.split-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: clamp(28px, 4vw, 64px);
  margin-top: clamp(30px, 4vw, 48px);
}

.split-cols h3 { margin: 0 0 12px; font-size: 17px; font-weight: 600; letter-spacing: -0.015em; }
.split-cols p { margin: 0 0 12px; font-size: 14.5px; line-height: 1.6; color: var(--xcp-dim); }

.ticks { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }

.ticks li {
  position: relative;
  padding-left: 20px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--xcp-dim);
}

.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  width: 7px;
  height: 7px;
  border: 1px solid var(--xcp-state);
  transform: rotate(45deg);
}

.ticks.wide { gap: 14px; max-width: 74ch; }
.ticks.wide li { font-size: 15px; }

/* ---------------------------------------------------------------- callout */

/*
  Used for the statements that are qualifications rather than claims. It is a
  quiet strip, not a warning panel: these are boundaries, and a boundary stated
  calmly is more credible than one stated in a coloured box.
*/
.callout {
  margin-top: clamp(30px, 4vw, 46px);
  display: flex;
  gap: clamp(14px, 3vw, 26px);
  align-items: baseline;
  flex-wrap: wrap;
  padding: 22px 24px;
  border: 1px solid var(--xcp-line);
  border-left: 2px solid rgba(28, 174, 28, 0.5);
  background: rgba(220, 226, 227, 0.022);
}

.callout p { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--xcp-dim); max-width: 66ch; }
.callout a { color: var(--xcp-state); }

/* ------------------------------------------------------------- spec tables */

/*
  A definition list rather than a table: these are one-to-one facts, they wrap
  well on a phone, and digests are printed in full. A digest shortened with an
  ellipsis cannot be compared against anything, which defeats the purpose of
  publishing it.
*/
.spec { display: grid; border-top: 1px solid var(--xcp-line); margin: 0; }

.spec > div {
  display: grid;
  grid-template-columns: 250px 1fr;
  gap: clamp(14px, 3vw, 36px);
  padding: 14px 0;
  border-bottom: 1px solid var(--xcp-line);
  align-items: baseline;
}

.spec dt {
  font-family: var(--xcp-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--xcp-faint);
}

.spec dd { margin: 0; font-size: 14px; line-height: 1.55; color: var(--xcp-dim); }
.spec dd.hash { color: var(--xcp-silver); }

@media (max-width: 760px) {
  .spec > div { grid-template-columns: 1fr; gap: 6px; }
}

/* --------------------------------------------------------------------- FAQ */

.faq { margin: 0; display: grid; border-top: 1px solid var(--xcp-line); }

.faq > div { padding: 24px 0; border-bottom: 1px solid var(--xcp-line); display: grid; gap: 10px; }

.faq dt { font-size: clamp(16px, 1.8vw, 19px); font-weight: 600; letter-spacing: -0.015em; color: var(--xcp-silver); }
.faq dd { margin: 0; font-size: 14.5px; line-height: 1.65; color: var(--xcp-dim); max-width: 70ch; }

/* ------------------------ the limit rows · used by 04 and 05 */

.limits { display: grid; gap: 0; border-top: 1px solid var(--xcp-line); }

.limit {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  align-items: center;
  gap: 20px;
  padding: 22px 4px;
  border-bottom: 1px solid var(--xcp-line);
}

.limit .glyph {
  width: 22px;
  height: 22px;
  border: 1px solid rgba(28, 174, 28, 0.55);
  border-radius: 2px;
  position: relative;
  justify-self: center;
}

.limit .glyph::after {
  content: "";
  position: absolute;
  inset: 4px;
  background: var(--xcp-state);
  opacity: 0.65;
}

.limit .name {
  font-family: var(--xcp-font-mono);
  font-size: clamp(13px, 1.5vw, 16px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.limit .verdict {
  font-family: var(--xcp-font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  color: var(--xcp-state);
}

.limits-close {
  margin-top: clamp(28px, 4vw, 48px);
  font-size: clamp(17px, 2vw, 23px);
  line-height: 1.45;
  color: var(--xcp-silver);
  max-width: 46ch;
}

/* ------------------------------------------------------ 06 · measured evidence */

/* Monumental, not a KPI grid: the figures are set at display size and given
   room, because the section's whole argument is that they were measured. */
.evidence { display: grid; gap: clamp(40px, 5vw, 72px); }

.figures { display: grid; gap: clamp(28px, 4vw, 56px); }

.figure-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(18px, 4vw, 60px);
  align-items: baseline;
  padding-bottom: clamp(22px, 3vw, 38px);
  border-bottom: 1px solid var(--xcp-line);
}

@media (max-width: 800px) { .figure-row { grid-template-columns: 1fr; gap: 10px; } }

.figure-row .value {
  font-size: clamp(34px, 6.4vw, 78px);
  font-weight: 600;
  letter-spacing: -0.045em;
  line-height: 0.98;
  font-variant-numeric: tabular-nums;
  background: linear-gradient(180deg, #FFFFFF 0%, #C4CCCE 52%, #7E8B8D 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.figure-row .value small { font-size: 0.36em; letter-spacing: 0.02em; margin-left: 0.3em; -webkit-text-fill-color: var(--xcp-faint); }

.figure-row .about { display: grid; gap: 8px; }
.figure-row .about strong { font-size: 15px; font-weight: 600; }
.figure-row .about p { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--xcp-faint); }
.figure-row .about .src { font-family: var(--xcp-font-mono); font-size: 11px; color: var(--xcp-state); letter-spacing: 0.08em; }

/* ----------------------------------------------------- 07 · system active */

.activate {
  text-align: center;
  display: grid;
  justify-items: center;
  gap: 26px;
  padding: clamp(96px, 14vw, 190px) 0;
  position: relative;
}

.activate h2 {
  font-size: clamp(32px, 5.6vw, 68px);
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1.04;
  max-width: 17ch;
  margin: 0;
}

.activate .status {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--xcp-font-mono);
  font-size: 11px;
  letter-spacing: 0.24em;
  padding-right: 0;
  margin-right: -0.24em;
  text-transform: uppercase;
  color: var(--xcp-state);
}

.activate .status i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--xcp-state);
  box-shadow: 0 0 10px 2px rgba(28, 174, 28, 0.60);
  animation: pulse-dot 2.6s ease-in-out infinite;
}

@keyframes pulse-dot {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 1; }
}

/*
  The closing section states that there is nothing to download yet. It is set as
  a sentence rather than as a warning panel: an amber box here would read as a
  fault, and this is not a fault, it is a date that has not arrived. The badge
  carries the state and the prose carries the reason.
*/
.get-state {
  margin: 0;
  max-width: 62ch;
  font-size: 14.5px;
  line-height: 1.65;
  color: var(--xcp-faint);
  text-wrap: pretty;
}

.get-state .state {
  display: inline-block;
  margin-right: 12px;
  vertical-align: baseline;
}

/* ------------------------------------------------------------------ footer */

.site-foot { border-top: 1px solid var(--xcp-line); padding: 56px 0 76px; }
.site-foot .rows { display: flex; flex-wrap: wrap; gap: 28px; justify-content: space-between; }
.site-foot p { margin: 0; font-size: 12.5px; color: var(--xcp-faint); max-width: 62ch; line-height: 1.6; }
.site-foot nav { display: flex; gap: 22px; flex-wrap: wrap; }
.site-foot nav a { font-size: 12.5px; color: var(--xcp-dim); text-decoration: none; }
.site-foot nav a:hover { color: var(--xcp-silver); }

/* ------------------------------------------------------------ reduced motion */

@media (prefers-reduced-motion: reduce) {
  .hero-phases span,
  .hero-copy .support,
  .hero-actions {
    animation: none;
    opacity: 1;
    color: rgba(220, 226, 227, 0.62);
  }

  .hero-phases .sep { animation: none; opacity: 1; transform: none; }

  /* The headline keeps its material and loses only the specular pass: the metal
     is the identity, the sweep is the motion. */
  .hero-copy h1 {
    animation: none;
    opacity: 1;
    background-position: 180% 0, 0 0;
  }

  /* No overture at all: the flag is never set, so nothing here has to undo it.
     These only guarantee the settled state if a stale attribute survives. */
  .hero xcp-hero-animation,
  .site-head,
  main > *:not(.hero),
  .hero-copy > * { transition: none; }
  .overture-hint { display: none; }
  .hero-phases .p2 { color: var(--xcp-state); }
  .flow-seam::after,
  .boundary-membrane::after,
  .loopfig-return .label::before,
  .activate .status i { animation: none; }

  /* The returning charge is the point of the loop figure, so without motion it
     rests at the top of its rail rather than disappearing. */
  .loopfig-return .label::before { top: 4%; opacity: 1; }

  /*
    The three figures fall back to their finished state rather than to an empty
    frame: the declared frame full, the foreign shape conformed with its offcut
    still marked, and the later revision reconciled with both overlays carried
    across. Each panel must still explain itself with no motion at all, which is
    also the state it should be read in.
  */
  .cr-frame rect,
  .cr-mods rect,
  .ad-source,
  .ad-travel rect,
  .ad-frame,
  .ad-fill,
  .ad-offcut,
  .ev-reuse path,
  .ev-rebuild path,
  .ev-overlay { animation: none; }

  .cr-frame rect { stroke-dashoffset: 0; }
  .cr-mods rect { opacity: 1; transform: none; }
  .ad-source { opacity: 1; }
  .ad-travel rect { opacity: 0; }
  .ad-frame { stroke-dashoffset: 0; }
  .ad-fill { transform: none; }
  .ad-offcut { opacity: 1; }
  .ev-reuse path,
  .ev-rebuild path { stroke-dashoffset: 0; }
  .ev-overlay { opacity: 1; transform: none; }
  .boundary-membrane::after { transform: none; opacity: 1; }
  .btn { transition: none; }
}
