/*
 * fusion (IVW-023). Variant overrides, loaded after tokens.css,
 * tokens-light.css, base.css and motion.css and before the page group's
 * pages-<group>.css. Every colour, size, spacing and duration below is a
 * var(--...) from shared/styles/tokens.css or tokens-light.css, or a
 * calc() of one; the file declares no custom property and no literal
 * colour.
 *
 * Concept (E-008, D-008): vercel-clean is the base register, ledger-light
 * is the technical furniture, and a page opts into the furniture by
 * class.
 *
 *   Base register (from vercel-clean): white --bg-0, near-black display
 *   type with tracking tightened by a fixed factor of each step's own
 *   token, hairline --bg-3 rules, bordered cards with no shadow,
 *   generous air, one filled orange CTA per screen (the nav CTA stays
 *   outlined per base.css), underline draw on links, a border glint on
 *   cards, soft rise reveals with two decaying overshoots.
 *
 *   Technical furniture (from ledger-light), opt-in by class:
 *     .fu-technical   a .section in the technical register: white
 *                     surface between two hairlines (the grid was
 *                     removed in IVW-032), section label ruled with
 *                     a hairline that draws on reveal
 *     .cp             compiler pane: title bar, tag, code, verdict,
 *                     caption under, note as an aside
 *     .ledger         numbered proof rows: claim, optional property,
 *                     source; three to five rows
 *     .fu-log         the rotation.log storyboard treatment, driven by
 *                     the container's data-frame-active attribute
 *     .table--hairline the hairline table (stacks under 40rem via the
 *                     shared <dl> parity markup)
 *     .card--hairline white card, one hairline, glint on hover
 *     .fu-card        the interior of every card grid (teasers, sector
 *                     cards, tickets, tiers); .fu-card-grid--2 and --3
 *     .label-mono     small uppercase mono label; every section label
 *                     is ruled with a hairline that draws on reveal
 *
 * Type: IBM Plex Sans for display and body; IBM Plex Mono for labels,
 * ledger, code and the log. Orange text under 24px is --brand-text
 * (TOKENS.md section 6); --brand is a fill, rule or marker only.
 *
 * Motion (all CSS, event-linked, each with a reduced-motion static state
 * at the bottom of this file; scoped by data-motion-variant="fusion" on
 * <html> where it replaces a shared entry's visual treatment):
 *   1. fu-rise          fade-and-rise with two decaying overshoots on the
 *                       shared hero-reveal and section-reveal hooks
 *   2. fu-glint         a border glint that travels once along a
 *                       .card--hairline edge on hover or focus-within
 *   3. .fu-link         underline draw on hover and focus
 *   4. fu-row-settle    ledger rows, compiler verdicts and log entries
 *                       settle in with two decaying overshoots
 *   5. fu-hairline-draw every section label's rule draws on reveal
 *   6. fu-band-shift    a slow gradient shift on the closing band
 * Nothing rotates as a whole element: nothing in this file rotates.
 */

/* -----------------------------------------------------------------------
 * 0. Type. Tracking is tightened relative to the shared scale: the 4xl
 * and 3xl steps run at 1.6 times their own token, the 2xl at 1.5.
 * --------------------------------------------------------------------- */

h1,
.hero__headline,
.page-title {
  font-weight: 600;
  letter-spacing: calc(var(--tracking-4xl) * 1.6);
}

.page-title {
  font-size: var(--text-3xl);
  line-height: var(--leading-3xl);
  letter-spacing: calc(var(--tracking-3xl) * 1.6);
}

h2,
.section-header__heading,
.cta-band__heading {
  font-weight: 600;
  letter-spacing: calc(var(--tracking-2xl) * 1.5);
}

/* Display type balances its lines so no heading, card title, claim
 * value or proof line ends on a single word (coherence round 4, C4-6:
 * 21 one-word last lines at 390 and 16 at 1280 before), and a small
 * label that wraps breaks between words, not at a compound's hyphen
 * (C5-2: "Defense C2 and cross-domain" in a 315px card). A progressive
 * enhancement: an engine without text-wrap keeps the plain wrap. */
h1,
h2,
h3,
.claim-card__value,
.fu-proof__item p,
.label-mono {
  text-wrap: balance;
}

h3 {
  font-weight: 600;
}

/* A hyphenated compound in display type ("Phishing-resistant",
 * "machine-checked", "compile-time") is one word to the reader, but a
 * hard hyphen is a break opportunity no CSS property withdraws, and the
 * balanced wrap took it (IVW-059: five headings, claims and scale
 * figures split at their hyphen between 360 and 834). The compound is
 * wrapped in this span in the markup; the hyphen in running prose is
 * left alone. */
.fu-nobreak {
  white-space: nowrap;
}

/* Inline code inside prose: the ledger-light chip (mono on --bg-2). */
.fu-prose code,
.fu-body code,
.fu-lead code,
.cp__note code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  color: var(--fg);
  background: var(--bg-2);
  padding: 0 0.3em;
  border-radius: var(--space-1);
}

/* Skip link: hidden until focused. */
.fu-skip {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 50;
  padding: var(--space-2) var(--space-4);
  background: var(--bg-0);
  color: var(--fg);
  border: 1px solid var(--fg);
  border-radius: var(--space-2);
  transform: translateY(calc(-100% - var(--space-4)));
}

.fu-skip:focus-visible {
  transform: none;
}

/* -----------------------------------------------------------------------
 * 1. Containers, measure and rhythm. Page-level spacing is the shared
 * padding rhythm from base.css; this block decides the one width each
 * page type runs, adds the hairline between sections, the home page's
 * extra air from 48rem and the reading measure.
 * --------------------------------------------------------------------- */

/* One width per page type (D-009). <body> carries fu-page--home on the
 * home page and fu-page--interior on every other route, and that class
 * decides every block's width here and nowhere else: the home page's
 * hero, sections and band run --container-xl (80rem); every other
 * page's header, sections and band run --container-lg (64rem). The nav
 * and the footer are site chrome and keep the shared 80rem on every
 * page. No page names a container modifier. The reading measure (68ch,
 * left-aligned to the column) and the figure cap below are content caps
 * inside the column, not a third width. */
.fu-page--home :is(.hero, .section, .cta-band) > .container {
  max-width: var(--container-xl);
}

.fu-page--interior :is(.page-header, .section, .cta-band) > .container {
  max-width: var(--container-lg);
}

/* Sections are separated by one hairline (a border, never a margin). */
.fu-section {
  border-top: 1px solid var(--bg-3);
}

/* Below 48rem the hero and the home sections keep the shared rhythm
 * (--space-12, base.css); from 48rem the hero and the home sections
 * take one and a half steps, above and below alike (round 4
 * refinement 1: the hero ran 96 over 64 before). Top and bottom padding
 * are always equal, and a visually hidden heading (the first child of a
 * home or technology container) takes none of the owl margin, so the
 * air above a section's first row equals the air below its last row. */
@media (min-width: 48rem) {
  .fu-hero,
  .fu-home-section {
    padding-block: calc(var(--space-16) * 1.5);
  }
}

.section > .container > .u-visually-hidden + * {
  margin-top: 0;
}

/* The reading measure, and the same 68ch on a figure or card inside a
 * column (the technology pages' 4:3 figures, the diagram frame, the
 * claim cards), so a deep page shows one right edge for prose, cards
 * and figures (round 4 refinement 3; the cap was 48rem before). */
.fu-prose p,
.fu-body,
.fu-measure {
  max-width: 68ch;
}

/* One tight step (--space-4) wherever two parts of one reading pair
 * stack: prose paragraphs, a lead or summary and its body, a note under
 * the pane it explains, and two diagnostics that read as one listing.
 * Inside a section the shared owl selector's --space-6 is replaced, not
 * added. */
.fu-prose p + p,
.fu-body + .fu-body,
.fu-lead + .fu-body,
.fu-summary + .fu-body,
.section > .container > .cp__note,
.section > .container > .diagnostic-block + .diagnostic-block {
  margin-top: var(--space-4);
}

/* A note closes one example; the next pane or diagnostic opens another,
 * so that step is one wider than the pane-to-note pairing. */
.section > .container > .cp__note + .cp,
.section > .container > .cp__note + .diagnostic-block {
  margin-top: var(--space-10);
}

.fu-lead {
  font-size: var(--text-lg);
  line-height: var(--leading-lg);
  color: var(--muted);
  max-width: 60ch;
}

.fu-summary {
  font-size: var(--text-lg);
  line-height: var(--leading-lg);
  font-weight: 400; /* founder 2026-10-13: summaries read too bold; regular weight, full colour */
  color: var(--fg);
  max-width: 60ch;
}

.fu-page-header .page-title {
  max-width: 20ch;
}

.fu-page-header > .container > * + * {
  margin-top: var(--space-6);
}

.fu-page-header .img-placeholder {
  margin-top: var(--space-12);
}

/* The header carries its own air above the first section's hairline
 * (base.css gives .page-header padding above only). */
.fu-page-header {
  padding-bottom: var(--space-12);
}

@media (min-width: 48rem) {
  .fu-page-header {
    padding-bottom: var(--space-16);
  }
}

/* Prose beside a placeholder frame or the contact form. */
.fu-split {
  display: grid;
  gap: var(--space-8);
  align-items: start;
}

@media (min-width: 48rem) {
  .fu-split {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    column-gap: var(--space-12);
  }
}

/* -----------------------------------------------------------------------
 * 2. Nav: white bar, hairline, inline mono wordmark, underline-draw
 * links, outlined CTA that fills near-black on hover. No glow anywhere.
 * --------------------------------------------------------------------- */

.nav__logo {
  display: block;
  width: 7.5rem;
  height: auto;
  color: var(--fg);
}

.nav__link {
  color: var(--muted);
  /* Founder, 2026-10-01: the link text sat 3.5px lower than the brand
   * and the CTA on desktop. The link is a 44px flex box centring its
   * text, but the underline draw (.fu-link) sets padding-bottom 1px
   * while the top kept the shared step, so the text centred low. Both
   * paddings 1px: the box keeps its min-height and the text centres on
   * the bar's axis with the brand and the CTA. */
  padding-block: 1px;
}

.nav__link:hover,
.nav__link:focus-visible {
  color: var(--fg);
  box-shadow: none;
}

.nav__link--current {
  color: var(--fg);
}

@media (min-width: 48rem) {
  .nav__inner {
    padding-block: var(--space-4);
  }

  /* In the row layout the link is its line (26px) and a tablet thumb
   * misses it (IVW-059, 834). The link pads itself out to 2.75rem and
   * takes the same amount back as margin, so its box is the 44px the
   * toggle and the CTA already are while the item it sits in, the bar's
   * height and the text's place on the bar's axis are unchanged; the
   * underline draw positions from the content box and stays under the
   * text. lh is the link's own line height; an engine without the unit
   * drops both declarations and keeps the line-high box. The tag in the
   * selector outranks .fu-link's padding-bottom, declared later. */
  a.nav__link {
    padding-block: calc((2.75rem - 1lh) / 2);
    margin-block: calc((1lh - 2.75rem) / 2);
    background-origin: content-box;
  }
}

/* Between 48rem and 64rem the shared desktop row does not hold five
 * links and the CTA (C4-3: at 768 and 834 three items wrapped and the
 * bar stood 96px tall): the brand keeps its 7.5rem, links and CTA never
 * wrap, the list closes its gap one step, links take --text-sm and the
 * CTA pads one step less. From 64rem the shared sizes return. */
@media (min-width: 48rem) and (max-width: 63.9375rem) {
  .nav__brand {
    flex: none;
  }

  .nav__link,
  .nav__cta {
    white-space: nowrap;
  }

  .nav__list,
  .nav__panel .nav__list {
    gap: var(--space-4);
  }

  .nav__link {
    font-size: var(--text-sm);
  }

  .nav__cta {
    padding-inline: var(--space-4);
  }
}

/* The two wordmark links and the skip link are 44px tall to a thumb
 * (C4-7); the bar's height is set by the toggle and the CTA, so nothing
 * moves there, the footer brand grows by 17px and the skip link, hidden
 * until focused, by less than a pixel. */
.fu-skip,
.nav__brand,
.site-footer__brand {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
}

/* The bar's height on a phone is the 2.75rem toggle plus the shared
 * --space-3 above and below it plus the hairline (69px); from 48rem the
 * inner padding is --space-4 (77px). Two things are measured from it
 * (IVW-059). The phone panel: base.css fixes it at 3.5rem, 13px short,
 * so the open panel covered the bar's lower padding and its hairline
 * (bug 7). Scrolling to a target: the bar is sticky, so an anchor, the
 * skip link's #main or an element that takes focus landed under it;
 * scroll-padding-top on the root keeps every scrolled-to box one
 * --space-4 clear of the bar at every width (bug 4). */
@media (max-width: 47.9375rem) {
  .nav__panel {
    top: calc(2.75rem + var(--space-3) * 2 + 1px);
  }
}

html {
  scroll-padding-top: calc(2.75rem + var(--space-3) * 2 + 1px + var(--space-4));
}

@media (min-width: 48rem) {
  html {
    scroll-padding-top: calc(2.75rem + var(--space-4) * 2 + 1px + var(--space-4));
  }
}

/* -----------------------------------------------------------------------
 * 3. Labels. .label-mono is the one small-label style: mono, uppercase,
 * tracked, muted. .label-mono--brand sets it in --brand-text.
 * --------------------------------------------------------------------- */

.label-mono {
  font-family: var(--label-font);
  font-size: var(--label-size);
  font-weight: var(--label-weight);
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
  line-height: var(--leading-xs);
  color: var(--muted);
}

.label-mono--brand {
  color: var(--brand-text);
}

/* The one in-prose action link: near-black, weight 600, with the
 * underline draw from .fu-link (motion entry 3). */
.fu-link--strong {
  color: var(--fg);
  font-weight: 600;
}

/* A standalone link shorter than 44px (the product-card link, the
 * teaser title link, a link that is a body paragraph on its own) pads
 * its own box one --space-3 step above and below and takes the same
 * step back as margin, so the box a thumb hits is the line plus 24px
 * (51px at the card link's 27px line, 50px at a body link's 26px) while
 * the layout around it is unchanged. C4-7 did this with a pseudo-element
 * that measured nothing (IVW-059 bug 6: the links audited at 21 to 27px);
 * the padding is the hit box itself. The underline draw (.fu-link)
 * positions from the content box so the line stays under the text, not
 * under the padding. The first selector carries its tag so it outranks
 * .fu-link's own 1px padding-bottom, declared later in this file. */
a.fu-product__link,
.fu-teasers .fu-card__title > a,
.fu-body > .fu-link--strong:only-child {
  display: inline-block;
  padding-block: var(--space-3);
  margin-block: calc(var(--space-3) * -1);
  background-origin: content-box;
}

/* -----------------------------------------------------------------------
 * 4. Buttons. One filled orange control per screen; every other control
 * is outlined (or white, on the band) and fills near-black on hover,
 * focus or when pressed.
 * --------------------------------------------------------------------- */

.hero__cta {
  transition: background-color var(--duration-base) var(--ease-standard),
    color var(--duration-base) var(--ease-standard),
    border-color var(--duration-base) var(--ease-standard);
}

.hero__cta:hover,
.hero__cta:focus-visible {
  background: var(--brand-hot);
  box-shadow: none;
}

.hero__cta--secondary {
  border-color: var(--fg);
  color: var(--fg);
}

/* The near-black fill: the nav CTA, the hero's secondary button, the
 * band's white button and the log's Play control while pressed. */
.nav__cta:hover,
.nav__cta:focus-visible,
.hero__cta--secondary:hover,
.hero__cta--secondary:focus-visible,
.cta-band__action:hover,
.cta-band__action:focus-visible,
.fu-log__btn[aria-pressed="true"] {
  background: var(--fg);
  border-color: var(--fg);
  color: var(--bg-0);
  box-shadow: none;
}

/* -----------------------------------------------------------------------
 * 5. Hero: centred, headline at the top of the scale, one filled CTA,
 * the hero frame below the actions at every width.
 * --------------------------------------------------------------------- */

.fu-hero .hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  text-align: center;
  gap: var(--space-12);
}

.fu-hero .hero__content,
.fu-hero .hero__media {
  grid-column: 1 / -1;
  width: 100%;
}

/* The hero figure (IVW-034): the type lattice as a Hasse diagram, the
 * founder's pick from seven concepts (concepts/hero/). Two inline <svg>
 * renderings of one drawing, generated by assets/hero-lattice.py into
 * index.html: the wide 1600 by 700 file from 48rem, the tall 800 by 880
 * file below it (founder, 2026-10-01: the wide file "doesn't feel like
 * a hero" on a phone). Inline rather than <img> so the stylesheet can
 * animate it (entry 7 of the motion block) and reduced motion can hold
 * it still. Intrinsic sizes from the width and height attributes; no
 * frame: the drawing's own outline is the only edge. */
.fu-hero__figure {
  display: block;
  width: 100%;
  height: auto;
}

.fu-hero__figure--wide {
  display: none;
}

/* Below 48rem the tall figure ran the whole column (358 by 394 at 390)
 * and, under a headline, six lines of subhead and two buttons, filled
 * the second screen on its own with no text in sight (IVW-059 bug 5).
 * Capped at 16rem and centred, with the hero's grid gap one step
 * tighter, the lattice sits under the buttons as part of the hero's
 * first screen at 390 by 844 and the proof strip follows it directly. */
@media (max-width: 47.9375rem) {
  .fu-hero .hero__inner {
    gap: var(--space-8);
  }

  .fu-hero__figure--phone {
    max-width: 16rem;
    margin-inline: auto;
  }
}

@media (min-width: 48rem) {
  .fu-hero__figure--wide {
    display: block;
  }

  .fu-hero__figure--phone {
    display: none;
  }
}

.fu-hero .hero__content {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.fu-hero .hero__headline {
  max-width: 14ch;
  margin-bottom: var(--space-6);
}

.fu-hero .hero__subhead {
  font-size: var(--text-lg);
  line-height: var(--leading-lg);
  max-width: 34ch;
  margin-bottom: var(--space-8);
}

.fu-hero .hero__actions {
  justify-content: center;
}

/* Below 40rem the headline steps down to 0.85 of the 4xl token so it
 * wraps to two lines (C4-5: at the token size "wrong." stood alone on a
 * third line on the founder's first screen). The hero's padding step
 * from 48rem is in section 1 with the home sections. */
@media (max-width: 39.9375rem) {
  .fu-hero .hero__headline {
    font-size: calc(var(--text-4xl) * 0.85);
  }
}

@media (min-width: 48rem) {
  .fu-hero .hero__inner {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
  }

  .fu-hero .hero__actions {
    flex-direction: row;
  }
}

/* -----------------------------------------------------------------------
 * 6. Home: proof strip (three hairline-divided statements, each carrying
 * a ledger numeral as the page's one technical touch), sector rows, two
 * product cards.
 * --------------------------------------------------------------------- */

.fu-proof {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

.fu-proof__item {
  padding-block: var(--space-6);
  border-top: 1px solid var(--bg-3);
}

/* Stacked, the strip closes with a rule as it opens with one, so the air
 * under 003 reads as the same step as the air above 001 (founder,
 * 2026-10-01: the last item looked spaced differently). The three-column
 * layout from 48rem uses left rules instead and clears this. */
.fu-proof__item:last-child {
  border-bottom: 1px solid var(--bg-3);
}

.fu-proof__no {
  display: block;
  margin-bottom: var(--space-3);
}

.fu-proof__item p {
  font-size: var(--text-xl);
  line-height: var(--leading-xl);
  letter-spacing: var(--tracking-xl);
  font-weight: 500;
  color: var(--fg);
  max-width: 18ch;
}

@media (min-width: 48rem) {
  .fu-proof {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-8);
  }

  .fu-proof__item {
    border-top: 0;
    border-left: 1px solid var(--bg-3);
    padding-block: var(--space-2);
    padding-left: var(--space-6);
  }

  .fu-proof__item:last-child {
    border-bottom: 0;
  }

  /* The first item starts on the column edge, like the rows' leads and
   * the product cards' borders below it (round 4 refinement 2); the two
   * dividers between items stay. */
  .fu-proof__item:first-child {
    border-left: 0;
    padding-left: 0;
  }
}

/* Between 48rem and 64rem the three items are 199 to 306px wide, and at
 * the xl step "Scope-Forge" and "Predictable" stood alone on a line
 * (C5-1); the lines take the lg step in that band, the same band the
 * nav sizes down in, and the token size returns from 64rem. */
@media (min-width: 48rem) and (max-width: 63.9375rem) {
  .fu-proof__item p {
    font-size: var(--text-lg);
    line-height: var(--leading-lg);
  }
}

.fu-rows__item {
  border-top: 1px solid var(--bg-3);
}

.fu-rows__item:last-child {
  border-bottom: 1px solid var(--bg-3);
}

/* Each row is a grid: below 40rem the lead sits above the body with one
 * --space-2 step; from 40rem a lead track of one fixed width, shared by
 * every row, so all three descriptions start at the same x. The track
 * reads --fu-rows-lead with an 8rem fallback (no stylesheet declares
 * the property; the fallback is the value). */
.fu-rows__link {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2);
  min-height: 2.75rem;
  padding-block: var(--space-5);
  font-size: var(--text-lg);
  line-height: var(--leading-lg);
  color: var(--fg);
}

.fu-rows__lead {
  font-weight: 600;
  justify-self: start;
}

.fu-rows__body {
  color: var(--muted);
}

@media (min-width: 40rem) {
  .fu-rows__link {
    grid-template-columns: var(--fu-rows-lead, 8rem) minmax(0, 1fr);
    column-gap: var(--space-4);
    align-items: baseline;
  }
}

/* The row's underline draw fires from the whole row, not only the word. */
.fu-rows__link:hover .fu-rows__lead,
.fu-rows__link:focus-visible .fu-rows__lead {
  background-size: 100% 1px;
}

.fu-products {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
}

@media (min-width: 48rem) {
  .fu-products {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-8);
  }
}

.fu-product {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-8);
  padding: var(--space-8);
}

.fu-product__body {
  font-size: var(--text-md);
  line-height: var(--leading-md);
  color: var(--muted);
}

.fu-product__name {
  color: var(--fg);
  font-weight: 600;
}

.fu-product__link {
  align-self: flex-start;
}

/* -----------------------------------------------------------------------
 * 7. Cards. .card--hairline: white, one hairline, no shadow, no top
 * accent; the glint is the hover state (motion entry 2, below).
 * .card--rule adds a 2px --brand top rule for a card that names a
 * proof (the technology pages' claim cards, the tier cards). The shared
 * claim and sector cards take the hairline look by default so a page
 * that forgets the class still matches. .fu-card is the interior of
 * every card in a grid (teasers, sector cards, recovery tickets, tiers):
 * a column with one --space-3 step between its parts and the shared
 * --space-6 padding; .fu-card__title is the one card title size;
 * .fu-card-grid--2 and --3 are the grids.
 * --------------------------------------------------------------------- */

.card--hairline,
.claim-card,
.sector-card {
  background: var(--bg-0);
  border: 1px solid var(--bg-3);
  border-radius: var(--space-2);
  box-shadow: none;
}

.card--rule {
  border-top: 2px solid var(--brand);
}

.sector-card:hover,
.sector-card:focus-visible {
  box-shadow: none;
}

.fu-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6);
}

/* A compact icon frame at the top of a card takes one extra step before
 * the label under it. */
.fu-card > .img-placeholder--compact {
  margin-bottom: var(--space-2);
}

/* Card icons (IVW-035, IVW-036): the eight sector icons and the five
 * technology teaser icons, one system: a 48-unit grid, one stroke
 * weight (2), near-black with a single brand-orange accent each
 * (/assets/icons/sector-*.svg, tech-*.svg). Unframed, 2.5rem, on the
 * card's left edge, one step above the label or title; decorative (alt
 * empty) because the label or title names the subject. */
.fu-card__icon {
  display: block;
  width: 2.5rem;
  height: 2.5rem;
  margin-bottom: var(--space-4);
}

.fu-card__title {
  font-size: var(--text-lg);
  line-height: var(--leading-lg);
  color: var(--fg);
  margin-bottom: 0;
}

.fu-card__body {
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  color: var(--muted);
}

/* A card with no title (the recovery tickets on /ironidp/) carries the
 * deck's own sentence as its main text: body size, --fg. */
.fu-card__body--md {
  font-size: var(--text-md);
  line-height: var(--leading-md);
  color: var(--fg);
}

.fu-card-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
}

@media (min-width: 40rem) {
  .fu-card-grid--2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-6);
  }
}

@media (min-width: 48rem) {
  .fu-card-grid--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-6);
  }
}

/* The sector card's body reads in --fg, not the shared muted. */
.fu-sector .sector-card__body {
  color: var(--fg);
}

.fu-sector__meta {
  font-size: var(--text-xs);
  line-height: var(--leading-xs);
  color: var(--muted);
  border-top: 1px solid var(--bg-3);
  padding-top: var(--space-3);
  margin-top: auto;
}

.fu-sector__meta + .fu-sector__meta {
  border-top: 0;
  padding-top: 0;
  margin-top: 0;
}

/* The eight sector cards: two columns from 40rem; from 64rem six
 * tracks, every card two wide, cards 7 and 8 start at tracks 2 and 4 so
 * the last row is centred, the pattern the five teasers use (C4-1:
 * three plain columns left the third row's last slot empty). In the
 * 64rem column the cards are 315px wide at 1280; four across would be
 * 230px. */
@media (min-width: 40rem) {
  .fu-sector-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 64rem) {
  .fu-sector-grid {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }

  .fu-sector-grid > * {
    grid-column: span 2;
  }

  .fu-sector-grid > :nth-child(7) {
    grid-column: 2 / span 2;
  }

  .fu-sector-grid > :nth-child(8) {
    grid-column: 4 / span 2;
  }
}

/* The five technology teasers (.fu-teasers on the shared
 * .claim-card-grid--five): one column below 40rem, then the shared
 * three-over-two layout at every width from 40rem (six tracks, every
 * card two tracks wide, cards 4 and 5 start at tracks 2 and 4 so the
 * second row is centred and the fifth card is never alone). base.css's
 * two-column phone layout is not taken (E-009 item 1: cards 2 to 5 were
 * 167px wide at 390) and neither is its 64rem step to five in a row: in
 * the 64rem column five cards would be 179px wide. The selectors carry
 * the body class so they outrank base.css's :has() form. */
.fu-page--interior .claim-card-grid.fu-teasers {
  grid-template-columns: minmax(0, 1fr);
}

.fu-page--interior .claim-card-grid.fu-teasers > :first-child {
  grid-column: auto;
}

@media (min-width: 40rem) {
  .fu-page--interior .claim-card-grid.fu-teasers {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }

  .fu-page--interior .claim-card-grid.fu-teasers > :nth-child(-n+3) {
    grid-column: span 2;
  }

  .fu-page--interior .claim-card-grid.fu-teasers > :nth-child(4) {
    grid-column: 2 / span 2;
  }

  .fu-page--interior .claim-card-grid.fu-teasers > :nth-child(5) {
    grid-column: 4 / span 2;
  }
}

/* -----------------------------------------------------------------------
 * 8. Image placeholders: white frame, hairline, description and slot id
 * both visible; the compact icon frame shows only the id.
 * --------------------------------------------------------------------- */

.img-placeholder {
  background: var(--bg-0);
  border: 1px solid var(--bg-3);
}

.img-placeholder__desc {
  font-family: var(--font-sans);
  max-width: 44ch;
}

.img-placeholder__id {
  display: block;
  background: var(--bg-0);
  font-size: var(--text-xs);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
}

.img-placeholder--compact {
  width: 4.5rem;
  flex: 0 0 auto;
}

.img-placeholder--compact .img-placeholder__frame {
  padding: 0;
}

.img-placeholder--compact .img-placeholder__id {
  position: static;
  padding: 0;
  font-size: calc(var(--text-xs) * 0.7);
  line-height: 1.25;
  letter-spacing: 0;
  text-align: center;
  color: var(--muted);
}

/* Below 40rem a placeholder that shows its brief (every frame that is
 * not the compact icon square and not a filled frame) drops the fixed
 * aspect ratio: the shared ratio plus overflow: hidden clipped a long
 * brief (img-ironidp-2 at 4:1 held 179px of text in a 90px box) and the
 * id label printed over the first line. The box takes a minimum height
 * of two --space-16 steps and grows with its brief; the frame pads its
 * top by --space-16 plus --space-2 so the brief starts one step under
 * the id label even when the id wraps to two lines (the chip ends at
 * 62px on the regular-languages figure; a one-line chip at 39px).
 * Compact and filled frames are untouched. 39.9375rem is 40rem less
 * one pixel at the default root size: the phone side of the shared
 * breakpoint where the tables stack and the teasers are one column. */
@media (max-width: 39.9375rem) {
  .img-placeholder:not(.img-placeholder--compact):not(.img-placeholder--filled) {
    aspect-ratio: auto;
    min-height: calc(var(--space-16) * 2);
  }

  .img-placeholder:not(.img-placeholder--compact):not(.img-placeholder--filled) > .img-placeholder__frame {
    padding-top: calc(var(--space-16) + var(--space-2));
  }
}

/* Inside a split the frame is 302 to 393px wide at 834 and 1280, so a
 * split's frame takes the same treatment at every width (C4-2: the
 * fixed 4:3 ratio held 9 to 12 lines of brief, clipped at 834 and
 * under the chip at 1280). The same two bodies as the phone rule, in a
 * different context; no media query can join the two conditions. */
.fu-split > .img-placeholder:not(.img-placeholder--compact):not(.img-placeholder--filled) {
  aspect-ratio: auto;
  min-height: calc(var(--space-16) * 2);
}

.fu-split > .img-placeholder:not(.img-placeholder--compact):not(.img-placeholder--filled) > .img-placeholder__frame {
  padding-top: calc(var(--space-16) + var(--space-2));
}

/* -----------------------------------------------------------------------
 * 9. Technical section. Apply .fu-technical to the <section class=
 * "section"> element itself, never to the .container inside it, so
 * base.css's rhythm (.section padding, the .section > .container > * + *
 * stack) is untouched. The section is the white page surface between
 * two hairlines (the ledger-light grid was removed in IVW-032 at the
 * founder's request); the seam with a clean section is one hairline.
 * No outer margin, no extra padding.
 * --------------------------------------------------------------------- */

.fu-technical {
  /* IVW-032: the founder asked for the grid removed outright (after a
   * softened pass), so a technical section is the white page surface
   * between two hairlines; the technical register now lives in the
   * panes, ledgers and mono labels alone. */
  background-color: var(--bg-0);
  border-top: 1px solid var(--bg-3);
  border-bottom: 1px solid var(--bg-3);
}

/* Seams. A technical section's bottom hairline is the seam: the next
 * section, technical or clean, drops its own top rule so two adjacent
 * sections never draw two lines. */
.fu-technical + .fu-technical,
.fu-technical + .fu-section {
  border-top: 0;
}

/* Every section label carries a hairline that draws on reveal (motion
 * entry 5), on graph paper and on white alike, so the label column reads
 * as one index down the page. */
.section-header__label {
  position: relative;
  padding-bottom: var(--space-3);
  gap: var(--space-3);
}

.section-header__label::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(var(--fg-rgb), 0.18);
  transform-origin: left center;
}

.section-header__heading {
  max-width: 24ch;
}

/* -----------------------------------------------------------------------
 * 10. Compiler pane. The shared code-block component with a title bar
 * (three muted dots, file name, tag), the highlighted policy, a verdict
 * line or two, the caption under the block and the "what this proves"
 * note as an aside after the figure. This is the only place terminal
 * chrome survives, and it frames real content: a showcase policy and
 * what the compiler says about it. The tag reads "example" or "example,
 * illustrative output" (the latter on every pane with a verdict line).
 * --------------------------------------------------------------------- */

/* The pane surface, shared with the rotation log's frame (section 12). */
.cp,
.fu-log-frame {
  margin: 0;
  background: var(--code-bg);
  border: 1px solid var(--code-border);
  border-radius: var(--space-2);
  overflow: hidden;
}

.cp__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--code-header-bg);
  border-bottom: 1px solid var(--code-border);
}

.cp__dots {
  display: inline-flex;
  gap: var(--space-1);
}

.cp__dots span {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: rgba(var(--fg-rgb), 0.18);
}

.cp__title,
.cp__tag {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: var(--leading-xs);
  color: var(--muted);
}

.cp__tag {
  margin-left: auto;
  text-align: right;
}

/* Below 40rem the bar wraps (flex-wrap above) and the tag takes the
 * whole second line, left-aligned under the file name, so it reads as
 * the bar's second line and not as a right-aligned fragment. */
@media (max-width: 39.9375rem) {
  .cp__tag {
    flex-basis: 100%;
    text-align: left;
  }
}

.cp .code-block__pre {
  padding: var(--space-4);
}

/* On a phone, examples wrap instead of scrolling sideways, so the line
 * that matters (the assertion, the counterexample record) is on screen
 * (release copy, round-two critics). A wrapped line keeps its leading
 * indentation through text-indent on the hanging part; the text itself
 * is unchanged, so every pane stays byte-identical to its snippet file. */
@media (max-width: 39.9375rem) {
  .cp .code-block__pre,
  .diagnostic-block,
  .diagnostic-block pre {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    overflow-x: visible;
  }

  .cp .code-block__code,
  .diagnostic-block {
    font-size: var(--text-xs);
  }
}

@media (min-width: 40rem) {
  .cp .code-block__pre {
    padding: var(--space-6);
  }
}

.cp__foot {
  border-top: 1px solid var(--code-border);
  padding: var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* From 40rem every pane and log part shares the pre's wider inline
 * padding: the foot, the caption, the log history, viewport and controls
 * (section 12). */
@media (min-width: 40rem) {
  .cp__foot,
  .cp__caption,
  .fu-log__history,
  .fu-log__viewport,
  .fu-log__controls {
    padding-inline: var(--space-6);
  }
}

.cp__verdict {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  color: var(--proof);
}

/* Everything set in --threat: a rejected verdict, the log's collision
 * frame and its history entry (section 12). */
.cp__verdict--rejected,
.fu-log__line--threat,
.fu-log__entry--threat .fu-log__text {
  color: var(--threat);
}

.cp__mark {
  flex: none;
  width: 0.5em;
  height: 0.5em;
  background: currentColor;
}

.cp__caption {
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: var(--leading-xs);
  color: var(--muted);
  border-top: 1px solid var(--code-border);
  background: var(--bg-0);
}

.cp__note {
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  color: var(--muted);
  padding-left: var(--space-4);
  border-left: 2px solid var(--brand);
  max-width: 68ch;
}

/* The diagnostic block keeps its shared look; only the source text
 * wraps inside its own box. */
.diagnostic-block__source {
  margin: 0;
  white-space: pre;
  overflow-x: auto;
}

/* -----------------------------------------------------------------------
 * 11. Proof ledger: an ordered list of rows (No., Claim, Source) on a
 * grid, with an optional Property line under the claim naming the
 * IronVelo property in mono. Below 40rem the source drops under the
 * claim so the ledger never scrolls sideways. Three to five rows.
 * --------------------------------------------------------------------- */

.ledger {
  border-top: 1px solid var(--fg);
}

/* A ledger that follows its section header directly (the /company/
 * commitments, whose h2 is hidden) would stack its rule 32px under the
 * label's hairline with nothing between (C5-3); the head row's own
 * bottom hairline still opens the ledger. */
.section-header + .ledger {
  border-top: 0;
}

.ledger__head,
.ledger__row {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr);
  column-gap: var(--space-4);
  row-gap: var(--space-1);
  padding-block: var(--space-4);
  border-bottom: 1px solid var(--bg-3);
}

.ledger__head {
  font-family: var(--label-font);
  font-size: var(--label-size);
  font-weight: var(--label-weight);
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
  color: var(--muted);
  padding-block: var(--space-2);
}

.ledger__head span:nth-child(3) {
  display: none;
}

.ledger__no {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--brand-text);
  line-height: var(--leading-md);
}

.ledger__claim {
  color: var(--fg);
}

.ledger__property {
  grid-column: 2;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: var(--leading-xs);
  color: var(--fg);
}

.ledger__source {
  grid-column: 2;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: var(--leading-xs);
  color: var(--muted);
}

/* The ledger's source link and an outbound citation in prose or a table
 * cell (a.cite, IVW-059: a standard, a memorandum, a vendor page) share
 * one treatment: the surrounding colour, a hairline underline at a
 * quarter of --fg (so it reads on the light and the dark theme alike),
 * and on hover and focus the full --fg with a 2px brand underline. */
.ledger__source a,
a.cite {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: rgba(var(--fg-rgb), 0.25);
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}

.ledger__source a:hover,
.ledger__source a:focus-visible,
a.cite:hover,
a.cite:focus-visible {
  color: var(--fg);
  text-decoration-color: var(--brand);
  text-decoration-thickness: 2px;
}

/* A source link stands on its own line of the ledger, so it is a target
 * in its own right (IVW-059: 18 to 39px tall), unlike a citation inside
 * a sentence. Vertical padding on an inline box takes no part in line
 * layout and draws nothing here, but it is hit-tested, so the link's box
 * reaches 2.75rem with the row's rhythm and underline unchanged. The
 * property line carries the same kind of link on /company/. */
.ledger__source a,
.ledger__property a {
  padding-block: calc((2.75rem - 1lh) / 2);
}

@media (min-width: 40rem) {
  .ledger__head,
  .ledger__row {
    grid-template-columns: 3.5rem minmax(0, 1fr) minmax(9rem, 14rem);
    column-gap: var(--space-6);
  }

  .ledger__head span:nth-child(3) {
    display: block;
  }

  .ledger__source {
    grid-column: 3;
    grid-row: 1;
    padding-top: 0.2em;
  }

  .ledger__property {
    grid-column: 2;
  }
}

/* -----------------------------------------------------------------------
 * 12. Rotation log (the storyboard treatment, /ironidp/). The shared
 * token-rotation markup, controls and script are unchanged; the pane
 * adds a history list whose entries appear from the data-frame-active
 * value motion.js writes on the container. The frame shows the current
 * line; the history shows the lines before it, so together they read as
 * a four-entry log that appends and clears when the sequence wraps. The
 * history reserves three lines so appending never moves the frame or
 * the controls (stable regions).
 * --------------------------------------------------------------------- */

.fu-log__history {
  padding: var(--space-4) var(--space-4) 0;
  min-height: calc(var(--space-4) + 3 * (var(--text-sm) * var(--leading-sm) + var(--space-2)));
}

.fu-log__entry {
  display: none;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  column-gap: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: var(--leading-sm);
  color: var(--muted);
  padding-bottom: var(--space-2);
}

.fu-log[data-frame-active="2"] .fu-log__entry[data-entry="1"],
.fu-log[data-frame-active="3"] .fu-log__entry[data-entry="1"],
.fu-log[data-frame-active="3"] .fu-log__entry[data-entry="2"],
.fu-log[data-frame-active="4"] .fu-log__entry[data-entry="1"],
.fu-log[data-frame-active="4"] .fu-log__entry[data-entry="2"],
.fu-log[data-frame-active="4"] .fu-log__entry[data-entry="3"] {
  display: grid;
  animation: fu-row-settle var(--duration-slow) var(--ease-standard) both;
}

.fu-log__seq {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: var(--leading-sm);
  color: var(--muted);
  margin: 0;
}

.fu-log__viewport {
  padding: var(--space-2) var(--space-4) var(--space-4);
  min-height: calc(var(--text-lg) * var(--leading-lg) + var(--text-xs) * var(--leading-sm) + var(--space-6));
}

.fu-log__frame {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  column-gap: var(--space-4);
  align-items: baseline;
}

/* A leaving frame is a cut, as it is on the hex byte storyboard (IVW-039
 * D8): the shared reverse cross-fade kept the old line on the same grid
 * cell while the new one faded in, and for most of the beat two lines
 * printed over each other, two wrapped lines on a phone (IVW-059 bug 3).
 * The entering frame keeps the shared iv-frame-in. */
.fu-log__frame.is-frame-leaving {
  animation: none;
  opacity: 0;
  visibility: hidden;
}

/* Below 40rem the history is not shown (IVW-059 bug 3): its reserved
 * three lines stood as 125px of empty pane above frame 1, and on a
 * phone the entries wrap, so the reserve could not keep the controls
 * still in any case. The frame's own "N of 4" carries the sequence; the
 * pane is the bar, the line and the controls, and nothing in it moves. */
@media (max-width: 39.9375rem) {
  .fu-log__history {
    display: none;
  }

  .fu-log__viewport {
    padding-top: var(--space-4);
  }
}

.fu-log__line {
  font-family: var(--font-mono);
  font-size: var(--text-lg);
  line-height: var(--leading-lg);
  color: var(--fg);
  margin: 0;
}

.fu-log__line--proof {
  color: var(--proof);
}

.fu-log__controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--code-border);
  background: var(--bg-0);
}

.fu-log__btn {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--fg);
  min-height: 2.75rem;
  padding-inline: var(--space-4);
  border: 1px solid var(--bg-3);
  border-radius: var(--space-1);
  background: var(--bg-0);
  transition: border-color var(--duration-base) var(--ease-standard),
    background-color var(--duration-base) var(--ease-standard),
    color var(--duration-base) var(--ease-standard);
}

.fu-log__btn:hover,
.fu-log__btn:focus-visible {
  border-color: var(--fg);
  box-shadow: none;
}

.fu-log__dots {
  display: flex;
  gap: var(--space-2);
  margin-left: auto;
}

.fu-log__dot {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  min-width: 2.75rem;
  min-height: 2.75rem;
  border: 1px solid var(--bg-3);
  border-radius: var(--space-1);
  color: var(--muted);
  background: var(--bg-0);
  transition: border-color var(--duration-base) var(--ease-standard);
}

.fu-log__dot:hover,
.fu-log__dot:focus-visible {
  border-color: var(--fg);
  color: var(--fg);
  box-shadow: none;
}

.fu-log__dot[aria-current="true"] {
  color: var(--on-brand);
  background: var(--brand);
  border-color: var(--brand);
}

/* -----------------------------------------------------------------------
 * 13. Tables: hairlines instead of filled rows. .table--hairline is the
 * explicit class on the .comparison-table or .regulatory-table wrapper;
 * the two shared wrappers take the same look by default. The <table>
 * hides and the <dl> parity list shows below 40rem (base.css), so the
 * table stacks under 640px with no page-level overflow.
 * --------------------------------------------------------------------- */

.table--hairline table thead th,
.comparison-table__table thead th,
.regulatory-table__table thead th {
  background: transparent;
  font-family: var(--font-sans);
  letter-spacing: 0;
  text-transform: none;
  font-weight: 600;
  color: var(--fg);
}

.table--hairline table tbody tr,
.comparison-table__table tbody tr,
.regulatory-table__table tbody tr {
  background: transparent;
}

.table--hairline table th[scope="row"],
.comparison-table__table th[scope="row"],
.regulatory-table__table th[scope="row"] {
  color: var(--fg);
  font-weight: 600;
}

.table--hairline table td,
.comparison-table__table td,
.regulatory-table__table td {
  color: var(--muted);
}

.table--hairline .comparison-table__cell--positive,
.comparison-table__cell--positive {
  color: var(--fg);
}

.table--hairline dl > div,
.comparison-table__group,
.regulatory-table__group {
  background: transparent;
  border: 1px solid var(--bg-3);
}

.table--hairline dl > div > p,
.comparison-table__group-title,
.regulatory-table__group-title {
  font-family: var(--font-sans);
  letter-spacing: 0;
  text-transform: none;
  font-weight: 600;
  color: var(--fg);
}

/* A stacked pair (below 40rem) is a two-column grid with one fixed term
 * track shared by every pair, so every description starts at the same x
 * and reads left-aligned; base.css lays the pair out as a flex row with
 * space-between, which right-aligned short descriptions and broke
 * "Scope-Forge" after its hyphen (E-009 item 2). The track is 7.5rem:
 * "Scope-Forge" measures 93 to 97px and "Standard JWT" 105px between
 * 390 and 639px, so 6.5rem (104px) would clip the widest term. A term
 * wraps between words, never inside one: "Scope-Forge" fits the track
 * and so never reaches its hyphen, and "Conventional policy engine"
 * takes two lines (IVW-059 bug 1: with white-space: nowrap it ran 188px
 * through its 120px track and printed over the description). The dl
 * groups keep the hairline style above. */
.comparison-table__pair,
.regulatory-table__pair {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  column-gap: var(--space-4);
}

.comparison-table__pair dt,
.regulatory-table__pair dt {
  overflow-wrap: normal;
  hyphens: manual;
}

.comparison-table__pair dd,
.regulatory-table__pair dd {
  text-align: left;
}

/* -----------------------------------------------------------------------
 * 14. CTA band: ember fill with a slow gradient shift (motion entry 6),
 * white --on-ember text (4.60:1 on --brand-ember; the shifting layer
 * only darkens the fill). The band's media frame stays in flow above
 * the heading at every width. The shifting layer is a pseudo-element
 * two and a half band widths wide that carries the gradient and moves
 * by transform, so the shift is compositor work (IVW-030); before, the
 * band animated its own background-position, a repaint of the largest
 * painted area on every page for as long as the band was on screen. The
 * band clips the layer and isolates it so z-index -1 sits between the
 * ember fill and the content.
 * --------------------------------------------------------------------- */

.fu-band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background-color: var(--brand-ember);
  padding-block: var(--space-16);
}

.fu-band::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 250%;
  z-index: -1;
  pointer-events: none;
  background-image: linear-gradient(
    115deg,
    rgba(var(--fg-rgb), 0) 0%,
    rgba(var(--fg-rgb), 0.24) 50%,
    rgba(var(--fg-rgb), 0) 100%
  );
  transform: translateX(0);
}

.fu-band .cta-band__inner {
  gap: var(--space-6);
}

.fu-band .cta-band__heading {
  max-width: 22ch;
}

/* The band carries no image (founder, 2026-10-01, after seeing the
 * IVW-033 mark: "I don't think any image should go there, it looks
 * better simple"). The ember fill, the gradient sweep, the heading and
 * the button are the whole closing beat. */

.cta-band__action {
  transition: background-color var(--duration-base) var(--ease-standard),
    color var(--duration-base) var(--ease-standard);
}

/* -----------------------------------------------------------------------
 * 15. Footer: white, hairline top.
 * --------------------------------------------------------------------- */

.site-footer {
  background: var(--bg-0);
  border-top: 1px solid var(--bg-3);
}

.site-footer__link:hover,
.site-footer__link:focus-visible {
  color: var(--fg);
}

/* From 48rem the footer links lose base.css's 2.75rem minimum and sit
 * in a row at their line height (23px); the same padding-and-margin
 * pair as the nav links (section 2) gives each a 44px box with the row
 * unchanged; the tag outranks .fu-link's padding-bottom as there. */
@media (min-width: 48rem) {
  a.site-footer__link {
    padding-block: calc((2.75rem - 1lh) / 2);
    margin-block: calc((1lh - 2.75rem) / 2);
    background-origin: content-box;
  }
}

/* -----------------------------------------------------------------------
 * 16. Contact form: white fields, hairline borders, near-black focus
 * border, the notice on --bg-1 with a brand rule.
 * --------------------------------------------------------------------- */

.contact-form__input,
.contact-form__textarea,
.contact-form__select {
  background: var(--bg-0);
  transition: border-color var(--duration-base) var(--ease-standard);
}

.contact-form__input:focus-visible,
.contact-form__textarea:focus-visible,
.contact-form__select:focus-visible {
  border-color: var(--fg);
  outline-offset: 0;
}

.contact-form__notice {
  background: var(--bg-1);
  border: 1px solid var(--bg-3);
  border-left: 2px solid var(--brand);
}

.contact-form__submit:hover,
.contact-form__submit:focus-visible {
  box-shadow: none;
}

/* =======================================================================
 * Motion
 * ===================================================================== */

/* 1. Fade-and-rise with a soft settle. Replaces the visual treatment of
 * the shared hero-reveal and section-reveal entries; triggers, selectors,
 * script hooks and static defaults are the shared ones. Amplitude: 0.6em
 * in, 0.08em past rest, 0.03em back. The animation is declared in
 * longhands, never the shorthand: the shorthand would reset
 * animation-delay from this rule's higher specificity and silence every
 * stagger below and the shared hero item stagger in motion.css. */
@keyframes fu-rise {
  0% {
    opacity: 0;
    transform: translateY(0.6em);
  }
  60% {
    opacity: 1;
    transform: translateY(-0.08em);
  }
  82% {
    opacity: 1;
    transform: translateY(0.03em);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

[data-motion-variant="fusion"] [data-motion="hero-reveal"].is-motion-armed [data-motion-item],
[data-motion-variant="fusion"] [data-motion="section-reveal"].is-motion-armed,
[data-motion-variant="fusion"] [data-motion="diagnostic-pulse"].is-motion-armed {
  opacity: 0;
  transform: translateY(0.6em);
}

[data-motion-variant="fusion"] [data-motion="hero-reveal"].is-motion-armed.is-revealed [data-motion-item],
[data-motion-variant="fusion"] [data-motion="section-reveal"].is-motion-armed.is-revealed {
  animation-name: fu-rise;
  animation-duration: var(--duration-slow);
  animation-timing-function: var(--ease-standard);
  animation-fill-mode: both;
}

/* 1a. Phone rule (IVW-030, E-010): a reveal plays once on the clock, not
 * on the thumb. motion.css's script-free baseline puts every reveal on a
 * scroll-driven timeline (animation-timeline: view(); animation-range:
 * entry 0% cover 35%) so a browser without script still plays them. The
 * shared variants' revealed rules use the animation shorthand, which
 * resets animation-timeline to auto, so their reveals play on the clock
 * once the observer fires; the fusion's revealed rules above are
 * longhands (to keep the stagger delays), set no timeline, and so left
 * every reveal on the view() timeline, its overshoot scrubbed by the
 * finger (home 10, /scope-forge/ 21 scroll-timeline animations after
 * load). The first rule below puts every armed element, hero item and
 * caret on the document timeline over the whole animation; the baseline
 * still stands for the unarmed state (no script, or reduced motion,
 * which never arms), so the script-free path is unchanged. The second
 * rule is why the first is not enough on its own: on the clock the
 * baseline's shorthand has no duration, so an armed element would land
 * at its last keyframe at once and stand visible before the observer
 * reveals it (measured: opacity 1 before entry with the first rule
 * alone); until revealed, the animation is named away and the shared
 * armed state (opacity 0) holds. The revealed rules above and in 4a to
 * 4c set the name back at a higher specificity. */
[data-motion-variant="fusion"] [data-motion].is-motion-armed,
[data-motion-variant="fusion"] [data-motion="hero-reveal"].is-motion-armed [data-motion-item],
[data-motion-variant="fusion"] [data-motion="diagnostic-pulse"].is-motion-armed .diagnostic-block__caret {
  animation-timeline: auto;
  animation-range: normal;
}

[data-motion-variant="fusion"] [data-motion].is-motion-armed:not(.is-revealed),
[data-motion-variant="fusion"] [data-motion="hero-reveal"].is-motion-armed:not(.is-revealed) [data-motion-item],
[data-motion-variant="fusion"] [data-motion="diagnostic-pulse"].is-motion-armed:not(.is-revealed) .diagnostic-block__caret {
  animation-name: none;
}

/* 1b. Band button on phones (IVW-031, founder 2026-10-01): the shared
 * cta-emphasis scales the button 0.9 to 1.05 to 0.98 to 1, a bounce
 * that draws the eye on a desktop, where the band is one block among
 * several on screen. On a phone the band fills the screen, the band
 * itself has just risen, and the bounce on top reads as noise. Below
 * 48rem the button takes the same quiet rise as every other element
 * (fu-rise, no scale), two fast beats after the band, so the band and
 * its button still land in sequence. From 48rem the shared emphasis is
 * unchanged. Longhands, never the shorthand, for the same reason as
 * entry 1; the armed state drops the shared scale(0.9) so the rise
 * starts from the button's own size. Reduced motion is handled by the
 * shared block, which lands cta-emphasis static at every width. */
@media (max-width: 47.9375rem) {
  [data-motion-variant="fusion"] [data-motion="cta-emphasis"].is-motion-armed {
    transform: translateY(0.6em);
  }

  [data-motion-variant="fusion"] [data-motion="cta-emphasis"].is-motion-armed.is-revealed {
    animation-name: fu-rise;
    animation-duration: var(--duration-slow);
    animation-timing-function: var(--ease-standard);
    animation-delay: calc(var(--duration-fast) * 2);
    animation-fill-mode: both;
  }
}

/* Sibling cards and rows that share one reveal stagger by --duration-fast
 * so overlapping movements run on different periods: proof items, sector
 * rows, product cards, teasers, sector cards (the rhythm repeats every
 * three cards, one row of the grid from 64rem), every .fu-card-grid and
 * the ledger's rows (entry 4a). */
[data-motion-variant="fusion"] .fu-proof__item:nth-child(2),
[data-motion-variant="fusion"] .fu-rows__item:nth-child(2),
[data-motion-variant="fusion"] .fu-products > :nth-child(2),
[data-motion-variant="fusion"] .fu-teasers > :nth-child(2),
[data-motion-variant="fusion"] .fu-sector-grid > :nth-child(3n+2),
[data-motion-variant="fusion"] .fu-card-grid > :nth-child(2),
[data-motion-variant="fusion"] .ledger__row:nth-child(2) {
  animation-delay: var(--duration-fast);
}

[data-motion-variant="fusion"] .fu-proof__item:nth-child(3),
[data-motion-variant="fusion"] .fu-rows__item:nth-child(3),
[data-motion-variant="fusion"] .fu-teasers > :nth-child(3),
[data-motion-variant="fusion"] .fu-sector-grid > :nth-child(3n+3),
[data-motion-variant="fusion"] .fu-card-grid > :nth-child(3),
[data-motion-variant="fusion"] .ledger__row:nth-child(3) {
  animation-delay: calc(var(--duration-fast) * 2);
}

[data-motion-variant="fusion"] .fu-teasers > :nth-child(4),
[data-motion-variant="fusion"] .ledger__row:nth-child(4) {
  animation-delay: calc(var(--duration-fast) * 3);
}

[data-motion-variant="fusion"] .fu-teasers > :nth-child(5),
[data-motion-variant="fusion"] .ledger__row:nth-child(5) {
  animation-delay: calc(var(--duration-fast) * 4);
}

@supports (animation-timeline: view()) {
  [data-motion-variant="fusion"] [data-motion="hero-reveal"] [data-motion-item],
  [data-motion-variant="fusion"] [data-motion="section-reveal"] {
    animation-name: fu-rise;
  }

  /* The diagnostic block settles like a row in the script-free baseline
   * too, so no fusion route animates a box-shadow in any state. */
  [data-motion-variant="fusion"] [data-motion="diagnostic-pulse"] {
    animation-name: fu-row-settle;
  }

  /* The ledger's rows carry the movement, not the block. */
  [data-motion-variant="fusion"] .ledger[data-motion="section-reveal"] {
    animation: none;
  }
}

/* 2. Border glint. A one-pixel ring (the card's own border box, masked to
 * its edge) carries a narrow orange band that travels from the left edge
 * to the right edge once, over two --duration-slower beats, then fades.
 * Runs only while :hover or :focus-within matches; leaving removes the
 * rule and the ring returns to transparent. The static hover state is
 * the darker border colour. */
.card--hairline {
  position: relative;
  transition: border-color var(--duration-base) var(--ease-standard);
}

.card--hairline::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  opacity: 0;
  background-image: linear-gradient(
    90deg,
    rgba(var(--brand-rgb), 0) 0%,
    rgba(var(--brand-rgb), 0) 38%,
    rgba(var(--brand-rgb), 1) 50%,
    rgba(var(--brand-rgb), 0) 62%,
    rgba(var(--brand-rgb), 0) 100%
  );
  background-size: 300% 100%;
  background-position: 100% 0;
  -webkit-mask: linear-gradient(var(--fg) 0 0) content-box, linear-gradient(var(--fg) 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(var(--fg) 0 0) content-box, linear-gradient(var(--fg) 0 0);
  mask-composite: exclude;
}

.card--hairline:hover,
.card--hairline:focus-within {
  border-color: var(--muted);
}

.card--hairline:hover::before,
.card--hairline:focus-within::before {
  animation: fu-glint calc(var(--duration-slower) * 2) var(--ease-standard) 1 both;
}

@keyframes fu-glint {
  0% {
    opacity: 0;
    background-position: 100% 0;
  }
  12% {
    opacity: 1;
  }
  88% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    background-position: 0% 0;
  }
}

/* 3. Underline draw. A 1px line grows from the left on hover and focus;
 * with the duration tokens zeroed under reduced motion it appears
 * instantly, which is the static state. */
.fu-link {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 0% 1px;
  background-position: 0 100%;
  padding-bottom: 1px;
  transition: background-size var(--duration-base) var(--ease-out),
    color var(--duration-base) var(--ease-standard);
}

.fu-link:hover,
.fu-link:focus-visible {
  background-size: 100% 1px;
}

/* 4. Row settle: ledger rows, compiler verdicts and log entries land
 * with two decaying overshoots (0.18em, then 0.06em) in the row's own
 * em. Triggers reuse the shared reveal family (motion.js arms and
 * reveals any element carrying data-motion="section-reveal") or the
 * storyboard's data-frame-active attribute, so no script change. */
@keyframes fu-row-settle {
  0% {
    opacity: 0;
    transform: translateY(0.6em);
  }
  55% {
    opacity: 1;
    transform: translateY(-0.18em);
  }
  80% {
    opacity: 1;
    transform: translateY(0.06em);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 4a. Ledger: the container's own reveal is suppressed so the rows, not
 * the block, carry the movement. Rows hide only once script has armed
 * the container; with no script they are simply visible. */
[data-motion-variant="fusion"] .ledger[data-motion="section-reveal"].is-motion-armed,
[data-motion-variant="fusion"] .ledger[data-motion="section-reveal"].is-motion-armed.is-revealed {
  opacity: 1;
  transform: none;
  animation: none;
}

[data-motion-variant="fusion"] .ledger[data-motion="section-reveal"].is-motion-armed .ledger__row {
  opacity: 0;
}

/* 4c. Diagnostic block: the shared entry (MOTION.md 2.7) flashes a
 * box-shadow, which no engine composites, so every frame in range
 * repainted the block; the fusion settles the block in like a ledger row
 * (opacity and transform only, with the pre-reveal state under entry 1)
 * and keeps the shared caret fade, which is opacity only. The shared
 * revealed rule's --duration-fast delay stays: the message lands a beat
 * after the pane above it, the way a verdict does. */
[data-motion-variant="fusion"] .ledger[data-motion="section-reveal"].is-motion-armed.is-revealed .ledger__row,
[data-motion-variant="fusion"] [data-motion="diagnostic-pulse"].is-motion-armed.is-revealed {
  animation-name: fu-row-settle;
  animation-duration: var(--duration-slow);
  animation-timing-function: var(--ease-standard);
  animation-fill-mode: both;
}

/* Rows 2 to 5 take the sibling stagger declared under entry 1. */

/* 4b. Compiler pane: the pane itself rises with entry 1; each verdict
 * line lands three, then four, --duration-fast beats later, the way a
 * compiler prints its result after the source scrolls past. */
[data-motion-variant="fusion"] .cp[data-motion="section-reveal"].is-motion-armed .cp__verdict {
  opacity: 0;
}

[data-motion-variant="fusion"] .cp[data-motion="section-reveal"].is-motion-armed.is-revealed .cp__verdict {
  animation: fu-row-settle var(--duration-slow) var(--ease-standard) calc(var(--duration-fast) * 3) both;
}

[data-motion-variant="fusion"] .cp[data-motion="section-reveal"].is-motion-armed.is-revealed .cp__verdict:nth-child(2) {
  animation-delay: calc(var(--duration-fast) * 4);
}

/* 5. Hairline draws under every section label on reveal. The line is a
 * pseudo-element scaled from the left; the overshoot is a 2 percent then
 * 0.5 percent stretch of its own width before rest. */
[data-motion-variant="fusion"] .section-header[data-motion="section-reveal"].is-motion-armed .section-header__label::after {
  transform: scaleX(0);
}

[data-motion-variant="fusion"] .section-header[data-motion="section-reveal"].is-motion-armed.is-revealed .section-header__label::after {
  animation: fu-hairline-draw var(--duration-slow) var(--ease-standard) var(--duration-fast) both;
}

@keyframes fu-hairline-draw {
  0% {
    transform: scaleX(0);
  }
  70% {
    transform: scaleX(1.02);
  }
  88% {
    transform: scaleX(0.995);
  }
  100% {
    transform: scaleX(1);
  }
}

/* 6. CTA band gradient shift, started by the band's own section-reveal
 * (the band carries data-motion="section-reveal", so entry 1 gives it
 * the rise); the gradient layer (section 14) slides from its left edge
 * to 60 percent of its own width leftward, which is the far edge of the
 * band, and back, over twelve --duration-slower beats. A transform on a
 * pseudo-element: the layer is painted once and moved by the
 * compositor (IVW-030; before, background-position on the band itself
 * repainted the band every frame). */
@keyframes fu-band-shift {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-60%);
  }
}

[data-motion-variant="fusion"] .fu-band.is-motion-armed.is-revealed::before {
  animation: fu-band-shift calc(var(--duration-slower) * 12) var(--ease-standard) infinite alternate;
}

/* 7. The hero lattice is written like a proof, once, on the hero's
 * reveal (the hero carries data-motion="hero-reveal"; the figure is item
 * 4, so this sequence starts after the figure's own rise). Order: the
 * grey lattice appears level by level (edges fade, nodes land with the
 * fusion's small settle), the orange refinement path draws itself from
 * the top in two strokes, the admitted type fills as the path arrives
 * and its halo lights, and the red empty set is the last thing to
 * appear. About two seconds end to end, nothing loops, nothing moves
 * after it lands. Segment lengths (431 wide, 364 phone) come from the
 * generator; a dash exactly one segment long is drawn by sliding its
 * offset to zero. Until the hero is revealed every part sits at opacity
 * 0 so the sequence has a blank page to write on; with no script the
 * armed class never arrives and the figure is simply visible. */
@keyframes lt-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes lt-land {
  0% { opacity: 0; transform: scale(0.6); }
  70% { opacity: 1; transform: scale(1.04); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes lt-draw {
  to { stroke-dashoffset: 0; }
}

[data-motion="hero-reveal"].is-motion-armed .lt-edge,
[data-motion="hero-reveal"].is-motion-armed .lt-node,
[data-motion="hero-reveal"].is-motion-armed .lt-halo,
[data-motion="hero-reveal"].is-motion-armed .lt-admit,
[data-motion="hero-reveal"].is-motion-armed .lt-admit-halo {
  opacity: 0;
}

[data-motion="hero-reveal"].is-motion-armed .lt-seg {
  stroke-dashoffset: 431;
  stroke-dasharray: 431;
}

[data-motion="hero-reveal"].is-motion-armed .fu-hero__figure--phone .lt-seg {
  stroke-dashoffset: 364;
  stroke-dasharray: 364;
}

.lt-node {
  transform-box: fill-box;
  transform-origin: center;
}

[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-edge {
  animation: lt-fade var(--duration-slow) var(--ease-standard) both;
  animation-delay: calc(var(--duration-fast) * 4);
}

[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-node {
  animation: lt-land var(--duration-slow) var(--ease-standard) both;
  animation-delay: calc(var(--duration-fast) * 4);
}

[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-l2 {
  animation-delay: calc(var(--duration-fast) * 5);
}

[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-l3 {
  animation-delay: calc(var(--duration-fast) * 6);
}

[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-seg {
  animation: lt-draw var(--duration-slow) var(--ease-standard) both;
  animation-delay: calc(var(--duration-fast) * 6);
}

[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-seg--2 {
  animation-delay: calc(var(--duration-fast) * 6 + var(--duration-slow));
}

[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-admit,
[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-admit-halo,
[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-halo {
  animation: lt-fade var(--duration-slow) var(--ease-standard) both;
  animation-delay: calc(var(--duration-fast) * 6 + var(--duration-slow) * 2);
}

[data-motion="hero-reveal"].is-motion-armed.is-revealed .lt-node--empty {
  animation-delay: calc(var(--duration-fast) * 6 + var(--duration-slow) * 2 + var(--duration-fast) * 2);
}

/* =======================================================================
 * Reduced motion: every entry above lands in its static end state.
 * tokens.css already zeroes the duration tokens; these rules remove the
 * keyframe animations outright, including the infinite band shift and
 * the glint, so nothing moves. !important matches the shared file's own
 * convention for this block.
 * ===================================================================== */

@media (prefers-reduced-motion: reduce) {
  .lt-edge,
  .lt-node,
  .lt-halo,
  .lt-admit,
  .lt-admit-halo,
  .lt-seg {
    animation: none !important;
    opacity: 1 !important;
    stroke-dasharray: none !important;
    stroke-dashoffset: 0 !important;
  }

  [data-motion-variant="fusion"] [data-motion="hero-reveal"] [data-motion-item],
  [data-motion-variant="fusion"] [data-motion="section-reveal"],
  [data-motion-variant="fusion"] [data-motion="diagnostic-pulse"],
  [data-motion-variant="fusion"] .fu-band,
  .ledger__row,
  .cp__verdict,
  .fu-log__entry,
  .section-header__label::after {
    animation: none !important;
    animation-timeline: none !important;
    transform: none !important;
    opacity: 1 !important;
  }

  .card--hairline::before {
    animation: none !important;
    opacity: 0 !important;
  }

  /* The gradient layer rests at the middle of its travel, the same
   * picture as the old background-position 50% 0. */
  .fu-band::before {
    animation: none !important;
    transform: translateX(-30%) !important;
  }

  .fu-link,
  .card--hairline,
  .hero__cta,
  .cta-band__action,
  .fu-log__btn,
  .fu-log__dot,
  .contact-form__input,
  .contact-form__textarea,
  .contact-form__select {
    transition: none !important;
  }
}

/* -----------------------------------------------------------------------
 * Unfilled image slots stay out of the page (release copy, D-011
 * amendment 2: strongest copy first). The markup stays so art can drop
 * in later; until a slot carries real media (img-placeholder--filled) it
 * takes no space, and a split that held one closes to a single column.
 * Both round-one critics found the empty slots pushing each page's
 * thesis below the first screen.
 * --------------------------------------------------------------------- */
.img-placeholder:not(.img-placeholder--filled):not(.img-placeholder--compact) {
  display: none;
}

.fu-split:has(> .img-placeholder:not(.img-placeholder--filled)) {
  grid-template-columns: minmax(0, 1fr);
}
