/* LearnBoard component stylesheet.
   Extracted verbatim from pages/library.html, which is the single source of
   visual truth for this site. Every page links this file and adds nothing:
   a component that looks different on a page is a bug in that page.
   Regenerate with scratchpad/extract_shell.py; do not hand-edit. */

/* ============================================================================
   COMPONENT: Header
   Every value resolves to a token. The only literals are structural
   (1px hairlines, 0, 100%, 50%) per standards.md section 6.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: var(--z-modal);
  background: var(--color-ink); color: var(--color-ink-on-dark);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm); font-weight: var(--weight-semibold);
}
.skip-link:focus { left: var(--space-4); top: var(--space-4); }

:where(a, button):focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

/* ---------- link variants ----------
   A directory whose entries are destinations needs its titles to be links. The
   link takes the card's own ink so the grid looks no different, and the accent
   on hover, so the only thing introduced is the pointer. Focus is the global
   :where(a, button):focus-visible ring. */
.uc-title a,
.fd-text a { color: inherit; text-decoration: none; }
.uc-title a:hover,
.fd-text a:hover { color: var(--color-accent-strong); text-decoration: underline; }

/* ---------- bar ---------- */
.site-header {
  position: sticky; top: 0; z-index: var(--z-sticky);
  background: var(--color-surface);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
.site-header.is-condensed {
  border-bottom-color: var(--color-rule);
  box-shadow: var(--shadow-1);
}
.header-inner {
  max-width: var(--container); margin-inline: auto;
  padding: var(--space-3) var(--gutter);
  display: flex; align-items: center; gap: var(--space-6);
}

.brand { display: inline-flex; align-items: center; flex: 0 0 auto; }
/* The supplied lockup is HORIZONTAL, 910x310: icon grid left, two-line wordmark
   right. Two lines of wordmark still cost height, so the logo keeps driving the
   bar height rather than the other way round. Scaled uniformly; never redrawn
   or re-spaced (standards section 0.1). */
.brand img { height: var(--space-12); width: auto; display: block; }

/* ---------- primary nav ---------- */
.primary-nav { flex: 1 1 auto; }
.primary-nav > ul {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: center; gap: var(--space-1);
}
.nav-item { position: static; }
/* Nav labels sit at medium weight, matching the reference bar. At semibold
   the row read heavier and blacker than the rest of the page. */
.nav-link, .nav-trigger {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font: inherit; font-size: var(--text-small); font-weight: var(--weight-medium);
  color: var(--color-ink); text-decoration: none;
  background: transparent; border: 0; cursor: pointer;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  transition: background-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard);
}
.nav-link:hover, .nav-trigger:hover { background: var(--color-surface-sunk); }
.nav-trigger[aria-expanded="true"] { background: var(--color-surface-sunk); }
.nav-trigger .chev {
  width: var(--space-3); height: var(--space-3); flex: 0 0 auto;
  transition: transform var(--dur-fast) var(--ease-standard);
}
.nav-trigger[aria-expanded="true"] .chev { transform: rotate(180deg); }

/* ---------- actions ---------- */
.header-actions { display: flex; align-items: center; gap: var(--space-2); flex: 0 0 auto; }
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font: inherit; font-size: var(--text-ui); font-weight: var(--weight-medium);
  letter-spacing: -0.015em; text-decoration: none; cursor: pointer; white-space: nowrap;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-btn); border: 1px solid transparent;
  /* Pinned, not derived: a 1px border resolves to 0.667px at some zoom
     levels while the ribbon's 2px does not, so equal padding still gave
     unequal heights. Every button now lands on the same line. */
  min-height: calc(var(--space-12) + var(--space-1));
  transition: background-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard);
}
.btn--ghost { background: var(--color-surface-sunk); color: var(--color-ink); }
.btn--ghost:hover { background: var(--color-rule); }
.btn--primary { background: var(--color-accent); color: var(--color-on-accent); }
.btn--primary:hover { background: var(--color-accent-hover); }
/* ---------- dropdown panels ---------- */
.panel {
  position: absolute; left: 0; right: 0; top: 100%;
  background: var(--color-surface);
  border-top: 1px solid var(--color-rule);
  box-shadow: var(--shadow-3);
  opacity: 0; visibility: hidden;
  transform: translateY(calc(var(--reveal-shift) * -1));
  transition: opacity var(--dur-base) var(--ease-assemble),
              transform var(--dur-base) var(--ease-assemble),
              visibility var(--dur-base) var(--ease-assemble);
}
.panel.is-open { opacity: 1; visibility: visible; transform: translateY(0); }
.panel-inner {
  max-width: var(--container); margin-inline: auto;
  padding: var(--space-8) var(--gutter);
  display: grid; gap: var(--space-8);
}
.panel--product .panel-inner { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
/* Six columns exactly, auto-fit dropped to five and orphaned the last sector
   onto a second row. Panels only render at desktop widths; the sheet handles
   everything below 900px. */
.panel--solutions .panel-inner {
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-6) var(--space-5);
}
@media (max-width: 1160px) {
  .panel--solutions .panel-inner { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.col-label {
  font-family: var(--font-mono); font-size: var(--text-caption);
  letter-spacing: var(--tracking-caption); text-transform: uppercase;
  color: var(--color-ink-muted);
  margin: 0 0 var(--space-4);
}
.col-label a { color: inherit; text-decoration: none; }
.col-label a:hover { color: var(--color-accent-strong); }

/* column heading carrying its sector icon */
.col-head { display: flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-4); }
.col-head .col-label { margin: 0; }

.panel ul, .acc-body ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-1); }

/* rich item: icon + title + one-liner.
   The hover surface is inset horizontally only. A negative BLOCK margin made
   each row's hover background bleed over the row beneath it. */
.rich {
  display: flex; gap: var(--space-3); align-items: flex-start;
  padding: var(--space-2);
  margin-inline: calc(var(--space-2) * -1);
  border-radius: var(--radius-md); text-decoration: none; color: inherit;
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.rich:hover { background: var(--color-surface-sunk); }
/* Icon tile. The glyph is inlined so its fill is CSS-driven; the tile colour
   arrives as --ico, always set from a token, never a literal. */
.ico {
  display: grid; place-items: center; flex: 0 0 auto;
  background: var(--ico);
  border-radius: var(--radius-sm);
}
.ico svg { width: 52%; height: 52%; display: block; }
.ico svg path { fill: var(--color-icon-glyph); }
/* display:grid is repeated here deliberately. `.ico` is a <span> inside
   `.rich`, and `.rich span { display: block }` two lines below has equal
   specificity and comes later, it was silently overriding the grid, so
   place-items did nothing and every glyph sat hard against the top of its
   tile. These scoped rules win the cascade. */
.rich .ico { display: grid; place-items: center; width: var(--space-8); height: var(--space-8); }
.col-head .ico { display: grid; place-items: center; width: var(--space-6); height: var(--space-6); border-radius: var(--radius-xs); }
.rich b { display: block; font-size: var(--text-small); font-weight: var(--weight-semibold); }
.rich span { display: block; font-size: var(--text-small); color: var(--color-ink-muted); }

/* dense item: plain link */
.dense {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-small); color: var(--color-ink-muted);
  text-decoration: none; padding: var(--space-1) var(--space-2);
  margin-inline: calc(var(--space-2) * -1);
  border-radius: var(--radius-sm);
  transition: background-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard);
}
.dense:hover { background: var(--color-surface-sunk); color: var(--color-ink); }

/* Product-matched item icons. One mask per item set inline as --mi, so the
   glyph ships once as a file instead of being inlined on every page. Grey at
   rest, ink on hover, the pack hue on a chosen chip. */
.mi {
  display: inline-block; flex: 0 0 auto;
  width: var(--space-4); height: var(--space-4);
  background-color: var(--color-ink-faint);
  -webkit-mask: var(--mi) center / contain no-repeat;
          mask: var(--mi) center / contain no-repeat;
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.dense:hover .mi { background-color: var(--color-ink-muted); }
.chip:hover .mi { background-color: var(--color-ink-muted); }
.chip[aria-selected="true"] .mi { background-color: var(--hue); }

.panel-foot {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  margin-top: calc(var(--space-2) * -1);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-rule);
  font-size: var(--text-small); color: var(--color-ink-muted);
}
.panel-foot a {
  color: var(--color-accent-strong); font-weight: var(--weight-semibold); text-decoration: none;
}
.panel-foot a:hover { text-decoration: underline; }
/* two links on the right of the foot: the whole catalogue and the solutions hub */
.panel-foot-links { display: flex; flex-wrap: wrap; gap: var(--space-5); }

/* ---------- mobile ---------- */
.nav-toggle { display: none; }
.mobile-sheet { display: none; }

@media (max-width: 900px) {
  .primary-nav, .header-actions .btn--ghost { display: none; }
  .header-inner { gap: var(--space-3); justify-content: space-between; }
  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 2.75rem; height: 2.75rem;   /* 44px, the touch minimum */
    background: var(--color-surface-sunk); border: 0; border-radius: var(--radius-md);
    cursor: pointer; color: var(--color-ink);
  }
  .mobile-sheet {
    display: block; position: fixed; inset: 0; z-index: var(--z-modal);
    background: var(--color-surface);
    overflow-y: auto; -webkit-overflow-scrolling: touch;
    opacity: 0; visibility: hidden;
    transform: translateY(var(--reveal-shift));
    transition: opacity var(--dur-base) var(--ease-assemble),
                transform var(--dur-base) var(--ease-assemble),
                visibility var(--dur-base) var(--ease-assemble);
  }
  .mobile-sheet.is-open { opacity: 1; visibility: visible; transform: translateY(0); }
  .sheet-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--space-3) var(--gutter);
    border-bottom: 1px solid var(--color-rule);
  }
  .sheet-head img { height: var(--space-12); }
  .sheet-body { padding: var(--space-4) var(--gutter) var(--space-24); }
  .acc-trigger, .sheet-link {
    display: flex; align-items: center; justify-content: space-between; width: 100%;
    font: inherit; font-size: var(--text-h4); font-weight: var(--weight-semibold);
    color: var(--color-ink); text-decoration: none;
    background: transparent; border: 0; cursor: pointer; text-align: left;
    padding: var(--space-4) 0;
    border-bottom: 1px solid var(--color-rule);
  }
  .acc-trigger .chev { transition: transform var(--dur-fast) var(--ease-standard); }
  .acc-trigger[aria-expanded="true"] .chev { transform: rotate(180deg); }
  .acc-body { display: none; padding: var(--space-2) 0 var(--space-4); }
  .acc-body.is-open { display: block; }
  .acc-body .col-label { margin-top: var(--space-4); }
  .sheet-actions {
    display: grid; gap: var(--space-3);
    padding-top: var(--space-6);
  }
  .sheet-actions .btn { padding: var(--space-4); font-size: var(--text-body); }
  body.no-scroll { overflow: hidden; }
}

/* ============================================================================
   COMPONENT: Hero
   Sized from the reference measured at a 1440 viewport: h1 38px / 650 / 44px /
   -1.6px, and every other element in the column 16px / 24px. Ours ran 17 to
   18px with a 56px headline, which is why it read oversized.
   ========================================================================== */

.hero { overflow: hidden; }  /* clips the bleeding grid at the viewport edge */
.hero-inner {
  max-width: var(--container); margin-inline: auto;
  /* Bottom trimmed from --space-16: the trust bar sits close under the hero
     rather than a full band away. */
  padding: var(--space-12) var(--gutter) var(--space-6);
  display: grid; gap: var(--space-10);
  /* the right column is given enough room for a square the height of the copy
     column, otherwise max-width clamps the frame and the tiles stop being square */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  align-items: stretch;
}
.hero-copy { font-size: var(--text-ui); line-height: var(--leading-normal); }

/* The border is a drifting gradient in the brand hues rather than a flat rule:
   two backgrounds, one clipped to the padding box for the fill and one to the
   border box for the edge. The drift is the one ambient loop on the page and
   it stops entirely under prefers-reduced-motion. */
.eyebrow {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 2px solid transparent; border-radius: var(--radius-pill);
  font-size: var(--text-fine); font-weight: var(--weight-semibold);
  letter-spacing: -0.006em;
  color: var(--color-ink); text-decoration: none;
  background:
    linear-gradient(var(--color-surface), var(--color-surface)) padding-box,
    linear-gradient(90deg,
      var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
      var(--lb-gold), var(--lb-green), var(--lb-teal)) border-box;
  background-size: auto, 300% 100%;
  background-position: 0 0, 0 50%;
  animation: eyebrow-drift var(--dur-ambient) linear infinite;
  transition: box-shadow var(--dur-fast) var(--ease-standard);
}
.eyebrow:hover { box-shadow: var(--shadow-2); }
@keyframes eyebrow-drift { to { background-position: 0 0, 300% 50%; } }
@media (prefers-reduced-motion: reduce) { .eyebrow { animation: none; } }
.eyebrow .spark { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
.eyebrow .spark path { fill: var(--lb-magenta); }
.eyebrow .ico-inline { width: var(--space-3); height: var(--space-3); }
.eyebrow .ico-inline path { fill: var(--color-ink-muted); }

.hero h1, .hero .hero-h {
  /* Scaled off the hero token rather than given a literal, so it keeps the
     token's clamp and still moves with the viewport. The opener carries the
     page, and at the token's own size it was losing to the grid beside it. */
  font-size: calc(var(--text-hero) * 1.2); line-height: var(--leading-hero);
  letter-spacing: var(--tracking-hero); font-weight: var(--weight-bold);
  margin: var(--space-5) 0 var(--space-4); max-width: 16ch;
}
.hero .lede {
  font-size: var(--text-ui); line-height: var(--leading-ui); letter-spacing: -0.02em;
  color: var(--color-ink-muted); max-width: none; margin: 0;
}
.hero .lede strong { color: var(--color-ink); font-weight: var(--weight-semibold); }

.hero-points { list-style: none; margin: var(--space-5) 0 0; padding: 0; display: grid; gap: var(--space-2); }
.hero-points li { display: flex; gap: var(--space-3); align-items: flex-start; font-size: var(--text-ui); line-height: var(--leading-ui); letter-spacing: -0.02em; }
.hero-points .ico-inline { width: var(--space-5); height: var(--space-5); flex: 0 0 auto; margin-top: 3px; }
.hero-points .ico-inline path { fill: var(--color-accent); }
.hero-points b { font-weight: var(--weight-semibold); color: var(--color-ink); }
.hero-points span { color: var(--color-ink-muted); }

.hero-cta { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; margin-top: var(--space-6); }
/* Narrowed from 15rem so the pair and the fine print share one row. Both
   buttons take the same width, so neither reads as the lesser option. */
.btn--lg { padding: var(--space-3) var(--space-6); min-width: 10.5rem; min-height: var(--space-14); font-size: var(--text-body-lg); font-weight: var(--weight-bold); letter-spacing: -0.02em; }
.hero-cta .fine { font-size: var(--text-fine); color: var(--color-ink-muted); line-height: 1.5; margin: 0; }
/* Two statements, not one sentence. The bullet ran them together, so they are
   separate spans with real space between and the dot drawn by the gap. */
.hero-label {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-6);
  margin: var(--space-6) 0 var(--space-3);
  font-family: var(--font-sans); font-size: var(--text-fine); font-weight: var(--weight-medium);
  letter-spacing: 0.01em; text-transform: uppercase; line-height: 1.25;
  color: var(--color-ink-muted);
}
.hero-label span + span::before {
  content: ""; display: inline-block; vertical-align: middle;
  width: var(--space-1); height: var(--space-1);
  margin-right: var(--space-6);
  border-radius: var(--radius-pill);
  background: var(--color-ink-faint);
}
/* Stacked on a phone, so the separator would lead the second line rather than
   sit between the two. The line break already does the separating. */
@media (max-width: 560px) {
  .hero-label span + span::before { display: none; }
}

/* chips: 16px / 400, pill, hairline border. Selected takes the accent border
   and a check badge on the corner rather than an inline tick. */
.chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; padding: 0; }
.chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font: inherit; font-size: var(--text-fine); font-weight: var(--weight-semibold);
  line-height: 1.5; color: var(--color-ink-muted); background: transparent;
  border: 1px dashed var(--color-rule-strong); border-radius: var(--radius-pill);
  padding: var(--space-1) var(--space-3); cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard);
}
/* Grey at rest, so the row stays calm and the twelve hues do not compete with
   the product panel. The pack colour appears only on the chosen chip, which
   makes the colour mean "this is what you are looking at" rather than decorate. */
.chip .cico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
.chip .cico path { fill: var(--color-ink-faint); transition: fill var(--dur-fast) var(--ease-standard); }
.chip:hover .cico path { fill: var(--color-ink-muted); }
.chip[aria-selected="true"] .cico path { fill: var(--hue); }
.chip:hover { border-color: var(--color-ink-faint); color: var(--color-ink); }
.chip[aria-selected="true"] {
  border-style: solid; border-width: 2px; border-color: var(--color-accent);
  color: var(--color-accent-strong);
  padding: calc(var(--space-1) - 1px) calc(var(--space-3) - 1px);
}

/* product visual: a real screen, shown plainly. No bleed, no fade. */
/* The panel runs past the container toward the viewport edge so the screen is
   not cropped to a narrow column; .hero clips the overflow and the mask has
   already faded the edge before the clip lands. */
.hero-visual { position: relative; display: flex; width: calc(100% + 9vw); }
/* The panel fills the column so both sides finish level, and its bottom and
   right edges dissolve into the page rather than stopping on a hard border. */
.hero-frame {
  position: relative; margin: 0; flex: 1 1 auto;
  min-height: 100%; overflow: hidden;
  -webkit-mask-image:
    linear-gradient(to bottom, #000 62%, transparent 100%),
    linear-gradient(to right, #000 78%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to bottom, #000 62%, transparent 100%),
    linear-gradient(to right, #000 78%, transparent 100%);
  mask-composite: intersect;
}
/* ---- review bar, directly under the primary call to action -------------- */
.hero-reviews {
  display: flex; align-items: center; gap: var(--space-2);
  flex-wrap: wrap; margin-top: var(--space-4);
}
.hero-reviews .stars { display: inline-flex; gap: 1px; color: var(--lb-gold); }
.hero-reviews .stars svg { width: 13px; height: 13px; fill: currentColor; }
.hero-reviews .stars .star-half { fill: none; }
.hero-reviews-text { font-size: 0.6875rem; color: var(--color-ink-muted); }
.hero-reviews-logos { height: 15px; width: auto; display: block; }

/* ---- the twelve-tile grid ------------------------------------------------ */
/* Tiles are absolutely placed inside the frame so growing one to cover the
   whole area is a geometry transition. CSS grid cannot animate a cell
   changing span, which is what this effect needs. */
/* The single screen used to run past the container and dissolve at its bottom
   and right edges. A grid cannot do that: the bottom row and the right
   column would be cut in half, so the visual is pulled back inside the
   column and the mask is dropped. Every tile now finishes inside the frame. */
/* The grid is sized by HEIGHT, not width: it matches the copy column exactly,
   from the eyebrow down to the last chip, and stays inside the container so
   both sides line up with the header bar. */
.hero-visual { width: 100%; align-items: stretch; justify-content: flex-end; }
.hero-frame {
  --tile-gap: 10px;
  /* Sized by WIDTH now, not by the copy column's height. Height-first plus a
     max-width cap meant the cap won at desktop and the frame came out nearly
     square, which put a band above and below every 16:9 dashboard in it. */
  /* Centred against the copy, not hung from its top. The frame is width-driven
     and the copy column is taller than it, so top-aligning left the whole
     bottom right of the section empty. */
  flex: 1 1 auto; align-self: center;
  width: 100%; max-width: 100%; height: auto;
  /* 4:3 with three columns and four rows makes every cell 16:9, which is what
     the captures are drawn at, so a tile is a whole dashboard rather than a
     piece of one. Square cells were losing half the width of every screen. */
  /* Four rows of 16:9 cells come to 0.75 of the width, and the three pixel gaps
     add the rest, so the frame is a shade taller than 4:3 rather than any tidy
     ratio. 1.28 leaves a pixel of slack at every width; anything larger clips
     the last row against the overflow. */
  aspect-ratio: 1.28;
  min-height: 0;
  overflow: hidden;
  /* The softening is an edge treatment, not a crop. At 66% and 80% it was
     eating the whole fourth row and most of the third column, which is a row
     and a column of dashboards nobody could read. It now touches only the last
     few per cent. */
  -webkit-mask-image:
    linear-gradient(to bottom, #000 90%, transparent 100%),
    linear-gradient(to right, #000 93%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to bottom, #000 90%, transparent 100%),
    linear-gradient(to right, #000 93%, transparent 100%);
  mask-composite: intersect;
}
.hero-tile {
  position: absolute;
  margin: 0; padding: 0; background: var(--color-surface);
  border: 1px solid var(--color-rule-strong);
  border-radius: var(--radius-btn);
  overflow: hidden; cursor: pointer;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .06), 0 8px 20px -14px rgba(16, 24, 40, .5);
  left: calc(var(--c) * (100% + var(--tile-gap)) / 3);
  top: calc(var(--r) * (100% + var(--tile-gap)) / 4);
  width: calc((100% - var(--tile-gap) * 2) / 3);
  /* The height comes from the width, so a cell is 16:9 exactly whatever the
     frame ends up being. Rows are pitched off the frame height and the pitch is
     always the larger of the two, so they never overlap. */
  height: auto; aspect-ratio: 16 / 9;
  transition:
    left var(--dur-tile) var(--ease-assemble),
    top var(--dur-tile) var(--ease-assemble),
    width var(--dur-tile) var(--ease-assemble),
    height var(--dur-tile) var(--ease-assemble),
    opacity var(--dur-swap, 520ms) var(--ease-standard),
    transform var(--dur-swap, 520ms) var(--ease-standard),
    box-shadow var(--dur-swap, 520ms) var(--ease-standard);
  will-change: left, top, width, height;
}
/* contain, not cover: the whole screen is the point of the tile, so nothing is
   allowed to be cropped off it even when the gap arithmetic leaves the cell a
   fraction away from 16:9. */
.hero-tile img { width: 100%; height: 100%; object-fit: contain; background: var(--color-surface); display: block; }
.hero-tile:hover { box-shadow: 0 2px 4px rgba(16,24,40,.08), 0 16px 34px -16px rgba(16,24,40,.55); z-index: 2; }
.hero-tile:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; z-index: 3; }
/* the tile's own label, only once it is the one on show */

/* expanded: one tile covers the frame, the rest step aside */
/* Everything that is not the chosen tile goes back: blurred, desaturated and
   dimmed. Without it the open tile sat at the same brightness as the eleven
   around it and read as merged into them rather than lifted off them. The blur
   is a couple of pixels, which is enough to say "behind" and not enough to turn
   the grid into soup. */
.hero-frame.is-open .hero-tile {
  pointer-events: none;
  filter: blur(3px) saturate(0.55);
  opacity: 0.42;
  transition:
    left var(--dur-tile) var(--ease-assemble),
    top var(--dur-tile) var(--ease-assemble),
    width var(--dur-tile) var(--ease-assemble),
    filter var(--dur-swap) var(--ease-standard),
    opacity var(--dur-swap) var(--ease-standard);
}
/* The open tile is 16:9 inside the frame, centred, rather than filling it:
   full width at three quarters of a 4:3 height is exactly 16:9, so the screen
   opens whole with no band above or below it. Still four animatable
   properties, so the geometry transition is unchanged. */
.hero-frame.is-open .hero-tile.is-open {
  pointer-events: auto;
  left: 0; top: 12.5%; width: 100%;
  z-index: 5; border-radius: var(--radius-btn);
  /* Raised off the blurred grid: the deepest shadow in the scale, and a ring so
     the edge of the screenshot is still readable against a pale tile behind
     it. */
  filter: none; opacity: 1;
  border-color: var(--color-rule-strong);
  box-shadow: var(--shadow-4), 0 0 0 1px var(--color-rule-strong);
}

@media (max-width: 1000px) {
  .hero-inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-8);
    padding-block: var(--space-10) var(--space-12);
  }
  .hero h1, .hero .hero-h { max-width: 100%; }
}

/* ============================================================================
   REVIEW ONLY: chip variants. Delete this block and the markup below the live
   chip row once a direction is chosen.
   ========================================================================== */
.samples { border-top: 1px dashed var(--color-rule-strong); margin-top: var(--space-10); padding-top: var(--space-6); }
.samples h2 { font-size: var(--text-small); font-weight: var(--weight-bold); margin: 0 0 var(--space-2); }
.samples .note { font-size: var(--text-fine); color: var(--color-ink-muted); margin: 0 0 var(--space-6); }
.samples h3 { font-family: var(--font-mono); font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; color: var(--color-ink-muted); margin: var(--space-6) 0 var(--space-3); }
.samples .row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.cico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }

/* shared shape */
.sc {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font: inherit; font-size: var(--text-fine); font-weight: var(--weight-semibold);
  line-height: 1.5; color: var(--color-ink-muted); background: transparent;
  border: 1px dashed var(--color-rule-strong); border-radius: var(--radius-pill);
  padding: var(--space-1) var(--space-3); cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard),
              background-color var(--dur-fast) var(--ease-standard);
}
.sc .cico path { fill: var(--hue); }

/* A: icon in its own hue, selected goes green */
.sc--a[data-on="true"] { border-style: solid; border-width: 2px; border-color: var(--lb-green);
  color: var(--lb-green-ink); padding: calc(var(--space-1) - 1px) calc(var(--space-3) - 1px); }

/* B: icon sits in a tinted tile */
.sc--b .cico { width: var(--space-3); height: var(--space-3); }
.sc--b .tile { display: grid; place-items: center; width: var(--space-5); height: var(--space-5);
  border-radius: var(--radius-xs); background: var(--hue); flex: 0 0 auto; }
.sc--b .tile .cico path { fill: var(--color-icon-glyph); }
.sc--b { padding-left: var(--space-1); }
.sc--b[data-on="true"] { border-style: solid; border-width: 2px; border-color: var(--lb-green);
  color: var(--lb-green-ink); padding: calc(var(--space-1) - 1px) calc(var(--space-3) - 1px) calc(var(--space-1) - 1px) calc(var(--space-1) - 1px); }

/* C: solid hairline instead of dashed, green fill when chosen */
.sc--c { border-style: solid; }
.sc--c[data-on="true"] { border-width: 2px; border-color: var(--lb-green);
  background: var(--color-positive-tint); color: var(--lb-green-ink);
  padding: calc(var(--space-1) - 1px) calc(var(--space-3) - 1px); }

/* D: hue only on the chosen one, greyscale at rest */
.sc--d .cico path { fill: var(--color-ink-faint); }
.sc--d[data-on="true"] { border-style: solid; border-width: 2px; border-color: var(--lb-green); color: var(--lb-green-ink);
  padding: calc(var(--space-1) - 1px) calc(var(--space-3) - 1px); }
.sc--d[data-on="true"] .cico path { fill: var(--hue); }

/* CTA with the eyebrow's drifting gradient edge */
.btn--gradient {
  border: 2px solid transparent;
  background:
    linear-gradient(var(--color-accent), var(--color-accent)) padding-box,
    linear-gradient(90deg, var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
      var(--lb-gold), var(--lb-green), var(--lb-teal)) border-box;
  background-size: auto, 300% 100%;
  background-position: 0 0, 0 50%;
  animation: eyebrow-drift var(--dur-ambient) linear infinite;
  color: var(--color-on-accent);
}
.btn--gradient-quiet {
  border: 2px solid transparent;
  background:
    linear-gradient(var(--btn-quiet-fill, var(--color-surface)),
      var(--btn-quiet-fill, var(--color-surface))) padding-box,
    linear-gradient(90deg, var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
      var(--lb-gold), var(--lb-green), var(--lb-teal)) border-box;
  background-size: auto, 300% 100%;
  background-position: 0 0, 0 50%;
  animation: eyebrow-drift var(--dur-ambient) linear infinite;
  color: var(--color-ink);
}
@media (prefers-reduced-motion: reduce) { .btn--gradient, .btn--gradient-quiet { animation: none; } }

/* REVIEW ONLY: gradient fills for the CTA. Delete with the .samples block. */
.bg-full, .bg-drift, .bg-duo { color: var(--color-icon-glyph); border: 0; }
.bg-full {
  background: linear-gradient(90deg, var(--lb-teal), var(--lb-blue), var(--lb-purple),
    var(--lb-magenta), var(--lb-red), var(--lb-orange));
}
.bg-drift {
  background: linear-gradient(90deg, var(--lb-teal), var(--lb-blue), var(--lb-purple),
    var(--lb-magenta), var(--lb-red), var(--lb-orange), var(--lb-teal));
  background-size: 300% 100%;
  animation: eyebrow-drift var(--dur-ambient) linear infinite;
}
.bg-duo { background: linear-gradient(100deg, var(--lb-teal), var(--lb-blue)); }
@media (prefers-reduced-motion: reduce) { .bg-drift { animation: none; } }

/* The primary call to action carries the full brand spectrum as a fluid fill,
   drifting right to left. Same technique as the eyebrow edge, moved to the
   background. It loops, which standards forbids in general: scoped here and to
   the eyebrow only, and stopped under prefers-reduced-motion. */
.btn--spectrum {
  /* Keeps .btn's 1px transparent border rather than removing it, so the
     filled and ribbon buttons compute to identical heights. */
  color: var(--color-icon-glyph); border: 1px solid transparent;
  background-image: linear-gradient(100deg,
    var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
    var(--lb-red), var(--lb-orange), var(--lb-gold),
    var(--lb-orange), var(--lb-red), var(--lb-magenta),
    var(--lb-purple), var(--lb-blue), var(--lb-teal));
  background-size: 1200px 100%;
  background-repeat: repeat-x;
  animation: spectrum-drift calc(var(--dur-ambient) * 2) linear infinite;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .28);
}
.btn--spectrum:hover { filter: saturate(1.15); }
@keyframes spectrum-drift {
  from { background-position-x: 0; }
  to   { background-position-x: -1200px; }
}
@media (prefers-reduced-motion: reduce) {
  .btn--spectrum { animation: none; background-position: 0 50%; }
  /* The grid still recedes behind the open tile, it just does not animate. */
  .hero-frame.is-open .hero-tile { transition: none; }
}

/* ============================================================================
   HERO, SMALL SCREENS
   All of this is inside max-width queries. Nothing above 1000px changes.
   ========================================================================== */
@media (max-width: 1000px) {
  /* On desktop the grid is sized by height, matching the copy column. Stacked,
     there is no column to match, and height:100% resolved against an auto-height
     flex parent collapsed the frame to zero: the grid vanished entirely.
     Below this width it is sized by width instead. */
  .hero-visual { width: 100%; justify-content: stretch; }
  .hero-frame {
    height: auto; width: 100%; max-width: 34rem;
    margin-inline: auto;
  }
  /* nothing is cut off when the grid is centred, so the dissolve comes off */
  .hero-frame { -webkit-mask-image: none; mask-image: none; }

  .hero h1, .hero .hero-h { max-width: 18ch; }
  .hero .lede, .hero-points li { max-width: 42ch; }
}

@media (max-width: 900px) {
  /* Touch targets. The chips were 28px and the buttons 40px against a 44px
     minimum, so every one of them failed. */
  .chip { padding: var(--space-2) var(--space-4); min-height: 2.75rem; }
  .chip[aria-selected="true"] { padding: calc(var(--space-2) - 1px) calc(var(--space-4) - 1px); }
  .eyebrow { padding: var(--space-3) var(--space-4); }
  /* .btn's base floor is already past the 44px touch minimum, and setting a
     lower one here let the ribbon and filled buttons diverge again. */
  .btn--lg { width: 100%; justify-content: center; }
  .hero-cta { gap: var(--space-3); }
  .hero-cta .fine { text-align: left; }

  /* Nine chips stacked into seven ragged rows read as a list, not a control.
     One scrollable row keeps the row shape and the swipe is the obvious
     gesture on a phone. */
  .chips {
    flex-wrap: nowrap; overflow-x: auto; scroll-snap-type: x proximity;
    padding-bottom: var(--space-2);
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .chips::-webkit-scrollbar { display: none; }
  /* snap to centre, not start: otherwise the snap fights the auto-centring
     and every chip lands about 60 to 100px off. */
  .chip { flex: 0 0 auto; scroll-snap-align: center; }
}

@media (max-width: 560px) {
  .hero-inner { padding-block: var(--space-8) var(--space-10); gap: var(--space-6); }
  .hero h1, .hero .hero-h { max-width: 100%; }
  .hero .lede, .hero-points li { max-width: 100%; }
  .hero-reviews { gap: var(--space-2); }
  /* Tighter gap on a phone: the cells are already small and the gap is the
     only thing competing with them for width. */
  .hero-frame { --tile-gap: 8px; aspect-ratio: 1.28; }
}

@media (max-width: 900px) {
  /* Under the button, everything centres: the microcopy on one line, the
     review row, and the label above the chips. */
  .hero-cta { flex-direction: column; align-items: center; }
  .hero-cta .fine { text-align: center; }
  .hero-cta .fine-break { display: none; }   /* one line, not two */
  .hero-reviews { justify-content: center; text-align: center; }
  .hero-label { text-align: center; }
}

/* ============================================================================
   COMPONENT: Trust bar
   Two rows of logos travelling in opposite directions, edges dissolved with the
   same mask the hero grid uses. Greyscale until hovered.

   This loops, which standards.md 5 forbids in general. Scoped here, to the
   eyebrow and to the two spectrum buttons, and stopped completely under
   prefers-reduced-motion, where the rows become a plain scrollable strip.
   ========================================================================== */
.trustbar { padding: var(--space-8) 0; background: var(--color-paper); }
/* Promoted off the caption scale to a true H3: Plus Jakarta Sans at
   --text-h3 (22px), semibold, heading tracking, sentence case. The trust bar
   now reads as a section in its own right rather than as a label above a
   marquee. */
.trustbar-title {
  margin: 0 0 var(--space-8);
  text-align: center;
  font-family: var(--font-sans);
  font-size: var(--text-body); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-heading); text-transform: none;
  color: var(--color-ink);
}

.marquee {
  /* held to the same rails as the rest of the page, and faded at both ends so
     the loop has no visible first or last logo */
  max-width: var(--container); margin-inline: auto;
  display: grid; gap: var(--space-6);
  -webkit-mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,.05) 3%, rgba(0,0,0,.35) 6%, #000 9%, #000 91%, rgba(0,0,0,.35) 94%, rgba(0,0,0,.05) 97%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,.05) 3%, rgba(0,0,0,.35) 6%, #000 9%, #000 91%, rgba(0,0,0,.35) 94%, rgba(0,0,0,.05) 97%, transparent 100%);
}
.mq-row { overflow: hidden; }
.mq-track {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: center; gap: var(--space-12);
  width: max-content;
  /* three copies, travelling exactly one copy. Two was not enough: a single
     set of eleven is narrower than the strip, so the loop showed a gap. */
  animation-duration: calc(var(--dur-ambient) * 5);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.mq-row--ltr .mq-track { animation-name: mq-left; }   /* travels right to left */
.mq-row--rtl .mq-track { animation-name: mq-right; }  /* travels left to right */
@keyframes mq-left  { from { transform: translateX(0); }         to { transform: translateX(-33.3333%); } }
@keyframes mq-right { from { transform: translateX(-33.3333%); } to { transform: translateX(0); } }

/* pausing on hover lets a visitor actually look at, and reach, a logo */
.mq-row:hover .mq-track, .mq-track:focus-within { animation-play-state: paused; }

/* Fixed cells. The track width must not depend on whether an image has
   loaded: the animation translates by a percentage of that width, so any
   change mid-loop makes the row jump. A fixed box also evens out wide
   wordmarks against compact marks. */
.mq-item { flex: 0 0 auto; display: flex; align-items: center; }
.mq-item img {
  height: var(--space-14); width: auto; display: block;
  /* Neutral for now, pending a decision on the treatment: greyscale at 78
     percent, no contrast or brightness push. Contrast made the already-dark
     marks heavy, and brightness is unusable while any file carries a white
     background. Full colour on hover. */
  filter: grayscale(1); opacity: .78;
  transition: filter var(--dur-base) var(--ease-standard),
              opacity var(--dur-base) var(--ease-standard);
}
.mq-item:hover img { filter: grayscale(0) contrast(1); opacity: 1; }

/* Dark variant of the strip. It sits directly under the customer stories band
   and carries the same --color-ink ground, so the two read as one dark region
   rather than two stacked bands.

   The marks have to be inverted, not just desaturated: these are dark navy and
   black wordmarks on transparent, and at the light strip's treatment they would
   vanish against the ground. Desaturate then invert, in that order, and do NOT
   flatten with brightness(0) first: several marks carry knocked-out white text
   inside a filled shape, GDPR and Section 508 among them, and flattening turns
   those into solid blocks with the wording gone. */
.trustbar--dark { background: var(--color-ink-deep); color: var(--color-ink-on-dark); }
.trustbar--dark .trustbar-title { color: var(--color-ink-on-dark); }
.trustbar--dark .mq-item img { filter: grayscale(1) invert(1); opacity: .72; }
.trustbar--dark .mq-item:hover img { filter: grayscale(1) invert(1); opacity: 1; }

@media (max-width: 900px) {
  .trustbar { padding: var(--space-6) 0; }
  .trustbar-title { margin-bottom: var(--space-6); }
  .mq-track { gap: var(--space-8); }
  .mq-item img { height: var(--space-12); }
}

@media (prefers-reduced-motion: reduce) {
  .mq-track { animation: none; }
  .mq-row { overflow-x: auto; }
  .mq-track > [aria-hidden="true"] { display: none; }  /* no duplicate set when static */
}



/* ---------- 03 the chart wall -------------------------------------------
   Same tile language as the hero grid, deliberately: --color-surface fill,
   a --color-rule-strong hairline, --radius-btn corners, the hero's own
   two-layer shadow, the same 10px tile gap, and cover crops anchored top
   left. The two grids sit one scroll apart, so anything else would read as
   a different product.

   Geometry: 8 columns by 6 rows inside --container, tiles square. Each card
   is 2 columns by 2 rows, so the 2x2 block covers columns 3 to 6 and rows 2
   to 5: dead centre on both axes, two tile columns each side, one tile row
   above and below. Both counts have to be even for that centring to be exact
   rather than approximate, which is why it is 6 rows and not 5.

   Every tile carries a different dashboard, thirty-two of them, at full
   opacity. No dimming, no edge fade, no hover opacity change: the only thing
   hover moves is the shadow, exactly as in the hero. */
.cw { --cw-gap: 10px; padding: var(--space-12) 0; background: var(--color-paper); }

.cw-head { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); text-align: center; }
/* Section heading and lede follow the house pattern set by .cs-head and
   .cs-lede: display scale, bold, --tracking-display, capped at 20ch, with the
   lede on --text-body-lg at 46ch. No hardcoded letter-spacing. */
.cw-h {
  margin: 0 auto var(--space-3); max-width: 20ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink);
}
.cw-lede {
  margin: 0 auto var(--space-12); max-width: 46ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}

.cw-grid {
  max-width: var(--container); margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: var(--cw-gap);
  /* Left and right edges dissolve, matched to the hero frame by ACTUAL WIDTH
     rather than by percentage. The hero fades over the last 20 percent of a
     581px frame, which is 116px of fade. The same 20 percent across a 1280px
     grid would be 256px and swallows two tile columns, which is wrong. 9
     percent of 1280 reproduces the hero's 116px, so the fade reads identical
     and the outer tiles stay legible.

     The band stays 9 percent. What changed is the curve inside it: a straight
     ramp is already half opaque a third of the way in, which left a readable
     tile edge at the extreme sides. The stops now hold near zero for the first
     3 percent and only reach 35 percent by 6 percent across, so the outermost
     sliver dissolves into the page while the fade occupies exactly the same
     width. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,.05) 3%, rgba(0,0,0,.35) 6%, #000 9%, #000 91%, rgba(0,0,0,.35) 94%, rgba(0,0,0,.05) 97%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,.05) 3%, rgba(0,0,0,.35) 6%, #000 9%, #000 91%, rgba(0,0,0,.35) 94%, rgba(0,0,0,.05) 97%, transparent 100%);
}

/* The homepage wall shows component cards rather than cropped dashboards, per
   the owner's direction: colorful single charts, uncut. The card family is
   16:10 by construction, so a 16:10 tile shows the whole capture with no crop
   and no letterbox. The catalogue and the product hero keep the square tile. */
.cw--cards .cw-tile { aspect-ratio: 16 / 10; }
.cw--cards .cw-tile img { object-position: center; }

/* The homepage sector switcher shows each sector's own dashboard whole: the
   capture is 16:9, so the box is too, and the margin that left a dead band to
   the panel's right is gone. */
/* The homepage sector switcher lays its panel out like the catalogue's
   audience switcher: copy in the left column, the sector's own dashboard in
   the right, and real padding all round. The homepage panel has no
   .sx-panel-in wrapper, so the grid sits on the panel itself. */
.sx--flush .sx-panel {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  gap: var(--space-8);
  align-items: center;
  padding: var(--space-10);
}
/* The base rules pin sx-body to row 2 and stretch the shot across both rows,
   which in a one-row grid pushed the copy below the picture. One row, both
   cells on it. */
.sx--flush .sx-body { grid-column: 1; grid-row: 1; min-width: 0; }
.sx--flush .sx-shot {
  grid-column: 2; grid-row: 1; margin: 0; align-self: center;
  aspect-ratio: 16 / 9; min-width: 0;
}
.sx--flush .sx-shot img { object-position: center top; }
@media (max-width: 1000px) {
  .sx--flush .sx-panel { grid-template-columns: minmax(0, 1fr); padding: var(--space-6); }
  .sx--flush .sx-body, .sx--flush .sx-shot { grid-column: 1; }
}

/* On the charts page the shape panel's example card runs the full height of
   the panel and sits flush against its right edge. */
.sx--edge .sx-panel { overflow: hidden; }
.sx--edge .sx-panel-in {
  padding: 0 0 0 var(--space-8);
  /* One row, two columns. The copy is a single column element now, so the
     picture column spans nothing: it can be as tall as it needs without
     prising the heading away from the words under it. */
  grid-template-rows: auto;
  align-content: stretch;
}
.sx--edge .sx-side {
  grid-column: 1; grid-row: 1;
  display: flex; flex-direction: column; justify-content: center;
  gap: var(--space-4);
  padding: var(--space-8) 0;
}
.sx--edge .sx-side .sx-panel-top { padding: 0 0 var(--space-2); }
.sx--edge .sx-side .sx-body { padding: 0; }
.sx--edge .sx-shot { grid-column: 2; grid-row: 1; }
.sx--edge .sx-panel-top { padding-bottom: var(--space-2); }
.sx--edge .sx-body { padding-bottom: var(--space-8); }
.sx--edge .sx-shot {
  /* Stretched to the panel and cropped to fill, the card lost its bottom row,
     which on a gauge is the number. The capture is a white card, so it is
     contained on the panel's own surface and nothing is cut. */
  margin: 0; aspect-ratio: auto; align-self: stretch;
  display: grid; place-items: center;
  padding: var(--space-6);
  background: var(--color-surface);
  border-radius: 0; border: 0;
  border-left: 1px solid color-mix(in srgb, var(--sx-hue) 22%, var(--color-rule));
  box-shadow: none;
}
.sx--edge .sx-shot {
  /* Two examples share the column, one above the other, so the panel is
     filled rather than banded above and below a single card. The column grows
     to hold them and the copy beside it centres against that. */
  display: flex; flex-direction: column; justify-content: center;
  align-items: stretch; gap: var(--space-4);
  align-self: center; overflow: hidden;
  /* No fixed ratio: the box hugs the picture. A square box around a card
     that is wider than tall left a band of panel above and below it, which
     read as wasted height. */
}
.sx--edge .sx-shot img {
  /* The library expander pins its capture absolutely so a tall one cannot
     drive the panel. Here the picture is the point, so it flows at its own
     height and the box takes exactly that. */
  position: static; inset: auto;
  width: 100%; height: auto; flex: 0 0 auto;
  object-fit: contain; object-position: center;
  border-radius: var(--radius-md);
}

@media (max-width: 1000px) {
  .sx--edge .sx-panel-in { padding: var(--space-6); }
  .sx--edge .sx-shot { height: auto; aspect-ratio: 16 / 10; border: 1px solid var(--color-rule); border-radius: var(--radius-lg); }
}

/* The eyebrow pill above a section heading, injected by the build so every
   section wears the same animated-border pill the homepage does. */
/* The pill centres with the heading it introduces. The alternating rows
   keep their own left-aligned eyebrow, which belongs to a column. */
.sec-eyebrow { margin: 0 0 var(--space-5); text-align: center; }
.dr-in .sec-eyebrow, .ql-in .sec-eyebrow, .cb-in .sec-eyebrow,
.uc-in .sec-eyebrow, .fd-in .sec-eyebrow, .rs-in .sec-eyebrow { text-align: center; }

/* The catalogue directory: linked names, and a plan badge on the right. */
.dr-text { min-width: 0; }
.dr-name a { color: inherit; text-decoration: none; }
.dr-name a:hover { color: var(--color-accent-strong); text-decoration: underline; }
.dr-badge {
  /* Beside the name, not across the row, where it collided with the next
     column's icon. */
  display: inline-block; vertical-align: middle;
  margin-left: var(--space-2);
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-fine); font-weight: var(--weight-bold);
  line-height: var(--leading-loose);
  background: color-mix(in srgb, var(--dr-hue) 16%, transparent);
  color: color-mix(in srgb, var(--dr-ink) 80%, var(--color-ink-deep));
}

/* The mark on a use-case card, in the card's own hue. */
.uc-mark {
  display: grid; place-items: center;
  width: var(--space-11); height: var(--space-11);
  margin: 0 0 var(--space-4);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--uc-hue, var(--lb-teal)) 15%, var(--color-surface));
}
.uc-ico { width: var(--space-5); height: var(--space-5); }
.uc-ico path { fill: color-mix(in srgb, var(--uc-hue-ink, var(--lb-teal-ink)) 88%, var(--color-ink-deep)); }
.uc-card:has(.uc-mark) .uc-chip { align-self: flex-start; }
/* The mark belongs to the heading, so it sits on the same line as the
   heading. Stacked above the badge it read as a third separate start and
   took a band of the card to say nothing. */
.uc-head {
  display: flex; align-items: center; gap: var(--space-3);
  margin: 0 0 var(--space-2);
}
.uc-head .uc-mark {
  margin: 0; flex: 0 0 auto;
  width: var(--space-9); height: var(--space-9);
}
.uc-head .uc-ico { width: var(--space-4); height: var(--space-4); }
.uc-head .uc-title {
  margin: 0; min-width: 0;
  /* A title can be one unbroken token - "department/role/location" - and a
     flex item will not shrink below its longest word unless told it may. */
  overflow-wrap: anywhere;
}

/* The question block reads as one centred column: eyebrow, heading, lede and
   the "still stuck" link. The category cards below keep their own left
   alignment, because a card's title and its summary are a reading column. */
.fq-head {
  /* Centred means centred on the section, not centred inside a 52ch column
     that still sits against the left edge. */
  text-align: center; margin-inline: auto;
}
.fq-head .fq-lede { margin-inline: auto; }
.fq-head .fq-eyebrow { display: flex; justify-content: center; }
.fq-help { display: flex; justify-content: center; gap: var(--space-2); }
.fq-cat-head, .fq-cat-body { text-align: left; }

/* tile: the hero's treatment, copied rather than reinterpreted */
.cw-tile {
  aspect-ratio: 1 / 1;
  background: var(--color-surface);
  border: 1px solid var(--color-rule-strong);
  border-radius: var(--radius-btn);
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .06), 0 8px 20px -14px rgba(16, 24, 40, .5);
  transition: opacity var(--dur-base) var(--ease-standard),
              box-shadow var(--dur-base) var(--ease-standard);
}
/* A square crop of a 16:9 dashboard has to choose which part of it to keep.
   Anchored at the top it lands on the header strip, which on the refined
   captures is mostly empty, and a wall of them reads as a wall of blank
   tiles. Just under half way down is where the charts are. */
.cw-tile img {
  /* A card, contained: it is already the shape of the tile give or take, and
     cropping one cuts off the title that says what it is. */
  width: 100%; height: 100%; object-fit: contain; object-position: center;
  display: block; background: var(--color-surface);
}
.cw-tile:hover { box-shadow: 0 2px 4px rgba(16, 24, 40, .08), 0 16px 34px -16px rgba(16, 24, 40, .55); }

.cw-card {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-3); text-align: center;
  padding: var(--space-6) var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-rule-strong);
  border-radius: var(--radius-btn);
  box-shadow: var(--shadow-3);
  animation: cw-lift var(--dur-slow) var(--ease-assemble) backwards;
}
.cw-card--a { grid-column: 3 / span 2; grid-row: 2 / span 2; }
.cw-card--b { grid-column: 5 / span 2; grid-row: 2 / span 2; animation-delay: var(--dur-instant); }
.cw-card--c { grid-column: 3 / span 2; grid-row: 4 / span 2; animation-delay: var(--dur-fast); }
.cw-card--d { grid-column: 5 / span 2; grid-row: 4 / span 2; animation-delay: calc(var(--dur-fast) + var(--dur-instant)); }

@keyframes cw-lift { from { opacity: 0; transform: translateY(var(--space-3)); } to { opacity: 1; transform: none; } }

.cw-ico { width: var(--space-8); height: var(--space-8); fill: var(--color-accent-strong); display: block; flex: 0 0 auto; }
.cw-label {
  margin: 0; font-size: var(--text-h4); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-heading); color: var(--color-ink);
}
.cw-line { margin: 0; font-size: var(--text-small); line-height: var(--leading-normal); color: var(--color-ink-muted); }

.cw-cta {
  display: flex; align-items: center; justify-content: center; gap: var(--space-6);
  flex-wrap: wrap; margin-top: var(--space-12); padding-inline: var(--gutter);
}
/* Laptop: 6 columns, cards hold columns 2 to 5, still exactly centred.
   6x6 less the 16 the cards occupy leaves 20 tiles, so the last 12 go. */
@media (max-width: 1100px) {
  /* the 9 percent fade needs no adjustment here: it lands well short of the
     cards, which start at 18.7 percent once the grid is six columns */
  .cw-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .cw-card--a, .cw-card--c { grid-column: 2 / span 2; }
  .cw-card--b, .cw-card--d { grid-column: 4 / span 2; }
  /* Counted from the START, not the end. The preview stage is the last grid
     child, so an nth-last-child rule silently trims one tile too few and the
     leftover wraps onto a seventh row. Cards are children 1 to 4, tiles are 5
     onward, and six columns needs 20 tiles, so everything from the 25th child
     goes. */
  .cw-tile:nth-child(n+25) { display: none; }
}

/* Phone: the field goes and the cards become an ordinary two-up, then one up.
   Squeezing eight tile columns into 375px produces slivers, not texture. */
@media (max-width: 760px) {
  .cw { padding: var(--space-16) 0; }
  /* no tiles left to fade, and a mask here would only eat the cards */
  .cw-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4);
    -webkit-mask-image: none; mask-image: none;
  }
  .cw-tile { display: none; }
  .cw-card { grid-column: auto / auto; grid-row: auto / auto; }
}
@media (max-width: 520px) {
  .cw-grid { grid-template-columns: minmax(0, 1fr); }
  .cw-cta { flex-direction: column; gap: var(--space-4); }
}

/* Touch targets. Scoped to 900px, which is where the mobile nav takes over,
   so the 1440 view cannot be affected. Two separate problems:
   the demo link is a 30px text link, fine for a cursor and too short for a
   thumb; and the sheet's close button carries .nav-toggle's 44px but is a flex
   item inside .sheet-head, so it was being shrunk to 32px. flex-shrink stops
   that rather than fighting it with width. */
@media (max-width: 900px) {
  .qa-more, .sx-link, .im-link {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: var(--space-12);
  }
  .nav-toggle { flex: 0 0 auto; min-width: var(--space-12); min-height: var(--space-12); }
}

@media (prefers-reduced-motion: reduce) {
  .cw-card { animation: none; }
}


/* --- card hues -----------------------------------------------------------
   One logo colour per card, taken from the --lb-*-ink set rather than the raw
   mark hues: the raw teal is 2.65:1 and fill-only, the ink variants clear 4.5
   so the glyphs stay legible as non-text UI. */
.cw-ico { fill: var(--cw-hue, var(--color-accent-strong)); }

/* --- tile becomes a control ---------------------------------------------- */
.cw-tile { padding: 0; cursor: pointer; font: inherit; color: inherit; }
.cw-tile:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; z-index: 3; }

/* --- the stage -----------------------------------------------------------
   Same contract as the hero: the element is absolutely placed and left, top,
   width and height are transitioned, so the picture travels from the tile that
   was clicked into the middle of the grid. Duration and easing are the hero's
   own --dur-tile and --ease-assemble, so the two read as one behaviour. */
.cw-grid { position: relative; }
.cw-stage {
  position: absolute; z-index: 5; overflow: hidden;
  background: var(--color-surface);
  border: 1px solid var(--color-rule-strong);
  border-radius: var(--radius-btn);
  box-shadow: var(--shadow-4);
  opacity: 0;
  transition:
    left var(--dur-tile) var(--ease-assemble),
    top var(--dur-tile) var(--ease-assemble),
    width var(--dur-tile) var(--ease-assemble),
    height var(--dur-tile) var(--ease-assemble),
    opacity var(--dur-fast) var(--ease-standard);
  will-change: left, top, width, height;
}
.cw-stage.is-open { opacity: 1; }
.cw-stage img {
  /* LB-01. Contained, never cropped: the stage is sized by the script, so
     the picture scales to whichever side hits first and centres on the
     other. A grid area's auto row made the height cap circular and the
     bottom of every card was cut; flex against the stage's set height is
     definite, so the cap holds. */
  display: block;
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  object-fit: contain;
}
.cw-stage { display: flex; align-items: center; justify-content: center; }
.cw-stage-close {
  position: absolute; top: var(--space-2); right: var(--space-2);
  display: grid; place-items: center;
  width: var(--space-8); height: var(--space-8); padding: 0;
  border: 1px solid var(--color-rule-strong); border-radius: var(--radius-pill);
  background: var(--color-surface); color: var(--color-ink); cursor: pointer;
  box-shadow: var(--shadow-1);
}
.cw-stage-close svg { width: var(--space-4); height: var(--space-4); }
.cw-stage-close:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* the four cards step aside while a preview is up, the way the hero's other
   tiles do */
.cw-grid.is-previewing .cw-card {
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-standard);
}


/* ---------- 04 log in Monday ---------------------------------------------
   Amplemarket's shape: a dark ground, an eyebrow pill, the headline, one line
   under it, a single quiet CTA, then the product shot sitting large with small
   cards floating over its edges. The cards drift at different rates as the
   section crosses the viewport, which is the parallax; the shot itself holds
   still so nothing important slides out from under the reader.

   The shot is the same capture the hero and the chart wall use. On a dark
   ground a light screenshot reads as the brightest thing on the page, which is
   the whole reason Amplemarket inverts here. */
.mm {
  /* --color-ink-deep is the dark ground for every dark band on this page. */
  background: var(--color-ink-deep);
  color: var(--color-ink-on-dark);
  padding: var(--space-12) 0;
  overflow: hidden;
}
.mm-head { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); text-align: center; }
.mm-head .eyebrow { margin-bottom: var(--space-5); }


.mm-h {
  margin: 0 auto var(--space-3); max-width: 20ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink-on-dark);
}
.mm-lede {
  margin: 0 auto var(--space-12); max-width: 54ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 74%, transparent);
}

/* ---- the stage ---- */
.mm-stage {
  position: relative;
  /* The stage spans the full site width because it is what the cards are
     positioned against: card one starts at the left edge of the container and
     card two finishes at the right edge, the way Amplemarket hangs them off
     the page rails. The screenshot itself is narrower and centred inside, so
     the cards overlap only its outer corners instead of burying it. */
  max-width: var(--container); margin: var(--space-16) auto 0;
  padding-inline: var(--gutter);
}
.mm-shot {
  position: relative; z-index: 1;
  max-width: 54rem; margin-inline: auto;
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
  overflow: hidden;
  box-shadow: var(--shadow-4);
}
.mm-shot img { width: 100%; height: auto; display: block; }

/* ---- the floating cards ----
   Positioned as a share of the stage so they hold their relationship to the
   screenshot at any width. --mm-shift is written by script on scroll; each
   card carries its own --mm-rate so they travel at different speeds. */
.mm-card {
  position: absolute; z-index: 2;
  /* Fixed width and a floor on the height so all three read as one set. The
     floor is what keeps them equal once the shortest card's copy fits on
     fewer lines than the others. */
  width: 18rem; min-height: var(--space-20);
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--space-3) var(--space-4);
  /* 10 percent transparent, so the screenshot reads through without the copy
     losing contrast. The blur keeps the text crisp over busy chart areas. */
  background: color-mix(in srgb, var(--color-surface) 90%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border-radius: var(--radius-md);
  /* Two token shadows stacked: the tight one separates the card from the light
     screenshot underneath, the wide one drops it away from the dark ground. */
  box-shadow: var(--shadow-3), var(--shadow-4);
  transform: translate3d(
    calc(var(--mm-pointer, 0px) * var(--mm-rate-x, 1)),
    calc(var(--mm-shift, 0px) * var(--mm-rate, 1)), 0)
    rotate(var(--mm-tilt, 0deg));
  transition: transform var(--dur-base) var(--ease-standard);
  will-change: transform;
}
.mm-card .mm-q {
  display: flex; align-items: center; gap: var(--space-2);
  margin: 0 0 var(--space-1);
  font-size: var(--text-small); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-heading); color: var(--color-ink);
  white-space: nowrap;
}
.mm-card .mm-q::before {
  content: ""; width: var(--space-3); height: var(--space-3); flex: 0 0 auto;
  border-radius: var(--radius-pill); background: var(--mm-hue, var(--lb-teal));
}
.mm-card p { margin: 0; font-size: var(--text-fine); line-height: var(--leading-normal); color: var(--color-ink-muted); }

/* left and right are the gutter, not 0: the cards must finish inside the
   site's safe width, on the same rails as the heading, not on the raw
   container edge. */
.mm-card--1 { left: var(--gutter); top: 24%; --mm-rate: 1; --mm-rate-x: 1; }
.mm-card--2 { right: var(--gutter); top: 44%; --mm-rate: -0.62; --mm-rate-x: -0.75; }
.mm-card--3 { left: 28%; bottom: -4%; --mm-tilt: -2.5deg; --mm-rate: 0.38; --mm-rate-x: 0.5; }

@media (max-width: 900px) {
  .mm { padding: var(--space-16) 0; }
  .mm-stage { margin-top: var(--space-10); }
  /* Cards stop floating and become an ordinary list. Overlapping a 375px wide
     screenshot with three cards buries the thing they point at. */
  .mm-card {
    position: static; width: auto; min-height: 0; transform: none;
    margin-top: var(--space-4);
  }
  .mm-card .mm-q { white-space: normal; }
  .mm-cards { display: grid; gap: 0; }
}

@media (prefers-reduced-motion: reduce) {
  /* keep the tilt, drop the drift */
  .mm-card { transform: rotate(var(--mm-tilt, 0deg)); }
}


/* ---------- 05 the capability marquee -------------------------------------
   Amplemarket's closing band: rows of small pills drifting in alternating
   directions under one heading. Three rows of ten, thirty in total, every one
   drawn from the safe-as-fact list in brief.md.

   Same mechanics as the logo strips: three copies of each row translating by
   exactly one copy, so the loop has no seam. Top and bottom rows travel right
   to left, the middle row travels left to right. */
.op {
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  padding: var(--space-12) 0;
  overflow: hidden;
}
.op-head { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); text-align: center; }
/* Same treatment as the compliance strip's title: body scale, semibold,
   heading tracking, sentence case. It labels the band rather than competing
   with the section headings above it. */
.op-h {
  margin: 0 auto var(--space-12); max-width: 22ch;
  font-family: var(--font-sans); font-size: var(--text-display);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  line-height: var(--leading-tight); color: var(--color-ink-on-dark);
}

/* Held to the site width, and masked with the SAME gradient the logo strips
   use, stops and all. Two differences were making this read as a hard clip
   rather than a fade: it carried the plain two stop ramp while the strips had
   moved to the steeper multi stop one, and it had gutter padding the strips do
   not, which pushed the ramp off the content. Both are gone. */
.op-rows {
  max-width: var(--container); margin-inline: auto;
  display: grid; gap: var(--space-4);
  -webkit-mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,.05) 3%, rgba(0,0,0,.35) 6%, #000 9%, #000 91%, rgba(0,0,0,.35) 94%, rgba(0,0,0,.05) 97%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, rgba(0,0,0,.05) 3%, rgba(0,0,0,.35) 6%, #000 9%, #000 91%, rgba(0,0,0,.35) 94%, rgba(0,0,0,.05) 97%, transparent 100%);
}
.op-row { overflow: hidden; }
.op-track {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: center; gap: var(--space-4);
  width: max-content;
  animation-duration: calc(var(--dur-ambient) * 6);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.op-row--ltr .op-track { animation-name: op-left; }
.op-row--rtl .op-track { animation-name: op-right; }
/* the middle row is slower, so the three never lock into one rhythm */
.op-row:nth-child(2) .op-track { animation-duration: calc(var(--dur-ambient) * 8); }
@keyframes op-left  { from { transform: translateX(0); }         to { transform: translateX(-33.3333%); } }
@keyframes op-right { from { transform: translateX(-33.3333%); } to { transform: translateX(0); } }
/* hovering anywhere in the band stops all three rows, not just the one under
   the cursor: stopping one while the others run reads as a glitch */
.op-rows:hover .op-track, .op-track:focus-within { animation-play-state: paused; }

.op-pill {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  /* the button radius, not a pill: these sit on the same page as Start free */
  border-radius: var(--radius-btn);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
  background: color-mix(in srgb, var(--color-ink-on-dark) 6%, transparent);
  font-size: var(--text-fine); font-weight: var(--weight-semibold);
  white-space: nowrap; color: var(--color-ink-on-dark);
}
.op-ico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; fill: var(--op-hue, var(--lb-teal)); }

@media (max-width: 900px) {
  .op { padding: var(--space-16) 0; }
  .op-h { margin-bottom: var(--space-6); }
  .op-track { gap: var(--space-3); }
  .op-pill { padding: var(--space-2) var(--space-3); }
}

@media (prefers-reduced-motion: reduce) {
  .op-track { animation: none; }
  .op-row { overflow-x: auto; }
  .op-track > [aria-hidden="true"] { display: none; }
}



/* ---------- 05 the nine questions -----------------------------------------
   A plain 3x3. Every tile is the same: white ground, hairline border, a mono
   label naming the dashboard, and the question. No imagery and no filled
   variants, so the reader compares questions rather than decorations.

   Light ground on purpose: the four sections before this are dark. */
/* overflow-x: clip, not hidden. The marks travel wide during the merge and one
   of them reached past the viewport, which added a horizontal scrollbar to the
   whole page. clip contains that without creating a scroll container, so the
   sticky pins inside keep working; overflow: hidden would break them. */
/* Bottom padding is deliberately short. The section ends on a single small
   pill, not a full-width block, and it hands over to another paper section, so
   the standard 96px reads as a void. Matches the mm to op handover, which is
   the other same-ground boundary on the page. */
.qa { background: var(--color-paper); padding: var(--space-12) 0 var(--space-4); overflow-x: clip; }
.qa-inner { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }

.qa-head { text-align: center; }
.qa-h {
  margin: 0 auto var(--space-3); max-width: 20ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink);
}
.qa-lede {
  margin: 0 auto var(--space-12); max-width: 46ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}

/* grid-auto-rows: 1fr so all three rows match, otherwise the row holding a
   two-line question sits taller than the other two and the 3x3 stops reading
   as a grid */
.qa-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: 1fr; gap: var(--space-4);
}

/* Tiles are white at rest and only take colour under the cursor. The hue pair
   lives on a ::before overlay rather than on the card itself, because a
   background-image cannot be transitioned: fading the overlay's opacity gives
   a real animation instead of a hard swap.
   Each tile still has its own unique pair, twelve pairs across twelve cards.
   Orange and magenta stay out; tokens.css marks them LEGACY. */
.qa-card {
  position: relative; isolation: isolate;
  display: flex; align-items: center; gap: var(--space-5);
  padding: var(--space-6);
  min-height: var(--space-24);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  border: 1px solid var(--color-rule);
  transition: border-color var(--dur-base) var(--ease-standard),
              box-shadow var(--dur-base) var(--ease-standard);
}
/* The tile is white until its own mark arrives, then takes its colour: the
   overlay's opacity is driven by the same --qa-reveal the flight writes, so the
   ground fades up exactly as the mark lands. It defaults to 1, so with no
   script, on a phone, or under reduced motion the tiles are simply coloured.

   Hover is the inverse: the ground drops back to white while the hue border
   stays, so the tile reads as outlined rather than filled. */
.qa-card::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--qa-a) 26%, var(--color-surface)) 0%,
      color-mix(in srgb, var(--qa-a) 9%, var(--color-surface)) 58%,
      color-mix(in srgb, var(--qa-b) 16%, var(--color-surface)) 100%);
  opacity: var(--qa-reveal, 1);
  transition: opacity var(--dur-base) var(--ease-standard);
}
.qa-card:hover::before { opacity: 0; }
.qa-card:hover {
  border-color: color-mix(in srgb, var(--qa-a) 38%, var(--color-rule));
  box-shadow: var(--shadow-2);
}

/* The chip is neutral at rest and picks up the tile's hue on hover, so the
   colour arrives as one movement rather than in two places at once. */
/* The chip is drawn exactly like the mark that lands on it, same hue, so the
   handover is invisible. */
.qa-chip {
  flex: 0 0 auto; display: grid; place-items: center;
  width: var(--space-14); height: var(--space-14);
  border-radius: var(--radius-pill);
  border: 0;
  background: var(--qa-m, var(--lb-teal));
  box-shadow: var(--shadow-2);
  /* The numeral inside is drawn with currentColor by a later rule, so the
     colour has to be set here or the glyph inherits the body ink and
     disappears into the pill. */
  color: var(--color-ink-on-dark);
}
/* The numeral sits on a saturated pill, so it is white on every hue
   rather than the glyph ink, which vanished on the lighter ones. */
.qa-ico { width: var(--space-6); height: var(--space-6); fill: var(--color-ink-on-dark); }
.qa-text { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }

.qa-label {
  margin: 0; font-family: var(--font-mono); font-size: var(--text-caption);
  letter-spacing: var(--tracking-caption); text-transform: uppercase;
  color: var(--color-ink-muted);
}
.qa-q {
  margin: 0; font-size: var(--text-h4); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-heading);
  color: var(--color-ink);
}

.qa-foot { margin-top: var(--space-10); text-align: center; }
.qa-more {
  font-size: var(--text-ui); font-weight: var(--weight-semibold);
  color: var(--color-accent-strong); text-decoration: none;
  border-bottom: 1px solid currentColor; padding-bottom: var(--space-1);
}
.qa-more:hover { color: var(--color-ink); }

@media (max-width: 900px) {
  .qa { padding: var(--space-16) 0 var(--space-4); }
  .qa-lede { margin-bottom: var(--space-8); }
  .qa-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .qa-card { padding: var(--space-5); min-height: 0; gap: var(--space-4); }
  .qa-chip { width: var(--space-12); height: var(--space-12); }
  .qa-ico { width: var(--space-5); height: var(--space-5); }
  .qa-q { font-size: var(--text-body); }
}
@media (max-width: 560px) {
  .qa-grid { grid-template-columns: minmax(0, 1fr); }
}


/* ---- the sticky split ---------------------------------------------------
   The heading, the lede and the mark travel together. They scroll up as
   ordinary content, then pin near the top of the screen and hold there while
   the marks divide and fly down into the twelve tiles. The heading is still on
   screen when they land, which is the point: you read the question the section
   is asking while its answers arrive.

   Sticky, not fixed. A fixed layer paints whether or not its section is on
   screen, which is what put the mark over the section above.

   The marks do not vanish on arrival. They land exactly on each tile's chip,
   which is the same mark drawn the same way, and only then are the flying
   copies dropped. What you are left with is a question mark sitting on every
   card, permanently. */
/* The runway sits ABOVE the grid in paint order. Without this the flying marks
   travel behind the tiles, which are opaque and come later in the DOM, so they
   vanish mid-flight and only seem to reappear when the tiles' own chips show. */
.qa-run { height: 96vh; position: relative; z-index: 2; }
/* Clear of the sticky site header, which is 76px, with the section's own
   breathing room kept above the heading rather than collapsing onto the bar. */
.qa-pin { position: sticky; top: calc(var(--space-16) + var(--space-20)); }
.qa-mark {
  position: relative;
  width: var(--space-14); height: var(--space-14);
  margin: var(--space-16) auto 0;
}

/* The parent mark is double the size the landed ones are, and carries the same
   drifting spectrum the Start free button uses. It is a separate element from
   the flyers so the gradient never has to be animated on twelve nodes at once:
   it simply fades out as they leave, and they scale down into place. */
.qa-parent {
  position: absolute; left: 50%; top: 50%;
  width: calc(var(--space-14) * 2); height: calc(var(--space-14) * 2);
  margin-left: calc(var(--space-14) * -1); margin-top: calc(var(--space-14) * -1);
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  color: var(--color-icon-glyph);
  background-image: linear-gradient(100deg,
    var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
    var(--lb-red), var(--lb-orange), var(--lb-gold),
    var(--lb-orange), var(--lb-red), var(--lb-magenta),
    var(--lb-purple), var(--lb-blue), var(--lb-teal));
  background-size: 1200px 100%;
  background-repeat: repeat-x;
  animation: spectrum-drift calc(var(--dur-ambient) * 2) linear infinite;
  box-shadow: var(--shadow-3);
  will-change: opacity, transform;
}
.qa-parent .qa-ico { width: var(--space-10); height: var(--space-10); fill: var(--color-icon-glyph); }
@media (prefers-reduced-motion: reduce) {
  .qa-parent { animation: none; background-position: 0 50%; }
}
.qa-fly { position: relative; width: 100%; height: 100%; pointer-events: none; }
.qa-fly.is-done { display: none; }

/* Trailing runway. Two jobs. It gives the marks somewhere to travel back to,
   and it keeps the sticky ranges inside the document: this section is the last
   on the page, so without space after the grid the browser simply runs out of
   scroll and the pins get clamped. */
.qa-tail { height: 115vh; position: relative; z-index: 2; }
/* The collector holds at the centre of the screen, matching where the opening
   mark pins. */
/* Half the mark box, not a whole one: the parent is centred on that box, so
   offsetting by the full height put its centre 28px above the screen centre. */
.qa-pin2 { position: sticky; top: calc(50vh - (var(--space-14) / 2)); }

/* The closing parent starts invisible and is faded in by the merge. */
.qa-parent--end { opacity: 0; }

/* Children sit BEHIND the parent, so while they are stacked the parent is the
   only one you see. Each carries one of the seven hues the Start free spectrum
   runs through, solid rather than drifting: twelve animated gradients at once
   is work for no gain, and solid colour reads better at this size. */
.qa-fly { z-index: 1; }
.qa-parent { z-index: 2; }
.qa-flyer {
  position: absolute; top: 0; left: 0;
  display: grid; place-items: center;
  width: var(--space-14); height: var(--space-14);
  border-radius: var(--radius-pill);
  border: 0;
  background: var(--qa-m, var(--lb-teal));
  box-shadow: var(--shadow-2);
  will-change: transform;
}
.qa-flyer svg { width: var(--space-6); height: var(--space-6); fill: var(--color-icon-glyph); }

.qa-stage { position: relative; z-index: 1; }

/* tiles hold their place but stay quiet until their own mark arrives */
.qa-grid.is-splitting .qa-chip,
.qa-grid.is-merging .qa-chip { opacity: 0; }
.qa-grid.is-splitting .qa-card { opacity: var(--qa-reveal, 0); }
.qa-grid .qa-card { transition: opacity var(--dur-base) var(--ease-standard); }
.qa-chip { transition: opacity var(--dur-fast) var(--ease-standard),
                       background var(--dur-base) var(--ease-standard),
                       border-color var(--dur-base) var(--ease-standard); }

@media (prefers-reduced-motion: reduce) {
  /* No animation here, but the call to action the morph produces is real
     content and has to exist regardless. The runways collapse, the opening
     mark goes, and the closing one is simply drawn as an ordinary button. */
  .qa-run { height: auto; }
  .qa-tail { height: auto; }
  .qa-pin2, .qa-pin { position: static; }
  .qa-run .qa-mark, .qa-fly { display: none; }
  .qa-run .qa-parent { display: none; }
  .qa-tail .qa-mark {
    width: auto; height: auto; margin: var(--space-10) auto 0;
    display: grid; place-items: center;
  }
  /* Specificity, not source order: the morph rules are defined later in the
     stylesheet than this block, so a bare .qa-cta-label here loses to them. */
  .qa-tail .qa-parent--end {
    position: static; margin: 0; width: auto; height: auto;
    opacity: 1; transform: none;
    padding: var(--space-3) var(--space-8);
    border-radius: var(--radius-btn);
  }
  .qa-tail .qa-parent--end .qa-ico { display: none; }
  .qa-tail .qa-parent--end .qa-cta-label {
    position: static; transform: none; opacity: 1;
  }
  .qa-grid.is-splitting .qa-chip { opacity: 1; }
  .qa-grid.is-splitting .qa-card { opacity: 1; }
}
@media (max-width: 700px) {
  /* No animation here, but the call to action the morph produces is real
     content and has to exist regardless. The runways collapse, the opening
     mark goes, and the closing one is simply drawn as an ordinary button. */
  .qa-run { height: auto; }
  .qa-tail { height: auto; }
  .qa-pin2, .qa-pin { position: static; }
  .qa-run .qa-mark, .qa-fly { display: none; }
  .qa-run .qa-parent { display: none; }
  .qa-tail .qa-mark {
    width: auto; height: auto; margin: var(--space-10) auto 0;
    display: grid; place-items: center;
  }
  /* Specificity, not source order: the morph rules are defined later in the
     stylesheet than this block, so a bare .qa-cta-label here loses to them. */
  .qa-tail .qa-parent--end {
    position: static; margin: 0; width: auto; height: auto;
    opacity: 1; transform: none;
    padding: var(--space-3) var(--space-8);
    border-radius: var(--radius-btn);
  }
  .qa-tail .qa-parent--end .qa-ico { display: none; }
  .qa-tail .qa-parent--end .qa-cta-label {
    position: static; transform: none; opacity: 1;
  }
  .qa-grid.is-splitting .qa-chip { opacity: 1; }
  .qa-grid.is-splitting .qa-card { opacity: 1; }
}


/* ---- the closing mark morphs into a call to action -----------------------
   Once the twelve have merged, the circle widens into a pill, the glyph fades
   out and the label fades in. It is a real link the whole time rather than a
   shape that becomes one, so it is focusable and clickable the moment it reads
   as a button. It stays hidden from assistive tech until it does. */
.qa-parent--end {
  overflow: hidden; text-decoration: none;
  white-space: nowrap;
  will-change: width, opacity;
}
.qa-parent--end .qa-ico { transition: none; }
.qa-cta-label {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  font-size: var(--text-body-lg); font-weight: var(--weight-bold);
  letter-spacing: -0.02em; color: var(--color-icon-glyph);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .28);
  opacity: 0; pointer-events: none;
}
.qa-parent--end:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }


/* ---------- 06 built for your kind of organisation -------------------------
   Amplemarket's shape: a row of tabs carrying an icon and a name, then a panel
   holding a headline and a link on the left with that sector's real dashboards
   on the right. One pack hue per sector, straight from tokens.css.

   Every dashboard named here was checked against a backend controller. The 24
   across the five sectors are what make the 49 we claim elsewhere countable. */
.sx { background: var(--color-paper); padding: var(--space-12) 0; }
.sx-inner { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }

.sx-head { text-align: center; }
.sx-h {
  margin: 0 auto var(--space-3); max-width: 24ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink);
}
.sx-lede {
  margin: 0 auto var(--space-12); max-width: 52ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}

/* ---- tabs ---- */
  /* The strip. Nine tiles, one row, each keeping the tile's own shape so a long
   name wraps to a second line rather than being clipped at either end. */
.sx-tabs {
  display: flex; flex-wrap: nowrap; gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.sx-tab {
  flex: 1 1 0; min-width: 0;
  flex-direction: column; gap: var(--space-2);
  padding: var(--space-3) var(--space-2);
  border-radius: var(--radius-md);
}
.sx-tab .sx-glyph { width: var(--space-6); height: var(--space-6); }
.sx-tab .sx-ico { width: var(--space-4); height: var(--space-4); }
.sx-tab .sx-name {
  font-size: var(--text-fine); line-height: var(--leading-snug);
  text-align: center; overflow-wrap: anywhere;
}
/* The grid's selected lift would read as one tile sitting crooked in a tight
   row, so the colour carries the state instead. */
@media (max-width: 760px) {
  /* Nine equal tiles on a phone would be 37px wide, so they keep their own
     width and the row scrolls instead. */
  .sx-tabs { overflow-x: auto; scrollbar-width: none; }
  .sx-tabs::-webkit-scrollbar { display: none; }
  .sx-tab { flex: 0 0 auto; width: var(--space-24); }
}


/* The tile flies from the grid to the strip. Set by script, cleared on the
   next frame, so the browser animates the difference and nothing else moves. */
.sx-tab.is-flip {
  transition: transform var(--dur-sweep) var(--ease-assemble),
              opacity var(--dur-base) var(--ease-standard);
  will-change: transform;
}
/* The panel is scaled and moved to sit exactly on the tile the reader pressed,
   then released. Its inner wrapper takes the inverse scale, so the box grows
   while the list and the picture inside it stay their true size. */
.sx-panel.is-growing, .sx-panel.is-growing .sx-panel-in {
  transition: transform var(--dur-sweep) var(--ease-assemble),
              opacity var(--dur-base) var(--ease-standard);
  will-change: transform;
}
/* Its glyph and name make the same trip, from the tile to the panel's top
   left, so the mark never blinks out and back. */
/* The head of the panel: the pack's glyph and name, top left. It sits in the
   left column above the body, so the shot beside it can start at the very top
   of the panel. */
.sx-panel-top {
  grid-column: 1; grid-row: 1;
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-8) var(--space-8) var(--space-6) 0;
}
.sx-panel-glyph { flex: 0 0 auto; }
.sx-panel-top .sx-panel-h { margin: 0; }
.sx-panel-top.is-flip {
  transition: transform var(--dur-sweep) var(--ease-assemble);
  will-change: transform;
}
/* Everything the box now holds rises into it, a beat behind the box itself. */
.sx-panel.is-growing .sx-body > *,
.sx-panel.is-growing .sx-shot {
  animation: sx-fill var(--dur-base) var(--ease-assemble) both;
  animation-delay: calc(var(--dur-fast) + var(--sx-fill, 0) * var(--stagger-step));
}
@keyframes sx-fill {
  from { opacity: 0; transform: translateY(var(--reveal-shift)); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .sx-tab.is-flip, .sx-panel.is-growing,
  .sx-panel.is-growing .sx-panel-in, .sx-panel-top.is-flip { transition: none; }
  .sx-panel.is-growing .sx-body > *,
  .sx-panel.is-growing .sx-shot { animation: none; }
}
.sx-tab {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  padding: var(--space-5) var(--space-3);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  border: 1px solid var(--color-rule);
  font: inherit; color: var(--color-ink-muted); cursor: pointer;
  transition: border-color var(--dur-base) var(--ease-standard),
              color var(--dur-base) var(--ease-standard),
              background var(--dur-base) var(--ease-standard),
              box-shadow var(--dur-base) var(--ease-standard),
              transform var(--dur-base) var(--ease-standard);
}
.sx-tab:hover { border-color: var(--color-rule-strong); color: var(--color-ink); }
/* The chosen tab takes its panel's own ground, the same gradient at the same
   strengths, so the tab and the panel below it read as one piece. */
.sx-tab[aria-selected="true"] {
  color: var(--color-ink);
  border-color: color-mix(in srgb, var(--sx-hue) 46%, var(--color-rule));
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--sx-hue) 16%, var(--color-surface)) 0%,
      color-mix(in srgb, var(--sx-hue) 5%, var(--color-surface)) 100%);
  box-shadow: var(--shadow-2);
  /* No lift. In the strip it read as one tile sitting crooked rather than as
     emphasis, and the colour and border already say which one is open. */
}
.sx-tab:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

.sx-glyph {
  display: grid; place-items: center;
  width: var(--space-14); height: var(--space-14);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--sx-hue) 14%, var(--color-surface));
  transition: background var(--dur-base) var(--ease-standard),
              transform var(--dur-tile) var(--ease-assemble);
}
/* Tinted tile on a tinted tab disappears, so the glyph inverts to plain
   surface once the tab is the chosen one. */
.sx-tab[aria-selected="true"] .sx-glyph {
  background: var(--color-surface);
  transform: scale(1.06);
}
.sx-ico { width: var(--space-6); height: var(--space-6); fill: var(--sx-hue-ink); }
.sx-name { font-size: var(--text-small); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-heading); }

/* ---- panels ---- */
/* Copy left, dashboard right. Reading order gives the claim before the
   picture, and the bullets keep a straight left edge to scan down. */
.sx-panel {
  /* The box that grows. It keeps the frame; the layout moved inside. */
  transform-origin: top left;

  border-radius: var(--radius-xl);
  border: 1px solid color-mix(in srgb, var(--sx-hue) 22%, var(--color-rule));
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--sx-hue) 16%, var(--color-surface)) 0%,
      color-mix(in srgb, var(--sx-hue) 5%, var(--color-surface)) 100%);
}
.sx-panel-in {
  /* Carries the inverse scale so nothing inside is squashed while the
     box around it opens. */
  transform-origin: top left;
  /* Two rows in the left column, head then body; the shot takes the right
     column whole, so it starts at the very top of the panel. */
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  grid-template-rows: auto 1fr;
  align-items: stretch;
  gap: 0 var(--space-8);
  padding: 0 0 var(--space-8) var(--space-8);
}
.sx-body { grid-column: 1; grid-row: 2; }
.sx-shot { grid-column: 2; grid-row: 1 / -1; }

/* All five share one grid cell, so the row is as tall as the tallest panel and
   the section does not jump height as you move between tabs. The inactive ones
   are hidden with visibility, which keeps them out of the accessibility tree
   the same way display: none would, without collapsing the row. */
.sx-panels { display: grid; }
.sx:not(.is-open) .sx-panels { display: none; }
.sx-panel { grid-area: 1 / 1; }
.sx-panel[hidden] { display: none; }
@keyframes sx-in { from { opacity: 0; transform: translateY(var(--space-3)); } to { opacity: 1; transform: none; } }

/* No measure cap: the headings are written short enough to sit on one line,
   and a cap would only wrap them while leaving the space to their right. */
.sx-panel-h {
  margin: 0 0 var(--space-3);
  font-size: var(--text-h3); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink);
}
.sx-link {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-ui); font-weight: var(--weight-semibold);
  color: var(--sx-hue-ink); text-decoration: none;
  border-bottom: 1px solid currentColor; padding-bottom: var(--space-1);
}
.sx-link:hover { color: var(--color-ink); }

.sx-intro {
  margin: 0 0 var(--space-5); max-width: 46ch;
  font-size: var(--text-body); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
/* One column now that every dashboard carries a line of explanation: two
   columns of two-line items reads as a wall rather than a list. */
.sx-list { list-style: none; margin: 0 0 var(--space-6); padding: 0; display: grid; gap: var(--space-3); align-content: start; }
.sx-list li {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--space-3); row-gap: 0;
  font-size: var(--text-small); line-height: var(--leading-normal); color: var(--color-ink);
}
/* The icon replaces the marker dot. Each one is the symbol the product's own
   navigation uses for that surface, so a visitor who signs up meets the same
   glyph in the same place. */
.sx-item-glyph {
  grid-row: 1 / span 2; align-self: start;
  display: grid; place-items: center;
  width: var(--space-8); height: var(--space-8);
  border-radius: var(--radius-sm);
  /* A plain surface tile with a hue hairline. The panel ground is already
     tinted, so a tinted tile on top of it disappeared. */
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--sx-hue) 40%, var(--color-rule));
}
.sx-item-ico { width: var(--space-4); height: var(--space-4); fill: var(--sx-hue-ink); }
.sx-item-h { font-weight: var(--weight-semibold); color: var(--color-ink); }
.sx-item-p { color: var(--color-ink-muted); font-size: var(--text-small); line-height: var(--leading-normal); }
.sx-body { display: flex; flex-direction: column; justify-content: center; }

/* The sector button borrows the eyebrow's drifting edge and the hero button's
   size, so it reads as the same family without competing with Start free. */
/* The 2px gradient edge is drawn inside the box, so the padding gives it back
   and the button matches the standard button height rather than growing past
   it. Type and weight are the button default, which is what Start free uses in
   the header. */
.sx-cta {
  justify-self: start; align-self: start;
  padding: calc(var(--space-3) - 2px) calc(var(--space-5) - 2px);
}

/* Same treatment as the hero panel: no border, no card. The shot runs the
   full height of the sector panel and its bottom and right edges dissolve into
   the ground rather than stopping on an edge. The percentages are the hero's,
   copied rather than re-invented so the two read as one language. */
/* A framed box inside the panel, on the card ratio. It used to run past the
   panel's right edge on a negative margin and fade out there, which put the
   picture outside its own box and gave every panel a different amount of it.
   Fixed ratio, fixed margin: twelve panels, one size. */
.sx-shot {
  align-self: center; justify-self: stretch;
  margin: 0 var(--space-8) 0 0;
  aspect-ratio: 16 / 10; min-height: 0;
  overflow: hidden; border-radius: var(--radius-lg);
  border: 1px solid var(--color-rule);
  background: var(--color-surface);
  box-shadow: var(--shadow-2);
}
.sx-shot img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: left top;
}

@media (max-width: 1000px) {
  /* Stacked, and the shot goes under the copy: on a narrow screen the words
     have to arrive first or the picture is all you see above the fold. */
  .sx-panel-in { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto auto; gap: var(--space-6); padding: var(--space-6); padding-right: var(--space-6); }
  /* Stacked, the shot is a picture again rather than an edge treatment. */
  .sx-panel-top { grid-column: 1; grid-row: 1; padding: var(--space-6) var(--space-6) var(--space-4) 0; }
  .sx-body { grid-column: 1; grid-row: 2; }
  .sx-shot { grid-column: 1; grid-row: 3; margin: 0; }
  .sx-shot img { height: auto; }
  .sx-panel-h { font-size: var(--text-h3); max-width: none; }
  .sx-intro { max-width: none; }
  .sx-body { order: 1; }
  .sx-shot { order: 2; }
}
@media (max-width: 900px) {
  .sx { padding: var(--space-16) 0; }
  .sx-lede { margin-bottom: var(--space-8); }
  /* five across is unreadable on a phone: the strip scrolls instead */
  .sx-tabs {
    grid-template-columns: repeat(5, minmax(9.5rem, 1fr));
    overflow-x: auto; padding-bottom: var(--space-2);
    scroll-snap-type: x proximity;
  }
  .sx-tab { scroll-snap-align: start; }
  .sx-cta { width: 100%; justify-self: stretch; }
}

@media (prefers-reduced-motion: reduce) {
  .sx-panel { animation: none; }
  .sx-tab[aria-selected="true"] { transform: none; }
  .sx-tab[aria-selected="true"] .sx-glyph { transform: none; }
}


/* ---------- 07 inside Moodle ----------
   Copy left, product shot right. The shot runs past the container to the
   viewport edge and dissolves there, the same move the hero makes, so the
   claim reads as a window onto Moodle rather than a framed picture. */
.im {
  /* --color-ink-deep is the dark ground for every dark band on this page. */
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  padding: var(--space-12) 0; overflow-x: clip;
}
.im-inner {
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--space-10); align-items: center;
}
.im-body { min-width: 0; }
.im-body .eyebrow { margin-bottom: var(--space-5); }
.im-h {
  margin: 0 0 var(--space-3);
  font-size: var(--text-h2); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink-on-dark);
}
.im-lede {
  margin: 0 0 var(--space-12); max-width: 46ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 74%, transparent);
}
.im-points { list-style: none; margin: 0 0 var(--space-8); padding: 0; display: grid; gap: var(--space-5); }
.im-point {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--space-4); row-gap: var(--space-1);
}
.im-glyph {
  grid-row: 1 / span 2; align-self: start;
  display: grid; place-items: center;
  width: var(--space-10); height: var(--space-10);
  border-radius: var(--radius-pill);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
  background: color-mix(in srgb, var(--color-ink-on-dark) 8%, transparent);
}
/* Accent teal is fill-only on paper, but on this ground it clears 3:1 as
   non-text UI, which is all a glyph is. */
.im-ico { width: var(--space-5); height: var(--space-5); fill: var(--color-accent); }
.im-point-h { font-size: var(--text-body); font-weight: var(--weight-semibold); color: var(--color-ink-on-dark); }
.im-point-p { font-size: var(--text-small); line-height: var(--leading-normal); color: color-mix(in srgb, var(--color-ink-on-dark) 70%, transparent); }
.im-link {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-ui); font-weight: var(--weight-semibold);
  /* Small text on dark takes the on-dark ink, never a hue. */
  color: var(--color-ink-on-dark); text-decoration: none;
  border-bottom: 1px solid currentColor; padding-bottom: var(--space-1);
}
.im-link:hover { color: color-mix(in srgb, var(--color-ink-on-dark) 74%, transparent); }
/* The stage is what the floating charts are positioned against. It is a
   separate element from the shot because the shot carries a mask, and anything
   inside a masked element fades with it. */
.im-stage { position: relative; }

/* Three product charts floating over the Moodle page underneath. Positioned as
   a share of the stage so they hold their relationship to the shot at any
   width, and moved only by transform. --im-shift is written by script on
   scroll; each one carries its own rate so they travel at different speeds. */
.im-floats { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.im-float {
  position: absolute; margin: 0; width: 13rem;
  border-radius: var(--radius-md); overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 16%, transparent);
  /* Two token shadows stacked, the same pair the Log in Monday cards use: the
     tight one lifts the chart off the screenshot, the wide one drops it away
     from the dark ground. */
  box-shadow: var(--shadow-3), var(--shadow-4);
  transform: translate3d(
    calc(var(--im-pointer, 0px) * var(--im-rate-x, 1)),
    calc(var(--im-shift, 0px) * var(--im-rate, 1)), 0)
    rotate(var(--im-tilt, 0deg));
  transition: transform var(--dur-base) var(--ease-standard);
  will-change: transform;
}
.im-float img { display: block; width: 100%; height: auto; }
/* One hangs off the left edge into the column gap, one sits on the right, one
   centres along the bottom. The centred one is offset by half its own width
   rather than translated, because transform is already carrying the parallax. */
.im-float--1 { left: -5%; top: 8%; --im-rate: 1; --im-rate-x: 1; }
.im-float--2 { right: 2%; top: 34%; --im-tilt: -2.5deg; --im-rate: -0.62; --im-rate-x: -0.75; }
.im-float--3 { left: calc(50% - 6.5rem); bottom: 0; --im-rate: 0.38; --im-rate-x: 0.5; }

/* Same dissolve as the hero panel and the sector shots. */
.im-shot {
  margin: 0; width: calc(100% + 9vw); overflow: hidden;
  -webkit-mask-image:
    linear-gradient(to bottom, #000 62%, transparent 100%),
    linear-gradient(to right, #000 78%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to bottom, #000 62%, transparent 100%),
    linear-gradient(to right, #000 78%, transparent 100%);
  mask-composite: intersect;
}
.im-shot img { display: block; width: 100%; height: auto; }

@media (max-width: 1000px) {
  .im-inner { grid-template-columns: minmax(0, 1fr); gap: var(--space-8); }
  .im-h { font-size: var(--text-h3); }
  .im-lede { max-width: none; }
  /* Stacked, the shot is a picture again rather than an edge treatment. */
  .im-shot {
    width: 100%; border-radius: var(--radius-lg);
    -webkit-mask-image: none; mask-image: none;
  }
  /* The floating charts are a depth effect, not content: at this width they
     would cover the screenshot they are meant to sit over. */
  .im-floats { display: none; }
}
@media (max-width: 900px) {
  .im { padding: var(--space-16) 0; }
}


/* ---------- 08 every chart explains itself ----------
   Still, for now. Heading, lede, the AI mark, then the six report cards. */
.ai { background: var(--color-paper); padding: var(--space-12) 0 var(--space-6); overflow-x: clip; }
.ai-head { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); text-align: center; }
.ai-h {
  margin: 0 auto var(--space-3); max-width: 20ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink);
}
.ai-lede {
  margin: 0 auto; max-width: 54ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
/* The runway. Emit, swell, shrink, park. */
.ai-run { height: 380vh; position: relative; }
.ai-pin {
  position: sticky; top: var(--space-4);
  /* Fit the viewport rather than overflow it. Sticky cannot hold a block
     taller than the screen, and when it cannot, the last thing in the block,
     the button, sits under the fold for the whole scroll. */
}
.ai-body {
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
  margin-top: var(--space-5);
  /* Same distance from the cards to the button as the chart wall uses, which
     is the other grid on the page that ends in a centred button. */
  display: grid; justify-items: center; gap: var(--space-6);
}

/* The mark. Same diameter and the same drifting spectrum as the one section 05
   uses, so the two read as one idea. */
.ai-mark {
  /* Above the cards, so the six leave from behind it rather than appearing in
     front of it. */
  position: relative; z-index: 5;
  width: calc(var(--space-14) * 2); height: calc(var(--space-14) * 2);
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  background-image: linear-gradient(100deg,
    var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
    var(--lb-red), var(--lb-orange), var(--lb-gold),
    var(--lb-orange), var(--lb-red), var(--lb-magenta),
    var(--lb-purple), var(--lb-blue), var(--lb-teal));
  background-size: 1200px 100%;
  background-repeat: repeat-x;
  animation: spectrum-drift calc(var(--dur-ambient) * 2) linear infinite;
  box-shadow: var(--shadow-3);
}
.ai-mark-ico { width: var(--space-10); height: var(--space-10); fill: var(--color-icon-glyph); }

/* ---- the closing mark ----
   The six merge back into this one, and it then widens into the section's call
   to action. It is a real link the whole way through rather than a shape that
   becomes one, so it is clickable the moment it reads as a button. It stays out
   of the accessibility tree until then. */
.ai-cta {
  position: relative; display: grid; place-items: center;
  width: calc(var(--space-14) * 2); height: calc(var(--space-14) * 2);
  border-radius: var(--radius-pill);
  overflow: hidden; white-space: nowrap; text-decoration: none;
  color: var(--color-icon-glyph);
  background-image: linear-gradient(100deg,
    var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
    var(--lb-red), var(--lb-orange), var(--lb-gold),
    var(--lb-orange), var(--lb-red), var(--lb-magenta),
    var(--lb-purple), var(--lb-blue), var(--lb-teal));
  background-size: 1200px 100%;
  background-repeat: repeat-x;
  animation: spectrum-drift calc(var(--dur-ambient) * 2) linear infinite;
  box-shadow: var(--shadow-3);
  opacity: 0;
  will-change: width, opacity;
}
.ai-cta-ico { width: var(--space-10); height: var(--space-10); fill: var(--color-icon-glyph); }
.ai-cta-label {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  font-size: var(--text-ui); font-weight: var(--weight-medium);
  letter-spacing: -0.015em; color: var(--color-icon-glyph);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .28);
  opacity: 0; pointer-events: none;
}
.ai-cta:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .ai-cta { animation: none; } }

.ai-grid {
  width: 100%;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  /* wide enough that two neighbouring 10px bands never touch */
  gap: var(--space-4);
}
.ai-card {
  /* Never clipped: the orb starts life on the parent mark, outside this card,
     and has to be visible the whole way across. Only the cover layer clips. */
  position: relative;
  /* 5px, built off the spacing scale rather than typed as a literal. */
  --ai-edge: calc(var(--space-1) * 1.25);
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: calc(var(--space-3) - 1px);
  border-radius: var(--radius-lg);
  border: 2px solid var(--color-rule);
  background: var(--color-surface);
  box-shadow: var(--shadow-1);
  transition: background-color var(--dur-base) var(--ease-standard),
              box-shadow var(--dur-base) var(--ease-standard);
}

/* ---- the orb ----
   .ai-orb is the frame that travels. The disc inside it is what swells, and
   the glyph is a sibling of the disc rather than a child, so the background can
   scale to cover the card while the icon stays the size it started. */
.ai-orb {
  position: absolute; z-index: 4;   /* above .ai-cover */
  left: 50%; top: 50%;
  width: calc(var(--space-14) * 2); height: calc(var(--space-14) * 2);
  margin-left: calc(var(--space-14) * -1); margin-top: calc(var(--space-14) * -1);
  display: grid; place-items: center;
  pointer-events: none; opacity: 0;
  will-change: transform, opacity;
}
.ai-orb-disc {
  position: absolute; inset: 0; border-radius: var(--radius-pill);
  background: var(--ai-hue, var(--lb-teal));
  box-shadow: var(--shadow-3);
}
.ai-orb-ico {
  position: relative; z-index: 1;
  width: var(--space-10); height: var(--space-10);
  fill: var(--color-icon-glyph);
}

/* ---- the answered card ----
   Ground tinted with the same hue its orb carried, the eyebrow's drifting edge,
   and a glow in that hue so the reveal reads as the AI having touched it. */
/* ---- the answered card ----
   Ground tinted with its own hue, a short lit segment running round the border,
   and a glow held tight enough not to reach its neighbours. */
.ai-card.is-answered {
  /* The edge under the band, so the card still has a defined outline. */
  border-color: color-mix(in srgb, var(--ai-hue) 45%, var(--color-rule));
  /* Its own stacking context, so the ring underneath cannot fall behind the
     section, and the padding-box clip so the tint stops at the border. */
  isolation: isolate;
  background: linear-gradient(
    color-mix(in srgb, var(--ai-hue) 15%, var(--color-surface)),
    color-mix(in srgb, var(--ai-hue) 15%, var(--color-surface))) padding-box;
  box-shadow: 0 0 var(--space-6) var(--ai-edge)
                color-mix(in srgb, var(--ai-hue) 34%, transparent),
              var(--shadow-2);
}
/* The snake. A conic gradient that is transparent almost all the way round,
   with one lit arc running through four hues. The ANGLE is animated, not the
   element: rotating the box itself swings its corners outside the card and
   throws coloured wedges across the page. */
@property --ai-angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}
.ai-card.is-answered::before {
  content: ""; position: absolute;
  /* A band exactly --ai-edge wide sitting outside the card. The mask removes
     the middle, so the gradient is only ever visible as a border and never as
     a wash behind the card. */
  inset: calc(var(--ai-edge) * -1);
  z-index: -1;
  border-radius: calc(var(--radius-lg) + var(--ai-edge));
  padding: var(--ai-edge);
  background: conic-gradient(from var(--ai-angle),
    transparent 0 58%,
    color-mix(in srgb, var(--lb-teal) 70%, transparent) 64%,
    var(--lb-blue) 70%,
    var(--lb-magenta) 76%,
    color-mix(in srgb, var(--lb-gold) 70%, transparent) 82%,
    transparent 88% 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  animation: ai-snake var(--dur-ambient) linear infinite;
}
@keyframes ai-snake { to { --ai-angle: 360deg; } }
@media (prefers-reduced-motion: reduce) {
  .ai-card.is-answered::before { animation: none; }
}
/* The only clipped thing on the card. The card itself stays unclipped, so the
   orb can fly in from the parent and the border band is never cut off. */
.ai-cover {
  /* Reaches out over the border band, so while a disc is fully grown the band
     is behind it, and the band is revealed by the disc shrinking rather than
     by being switched on. */
  position: absolute; inset: calc(var(--ai-edge, 0px) * -1); z-index: 3;
  border-radius: calc(var(--radius-lg) + var(--ai-edge, 0px));
  overflow: hidden; pointer-events: none;
}
.ai-cover-disc {
  position: absolute; left: 50%; top: 50%;
  width: calc(var(--space-14) * 2); height: calc(var(--space-14) * 2);
  margin-left: calc(var(--space-14) * -1); margin-top: calc(var(--space-14) * -1);
  border-radius: var(--radius-pill);
  background: var(--ai-hue, var(--lb-teal));
  transform: scale(var(--ai-swell, 0));
  will-change: transform;
}
.ai-card.is-answered .ai-cap--q { display: none; }
.ai-card.is-answered .ai-cap--a { display: block; }
.ai-shot {
  display: block; border-radius: var(--radius-sm); overflow: hidden;
  border: 1px solid var(--color-rule);
}
.ai-shot img {
  display: block; width: 100%;
  /* Sized off the viewport, not the source file, so the pinned block always
     clears the fold and the button never sits under it. */
  height: clamp(5rem, 15vh, 9rem);
  /* A card contained, not cropped: cutting one takes off the title that says
     what the chart is, which is the thing being asked about. */
  object-fit: contain; object-position: center;
  background: var(--color-surface);
}
/* Grey until the orb uncovers it. The picture is there the whole time, which is
   the point of the section: the chart already exists, the answer is the thing
   that arrives. */
.ai-shot img { filter: grayscale(1); transition: filter var(--dur-swap) var(--ease-standard); }
.ai-card.is-answered .ai-shot img { filter: none; }
.ai-cap {
  margin: 0; font-size: var(--text-small); line-height: var(--leading-normal);
  font-weight: var(--weight-semibold); color: var(--color-ink);
}
.ai-cap--a { display: none; }
.ai-tag {
  display: inline-block; margin-right: var(--space-2);
  font-size: var(--text-fine); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caption); text-transform: uppercase;
  color: var(--color-accent-strong);
}

@media (max-width: 1000px) {
  .ai-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .ai { padding: var(--space-16) 0 var(--space-8); }
}
/* Still version. No runway and no travel: every card is already answered and
   its orb is parked in the corner. A phone, a reduced-motion setting and a
   JS-off browser all get this. */
.ai-run.is-static { height: auto; }
.ai-run.is-static .ai-pin { position: static; }
.ai-run.is-static .ai-mark { display: none; }
.ai-run.is-static .ai-orb {
  opacity: 1; left: auto; right: var(--space-2); top: var(--space-2);
  margin: 0; width: var(--space-10); height: var(--space-10);
}
.ai-run.is-static .ai-orb-ico { width: var(--space-5); height: var(--space-5); }
.ai-run.is-static .ai-cta {
  opacity: 1; width: auto; height: auto;
  padding: var(--space-3) var(--space-8);
  border-radius: var(--radius-btn);
}
.ai-run.is-static .ai-cta-ico { display: none; }
.ai-run.is-static .ai-cta-label { position: static; transform: none; opacity: 1; }

@media (max-width: 700px) {
  .ai-body { gap: var(--space-8); }
  .ai-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
}
@media (prefers-reduced-motion: reduce) {
  .ai-mark { animation: none; }
}


/* ---------- 09 build your own dashboard ----------
   The eight cards and the centre cell share one grid, so the eight always know
   exactly where the centre is. The revealed dashboard sits over that cell and
   grows from it. Everything animated is transform and opacity. */
.pd {
  /* --color-ink-deep is the dark ground for every dark band on this page. */
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  padding: var(--space-6) 0 var(--space-12);
  /* Sideways only: the pinned board inside is sticky, and clipping the block
     axis here risks the sticky it depends on. The revealed dashboard is held
     inside the pinned box by its own growth cap instead. */
  overflow-x: clip;
}
.pd-run { height: 400vh; position: relative; }
.pd-pin { position: sticky; top: calc(var(--space-16) + var(--space-4)); }
.pd-head { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); text-align: center; }
.pd-h {
  margin: 0 auto var(--space-3); max-width: 20ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink-on-dark);
}
.pd-lede {
  margin: 0 auto; max-width: 54ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 74%, transparent);
}
.pd-stage {
  position: relative;
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
  margin-top: var(--space-6); padding-bottom: var(--space-16);
}
/* Narrower than the stage on purpose. The stage stays full width because the
   revealed dashboard grows into it, but nine cards across the whole container
   read as a wall. */
.pd-grid {
  width: 72%; margin-inline: auto;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
}
.pd-card {
  /* Not clipped: a pin starts on the parent, outside this card, and has to be
     visible the whole way across. The picture rounds its own corners instead. */
  position: relative;
  /* Fixed, and shorter than the cards are: eight of them at their own height
     took half a screen more than the board they replaced. */
  aspect-ratio: 3 / 2;
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 16%, transparent);
  background: var(--color-surface);
  box-shadow: var(--shadow-3);
  will-change: transform, opacity;
}
.pd-card img {
  /* The tiles carry whole cards now, not crops of a screen, so they are
     contained: cropping a card cuts its title or its legend off, which is
     the one thing a card cannot lose. */
  display: block; width: 100%; height: 100%; object-fit: contain;
  background: var(--color-surface);
  border-radius: inherit;
}

/* One pin per card, in its own hue. It starts life on the parent pin and is
   flown to the middle of its card; after that it simply rides the card. */
.pd-pinlet {
  position: absolute; left: 50%; top: 50%; z-index: 2;
  width: var(--space-12); height: var(--space-12);
  margin-left: calc(var(--space-12) / -2); margin-top: calc(var(--space-12) / -2);
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  background: var(--pd-hue, var(--lb-teal));
  box-shadow: var(--shadow-3);
  opacity: 0; pointer-events: none;
  will-change: transform, opacity;
}
.pd-pinlet-ico { width: var(--space-5); height: var(--space-5); fill: var(--color-icon-glyph); }

/* ---- the centre cell ---- */
.pd-core {
  grid-column: 2; grid-row: 2;
  position: relative; z-index: 2;
  /* The label sits under the middle of the cell, because the pin owns the
     middle itself and is drawn over the top from the stage. */
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  border: 2px dashed color-mix(in srgb, var(--color-ink-on-dark) 26%, transparent);
  background: color-mix(in srgb, var(--color-ink-on-dark) 5%, transparent);
}
.pd-core-label {
  margin: 0; text-align: center;
  transition: opacity var(--dur-fast) var(--ease-standard);
  font-size: var(--text-body); font-weight: var(--weight-semibold);
  color: var(--color-ink-on-dark);
}
/* The parent pin. It is a stage layer rather than a child of the cell, so it
   can sit above the background it grows, and its box is set from the cell in
   script so it lands exactly in the middle of it. Same drifting spectrum as the
   marks in sections 05 and 08. */
.pd-mark {
  position: absolute; z-index: 6;
  display: grid; place-items: center;
  width: var(--space-16); height: var(--space-16);
  margin-left: calc(var(--space-16) / -2); margin-top: calc(var(--space-16) / -2);
  border-radius: var(--radius-pill);
  pointer-events: none;
  background-image: linear-gradient(100deg,
    var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
    var(--lb-red), var(--lb-orange), var(--lb-gold),
    var(--lb-orange), var(--lb-red), var(--lb-magenta),
    var(--lb-purple), var(--lb-blue), var(--lb-teal));
  background-size: 1200px 100%;
  background-repeat: repeat-x;
  animation: spectrum-drift calc(var(--dur-ambient) * 2) linear infinite;
  box-shadow: var(--shadow-3);
}
.pd-mark-ico { width: var(--space-6); height: var(--space-6); fill: var(--color-icon-glyph); }
.pd-mark { overflow: hidden; white-space: nowrap; text-decoration: none; }
.pd-cta-label {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  font-size: var(--text-ui); font-weight: var(--weight-medium);
  letter-spacing: -0.015em; color: var(--color-icon-glyph);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .28);
  opacity: 0; pointer-events: none;
}
.pd-mark:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }

/* The disc that swells over the centre cell, clipped to the cell so it can
   never spill onto the grid. */
/* Above the revealed dashboard, not inside the cell: the disc has to be able
   to hide it and then uncover it by shrinking. Its box is set from the cell in
   script, and it clips so the disc can never spill onto the grid. */
.pd-cover {
  position: absolute; z-index: 5;
  border-radius: var(--radius-lg); overflow: hidden; pointer-events: none;
}
/* This is the parent's own background, grown. It starts exactly the size and
   position of the pin, so what you see is the pin expanding rather than a
   second circle arriving. */
.pd-cover-disc {
  position: absolute; left: 50%; top: 50%;
  width: var(--space-16); height: var(--space-16);
  margin-left: calc(var(--space-16) / -2); margin-top: calc(var(--space-16) / -2);
  border-radius: var(--radius-pill);
  background-image: linear-gradient(100deg,
    var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
    var(--lb-red), var(--lb-orange), var(--lb-gold),
    var(--lb-orange), var(--lb-red), var(--lb-magenta),
    var(--lb-purple), var(--lb-blue), var(--lb-teal));
  background-size: 1200px 100%;
  background-repeat: repeat-x;
  animation: spectrum-drift calc(var(--dur-ambient) * 2) linear infinite;
  transform: scale(var(--pd-swell, 0));
  will-change: transform;
}

/* ---- the one dashboard ----
   Sits exactly over the centre cell and is scaled up from it, so the growth
   starts from the place the eight went into. */
.pd-final {
  /* The picture's own ratio, whole: filling a 3:2 frame with a 16:9 capture
     cut the workspace switcher and the logo off the top left corner. The
     frame follows the picture instead, which is what the border is drawn
     around; the disc that uncovers it is the cell's size and slightly taller,
     which nobody can see because it is the thing being removed. */
  aspect-ratio: 16 / 9;
  position: absolute; margin: 0; z-index: 3;
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 16%, transparent);
  background: var(--color-surface);
  box-shadow: var(--shadow-4);
  opacity: 0; pointer-events: none;
  transform-origin: 50% 50%;
  will-change: transform, opacity;
}
.pd-final img {
  display: block; width: 100%; height: 100%;
  object-fit: contain; object-position: center;
}

/* Three tips over the revealed dashboard, the same device as the cards
   floating over the Moodle shot in section 07. The box is set from the picture
   every frame, because the picture is still growing underneath them. */
.pd-tips { position: absolute; z-index: 4; pointer-events: none; opacity: 0; }
.pd-tip {
  position: absolute; width: 14rem;
  display: flex; flex-direction: column; gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--color-surface) 92%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow-3), var(--shadow-4);
}
.pd-tip-h {
  font-size: var(--text-small); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-heading); color: var(--color-ink);
}
.pd-tip-p {
  font-size: var(--text-fine); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.pd-tip--1 { left: 2%; top: 12%; }
.pd-tip--2 { right: 2%; top: 42%; left: auto; }
.pd-tip--3 { left: 12%; bottom: 8%; }

.pd-run.is-static .pd-tips { display: none; }

@media (max-width: 1200px) {
  .pd-tip { width: 12rem; }
}

/* Still version: the grid simply reads as a grid, no runway and no travel. */
.pd-run.is-static { height: auto; }
.pd-run.is-static .pd-pin { position: static; }
.pd-run.is-static .pd-final { display: none; }
.pd-run.is-static .pd-cover { display: none; }
.pd-run.is-static .pd-pinlet { display: none; }
.pd-run.is-static .pd-mark {
  position: static; margin: 0 auto var(--space-4);
  width: auto; height: auto; padding: var(--space-3) var(--space-8);
  border-radius: var(--radius-btn); pointer-events: auto;
}
.pd-run.is-static .pd-mark-ico { display: none; }
.pd-run.is-static .pd-cta-label { position: static; transform: none; opacity: 1; }
.pd-run.is-static .pd-core { justify-content: center; }

@media (max-width: 1000px) {
  .pd-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pd-core { grid-column: auto; grid-row: auto; }
}
@media (max-width: 900px) {
  .pd { padding: var(--space-8) 0 var(--space-16); }
}
@media (max-width: 1000px) {
  .pd-grid { width: 100%; }
}
@media (max-width: 700px) {
  .pd-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
}
@media (prefers-reduced-motion: reduce) {
  .pd-mark, .pd-cover-disc { animation: none; }
}


/* ---------- 10 two ways in ----------
   Dark band, house eyebrow, two cards the same size carrying different weight.
   The quiet one takes the eyebrow's drifting edge, the leading one takes the
   filled spectrum button, so the primary action is obvious without the other
   reading as a lesser option. */
.tw {
  background: var(--color-paper); color: var(--color-ink);
  padding: var(--space-12) 0;
}
.tw-inner {
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
  text-align: center;
}
.tw-eyebrow { margin: 0 0 var(--space-5); }
.tw-h {
  margin: 0 auto var(--space-3); max-width: 20ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink);
}
.tw-lede {
  margin: 0 auto var(--space-12); max-width: 54ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}

.tw-cards {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6); text-align: left;
}
.tw-card {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--space-4); padding: var(--space-8);
  border-radius: var(--radius-xl);
  border: 1px solid var(--color-rule);
  background: var(--color-surface);
}
/* Assembly: rises and scales up once, staggered, and never again. Gated on a
   class the script sets, so with JS off the cards are simply there. */
.js-reveal .tw-card {
  opacity: 0;
  transform: translateY(var(--reveal-shift)) scale(var(--reveal-scale));
  transition: opacity var(--dur-base) var(--ease-assemble),
              transform var(--dur-base) var(--ease-assemble);
  transition-delay: calc(var(--tw-i, 0) * var(--stagger-step));
}
.js-reveal .tw-cards.is-in .tw-card { opacity: 1; transform: none; }
.tw-card--lead {
  border-color: color-mix(in srgb, var(--color-accent) 42%, transparent);
  background: color-mix(in srgb, var(--color-accent) 12%, transparent);
}
.tw-glyph {
  display: grid; place-items: center;
  width: var(--space-12); height: var(--space-12);
  border-radius: var(--radius-md);
  background: var(--color-accent-tint);
  border: 1px solid color-mix(in srgb, var(--color-accent) 24%, transparent);
}
.tw-ico { width: var(--space-6); height: var(--space-6); fill: var(--color-accent); }
.tw-card-h {
  margin: 0; font-size: var(--text-h3); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-heading);
  color: var(--color-ink);
}
.tw-card-p {
  margin: 0; max-width: 42ch;
  font-size: var(--text-body); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.tw-cta { margin-top: auto; }
/* Both cards' actions share one width, so neither card's button looks like
   the lesser control. */
.tw-card .tw-cta { align-self: stretch; text-align: center; }
/* The 2px gradient edge is drawn inside the box, so the padding gives it back
   and the quiet button matches the standard button height. */
/* .btn draws a 1px transparent border, the ribbon needs 2px. Give back exactly
   that one pixel, on both axes, so a ribbon button is always the same height
   and width as the filled button beside it. Subtracting 2px, as this did
   before, made it 2px short. */
.btn.btn--gradient-quiet {
  padding: calc(var(--space-3) - 1px) calc(var(--space-5) - 1px);
}
.btn--lg.btn--gradient-quiet {
  padding: calc(var(--space-3) - 1px) calc(var(--space-6) - 1px);
}
.tw-fine {
  margin: var(--space-8) 0 0;
  font-size: var(--text-small);
  color: var(--color-ink-muted);
}

@media (max-width: 900px) {
  .tw { padding: var(--space-16) 0; }
  .tw-cards { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
  .tw-card { padding: var(--space-6); }
  .tw-cta { width: 100%; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
  .tw-card { opacity: 1; transform: none; transition: none; }
}


/* ---------- 12 safe with your data ----------
   A lattice, not a card grid. The hairlines are the grid's own background
   showing through a 1px gap, so every rule is exactly one pixel and the outer
   frame needs no separate border. */
.sd {
  /* --color-ink-deep is the dark ground for every dark band on this page. */
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  /* Short at the bottom on purpose: the cloned compliance strip follows on the
     same ground and brings its own space. */
  padding: var(--space-12) 0;
  overflow-x: clip;
}
/* The clone carries the section's closing space instead of the lattice. */
.sd-frameworks { padding: var(--space-8) 0 var(--space-24); }
.sd-inner {
  position: relative;
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
  /* Head centred, like every other section head on the page. The cells below
     set their own left alignment, because a paragraph of body copy centred is
     a paragraph nobody finishes. */
  text-align: center;
}
/* Plotted paper behind the heading, fading out towards the copy. Drawn from
   gradients rather than an image, so it costs nothing to load. */
.sd-inner::before {
  content: ""; position: absolute; z-index: 0; pointer-events: none;
  right: var(--gutter); top: calc(var(--space-10) * -1);
  width: 28%; height: var(--space-32);
  background-image:
    repeating-linear-gradient(to right,
      color-mix(in srgb, var(--color-ink-on-dark) 10%, transparent) 0 1px,
      transparent 1px var(--space-12)),
    repeating-linear-gradient(to bottom,
      color-mix(in srgb, var(--color-ink-on-dark) 10%, transparent) 0 1px,
      transparent 1px var(--space-12));
  -webkit-mask-image: linear-gradient(to bottom left, #000, transparent 72%);
  mask-image: linear-gradient(to bottom left, #000, transparent 72%);
}
.sd-eyebrow, .sd-h, .sd-lede, .sd-grid { position: relative; z-index: 1; }

.sd-eyebrow { margin: 0 0 var(--space-5); }
.sd-h {
  margin: 0 auto var(--space-3); max-width: 22ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink-on-dark);
}
.sd-lede {
  margin: 0 auto var(--space-12); max-width: var(--measure-lede);
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 74%, transparent);
}

.sd-grid {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px; padding: 1px;
  background: color-mix(in srgb, var(--color-ink-on-dark) 13%, transparent);
  border-radius: var(--radius-lg); overflow: hidden;
}
.sd-cell {
  display: flex; flex-direction: column; text-align: left;
  padding: var(--space-6);
  background: color-mix(in srgb, var(--color-ink-on-dark) 3%, var(--color-ink-deep));
}
/* The drawing gets its own fixed band so ten glyphs of different proportions
   still line their captions up across a row. */
.sd-art {
  display: grid; place-items: center;
  min-height: var(--space-16); margin-bottom: var(--space-6);
}
.sd-ico {
  width: var(--space-12); height: var(--space-12);
  fill: color-mix(in srgb, var(--color-ink-on-dark) 44%, transparent);
}
.sd-cell-h {
  margin: 0 0 var(--space-2);
  font-size: var(--text-h4); line-height: var(--leading-snug);
  font-weight: var(--weight-semibold); letter-spacing: var(--tracking-heading);
  color: var(--color-ink-on-dark);
}
.sd-cell-p {
  margin: 0;
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 66%, transparent);
}
/* The way out sits under the compliance strip, at the very end of the run, so
   the section reads claims, then the laws behind them, then the door. */
/* The strip it sits under is full bleed and carries no gutter of its own, so
   the tail supplies one. */
.sd-tail {
  margin: var(--space-12) 0 0; text-align: center;
  padding-inline: var(--gutter);
}
.sd-cta { padding: calc(var(--space-3) - 2px) calc(var(--space-5) - 2px); gap: var(--space-2); }
/* .ico-inline carries no size of its own, it is sized by whatever holds it. */
.sd-cta .ico-inline { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
.sd-cta .ico-inline path { fill: currentColor; }

/* Assembly: each cell rises once, staggered along the reading order, and never
   again. Gated on a class the script sets, so with JS off they are simply
   there. */
.js-reveal .sd-cell {
  opacity: 0;
  transform: translateY(var(--reveal-shift)) scale(var(--reveal-scale));
  transition: opacity var(--dur-base) var(--ease-assemble),
              transform var(--dur-base) var(--ease-assemble);
  transition-delay: calc(var(--sd-i, 0) * var(--stagger-step));
}
.js-reveal .sd-grid.is-in .sd-cell { opacity: 1; transform: none; }

@media (max-width: 1100px) {
  .sd-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sd-cell { padding: var(--space-6); }
  .sd-art { min-height: var(--space-14); margin-bottom: var(--space-5); }
  .sd-ico { width: var(--space-10); height: var(--space-10); }
}
@media (max-width: 700px) {
  .sd { padding: var(--space-16) 0; }
  .sd-inner::before { display: none; }
  .sd-lede { margin-bottom: var(--space-8); }
  .sd-grid { grid-template-columns: minmax(0, 1fr); }
  .sd-cta { width: 100%; }
  .sd-art { min-height: var(--space-12); margin-bottom: var(--space-4); }
  .sd-frameworks { padding: var(--space-8) 0 var(--space-16); }
}
@media (prefers-reduced-motion: reduce) {
  .js-reveal .sd-cell { opacity: 1; transform: none; transition: none; }
}


/* ---------- 13 straight answers ----------
   Two columns of category cards. A card names what it covers before it is
   opened, so the section can be read without opening anything, and the
   questions inside are their own disclosures so a long answer never pushes a
   whole column. */
.fq { background: var(--color-paper); padding: var(--space-12) 0 var(--space-6); }
.fq-inner {
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
}
.fq-head { max-width: 52ch; margin-bottom: var(--space-12); }
.fq-eyebrow { margin: 0 0 var(--space-5); }
.fq-h {
  margin: 0 0 var(--space-3);
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink);
}
.fq-lede {
  margin: 0 0 var(--space-12); max-width: 46ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.fq-help { margin: 0; font-size: var(--text-body); color: var(--color-ink-muted); }
.fq-help-link {
  display: inline-flex; align-items: center; gap: var(--space-2);
  color: var(--color-accent-strong); font-weight: var(--weight-semibold);
  text-decoration: none;
}
.fq-help-link:hover { text-decoration: underline; }
.fq-help-link .ico-inline { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
.fq-help-link .ico-inline path { fill: currentColor; }

/* Two explicit stacks rather than CSS multi-column. Multi-column rebalances
   on every open and drags cards across the gutter; a stack only lengthens
   itself. */
.fq-cats {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6); align-items: start;
}
.fq-col { display: flex; flex-direction: column; gap: var(--space-6); }
.fq-cat {
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}
.fq-cat[open] { border-color: var(--color-rule-strong); box-shadow: var(--shadow-2); }

.fq-cat-head {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  cursor: pointer; list-style: none;
}
.fq-cat-head::-webkit-details-marker { display: none; }
.fq-cat-head:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; border-radius: var(--radius-lg); }
.fq-cat-ico {
  flex: 0 0 auto; display: grid; place-items: center;
  width: var(--space-12); height: var(--space-12);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--fq-hue, var(--color-accent)) 14%, var(--color-surface));
}
.fq-cat-glyph { width: var(--space-5); height: var(--space-5); }
.fq-cat-glyph path { fill: var(--fq-ink, var(--color-accent-strong)); }
/* Four categories, four of the logo's hues, keyed off the index each card
   already carries for its reveal stagger. */
.fq-cat[style*="--fq-i: 0"] { --fq-hue: var(--lb-teal); --fq-ink: var(--lb-teal-ink); }
.fq-cat[style*="--fq-i: 1"] { --fq-hue: var(--lb-purple); --fq-ink: var(--lb-purple-ink); }
.fq-cat[style*="--fq-i: 2"] { --fq-hue: var(--lb-green); --fq-ink: var(--lb-green-ink); }
.fq-cat[style*="--fq-i: 3"] { --fq-hue: var(--lb-orange); --fq-ink: var(--lb-orange-ink); }
.fq-cat-text { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.fq-cat-name {
  font-size: var(--text-h4); line-height: var(--leading-snug);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-heading);
  color: var(--color-ink);
}
.fq-cat-sub {
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
/* One chevron serves both levels; it points down closed and up open. */
.fq-chev {
  flex: 0 0 auto; width: var(--space-4); height: var(--space-4);
  margin-left: auto;
  transition: transform var(--dur-fast) var(--ease-standard);
}
.fq-chev path { fill: var(--color-ink-faint); }
.fq-cat[open] > .fq-cat-head .fq-chev,
.fq-item[open] > .fq-q .fq-chev { transform: rotate(180deg); }

.fq-qs { padding: 0 var(--space-6) var(--space-3); }
.fq-item { border-top: 1px solid var(--color-rule); }
.fq-q {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) 0;
  cursor: pointer; list-style: none;
  font-size: var(--text-body); line-height: var(--leading-snug);
  font-weight: var(--weight-semibold); letter-spacing: var(--tracking-heading);
  color: var(--color-ink);
}
.fq-q::-webkit-details-marker { display: none; }
.fq-q:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; border-radius: var(--radius-sm); }
.fq-a { padding: 0 0 var(--space-4); }
.fq-a p {
  margin: 0; max-width: var(--measure);
  font-size: var(--text-small); line-height: var(--leading-relaxed);
  color: var(--color-ink-muted);
}
/* Height is left to the browser; only opacity and transform are animated. */
.fq-item[open] .fq-a, .fq-cat[open] .fq-qs {
  animation: fq-open var(--dur-base) var(--ease-assemble) both;
}
@keyframes fq-open {
  from { opacity: 0; transform: translateY(calc(var(--reveal-shift) * -0.4)); }
  to   { opacity: 1; transform: none; }
}

/* Assembly: each card rises once, staggered, and never again. Gated on a class
   the script sets, so with JS off they are simply there. */
.js-reveal .fq-cat {
  opacity: 0;
  transform: translateY(var(--reveal-shift)) scale(var(--reveal-scale));
  transition: opacity var(--dur-base) var(--ease-assemble),
              transform var(--dur-base) var(--ease-assemble);
  transition-delay: calc(var(--fq-i, 0) * var(--stagger-step));
}
.js-reveal .fq-cats.is-in .fq-cat { opacity: 1; transform: none; }

@media (max-width: 900px) {
  .fq { padding: var(--space-16) 0 var(--space-8); }
  .fq-head { margin-bottom: var(--space-8); }
  .fq-cats { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
  .fq-col { gap: var(--space-4); }
  .fq-cat-head { padding: var(--space-4) var(--space-5); gap: var(--space-3); }
  .fq-cat-ico { width: var(--space-10); height: var(--space-10); }
  .fq-qs { padding: 0 var(--space-5) var(--space-2); }
}
@media (prefers-reduced-motion: reduce) {
  .js-reveal .fq-cat { opacity: 1; transform: none; transition: none; }
  .fq-item[open] .fq-a, .fq-cat[open] .fq-qs { animation: none; }
  .fq-chev { transition: none; }
}


/* ---------- 14 closing call to action ----------
   An inset panel, not a band. Everything inside it is centred on one axis and
   the product runs off the bottom edge, so the page ends on the thing it has
   been describing rather than on a sentence. */
/* Full-bleed closer: the mesh panel runs edge to edge like the footer band,
   so the page ends on colour, not on a floating card. */
.cx { background: var(--color-paper); padding: var(--space-6) 0 0; }
.cx-inner { max-width: none; margin-inline: auto; padding-inline: 0; }
.cx-panel {
  position: relative; overflow: hidden;
  border-radius: 0;
  padding: var(--space-16) var(--gutter) 0;
  text-align: center;
  /* A mesh built from the brand hues. Gold is deliberately absent: at 1.49:1
     it is the one hue white type cannot sit on. */
  background:
    /* A scrim over the top of the mesh, where all the type sits. Without it
       white body copy lands on --lb-purple at 4.17:1, under the 4.5 minimum;
       with it the worst hue under the text measures 6.4:1. */
    linear-gradient(color-mix(in srgb, var(--color-ink-deep) 26%, transparent) 0%, transparent 62%),
    radial-gradient(120% 90% at 12% 100%, var(--lb-blue) 0%, transparent 58%),
    radial-gradient(110% 85% at 88% 4%, var(--lb-red) 0%, transparent 55%),
    radial-gradient(95% 80% at 82% 96%, var(--lb-teal) 0%, transparent 52%),
    radial-gradient(120% 100% at 50% 40%, var(--lb-magenta) 0%, transparent 62%),
    var(--lb-purple);
}
.cx-tile {
  display: inline-grid; place-items: center;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-3);
}
.cx-tile img { display: block; width: auto; height: var(--space-8); }
.cx-h {
  margin: var(--space-8) auto var(--space-3); max-width: 18ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-icon-glyph);
}
.cx-lede {
  margin: 0 auto var(--space-12); max-width: 42ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  /* Full white, not a mix: on a saturated ground every point of contrast is
     needed to clear 4.5:1 at body size. */
  color: var(--color-icon-glyph);
}
/* White on the mesh, rather than the spectrum fill the rest of the page uses:
   a gradient button on a gradient ground disappears. */
.cx-cta { background: var(--color-surface); color: var(--color-ink); }
.cx-cta:hover { background: color-mix(in srgb, var(--color-surface) 88%, var(--color-ink)); }
/* Padded to a real touch target rather than left at its 20px line box. */
/* Dark fill rather than white or hollow. White would clone the primary above
   it, and hollow let the mesh through until the ribbon stopped reading as a
   border at all. On --color-ink-deep the label measures 15.9:1. */
.cx-alt-link {
  --btn-quiet-fill: var(--color-ink-deep);
  color: var(--color-icon-glyph);
}
.cx-alt-link:hover {
  --btn-quiet-fill: color-mix(in srgb, var(--color-ink-deep) 86%, var(--color-icon-glyph));
}

/* The pair sits on one row, then the figures that back the claim. Each number
   traces to brief.md: 50 surfaces, every chart, 5 sector packs, $0 to start,
   $899 the published ceiling. */
.cx-cta-row {
  display: flex; justify-content: center; align-items: center;
  gap: var(--space-4); flex-wrap: wrap;
}
/* The two supported platforms, named side by side under the buttons as a
   claim rather than folded into a sentence. Same white as the lede: the
   panel ground is saturated and muted ink would not clear contrast. */
.cx-compat {
  margin: var(--space-4) 0 0;
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-icon-glyph);
}
.cx-stats {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-6);
  max-width: var(--container); margin: var(--space-12) auto var(--space-12);
  padding-inline: var(--gutter); text-align: left;
}
.cx-stat { padding-top: var(--space-4); border-top: 2px solid var(--cx-tint); }
/* Each figure gets its own mark. The tile lifts its hue from --cx-i so the
   five read as a set rather than five copies, and the whole cell answers to
   the pointer, which is the only interactivity a figure should have. */
.cx-stat { --cx-tint: var(--color-icon-glyph); }
.cx-stat:nth-child(1) { --cx-tint: var(--lb-teal); }
.cx-stat:nth-child(2) { --cx-tint: var(--lb-blue); }
.cx-stat:nth-child(3) { --cx-tint: var(--lb-purple); }
.cx-stat:nth-child(4) { --cx-tint: var(--lb-green); }
.cx-stat:nth-child(5) { --cx-tint: var(--lb-gold); }
.cx-stat-ico {
  display: grid; place-items: center;
  width: var(--space-10); height: var(--space-10);
  margin-bottom: var(--space-3);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--color-icon-glyph) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-icon-glyph) 24%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transition: background-color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.cx-stat-ico svg { width: var(--space-5); height: var(--space-5); }
.cx-stat-ico svg path { fill: var(--color-icon-glyph); }
.cx-stat:hover .cx-stat-ico {
  background: color-mix(in srgb, var(--cx-tint) 62%, transparent);
  border-color: var(--cx-tint);
}

.cx-stat-n {
  margin: 0 0 var(--space-2);
  font-size: var(--text-h1); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  font-variant-numeric: tabular-nums;
  color: var(--color-icon-glyph);
}
.cx-stat-p {
  margin: 0;
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-icon-glyph) 82%, transparent);
}
.cx-stat-p b { font-weight: var(--weight-bold); color: var(--color-icon-glyph); }

@media (max-width: 900px) {
  .cx-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); margin-top: var(--space-8); }
  .cx-stat-ico { width: var(--space-8); height: var(--space-8); margin-bottom: var(--space-2); }
  .cx-stat-ico svg { width: var(--space-4); height: var(--space-4); }
}
@media (max-width: 520px) {
  .cx-stats { grid-template-columns: minmax(0, 1fr); }
}

/* The product runs off the bottom edge of the panel, clipped by the panel. */
/* The screenshot sits centred with the panel's ground visible either side,
   per the owner's direction: a framed picture, not a full bleed. */
.cx-shot { margin: var(--space-14) 0 0; }
.cx-shot img {
  display: block; width: 100%; max-width: 62rem; margin-inline: auto;
  height: auto;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 24%, transparent);
  border-bottom: 0;
  box-shadow: var(--shadow-4);
}

/* Assembly: the panel's contents rise once, staggered, and never again. Gated
   on a class the script sets, so with JS off they are simply there. */
.js-reveal .cx-panel > * {
  opacity: 0;
  transform: translateY(var(--reveal-shift)) scale(var(--reveal-scale));
  transition: opacity var(--dur-base) var(--ease-assemble),
              transform var(--dur-base) var(--ease-assemble);
}
.js-reveal .cx-panel > :nth-child(2) { transition-delay: var(--stagger-step); }
.js-reveal .cx-panel > :nth-child(3) { transition-delay: calc(var(--stagger-step) * 2); }
.js-reveal .cx-panel > :nth-child(4) { transition-delay: calc(var(--stagger-step) * 3); }
.js-reveal .cx-panel > :nth-child(5) { transition-delay: calc(var(--stagger-step) * 4); }
.js-reveal .cx-panel > :nth-child(6) { transition-delay: calc(var(--stagger-step) * 5); }
.js-reveal .cx-panel.is-in > * { opacity: 1; transform: none; }

@media (max-width: 900px) {
  .cx { padding: var(--space-8) 0 var(--space-16); }
  .cx-panel { padding-top: var(--space-12); border-radius: var(--radius-lg); }
  .cx-tile img { height: var(--space-6); }
  .cx-h { margin-top: var(--space-6); max-width: none; }
  .cx-cta, .cx-alt-link { width: 100%; }
  .cx-shot { margin-top: var(--space-10); }
}
@media (prefers-reduced-motion: reduce) {
  .js-reveal .cx-panel > * { opacity: 1; transform: none; transition: none; }
}


/* ---------- 15 footer ----------
   The deepest ground on the page, and the widest column count. The product
   block takes half the width because it carries three sub-groups; the other
   three columns are single lists and sit in the remaining half. */
/* No vertical gap above the footer: whatever section lands last on a page
   gives up its bottom spacing, so the page hands straight to the dark ground. */
:is(section, div):has(+ footer.ft),
main:has(+ footer.ft) > :is(section, div):last-child,
main:has(+ footer.ft) {
  padding-bottom: 0; margin-bottom: 0;
}

.ft {
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  padding: var(--space-24) 0 var(--space-10);
  /* Full-bleed band: square corners, no inset, the dark ground runs to the
     viewport edges and the bottom of the page. */
  margin: 0;
  border-radius: 0;
  overflow: hidden;
}
.ft-inner { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }

.ft-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-6); flex-wrap: wrap;
  padding-bottom: var(--space-6);
  border-bottom: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
}
.ft-brand { display: inline-flex; }
.ft-brand img { display: block; width: auto; height: var(--space-10); }
/* The ask-about row. Real deep links into each assistant with the question
   pre-filled, opened in a new tab. No assistant logos: this icon set has none,
   and drawing someone else's mark from memory is not an option. */
.ft-ask { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.ft-ask-label {
  font-size: var(--text-small); font-weight: var(--weight-semibold);
  color: color-mix(in srgb, var(--color-ink-on-dark) 72%, transparent);
}
.ft-ask-list { list-style: none; margin: 0; padding: 0; display: flex; gap: var(--space-2); flex-wrap: wrap; }
.ft-ask-btn {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--space-10); padding: 0 var(--space-4);
  border-radius: var(--radius-pill);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 18%, transparent);
  background: color-mix(in srgb, var(--color-ink-on-dark) 5%, transparent);
  font-size: var(--text-small); color: var(--color-ink-on-dark);
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.ft-ask-btn:hover {
  background: color-mix(in srgb, var(--color-ink-on-dark) 12%, transparent);
  border-color: color-mix(in srgb, var(--color-accent) 60%, transparent);
}
.ft-ask-ico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
/* Each assistant carries its own hue from the logo palette, set per button in
   the markup, so the row does not read as four copies of the accent. */
.ft-ask-ico path { fill: var(--ask-hue, var(--color-accent)); }

.ft-cols {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-10) var(--space-5);
  align-items: start;
  padding: var(--space-10) 0;
}

.ft-h {
  display: flex; align-items: center; gap: var(--space-2);
  margin: 0 0 var(--space-5);
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold);
  color: var(--color-ink-on-dark);
}
.ft-h-ico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
/* The column marks take the logo's own hues, set per heading in the markup,
   so the footer reads as the lockup does rather than as nine copies of the
   accent. */
.ft-h-ico path { fill: var(--ft-hue, var(--color-accent)); }

.ft-list + .ft-h { margin-top: var(--space-8); }
.ft-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.ft-link {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) 0;
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 72%, transparent);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-standard);
}
.ft-link:hover { color: var(--color-ink-on-dark); }
.ft-li { display: grid; place-items: center; flex: 0 0 auto; }
.ft-li-ico { width: var(--space-4); height: var(--space-4); }
.ft-li-ico path { fill: color-mix(in srgb, var(--color-ink-on-dark) 46%, transparent); }
.ft-link:hover .ft-li-ico path { fill: var(--color-accent); }

.ft-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4) var(--space-8); flex-wrap: wrap;
  padding-top: var(--space-8);
  border-top: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
}
.ft-copy {
  margin: 0;
  font-size: var(--text-fine); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 52%, transparent);
}
.ft-legal { list-style: none; margin: 0; padding: 0; display: flex; gap: var(--space-6); }
.ft-legal .ft-link { font-size: var(--text-fine); }


/* The marks the product is built against. Same files as the strip higher up
   the page, at a quarter of the size. Headed "Built against" and never
   "certified" or "compliant": nothing here is a certification we hold. */
.ft-frameworks {
  padding: var(--space-8) 0;
  border-top: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
}
/* Two equal rows of nine, no heading: the grid splits the eighteen marks
   evenly instead of letting flex wrap wherever the widths land. */
.ft-marks {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(9, minmax(0, 1fr));
  align-items: center; justify-items: center;
  gap: var(--space-4) var(--space-8);
}
.ft-mark img {
  display: block; width: auto; height: var(--space-8);
  filter: grayscale(1) invert(1); opacity: .58;
  transition: opacity var(--dur-fast) var(--ease-standard);
}
.ft-mark img:hover { opacity: 1; }

/* Circular social buttons, the one detail taken from the other reference. */
.ft-social-list { list-style: none; margin: 0; padding: 0; display: flex; gap: var(--space-3); }
.ft-social {
  display: grid; place-items: center;
  width: var(--space-10); height: var(--space-10);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--color-ink-on-dark) 10%, transparent);
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.ft-social:hover { background: var(--color-accent); }
.ft-social-ico { width: var(--space-4); height: var(--space-4); }
.ft-social-ico path { fill: color-mix(in srgb, var(--color-ink-on-dark) 78%, transparent); }
.ft-social:hover .ft-social-ico path { fill: var(--color-icon-glyph); }

.ft-copy { margin: var(--space-6) 0 0; }

@media (max-width: 1200px) {
  .ft-cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ft-marks { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}
@media (max-width: 820px)  {
  .ft-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ft-marks { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  .ft { padding: var(--space-16) 0 var(--space-8); }
  .ft-top { justify-content: flex-start; }
  .ft-cols { grid-template-columns: minmax(0, 1fr); }
  .ft-mark img { height: var(--space-6); }
  .ft-marks { gap: var(--space-3) var(--space-5); grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ft-cols { padding: var(--space-10) 0; gap: var(--space-8); }
  /* Real touch targets once a finger is doing the pointing. The legal links
     are --text-fine and measured 35px without this. */
  .ft-link { min-height: var(--space-12); padding: var(--space-2) 0; }
  .ft-legal { flex-wrap: wrap; gap: var(--space-5); }
}

/* ---- section two, the five figures --------------------------------------
   Tiles rather than bare columns on a dark ground: each one carries its own
   hue on the rule, the mark and the wash, so the five read as a set and the
   number is the loudest thing in the box. */
.cx-stat {
  padding: var(--space-5) var(--space-5) var(--space-6);
  border-top: 2px solid var(--cx-tint);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  background: linear-gradient(
    color-mix(in srgb, var(--cx-tint) 14%, transparent),
    color-mix(in srgb, var(--cx-tint) 3%, transparent));
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.cx-stat-ico {
  background: color-mix(in srgb, var(--cx-tint) 26%, transparent);
  border-color: color-mix(in srgb, var(--cx-tint) 44%, transparent);
}
.cx-stat-n { font-size: var(--text-display); }
.op-cta { margin: var(--space-10) 0 0; text-align: center; padding-inline: var(--gutter); }
.op-cta-link { padding: calc(var(--space-3) - 1px) calc(var(--space-6) - 1px); gap: var(--space-2); }
.op-cta-link .ico-inline { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
.op-cta-link .ico-inline path { fill: currentColor; }
/* The roll call is a second beat inside the same section, so it gets a
   heading of its own rather than borrowing the section's. */
.op-sub {
  max-width: var(--container); margin: var(--space-16) auto var(--space-6);
  padding-inline: var(--gutter);
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold);
  color: color-mix(in srgb, var(--color-ink-on-dark) 62%, transparent);
  text-align: center;
}
@media (max-width: 900px) {
  .op-sub { margin-top: var(--space-12); }
  .op-cta-link { width: 100%; justify-content: center; }
}

/* ---------- customer stories, per-customer stat stack ----------
   Namespaced cs-* so nothing collides with the page's own .ico, .eyebrow,
   .head or .lede. Figures are SAMPLE DATA until real numbers land. */
/* ---------- section frame ---------- */
/* Dark band, matching section 04. The section itself now spans the full
   width so the ground bleeds edge to edge, and .cs-inner carries the container
   and gutters that used to sit on the section. */
.cs-proof {
  background: var(--color-ink-deep);
  color: var(--color-ink-on-dark);
  padding: var(--space-24) 0;
}
/* Same phone rhythm as every other section. Declared here rather than with the
   other mobile rules, because .cs-proof's base padding is declared further down
   the sheet and would otherwise win. */
@media (max-width: 900px) { .cs-proof { padding: var(--space-16) 0; } }
.cs-inner { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
/* The cards keep their own pale grounds, so their copy has to go back to ink.
   Without this they inherit the band's light text and vanish. */
.cs-flagship, .cs-compact { color: var(--color-ink); }
/* Every dark band opens with the drifting-edge eyebrow. This one carries no
   destination, so it is a span rather than a link: standards forbids writing a
   link against a slug that does not exist. */
.cs-eyebrow { margin: 0 0 var(--space-5); text-align: center; }
.eyebrow--plain { cursor: default; }
.eyebrow--plain:hover { box-shadow: none; }
.eyebrow--plain .spark path { fill: var(--lb-teal); }
.cs-head {
  margin: 0 auto var(--space-3); text-align: center; max-width: 20ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-display);
  color: var(--color-ink-on-dark);
}
.cs-lede {
  margin: 0 auto var(--space-10); text-align: center; max-width: 46ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 74%, transparent);
}

/* ---------- grids ---------- */
.cs-flagships { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5); }
.cs-compacts  { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-5); margin-top: var(--space-5); }

/* ---------- flagship ---------- */
.cs-flagship {
  position: relative;
  border-radius: var(--radius-xl);
  padding: var(--space-6);
  display: flex; flex-direction: column; gap: var(--space-6);
  background:
    linear-gradient(107deg,
      color-mix(in srgb, var(--hue-a) 34%, var(--color-surface)) 0%,
      color-mix(in srgb, var(--hue-a) 20%, var(--color-surface)) 38%,
      color-mix(in srgb, var(--hue-b) 42%, var(--color-surface)) 100%);
}

/* LB-05. The mark sits on the card's own wash, not on a paper notch cut out
   of it. The plate was there to keep white-background logo files from
   showing their corners; multiplying the file onto the wash does that
   without carving a second colour into the card. Phone width already had
   this; the two flagship cards were the last place the plate survived. */
.cs-tab {
  position: absolute; top: 0; right: 0;
  background: none;
  padding: var(--space-5) var(--space-5) var(--space-4) var(--space-6);
  display: flex; align-items: center; justify-content: flex-end;
}
/* Sized inside a bounded box rather than by height alone: these marks range
   from 1.9:1 to 4.9:1, so a single height made the wide ones tiny. Width and
   height caps together let each logo grow until it hits whichever edge it
   reaches first, which is what keeps them optically even. */
.cs-tab img { max-width: 9rem; max-height: 3.25rem; width: auto; height: auto; object-fit: contain; display: block; }

.cs-story {
  margin: 0; max-width: 22ch;
  font-size: var(--text-h3); line-height: var(--leading-snug);
  font-weight: var(--weight-medium); letter-spacing: var(--tracking-heading);
}
.cs-story b { font-weight: var(--weight-bold); }

.cs-tiles { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); margin-top: auto; }
.cs-tile {
  background: color-mix(in srgb, var(--color-surface) 55%, transparent);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-2);
}

/* ---------- figure ---------- */
.cs-n {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--text-h2); line-height: 1; font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-heading); font-variant-numeric: tabular-nums;
}
/* The glyph is masked, not filled, so one file serves every hue. */
.cs-ico {
  flex: 0 0 auto; width: var(--space-6); height: var(--space-6);
  background: var(--ico, color-mix(in srgb, var(--hue-a) 62%, var(--color-ink)));
  -webkit-mask: var(--glyph) center / contain no-repeat;
          mask: var(--glyph) center / contain no-repeat;
}
.cs-l { font-size: var(--text-small); line-height: var(--leading-normal); color: var(--color-ink-muted); }

/* ---------- compact ---------- */
.cs-compact {
  border-radius: var(--radius-xl);
  padding: var(--space-5);
  display: flex; flex-direction: column; justify-content: space-between; gap: var(--space-6);
  background: color-mix(in srgb, var(--hue-a) 14%, var(--color-surface));
  border: 1px solid color-mix(in srgb, var(--hue-a) 16%, var(--color-rule));
}
.cs-compact .cs-mark img { max-width: 7.25rem; max-height: 2.6rem; width: auto; height: auto; object-fit: contain; display: block; }
.cs-compact .cs-l { margin-top: var(--space-2); }

/* ---------- motion ---------- */
.cs-flagship, .cs-compact { animation: cs-rise var(--dur-slow) var(--ease-assemble) backwards; }
.cs-flagships > :nth-child(2) { animation-delay: var(--dur-instant); }
.cs-compacts  > :nth-child(1) { animation-delay: calc(var(--dur-instant) * 2); }
.cs-compacts  > :nth-child(2) { animation-delay: calc(var(--dur-instant) * 3); }
.cs-compacts  > :nth-child(3) { animation-delay: calc(var(--dur-instant) * 4); }
.cs-compacts  > :nth-child(4) { animation-delay: calc(var(--dur-instant) * 5); }
@keyframes cs-rise { from { opacity: 0; transform: translateY(var(--space-3)); } }

/* ---------- responsive ---------- */
@media (max-width: 1000px) {
  .cs-compacts { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
  .cs-flagships { grid-template-columns: 1fr; }
}
@media (max-width: 700px) {
  /* too narrow to run the story alongside the notch, so the tab leaves its
     corner and sits in the flow above the story */
  .cs-tab {
    position: static; align-self: flex-start;
    background: none; padding: 0; border-radius: 0;
  }
}
@media (max-width: 560px) {
  .cs-compacts { grid-template-columns: 1fr; }
  .cs-tiles { grid-template-columns: 1fr; }
  }

/* ---- expander shot: the capture must not set the panel height -----------
   The shot spans both rows, so an image taller than the copy dragged the
   whole panel with it - a 334x579 capture opened the panel to 965px against
   400px of text. Pinning the image inside its own box hands the height back
   to the copy. Only above the stacking breakpoint: once the shot sits under
   the copy, auto height is the correct behaviour. */
@media (min-width: 761px) {
  .sx-shot { position: relative; }
  .sx-shot img { position: absolute; inset: 0; width: 100%; height: 100%; }
}

/* ---- library chrome ------------------------------------------------------
   The catalogue's own furniture, and nothing else: a contents grid at the top
   and a label above each entry. Deliberately plain, so it never competes with
   the thing it is labelling. */
.lib-top {
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  padding: var(--space-16) 0 var(--space-20);
}
.lib-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.lib-kicker {
  margin: 0; font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold);
  color: color-mix(in srgb, var(--color-ink-on-dark) 56%, transparent);
}
.lib-top h1 {
  margin: var(--space-5) 0 var(--space-3);
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.lib-top p {
  margin: 0 0 var(--space-10); max-width: var(--measure);
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 74%, transparent);
}
.lib-toc {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3);
}
.lib-toc a {
  display: flex; align-items: baseline; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 16%, transparent);
  border-radius: var(--radius-md);
  color: var(--color-ink-on-dark); text-decoration: none;
  font-size: var(--text-small); line-height: var(--leading-snug);
  transition: border-color var(--dur-fast) var(--ease-standard),
              background-color var(--dur-fast) var(--ease-standard);
}
.lib-toc a:hover, .lib-toc a:focus-visible {
  background: color-mix(in srgb, var(--color-ink-on-dark) 8%, transparent);
  border-color: color-mix(in srgb, var(--color-ink-on-dark) 34%, transparent);
}
.lib-toc b {
  font-variant-numeric: tabular-nums; font-weight: var(--weight-bold);
  color: color-mix(in srgb, var(--color-ink-on-dark) 52%, transparent);
}

/* The label is a band, not a card: it has to read as page furniture so nobody
   mistakes it for part of the section underneath. */
/* The site header is sticky, so an anchor jump has to clear it or the label
   it just landed on sits underneath. */
.lib-label {
  background: var(--color-surface); border-top: 1px solid var(--color-rule);
  scroll-margin-top: var(--space-20);
}
.lib-label .lib-in {
  display: grid; grid-template-columns: auto minmax(0, 26rem) minmax(0, 1fr);
  align-items: baseline; gap: var(--space-3) var(--space-6);
  padding-block: var(--space-6);
}
.lib-n {
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  font-weight: var(--weight-bold); font-variant-numeric: tabular-nums;
  color: var(--color-ink-muted);
}
.lib-name { margin: 0; font-size: var(--text-h4); font-weight: var(--weight-bold); }
.lib-what {
  margin: 0; font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.lib-tags { grid-column: 2; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.lib-tag {
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
  border: 1px solid var(--color-rule); background: var(--color-paper);
  font-size: var(--text-fine); color: var(--color-ink-muted);
}
.lib-tag code { font-family: inherit; color: var(--color-ink); }

@media (max-width: 900px) {
  .lib-toc { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lib-label .lib-in { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
  .lib-tags { grid-column: 1; }
}
@media (max-width: 600px) {
  .lib-toc { grid-template-columns: minmax(0, 1fr); }
}

/* ---- 19: scroll-morph hero ----------------------------------------------
   The objects orbit the headline, then sink behind the capture while the
   button opens into a nav. One scroll listener writes --mh-p (0 to 1) onto
   the section and every piece reads it; nothing else is measured per frame,
   and only transform and opacity move.

   The stage is sticky inside a taller runway, which is what buys the beat
   where the copy leaves and the capture stays. */
.mh {
  --mh-p: 0;
  position: relative;
  background: var(--color-paper);
  overflow: clip;
}
/* The faint ruled ground the objects float over. Two gradients, no asset. */
.mh::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    repeating-linear-gradient(to right, var(--color-rule) 0 1px, transparent 1px 12.5%),
    repeating-linear-gradient(to bottom, var(--color-rule) 0 1px, transparent 1px 33%);
  opacity: 0.5;
  -webkit-mask-image: radial-gradient(120% 80% at 50% 30%, #000 40%, transparent 100%);
  mask-image: radial-gradient(120% 80% at 50% 30%, #000 40%, transparent 100%);
}
.mh-run { height: 220vh; }
.mh-stage {
  position: sticky; top: 0; height: 100vh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  max-width: var(--container); margin-inline: auto;
  /* The foot of the frame is reserved for the capture, which is positioned
     rather than laid out, so the copy never has to move around it. */
  padding: calc(var(--space-16) + var(--space-8)) var(--gutter) 46vh;
  overflow: clip;
}

/* --- the objects --- */
.mh-objects { position: absolute; inset: 0; z-index: 1; }
/* Product cards rather than icons: what sinks behind the capture is the same
   material the capture is made of. Four each side, sized off the viewport so
   the pair of columns never crowds the headline. */
.mh-tile {
  position: absolute; left: var(--l); top: var(--t);
  display: block; overflow: hidden;
  width: clamp(6.5rem, 12vw, 11rem); aspect-ratio: 16 / 10;
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  border: 1px solid var(--color-rule);
  box-shadow: var(--shadow-3);
  color: var(--color-ink);
  /* Sink: drift toward the capture, shrink, turn a little, then fade out just
     before the capture would have to clip them. */
  transform:
    translate3d(calc(var(--dx) * var(--mh-p)), calc(var(--dy) * var(--mh-p)), 0)
    rotate(calc(var(--r) * var(--mh-p)))
    scale(calc(1 - 0.45 * var(--mh-p)));
  opacity: calc(1 - 1.6 * var(--mh-p));
  will-change: transform, opacity;
}
.mh-tile img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: left top;
}
.mh-ico { width: var(--space-6); height: var(--space-6); }
.mh-ico path { fill: currentColor; }
.mh-num {
  font-size: var(--text-h3); font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums; letter-spacing: var(--tracking-display);
}

/* --- the copy --- */
.mh-copy {
  position: relative; z-index: 2; text-align: center;
  transform: translate3d(0, calc(var(--space-8) * var(--mh-p) * -1), 0);
  opacity: calc(1 - 1.9 * var(--mh-p));
  will-change: transform, opacity;
}
.mh-badge {
  display: inline-flex; align-items: center; gap: var(--space-3);
  margin: 0 0 var(--space-8); padding: var(--space-1) var(--space-4) var(--space-1) var(--space-1);
  border: 1px solid var(--color-rule); border-radius: var(--radius-pill);
  background: var(--color-surface); box-shadow: var(--shadow-1);
  font-size: var(--text-small); color: var(--color-ink-muted);
}
.mh-badge b {
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--lb-teal) 22%, var(--color-surface));
  color: var(--color-ink); font-weight: var(--weight-bold);
}
.mh-h {
  margin: 0 auto var(--space-5); max-width: 16ch;
  font-size: var(--text-hero); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
/* The highlight is painted behind the words, so descenders stay readable. */
.mh-mark {
  position: relative; z-index: 0; white-space: nowrap;
  padding-inline: var(--space-2);
}
.mh-mark::before {
  content: ""; position: absolute; z-index: -1; inset: 12% -2% 8% -2%;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--lb-teal) 26%, transparent);
}
.mh-lede {
  /* Held to a reading measure and centred: run full width and the line
     passes under the floating tiles at both edges, which is what put a
     screenshot through the middle of a sentence. */
  margin: 0 auto; max-width: 46ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}

/* --- the pill --- */
.mh-pill-wrap {
  position: relative; z-index: 3; margin: var(--space-10) 0 var(--space-12);
  /* Travel is viewport-relative, like the runway, so the pill lands at the
     top of the frame whatever the window height. */
  transform: translate3d(0, calc(var(--mh-p) * -20vh), 0);
  will-change: transform;
}
/* Once it has become the nav it stops travelling and holds under the site
   header, because a page navigation that scrolls away is not one. It goes when
   the section does: this bar belongs to this section and nothing below it. */
.mh.is-nav .mh-pill-wrap {
  position: fixed; z-index: 40;
  top: var(--mh-top, 5rem); left: 50%;
  margin: 0; transform: translate3d(-50%, 0, 0);
  transition: opacity var(--dur-base) var(--ease-standard),
              visibility 0s linear var(--dur-base);
}
.mh.is-past .mh-pill-wrap {
  opacity: 0; visibility: hidden; pointer-events: none;
}
.mh-pill {
  /* Near-black is not in the palette, and a floating black bar on a white
     page reads as someone else's component. The page's own surface, the
     rule it uses everywhere and one lit action. */
  display: flex; align-items: center; gap: 0;
  padding: var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  border: 1px solid var(--color-rule);
  box-shadow: var(--shadow-3);
}
.mh-pill-cta {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-pill);
  background: var(--lb-teal); color: var(--color-ink);
  text-decoration: none;
  font-size: var(--text-ui); font-weight: var(--weight-bold);
  white-space: nowrap;
}
.mh-pill-cta--demo .mh-dot { background: var(--color-ink); }
.mh-dot {
  width: var(--space-2); height: var(--space-2); border-radius: var(--radius-pill);
  background: var(--lb-teal);
}
/* The nav is present from the start and closed to nothing, so opening it is a
   width and opacity change rather than a swap. Order flips at the same moment
   the button reaches the top, which is what makes it read as one object
   turning into another. */
.mh-nav {
  display: flex; align-items: center; overflow: hidden;
  max-width: 0; opacity: 0;
  transition: max-width var(--dur-sweep) var(--ease-assemble),
              opacity var(--dur-base) var(--ease-standard);
}
.mh-nav a {
  padding: var(--space-2) var(--space-4);
  color: var(--color-ink-muted);
  text-decoration: none; font-size: var(--text-ui); white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-standard);
}
.mh-nav a:hover, .mh-nav a:focus-visible { color: var(--color-ink); }
.mh.is-nav .mh-nav, .mh--actions .mh-nav { max-width: 24rem; opacity: 1; }
.mh.is-nav .mh-pill-cta { order: 2; background: var(--lb-teal); color: var(--color-ink); }
.mh.is-nav .mh-dot { background: var(--color-ink); }

/* --- the capture --- */
.mh-shot {
  position: absolute; z-index: 2; left: var(--gutter); right: var(--gutter); bottom: 0;
  margin: 0;
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  overflow: hidden;
  box-shadow: var(--shadow-4);
  /* Anchored to the foot of the frame and grown from there, rather than moved:
     the file is shorter than the frame, so anything that lifts it leaves a band
     of paper underneath. Out of flow, so growing it moves nothing else. */
  /* Its own ratio, so nothing is cropped: growing a 16:9 file inside a box
     that gets taller than it is wide cuts both sides off, which is what put
     half a dashboard on the page. Height first, width from the ratio, and
     capped so the picture cannot climb into the headline. It rises into
     place rather than growing. */
  left: 50%; right: auto; width: auto;
  height: min(52vh, calc((100vw - var(--gutter) * 2) * 9 / 16));
  aspect-ratio: 16 / 9;
  transform: translate3d(-50%, calc((1 - var(--mh-p)) * 10vh), 0);
}
.mh-shot img {
  display: block; width: 100%; height: auto;
  object-fit: contain; object-position: top center;
}

@media (max-width: 900px) {
  .mh-run { height: 190vh; }
  .mh-stage { padding-top: calc(var(--space-12) + var(--space-8)); }
  .mh-tile { width: 30vw; }
  .mh-ico { width: var(--space-5); height: var(--space-5); }
  .mh-num { font-size: var(--text-h4); }
  .mh-h { font-size: var(--text-h1); max-width: 12ch; }
  /* Four of the eight would sit under the type once the column narrows. */
  .mh-tile:nth-child(3), .mh-tile:nth-child(4),
  .mh-tile:nth-child(7), .mh-tile:nth-child(8) { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  /* No runway and no travel: the section is simply its finished state. */
  .mh-run { height: auto; }
  .mh-stage { position: static; height: auto; padding-bottom: var(--space-16); }
  .mh-shot { position: static; left: auto; right: auto; }
  .mh-tile, .mh-copy, .mh-pill-wrap { transform: none; }
  .mh-shot { height: auto; }
  .mh-shot img { height: auto; }
  .mh-nav { transition: none; }
}

/* ---- 20: tunnel hero -----------------------------------------------------
   Panels arrive at a vanishing point behind the headline and fly out past the
   reader. The stage holds the perspective, each panel holds a fixed x and y in
   that space, and the keyframe moves nothing but z. The projection turns one
   fixed x into the whole outward sweep: small and near the centre at the far
   end, magnified off the edge of the frame at the near end.

   Sixteen panels share one animation. The negative delay is derived from each
   panel's index, so they sit evenly along the path and the loop is seamless
   without a second keyframe or a line of script. */
.tn {
  /* The loop reads off the ambient duration token, like the marquee. */
  --tn-cycle: calc(var(--dur-ambient) * 2.4);
  --tn-lanes: 10;
  position: relative;
  overflow: clip;
  background: var(--color-paper);
  /* Tall enough to have a top, a middle and a bottom. The type takes the top
     and the bottom; the flight path owns the middle band between them, which
     is the only reason the panels never cross a word. */
  min-height: 96vh;
  display: grid; align-items: stretch;
  padding: calc(var(--space-20) + var(--space-8)) 0 var(--space-20);
  isolation: isolate;
}
/* The dotted ground of the reference, as a gradient rather than an asset. */
.tn::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(var(--color-rule) 1px, transparent 1px);
  background-size: var(--space-6) var(--space-6);
  opacity: 0.7;
}

.tn-stage {
  position: absolute; inset: 0; z-index: 1;
  perspective: 1000px; perspective-origin: 50% 50%;
  transform-style: preserve-3d;
  /* The section clips, but a 3D subtree still widened the document on a
     narrow window, which put a sideways scrollbar on the page. The stage
     clips its own flight path as well. */
  overflow: clip;
}
.tn-lane { position: absolute; inset: 0; transform-style: preserve-3d; }

.tn-panel {
  position: absolute; left: 50%; top: 50%;
  /* Sized and centred from a token, not a literal: the aspect ratio gives the
     height, so the two offsets are the token halved and two thirds of it. */
  width: calc(var(--space-32) * 1.5);
  margin: calc(var(--space-32) * -1) 0 0 calc(var(--space-32) * -0.75);
  /* The cards are landscape, near square. A portrait panel cropped away
     half of every one of them. */
  aspect-ratio: 5 / 4;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-surface);
  box-shadow: var(--shadow-3);
  animation: tn-fly var(--tn-cycle) var(--ease-linear) infinite;
  /* The stagger is the whole loop: one journey, sixteen positions along it. */
  animation-delay: calc(var(--tn-cycle) / var(--tn-lanes) * var(--i) * -1);
  will-change: transform, opacity;
}
.tn-lane--r .tn-panel {
  animation-delay: calc(var(--tn-cycle) / var(--tn-lanes) * var(--i) * -1
                        - var(--tn-cycle) / var(--tn-lanes) / 2);
}
.tn-panel img {
  display: block; width: 100%; height: 100%;
  object-fit: contain; background: var(--color-surface);
}

/* One path per side, shared by every panel on it. The panels differ only in
   when they start, so they read as one row moving outward rather than as a
   scatter. Angled away from the centre line for the opened-door look. */
.tn-lane--l .tn-panel { --x0: -14px; --x1: -560px; --ry: 16deg; }
.tn-lane--r .tn-panel { --x0: 14px; --x1: 560px; --ry: -16deg; }

@keyframes tn-fly {
  0%   { transform: translate3d(var(--x0), 0, -2600px) rotateY(var(--ry)); opacity: 0; }
  6%   { opacity: 1; }
  96%  { opacity: 1; }
  /* Stops short of the camera: at 700px the nearest panel rendered wider than
     the capture itself, and a card blown past its own pixels is the blur you
     see for the half second it is closest. */
  100% { transform: translate3d(var(--x1), 0, 460px) rotateY(var(--ry)); opacity: 0; }
}


/* --- the copy sits over the tunnel, with a gap where the strip shows --- */
.tn-copy {
  position: relative; z-index: 2;
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
  display: flex; flex-direction: column; align-items: center; text-align: center;
  justify-content: space-between;
}
.tn-h {
  margin: 0; max-width: 18ch; font-size: var(--text-hero); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
/* The middle band. It takes whatever is left between the heading and the
   lede, which is the corridor the tunnel flies through. */
.tn-gap { flex: 1 1 auto; min-height: var(--space-24); }
.tn-lede {
  margin: 0 0 var(--space-10); max-width: var(--measure);
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}

.tn-prompt {
  display: inline-flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-5);
  border-radius: var(--radius-pill);
  background: var(--color-ink-deep);
  box-shadow: var(--shadow-4);
  color: var(--color-ink-on-dark); text-decoration: none;
  transition: transform var(--dur-fast) var(--ease-standard);
}
.tn-prompt:hover, .tn-prompt:focus-visible { transform: translateY(-2px); }
.tn-prompt-text { font-size: var(--text-ui); }
.tn-spark { width: var(--space-5); height: var(--space-5); }
.tn-spark path { fill: var(--lb-teal); }
.tn-prompt-go {
  display: grid; place-items: center;
  width: var(--space-8); height: var(--space-8);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--color-ink-on-dark) 16%, transparent);
}
.tn-go-ico { width: var(--space-4); height: var(--space-4); }
.tn-go-ico path { fill: var(--color-ink-on-dark); }

@media (max-width: 900px) {
  .tn {
    padding: calc(var(--space-16) + var(--space-8)) 0 var(--space-16);
    min-height: 96vh;
  }
  .tn-h { font-size: var(--text-h1); }
  /* The row is confined to the top half: the lede owns the bottom. */
  .tn-stage { inset: 20% 0 55% 0; }
  .tn-panel {
    width: calc(var(--space-24) * 1.5);
    margin: calc(var(--space-24) * -1) 0 0 calc(var(--space-24) * -0.75);
  }
  .tn-gap { min-height: var(--space-16); }
  /* Every other one leaves, which doubles the spacing rather than breaking the
     row: at this width the full procession would reach the type. */
  .tn-panel:nth-child(even) { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  /* The loop is the section, so with motion off it holds one frame of it
     rather than pretending to be something else. Pausing is not enough: the
     duration token is 0ms here, which would park every panel on its opening
     frame, and that frame is transparent. Without the stagger they would all
     land on the same spot, so the row is spread by index instead. */
  .tn-panel {
    animation: none;
    opacity: 1;
    transform: translate3d(calc(var(--x0) + (var(--x1) - var(--x0)) * 0.45), 0, -900px)
               rotateY(var(--ry));
    margin-left: calc(var(--space-32) * -0.75 + var(--i) * var(--space-16));
  }
  .tn-lane--l .tn-panel {
    margin-left: calc(var(--space-32) * -0.75 - var(--i) * var(--space-16));
  }
  .tn-prompt { transition: none; }
}

/* ---- 21: comparison table ------------------------------------------------
   The own-brand column is lifted out of the grid rather than coloured in: it
   carries the surface, the full-strength marks and a rule down each side, so
   the eye lands on it before it reads a word. Everything else is deliberately
   quiet.

   It is a real table. The claims are row headers and the brand names are column
   headers, so a screen reader announces "IntelliBoard: not established" on the
   cell rather than reading a wall of loose icons. */
.cp { background: var(--color-paper); padding: var(--space-12) 0; }
.cp-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }

.cp-eyebrow {
  margin: 0 0 var(--space-4); text-align: center;
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.cp-h {
  margin: 0 auto var(--space-4); max-width: 22ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
/* The second half of the heading is the concession, so it is said quietly. */
.cp-h-quiet { color: var(--color-ink-muted); }
.cp-lede {
  margin: 0 auto var(--space-8); max-width: var(--measure); text-align: center;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}

/* Not decoration: this has to be impossible to miss and easy to delete. */
.cp-gate {
  /* Dashed amber reads as an error on a page, whatever it says. A note is a
     note: the surface it sits on, one accent edge, nothing flashing. */
  /* No accent rail: the owner ruled the coloured left edge out everywhere.
     A note is a soft tinted card instead. */
  margin: 0 auto var(--space-10); max-width: var(--measure-lede);
  padding: var(--space-4) var(--space-5);
  border: 1px solid color-mix(in srgb, var(--lb-teal) 24%, var(--color-rule));
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--lb-teal) 6%, var(--color-surface));
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink);
}

/* ---- the legend for a comparison table ----------------------------------
   Three marks at the size the table draws them, each with what it means. A
   symbol explained in a paragraph has to be held in the head while the eye
   is somewhere else; shown, it is read once. */
.cp-key {
  margin: 0 auto var(--space-8); max-width: var(--container);
  padding: var(--space-5) var(--space-6);
  border: 1px solid var(--color-rule); border-radius: var(--radius-lg);
  background: var(--color-surface); box-shadow: var(--shadow-1);
}
.cp-key-h {
  margin: 0 0 var(--space-4); font-size: var(--text-small);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; color: var(--lb-teal-ink);
}
.cp-key-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-5);
}
.cp-key-list li {
  display: grid; grid-template-columns: auto 1fr; gap: var(--space-3);
  align-items: start; font-size: var(--text-fine);
  line-height: var(--leading-normal); color: var(--color-ink-muted);
}
.cp-key-list b { color: var(--color-ink); font-weight: var(--weight-semibold); }
.cp-key-note {
  margin: var(--space-4) 0 0; padding-top: var(--space-3);
  border-top: 1px solid var(--color-rule);
  font-size: var(--text-fine); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
@media (max-width: 760px) {
  .cp-key-list { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
}
.cp-gate b { font-weight: var(--weight-bold); }

.cp-scroll { overflow-x: auto; }
.cp-table {
  width: 100%; min-width: 46rem;
  /* LB-25. The table is its own containing block. Its header cells are
     sticky and each carries an absolutely placed label for screen readers;
     with a static table those labels resolved against the page instead, so
     they reached past the right edge and the whole page scrolled sideways
     on a phone even though the table's own box scrolls perfectly well. */
  position: relative;
  /* Fixed, so every rival gets the same column and no name can widen its own. */
  table-layout: fixed;
  border-collapse: separate; border-spacing: 0;
  border: 1px solid var(--color-rule); border-radius: var(--radius-lg);
  background: var(--color-surface);
  overflow: hidden;
}
.cp-table th, .cp-table td { padding: var(--space-5) var(--space-4); vertical-align: middle; }
.cp-table thead th {
  font-size: var(--text-ui); font-weight: var(--weight-bold);
  color: var(--color-ink); text-align: center;
  border-bottom: 1px solid var(--color-rule);
}
.cp-corner { width: 40%; }
.cp-claim {
  width: 40%; text-align: left; font-weight: var(--weight-regular);
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
  border-bottom: 1px solid var(--color-rule);
}
.cp-table tbody tr:last-child th, .cp-table tbody tr:last-child td { border-bottom: 0; }
/* The value column never set its own size, so it fell back to the page's
   default and read a size larger than the claim beside it. */
.cp-cell { text-align: center; font-size: var(--text-small); border-bottom: 1px solid var(--color-rule); }

/* The lifted column. */
.cp-them { border-bottom: 2px solid color-mix(in srgb, var(--cp-hue, var(--color-rule)) 40%, transparent); }
.cp-us, .cp-cell--us {
  background: color-mix(in srgb, var(--lb-teal) 9%, var(--color-surface));
  box-shadow: inset 1px 0 0 color-mix(in srgb, var(--lb-teal) 26%, transparent),
              inset -1px 0 0 color-mix(in srgb, var(--lb-teal) 26%, transparent);
}
.cp-us { color: var(--lb-teal-ink); }
.cp-us, .cp-them { width: 12%; }
.cp-them { color: var(--color-ink-muted); }

.cp-mark {
  display: inline-grid; place-items: center;
  width: var(--space-8); height: var(--space-8);
  border-radius: var(--radius-pill);
}
.cp-ico { width: var(--space-4); height: var(--space-4); }
.cp-ico path { fill: currentColor; }
.cp-mark--us { background: var(--lb-teal); color: var(--color-surface); }
.cp-mark--yes { background: color-mix(in srgb, var(--lb-teal) 30%, transparent);
                color: var(--lb-teal-ink); }
.cp-mark--no { background: color-mix(in srgb, var(--color-ink) 8%, transparent);
               color: var(--color-ink-muted); }
/* Not established is neither a yes nor a no, and must not read as either. */
.cp-mark--unknown { background: color-mix(in srgb, var(--color-ink) 5%, transparent);
                    color: color-mix(in srgb, var(--color-ink-muted) 60%, transparent); }

/* --- the badge row --- */
.cp-badges {
  list-style: none; margin: var(--space-16) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--space-4);
}
.cp-badge {
  /* Fixed tracks, so the coloured band sits on the same line across all six
     however many lines the foot wraps to. Each badge takes its own hue
     through --cp-badge-hue, set inline per badge, the same way a plan card
     takes --pl-hue: one shared component, six identities. */
  display: grid; grid-template-rows: auto auto auto 1fr; text-align: center;
  border: 1px solid var(--color-rule); border-radius: var(--radius-md);
  background: var(--color-surface);
  overflow: hidden;
  transition: box-shadow var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard);
}
.cp-badge:hover { box-shadow: var(--shadow-2); transform: translateY(calc(var(--space-1) * -0.5)); }
.cp-badge-ico {
  display: grid; place-items: center;
  margin: var(--space-3) auto 0;
  width: var(--space-8); height: var(--space-8);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--cp-badge-hue, var(--lb-teal)) 16%, var(--color-surface));
  color: var(--cp-badge-hue, var(--lb-teal));
}
.cp-badge-ico-svg { width: var(--space-4); height: var(--space-4); fill: currentColor; }
.cp-badge-top {
  padding: var(--space-2) var(--space-2) var(--space-3);
  font-size: var(--text-fine); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold);
  color: var(--color-ink-muted);
}
.cp-badge-mid {
  padding: var(--space-2);
  background: color-mix(in srgb, var(--cp-badge-hue, var(--lb-teal)) 14%, var(--color-surface));
  border-block: 1px solid var(--color-rule);
  font-size: var(--text-h4); font-weight: var(--weight-bold); color: var(--color-ink);
}
.cp-badge-foot {
  padding: var(--space-3) var(--space-2);
  font-size: var(--text-fine); line-height: var(--leading-snug);
  color: var(--color-ink-muted);
}
@media (prefers-reduced-motion: reduce) { .cp-badge { transition: none; } }

@media (max-width: 900px) {
  .cp { padding: var(--space-16) 0; }
  .cp-h { font-size: var(--text-h1); }
  .cp-badges { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cp-table { min-width: 42rem; }
  .cp-claim { font-size: var(--text-fine); }
}
@media (max-width: 600px) {
  .cp-badges { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---- 21: bento grid ------------------------------------------------------
   Eight cards on a six-column grid in the rhythm two, one wide, two, three.
   Each card is a claim and a small drawn figure; the figures are built from
   tokens and library icons rather than illustration files, so the whole set
   costs one request and inherits the palette.

   The wide card puts its figure on the left, which is the only place the
   reading order flips, so it is a modifier rather than a second component. */
.bn { background: var(--color-paper); padding: var(--space-12) 0; }
.bn-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.bn-h {
  margin: 0 auto var(--space-12); max-width: 24ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.bn-h-quiet { color: var(--color-ink-muted); }

.bn-grid {
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-4);
}
.bn-card {
  display: flex; flex-direction: column; gap: var(--space-6);
  padding: var(--space-6);
  border: 1px solid var(--color-rule); border-radius: var(--radius-lg);
  background: var(--color-surface);
  overflow: hidden;
}
.bn-card--a, .bn-card--b, .bn-card--d, .bn-card--e { grid-column: span 3; }
.bn-card--c { grid-column: span 6; }
.bn-card--f, .bn-card--g, .bn-card--h { grid-column: span 2; }
/* The wide one reads figure first, copy second. */
.bn-card--c { flex-direction: row-reverse; align-items: center; gap: var(--space-10); }
.bn-card--c .bn-copy, .bn-card--c .bn-fig { flex: 1 1 0; }

.bn-card-h {
  margin: 0 0 var(--space-2);
  font-size: var(--text-h4); font-weight: var(--weight-bold); color: var(--color-ink);
}
.bn-card-p {
  margin: 0; font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
/* Every figure sits on the same tinted plate, which is what holds eight
   different drawings together as one set. */
.bn-fig {
  margin-top: auto; padding: var(--space-5);
  border-radius: var(--radius-md);
  background: linear-gradient(var(--color-paper),
                              color-mix(in srgb, var(--lb-teal) 7%, var(--color-paper)));
  border: 1px solid var(--color-rule);
}

/* --- a: the three layers --- */
.bn-layers { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.bn-layer {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm); background: var(--color-surface);
  box-shadow: var(--shadow-1);
  font-size: var(--text-fine); color: var(--color-ink-muted);
}
.bn-layer b { display: block; font-weight: var(--weight-bold); color: var(--color-ink); }
.bn-tick { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; color: var(--lb-teal-ink); }
.bn-tick path { fill: currentColor; }

/* --- b: the dials --- */
.bn-dials {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3);
  text-align: center;
}
.bn-dial-ring {
  display: grid; place-items: center; margin: 0 auto var(--space-2);
  width: var(--space-16); height: var(--space-16); border-radius: var(--radius-pill);
  /* The arc is the number, drawn from one gradient rather than an SVG. */
  background:
    conic-gradient(var(--lb-teal) calc(var(--pct) * 1%),
                   color-mix(in srgb, var(--color-ink) 8%, transparent) 0);
}
.bn-dial-ring::before {
  content: ""; grid-area: 1 / 1; width: 72%; height: 72%;
  border-radius: inherit; background: var(--color-surface);
}
.bn-dial-ring b {
  grid-area: 1 / 1; font-size: var(--text-ui); font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}
.bn-dial-cap { font-size: var(--text-fine); color: var(--color-ink-muted); }

/* --- c: the admin chips --- */
.bn-admin { position: relative; min-height: calc(var(--space-32) * 1.6); display: grid; place-items: center; }
.bn-admin-core {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-pill);
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  font-size: var(--text-fine); box-shadow: var(--shadow-3);
}
.bn-chips { list-style: none; margin: 0; padding: 0; }
.bn-chip {
  position: absolute; display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-pill);
  background: var(--color-surface); border: 1px solid var(--color-rule);
  box-shadow: var(--shadow-2);
  font-size: var(--text-fine); color: var(--color-ink); white-space: nowrap;
}
.bn-chip-ico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
.bn-chip-ico path { fill: currentColor; }
.bn-chip--a { left: 0; top: 6%; }
.bn-chip--b { right: 0; top: 2%; }
.bn-chip--c { left: 4%; bottom: 8%; }
.bn-chip--d { right: 6%; bottom: 4%; }
.bn-chip--e { left: 22%; top: 40%; }
.bn-chip--f { right: 18%; top: 44%; }

/* --- d: the exchange --- */
.bn-chat { display: grid; gap: var(--space-3); }
.bn-ask, .bn-ans {
  margin: 0; padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md); font-size: var(--text-fine);
  line-height: var(--leading-normal);
}
.bn-ask {
  justify-self: end; max-width: 88%;
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
}
.bn-ans {
  justify-self: start; max-width: 92%;
  display: flex; gap: var(--space-2);
  background: var(--color-surface); border: 1px solid var(--color-rule);
  color: var(--color-ink); box-shadow: var(--shadow-1);
}
.bn-spark { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
.bn-spark path { fill: var(--lb-teal-ink); }

/* --- e: the framework marks --- */
.bn-fws {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-3);
  text-align: center;
}
.bn-fw img {
  display: block; width: var(--space-12); height: var(--space-12);
  margin: 0 auto var(--space-2); object-fit: contain;
  border-radius: var(--radius-pill); background: var(--color-surface);
  padding: var(--space-2); box-shadow: var(--shadow-1);
}
.bn-fw span {
  display: block; min-height: calc(var(--text-fine) * 2.6);
  font-size: var(--text-fine); line-height: var(--leading-snug);
  color: var(--color-ink-muted);
}

/* --- f: the board --- */
.bn-board {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: var(--space-10); gap: var(--space-2);
}
.bn-tile { border-radius: var(--radius-sm); background: var(--color-surface); box-shadow: var(--shadow-1); }
.bn-tile--1 { background: color-mix(in srgb, var(--lb-teal) 26%, var(--color-surface)); }
.bn-tile--2 { background: color-mix(in srgb, var(--lb-blue) 22%, var(--color-surface)); }
.bn-tile--3 { background: color-mix(in srgb, var(--lb-purple) 18%, var(--color-surface)); }
.bn-tile--4 { background: color-mix(in srgb, var(--lb-gold) 26%, var(--color-surface)); }

/* --- g: the three ways --- */
.bn-share { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.bn-share li {
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
  background: var(--color-surface); box-shadow: var(--shadow-1);
  font-size: var(--text-fine); color: var(--color-ink-muted);
}
.bn-share b { display: block; font-weight: var(--weight-bold); color: var(--color-ink); }

/* --- h: the checksum --- */
.bn-sum {
  display: grid; gap: var(--space-2); padding: var(--space-4);
  border-radius: var(--radius-sm); background: var(--color-surface); box-shadow: var(--shadow-1);
  font-size: var(--text-fine); color: var(--color-ink-muted);
}
.bn-sum-top { display: flex; align-items: center; gap: var(--space-2); color: var(--color-ink); }
.bn-sum-hash {
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-xs);
  background: color-mix(in srgb, var(--color-ink) 6%, transparent);
  font-variant-numeric: tabular-nums; color: var(--color-ink);
}

@media (max-width: 900px) {
  .bn { padding: var(--space-16) 0; }
  .bn-h { font-size: var(--text-h1); }
  .bn-card--a, .bn-card--b, .bn-card--d, .bn-card--e,
  .bn-card--f, .bn-card--g, .bn-card--h { grid-column: span 3; }
  .bn-card--c { flex-direction: column; align-items: stretch; gap: var(--space-6); }
}
@media (max-width: 600px) {
  .bn-card--a, .bn-card--b, .bn-card--c, .bn-card--d,
  .bn-card--e, .bn-card--f, .bn-card--g, .bn-card--h { grid-column: 1 / -1; }
  .bn-fws { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- 22: offer banner ----------------------------------------------------
   A dark panel that interrupts a page: mark, one offer, one supporting line,
   one button, and the product angled away on the right.

   The capture is the flat file rotated in perspective at render time rather
   than a pre-tilted image, so it stays sharp at any width and the angle is a
   number here instead of something baked into an asset. It bleeds off the right
   and bottom edges, which is what stops the panel reading as a slide. */
.ob { background: var(--color-paper); padding: var(--space-12) 0; }
.ob-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }

.ob-panel {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  align-items: center;
  min-height: calc(var(--space-32) * 3);
  border-radius: var(--radius-xl);
  background: var(--color-ink-deep);
}

.ob-copy {
  position: relative; z-index: 2;
  padding: var(--space-12) var(--space-6) var(--space-12) var(--space-12);
}
.ob-mark { display: block; width: var(--space-32); height: auto; margin-bottom: var(--space-8); }
.ob-h {
  margin: 0 0 var(--space-3); max-width: 16ch;
  font-size: var(--text-h1); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
  color: var(--color-ink-on-dark);
}
.ob-lede {
  margin: 0 0 var(--space-8); max-width: 34ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 76%, transparent);
}
.ob-cta {
  display: inline-flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-btn);
  background: var(--color-surface); color: var(--color-ink);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: transform var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
.ob-cta:hover, .ob-cta:focus-visible {
  transform: translateY(-2px); box-shadow: var(--shadow-3);
}
.ob-cta-ico { width: var(--space-4); height: var(--space-4); }
.ob-cta-ico path { fill: currentColor; }

/* The wash sits behind the capture and is the only colour on the panel, so it
   carries the whole thing. Brand hues, no image. */
.ob-stage { position: relative; align-self: stretch; }
/* On the panel, not inside the stage: the capture is opaque and was covering
   its own light. From here it spills around the capture and fades toward the
   copy, which is the only colour the panel carries. */
.ob-wash {
  position: absolute; inset: -45% -18% 26% 10%; z-index: 0;
  background:
    radial-gradient(60% 70% at 70% 20%, var(--lb-magenta), transparent 70%),
    radial-gradient(55% 60% at 95% 60%, var(--lb-orange), transparent 72%),
    radial-gradient(70% 80% at 40% 90%, var(--lb-purple), transparent 75%);
  filter: blur(var(--space-8));
  opacity: 1;
}
.ob-shot {
  position: absolute; z-index: 1; margin: 0;
  left: var(--space-10); top: var(--space-24); right: calc(var(--space-16) * -1); bottom: 0;
  /* Short enough for the turn to read. At sixteen times the panel depth a
     fourteen degree rotation was almost invisible. */
  perspective: calc(var(--space-32) * 2.5);
  perspective-origin: 0% 50%;
}
.ob-shot img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: left top;
  border-radius: var(--radius-md) 0 0 0;
  transform: rotateY(-16deg) rotateX(2deg);
  transform-origin: left center;
  box-shadow: var(--shadow-4);
  /* A hairline so the capture reads as a pane sitting on the light rather than
     a hole cut in the panel. */
  outline: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 22%, transparent);
  outline-offset: -1px;
}

@media (max-width: 900px) {
  .ob { padding: var(--space-16) 0; }
  /* Stacked: the copy takes the top, the capture the bottom, still bleeding
     off the right so the panel keeps its shape. */
  .ob-panel { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .ob-copy { padding: var(--space-10) var(--space-6) var(--space-8); }
  .ob-h { font-size: var(--text-h2); max-width: none; }
  .ob-lede { max-width: none; }
  .ob-stage { height: calc(var(--space-32) * 2.4); }
  .ob-shot { left: var(--space-6); top: 0; right: calc(var(--space-10) * -1); }
  .ob-shot img { transform: rotateY(-8deg); }
}
@media (prefers-reduced-motion: reduce) {
  .ob-cta { transition: none; }
}

/* ---- 23: opposing rails --------------------------------------------------
   Two rows travelling against each other behind a medallion. Each track holds
   its list twice and moves by exactly half its own width, so the second copy
   arrives where the first started and the loop closes with one keyframe and no
   script. The second rail runs the same keyframe in reverse, which is the only
   difference between the two.

   The rails are masked at both ends rather than simply clipped, so tiles fade
   out instead of being guillotined at the edge of the frame. */
.ig {
  --ig-cycle: calc(var(--dur-ambient) * 5);
  background: var(--color-paper); padding: var(--space-12) 0;
  overflow: clip;
}
.ig-head {
  max-width: var(--container); margin: 0 auto var(--space-16);
  padding-inline: var(--gutter); text-align: center;
}
.ig-eyebrow {
  margin: 0 0 var(--space-4);
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.ig-h {
  margin: 0 auto var(--space-4); max-width: 20ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.ig-h-quiet { color: var(--color-ink-muted); }
.ig-lede {
  margin: 0 auto; max-width: var(--measure-lede);
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}

.ig-rails { position: relative; display: grid; gap: var(--space-4); }
.ig-rail {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}
.ig-track {
  list-style: none; margin: 0; padding: 0;
  display: flex; width: max-content; gap: var(--space-4);
  animation: ig-run var(--ig-cycle) var(--ease-linear) infinite;
}
/* Same journey, run backwards. */
.ig-rail--b .ig-track { animation-direction: reverse; }

@keyframes ig-run {
  from { transform: translate3d(0, 0, 0); }
  /* Half, because the list is present twice: the copy lands exactly where the
     original began. */
  to { transform: translate3d(-50%, 0, 0); }
}

/* Square, icon only, like the reference. The name lives on the tile as its
   title and its accessible name, so the row reads as texture and still answers
   what any one of them is. */
.ig-tile {
  display: grid; place-items: center;
  width: var(--space-16); height: var(--space-16);
  border: 1px solid var(--color-rule); border-radius: var(--radius-lg);
  background: var(--color-surface); box-shadow: var(--shadow-1);
}
.ig-ico { width: var(--space-6); height: var(--space-6); color: var(--ig-hue); }
.ig-ico path { fill: currentColor; }

/* The point of the section: one wire, and all of it arrives through this. */
.ig-medallion {
  position: absolute; left: 50%; top: 50%;
  display: grid; place-items: center;
  width: calc(var(--space-32) * 1.25); height: calc(var(--space-32) * 1.25);
  transform: translate(-50%, -50%);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  border: var(--space-1) solid var(--color-paper);
  box-shadow: var(--shadow-4), 0 0 0 1px var(--color-rule);
}
.ig-medallion img { display: block; width: 62%; height: auto; }

@media (max-width: 900px) {
  .ig { padding: var(--space-16) 0; }
  .ig-h { font-size: var(--text-h1); }
  .ig-head { margin-bottom: var(--space-10); }
  .ig-tile { width: var(--space-12); height: var(--space-12); border-radius: var(--radius-md); }
  .ig-ico { width: var(--space-5); height: var(--space-5); }
  .ig-medallion { width: calc(var(--space-16) * 1.5); height: calc(var(--space-16) * 1.5); }
}
@media (prefers-reduced-motion: reduce) {
  /* The duration token is 0ms here, which would collapse the track onto its
     end frame, so the animation comes off entirely and the rails simply sit. */
  .ig-track { animation: none; }
}

/* ---- 24: gradient panel --------------------------------------------------
   Headline and one button across the top, a logo rail travelling right to
   left, and the product bleeding off the bottom edge of the panel.

   The logos are the homepage set, knocked to white with a filter rather than
   kept as a second set of files. They carry real transparency, so the filter
   hits the marks and nothing else.

   The grain is a repeating radial dot at low opacity. An image would cost a
   request for something nobody consciously sees. */
.gh { --gh-cycle: calc(var(--dur-ambient) * 4); background: var(--color-paper); padding: var(--space-12) 0 0; }
.gh-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }

.gh-panel {
  position: relative; isolation: isolate; overflow: hidden;
  padding: var(--space-16) 0 0;
  border-radius: var(--radius-xl);
  background:
    linear-gradient(115deg,
      var(--lb-blue) 0%,
      var(--lb-purple) 26%,
      var(--lb-magenta) 58%,
      /* The warm end is mixed toward the ink: white marks over raw orange
         measured 2.22 to 1, under the 3 to 1 a graphic needs. */
      color-mix(in srgb, var(--lb-red) 88%, var(--color-ink-deep)) 78%,
      color-mix(in srgb, var(--lb-orange) 68%, var(--color-ink-deep)) 100%);
}
.gh-grain {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(var(--color-ink-on-dark) 1px, transparent 1px);
  background-size: var(--space-1) var(--space-1);
  opacity: 0.06;
}

.gh-top {
  position: relative; z-index: 1;
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-8);
  padding-inline: var(--space-12);
}
.gh-h {
  margin: 0; max-width: 20ch;
  font-size: var(--text-h1); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
  color: var(--color-ink-on-dark);
}
.gh-cta {
  display: inline-flex; align-items: center; gap: var(--space-3); flex: 0 0 auto;
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-btn);
  background: var(--color-surface); color: var(--color-ink);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  box-shadow: var(--shadow-2);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.gh-cta:hover, .gh-cta:focus-visible { transform: translateY(-2px); }
.gh-cta-ico { width: var(--space-4); height: var(--space-4); }
.gh-cta-ico path { fill: currentColor; }

/* --- the rail --- */
.gh-logos {
  position: relative; z-index: 1; overflow: hidden;
  margin: var(--space-12) 0 var(--space-16);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}
.gh-track {
  list-style: none; margin: 0; padding: 0;
  display: flex; width: max-content; align-items: center; gap: var(--space-16);
  /* Right to left, and the list is present twice, so half a width closes it. */
  animation: gh-run var(--gh-cycle) var(--ease-linear) infinite;
}
@keyframes gh-run {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}
.gh-logo img {
  display: block; width: var(--space-24); height: auto;
  /* One asset for both grounds: the marks are transparent, so this hits the
     ink and leaves the rest alone. */
  filter: brightness(0) invert(1);
  opacity: 0.85;
}

/* --- the capture, cropped by the panel --- */
.gh-shot {
  position: relative; z-index: 1; margin: 0 var(--space-12) 0;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  overflow: hidden;
  box-shadow: var(--shadow-4);
}
.gh-shot img { display: block; width: 100%; height: auto; }

@media (max-width: 900px) {
  .gh { padding-top: var(--space-16); }
  .gh-panel { padding-top: var(--space-10); }
  .gh-top { flex-direction: column; align-items: stretch; padding-inline: var(--space-6); }
  .gh-h { font-size: var(--text-h2); max-width: none; }
  .gh-cta { align-self: flex-start; }
  .gh-logos { margin: var(--space-8) 0 var(--space-10); }
  .gh-track { gap: var(--space-10); }
  .gh-logo img { width: var(--space-16); }
  .gh-shot { margin-inline: var(--space-6); }
}
@media (prefers-reduced-motion: reduce) {
  /* The duration token is 0ms here, which would collapse the track onto its end
     frame, so the animation comes off and the rail simply sits. */
  .gh-track { animation: none; }
  .gh-cta { transition: none; }
}

/* ---- 25: orbit hero ------------------------------------------------------
   A centred column with a ringed field either side. Each field holds one soft
   colour, a set of concentric rings and the role bubbles pinned to them by
   percentage, so the whole composition scales with the section rather than
   being positioned in pixels.

   The bubbles carry role marks rather than photographs: we hold no people
   photography, and a hero is not the place to invent faces. Real portraits drop
   into the same box later without touching the layout. */
.oa {
  position: relative; isolation: isolate; overflow: clip;
  background: var(--color-paper);
  padding: var(--space-12) 0;
}

/* --- the two fields --- */
.oa-field { position: absolute; top: 0; bottom: 0; width: 34%; z-index: 0; }
.oa-field--l { left: 0; }
.oa-field--r { right: 0; }

/* One soft colour per side, drawn rather than loaded. */
.oa-blob {
  position: absolute; width: 78%; aspect-ratio: 1; border-radius: var(--radius-pill);
  filter: blur(var(--space-16));
  opacity: 0.5;
}
.oa-field--l .oa-blob {
  left: -18%; top: -22%;
  background: radial-gradient(circle at 40% 40%, var(--lb-magenta), transparent 62%),
              radial-gradient(circle at 70% 70%, var(--lb-orange), transparent 66%);
}
.oa-field--r .oa-blob {
  right: -20%; top: -16%;
  background: radial-gradient(circle at 55% 45%, var(--lb-blue), transparent 60%),
              radial-gradient(circle at 35% 70%, var(--lb-purple), transparent 64%);
}

/* Concentric rings, one element: repeating-radial-gradient rather than a stack
   of divs or an SVG. */
.oa-rings {
  position: absolute; inset: -30% -10%;
  background: repeating-radial-gradient(circle at 50% 42%,
    transparent 0 calc(var(--space-16) - 1px),
    color-mix(in srgb, var(--color-ink) 8%, transparent) calc(var(--space-16) - 1px)
      var(--space-16));
  -webkit-mask-image: radial-gradient(circle at 50% 42%, #000 55%, transparent 78%);
  mask-image: radial-gradient(circle at 50% 42%, #000 55%, transparent 78%);
}

.oa-dot {
  position: absolute; width: var(--space-4); height: var(--space-4);
  border-radius: var(--radius-pill);
  background: linear-gradient(140deg, var(--lb-magenta), var(--lb-purple));
  filter: blur(1px);
  opacity: 0.75;
}
.oa-dot--1 { left: 14%; top: 22%; }
.oa-dot--2 { left: 68%; top: 74%; width: var(--space-3); height: var(--space-3); }
.oa-dot--3 { left: 34%; top: 88%; width: var(--space-5); height: var(--space-5); }

/* --- the bubbles --- */
.oa-bubble {
  position: absolute; left: var(--x); top: var(--y);
  display: flex; align-items: flex-end;
  animation: oa-float calc(var(--dur-ambient) * 1.4) var(--ease-standard) infinite alternate;
  animation-delay: calc(var(--i) * var(--stagger-step) * -4);
}
.oa-face {
  display: grid; place-items: center;
  width: var(--space-16); height: var(--space-16);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  border: var(--space-1) solid var(--color-surface);
  box-shadow: var(--shadow-3);
  color: var(--oa-hue);
}
.oa-ico { width: var(--space-6); height: var(--space-6); }
.oa-ico path { fill: currentColor; }
/* The pill hangs off the lower left of the bubble, as in the reference. */
.oa-pill {
  /* Hangs off the lower right of the mark, overlapping it by a hair rather
     than sitting across it. */
  margin: 0 0 calc(var(--space-1) * -1) calc(var(--space-3) * -1);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  /* The ink partner of the hue, because white on the raw hue does not
     carry: orange measured 2.0 to 1. */
  background: color-mix(in srgb, var(--oa-ink) 86%, var(--color-ink-deep));
  color: var(--color-ink-on-dark);
  font-size: var(--text-fine); font-weight: var(--weight-bold); white-space: nowrap;
  box-shadow: var(--shadow-2);
}

@keyframes oa-float {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(0, calc(var(--space-3) * -1), 0); }
}

/* --- the column --- */
.oa-copy {
  position: relative; z-index: 1;
  max-width: var(--measure-lede); margin-inline: auto;
  padding-inline: var(--gutter); text-align: center;
}
.oa-h {
  margin: 0 0 var(--space-5);
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.oa-lede {
  margin: 0 auto var(--space-8); max-width: 48ch;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.oa-cta {
  display: inline-flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-btn);
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.oa-cta:hover, .oa-cta:focus-visible { transform: translateY(-2px); }
.oa-cta-ico { width: var(--space-4); height: var(--space-4); }
.oa-cta-ico path { fill: currentColor; }
.oa-fine {
  margin: var(--space-3) 0 0;
  font-size: var(--text-small); color: var(--color-ink-muted);
}

/* Wider than the copy above it: the row is a set and has to stay one line. */
.oa-proof {
  margin-top: var(--space-12);
  width: 100vw; max-width: var(--container);
  margin-inline: 50%; translate: -50% 0;
}
.oa-proof-label {
  margin: 0 0 var(--space-5);
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold);
  color: var(--color-ink-muted);
}
.oa-logos {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--space-10);
}
.oa-logos img {
  display: block; width: var(--space-20, var(--space-24)); height: auto;
  opacity: 0.65;
  filter: grayscale(1);
}

@media (max-width: 900px) {
  .oa { padding: var(--space-16) 0; }
  .oa-h { font-size: var(--text-h1); }
  /* The fields become atmosphere: the rings and colour stay, the bubbles go,
     because at this width they would sit on the type. */
  .oa-field { width: 52%; opacity: 0.5; }
  .oa-bubble { display: none; }
  .oa-logos { gap: var(--space-6); }
}
@media (prefers-reduced-motion: reduce) {
  .oa-bubble { animation: none; }
  .oa-cta { transition: none; }
}

/* ---- 26: audience switcher -----------------------------------------------
   Pill tabs over one panel: the argument on the left, what that audience gets
   on the right. Only the panel changes, so the frame never moves under the
   reader's eye.

   Each panel carries its sector's hue as a property, which is the only thing
   that distinguishes them: one set of rules, five fills. */
.aw { background: var(--color-paper); padding: var(--space-12) 0; }
.aw-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.aw-head {
  margin: 0 auto var(--space-3); max-width: 24ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.aw-head-quiet { color: var(--color-ink-muted); }
.aw-sub {
  margin: 0 0 var(--space-8); text-align: center;
  font-size: var(--text-body-lg); color: var(--color-ink-muted);
}

.aw-tabs {
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3);
  margin-bottom: var(--space-8);
}
.aw-tab {
  padding: var(--space-2) var(--space-5);
  border: 1px solid var(--color-rule); border-radius: var(--radius-pill);
  background: var(--color-surface); color: var(--color-ink-muted);
  font: inherit; font-size: var(--text-ui); cursor: pointer;
  transition: color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.aw-tab:hover { color: var(--color-ink); }
.aw-tab[aria-selected="true"] {
  color: var(--lb-teal-ink);
  border-color: var(--lb-teal);
  box-shadow: inset 0 0 0 1px var(--lb-teal);
}

.aw-stage {
  border-radius: var(--radius-xl);
  background: var(--color-paper-sunk, color-mix(in srgb, var(--color-ink) 4%, var(--color-surface)));
  padding: var(--space-12);
}
.aw-panel {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: var(--space-12);
  animation: aw-in var(--dur-base) var(--ease-standard) both;
}
.aw-panel[hidden] { display: none; }
@keyframes aw-in {
  from { opacity: 0; transform: translate3d(0, var(--space-2), 0); }
  to { opacity: 1; transform: none; }
}

.aw-h {
  margin: 0 0 var(--space-3);
  font-size: var(--text-h2); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.aw-lede {
  margin: 0 0 var(--space-8); max-width: 40ch;
  font-size: var(--text-body); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.aw-instead-label {
  margin: 0 0 var(--space-3);
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold);
  color: var(--color-ink-muted);
}
.aw-instead {
  list-style: none; margin: 0 0 var(--space-8); padding: 0;
  display: grid; gap: var(--space-2);
}
.aw-instead li {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--text-small); color: var(--color-ink);
}
.aw-instead-ico { width: var(--space-4); height: var(--space-4); color: var(--color-ink-muted); }
.aw-instead-ico path { fill: currentColor; }

.aw-cta {
  display: inline-flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-btn);
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.aw-cta:hover, .aw-cta:focus-visible { transform: translateY(-2px); }
.aw-cta-ico { width: var(--space-4); height: var(--space-4); }
.aw-cta-ico path { fill: currentColor; }

.aw-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.aw-card {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--color-rule); border-radius: var(--radius-md);
  background: var(--color-surface); box-shadow: var(--shadow-1);
}
.aw-card-mark {
  display: grid; place-items: center; flex: 0 0 auto;
  width: var(--space-10); height: var(--space-10);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--aw-hue) 20%, var(--color-surface));
  color: var(--aw-ink);
}
.aw-card-ico { width: var(--space-5); height: var(--space-5); }
.aw-card-ico path { fill: currentColor; }
.aw-card-text { font-size: var(--text-small); line-height: var(--leading-normal); color: var(--color-ink-muted); }
.aw-card-text b { display: block; font-weight: var(--weight-bold); color: var(--color-ink); }

@media (max-width: 900px) {
  .aw { padding: var(--space-16) 0; }
  .aw-head { font-size: var(--text-h1); }
  .aw-stage { padding: var(--space-6); }
  .aw-panel { grid-template-columns: minmax(0, 1fr); gap: var(--space-8); }
  .aw-tabs { flex-wrap: nowrap; overflow-x: auto; justify-content: flex-start; scrollbar-width: none; }
  .aw-tab { flex: 0 0 auto; }
}
@media (prefers-reduced-motion: reduce) {
  .aw-panel { animation: none; }
  .aw-cta { transition: none; }
}

/* ---- 27: resource grid ---------------------------------------------------
   Six cards, three up, each a thumbnail over a title that is the link. The
   thumbnails are deliberately three different treatments - a product capture, a
   hue field with a mark, and a cover with the number set large - because six
   identical tiles read as a list rather than a set of things worth opening.

   The whole card is clickable through the title's ::after, so the link stays a
   real link with real text for a screen reader while the hit area is the card. */
.rs { background: var(--color-paper); padding: var(--space-12) 0; }
.rs-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.rs-eyebrow {
  margin: 0 0 var(--space-4); text-align: center;
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.rs-h {
  margin: 0 auto var(--space-12); max-width: 24ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.rs-h-quiet { color: var(--color-ink-muted); }

.rs-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-6);
}
/* Four cards sit two by two: three and an orphan reads as a hole. */
.fd-grid.fd-grid--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.uc-grid.uc-grid--four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.rs-grid.rs-grid--four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.rs-card {
  position: relative;
  display: flex; flex-direction: column; gap: var(--space-5);
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--color-ink) 3%, var(--color-surface));
  transition: background-color var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard);
}
.rs-card:hover { background: var(--color-surface); transform: translateY(-2px); }
.rs-card:has(.rs-link:focus-visible) { outline: var(--space-1) solid var(--lb-teal); }

.rs-thumb {
  display: grid; place-items: center; overflow: hidden;
  aspect-ratio: 3 / 2;
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.rs-thumb--shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }
.rs-thumb--field {
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--rs-hue) 26%, var(--color-surface)),
    color-mix(in srgb, var(--rs-hue) 8%, var(--color-surface)));
  color: var(--rs-hue);
}
.rs-thumb-ico { width: var(--space-12); height: var(--space-12); }
.rs-thumb-ico path { fill: currentColor; }
/* The cover: the number is the artwork, so nothing has to be drawn. */
.rs-thumb--cover {
  align-content: center; padding: var(--space-6); text-align: left;
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
}
.rs-thumb--cover b {
  display: block;
  font-size: var(--text-display); line-height: 1;
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
  color: var(--rs-hue);
}
.rs-thumb--cover span {
  display: block; margin-top: var(--space-2); max-width: 18ch;
  font-size: var(--text-small); line-height: var(--leading-snug);
  color: color-mix(in srgb, var(--color-ink-on-dark) 72%, transparent);
}

.rs-title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-h4); line-height: var(--leading-snug);
  font-weight: var(--weight-bold);
}
.rs-link { color: var(--color-ink); text-decoration: none; }
/* Stretched over the card, so the hit area is the whole tile and the link
   still carries its own text. */
.rs-link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.rs-card:hover .rs-link { color: var(--lb-teal-ink); }

@media (max-width: 900px) {
  .rs { padding: var(--space-16) 0; }
  .rs-h { font-size: var(--text-h1); }
  .rs-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
}
@media (max-width: 600px) {
  .rs-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
  .rs-card { transition: none; }
  .rs-card:hover { transform: none; }
}

/* ---- 28: use-case grid ---------------------------------------------------
   Nine cards, three up: a chip, a title, a paragraph. The reference numbers its
   chips one to nine, which tells the reader nothing they could not count; ours
   name the pack the screen belongs to, so the chip does some work.

   Cards stretch to the tallest in their row, and the paragraph sits at the top
   rather than the bottom, so nine ragged blocks still line up. */
.uc { background: var(--color-paper); padding: var(--space-12) 0; }
.uc-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.uc-eyebrow {
  margin: 0 0 var(--space-4); text-align: center;
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.uc-h {
  margin: 0 auto var(--space-4); max-width: 26ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.uc-h-quiet { color: var(--color-ink-muted); }
.uc-lede {
  margin: 0 auto var(--space-12); max-width: var(--measure-lede); text-align: center;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.uc-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-5);
}
.uc-card {
  display: flex; flex-direction: column; align-items: flex-start;
  padding: var(--space-6);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--color-ink) 3%, var(--color-surface));
}
.uc-chip {
  align-self: flex-end; margin: 0 0 var(--space-4);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  /* The chip takes its pack's hue where the card names one, so the grid reads
     with the same colour code as the sector switcher and the sidebar. */
  background: color-mix(in srgb, var(--uc-hue, var(--lb-teal)) 16%, transparent);
  color: color-mix(in srgb, var(--uc-hue-ink, var(--lb-teal-ink)) 80%, var(--color-ink-deep));
  font-size: var(--text-fine); font-weight: var(--weight-bold);
}
.uc-title {
  margin: 0 0 var(--space-3);
  font-size: var(--text-h4); line-height: var(--leading-snug);
  font-weight: var(--weight-bold);
}
.uc-p {
  margin: 0; font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
@media (max-width: 900px) {
  .uc { padding: var(--space-16) 0; }
  .uc-h { font-size: var(--text-h1); }
  .uc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
}
@media (max-width: 600px) {
  .uc-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- 29: question list ---------------------------------------------------
   The plain version: a rule between each row and nothing else. Entry 16 sorts a
   long list into categories; this one takes six and gets out of the way, which
   is the right shape when the questions are the last thing before a decision.

   Native details and summary, so it opens with no script and works with none. */
.ql { background: var(--color-paper); padding: var(--space-12) 0; }
.ql-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.ql-h {
  margin: 0 auto var(--space-12); max-width: 24ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.ql-h-quiet { color: var(--color-ink-muted); }
/* The list ran to the reading measure while its own section is container
   width, which left the right third of every row empty. Two columns at
   desktop use that width and halve the run of the list. */
.ql-list {
  margin-inline: auto;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--space-12);
  align-content: start;
}
.ql-row { break-inside: avoid; }
@media (max-width: 900px) {
  .ql-list { grid-template-columns: minmax(0, 1fr); }
}
.ql-row { border-top: 1px solid var(--color-rule); }
.ql-row:last-child { border-bottom: 1px solid var(--color-rule); }
.ql-q {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-6); padding: var(--space-6) 0;
  cursor: pointer; list-style: none;
  font-size: var(--text-body); color: var(--color-ink-muted);
  transition: color var(--dur-fast) var(--ease-standard);
}
.ql-q::-webkit-details-marker { display: none; }
.ql-row:hover .ql-q, .ql-row[open] .ql-q { color: var(--color-ink); }
.ql-chev {
  width: var(--space-4); height: var(--space-4); flex: 0 0 auto;
  transition: transform var(--dur-fast) var(--ease-standard);
}
.ql-chev path { fill: currentColor; }
.ql-row[open] .ql-chev { transform: rotate(180deg); }
.ql-a { padding: 0 0 var(--space-6); }
.ql-a p {
  margin: 0;
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
@media (max-width: 900px) {
  .ql { padding: var(--space-16) 0; }
  .ql-h { font-size: var(--text-h1); }
}
@media (prefers-reduced-motion: reduce) {
  .ql-q, .ql-chev { transition: none; }
}

/* ---- 30: feature directory -----------------------------------------------
   Sixteen tiles, four up: a rounded coloured mark, a name and one line. The
   hue rotates through the brand set so the grid reads as a directory rather
   than a wall, and each mark takes its hue's ink partner for the glyph so the
   colour never has to carry contrast on its own. */
.fd { background: var(--color-paper); padding: var(--space-12) 0; }
.fd-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.fd-eyebrow {
  margin: 0 0 var(--space-4); text-align: center;
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.fd-h {
  margin: 0 auto var(--space-4); max-width: 24ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.fd-h-quiet { color: var(--color-ink-muted); }
.fd-lede {
  margin: 0 auto var(--space-12); max-width: var(--measure-lede); text-align: center;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.fd-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-6) var(--space-8);
}
.fd-item { display: flex; align-items: flex-start; gap: var(--space-4); min-width: 0; }
.fd-text {
  /* A label can be one unbroken token, "Time-to-productivity", and a flex
     item will not shrink below its longest word unless told it may. Four
     pages scrolled sideways on a phone because of it. */
  min-width: 0; overflow-wrap: anywhere;
}
.fd-mark {
  display: grid; place-items: center; flex: 0 0 auto;
  width: var(--space-12); height: var(--space-12);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--fd-hue) 22%, var(--color-surface));
  color: var(--fd-ink);
}
.fd-ico { width: var(--space-6); height: var(--space-6); }
.fd-ico path { fill: currentColor; }
.fd-text {
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.fd-text b {
  display: block; font-size: var(--text-ui); font-weight: var(--weight-bold);
  color: var(--color-ink);
}
@media (max-width: 900px) {
  .fd { padding: var(--space-16) 0; }
  .fd-h { font-size: var(--text-h1); }
  .fd-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
}
@media (max-width: 600px) {
  .fd-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- 31: article cards ---------------------------------------------------
   Thumbnail, title, and a full-width button on every card. The button is the
   whole point of this variant over entry 27: it is for a place where the
   reader is being asked to leave the page, so the affordance is explicit
   rather than the card being quietly clickable.

   The thumbnail sits on its own hue field, which is what stops three product
   captures reading as one grey block. */
.ar { background: var(--color-paper); padding: var(--space-12) 0; }
.ar-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.ar-eyebrow {
  margin: 0 0 var(--space-4); text-align: center;
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.ar-h {
  margin: 0 auto var(--space-12); text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.ar-h-quiet { color: var(--color-ink-muted); }
.ar-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-6);
}
.ar-card {
  display: flex; flex-direction: column;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--color-ink) 3%, var(--color-surface));
}
.ar-thumb {
  display: block; overflow: hidden; aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--ar-hue) 22%, var(--color-surface));
}
.ar-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }
.ar-title {
  margin: var(--space-6) 0 var(--space-6);
  font-size: var(--text-h4); line-height: var(--leading-snug);
  font-weight: var(--weight-bold);
}
.ar-btn {
  margin-top: auto;
  display: flex; align-items: center; justify-content: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-btn);
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.ar-btn:hover, .ar-btn:focus-visible { transform: translateY(-2px); }
.ar-btn-ico { width: var(--space-4); height: var(--space-4); }
.ar-btn-ico path { fill: currentColor; }
@media (max-width: 900px) {
  .ar { padding: var(--space-16) 0; }
  .ar-h { font-size: var(--text-h1); }
  .ar-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) { .ar-btn { transition: none; } }

/* ---- 32: stat row --------------------------------------------------------
   Six figures, three up, each a mark and two lines. The reference fills this
   shape with survey claims; ours are counted from the codebase, and the lede
   says which kind they are, because a reader cannot tell the difference by
   looking and deserves to be told. */
.st { background: var(--color-paper); padding: var(--space-12) 0; }
.st-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.st-eyebrow {
  margin: 0 0 var(--space-4); text-align: center;
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.st-h {
  margin: 0 auto var(--space-4); max-width: 22ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.st-h-quiet { color: var(--color-ink-muted); }
.st-lede {
  margin: 0 auto var(--space-16); max-width: var(--measure-lede); text-align: center;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.st-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-10) var(--space-8);
}
.st-item { display: flex; align-items: flex-start; gap: var(--space-4); }
.st-mark {
  display: grid; place-items: center; flex: 0 0 auto;
  width: var(--space-12); height: var(--space-12);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--st-hue, var(--lb-teal)) 14%, var(--color-surface));
  color: var(--st-ink, var(--lb-teal-ink));
}
/* Six figures, six of the logo's hues, in the logo's own order. */
.st-item:nth-child(2) { --st-hue: var(--lb-blue); --st-ink: var(--lb-blue-ink); }
.st-item:nth-child(3) { --st-hue: var(--lb-purple); --st-ink: var(--lb-purple-ink); }
.st-item:nth-child(4) { --st-hue: var(--lb-green); --st-ink: var(--lb-green-ink); }
.st-item:nth-child(5) { --st-hue: var(--lb-orange); --st-ink: var(--lb-orange-ink); }
.st-item:nth-child(6) { --st-hue: var(--lb-magenta); --st-ink: var(--lb-magenta-ink); }
.st-ico { width: var(--space-6); height: var(--space-6); }
.st-ico path { fill: currentColor; }
.st-text {
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.st-text b {
  display: block; font-size: var(--text-h4); font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums; color: var(--color-ink);
}
@media (max-width: 900px) {
  .st { padding: var(--space-16) 0; }
  .st-h { font-size: var(--text-h1); }
  .st-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-8) var(--space-6); }
}
@media (max-width: 600px) { .st-grid { grid-template-columns: minmax(0, 1fr); } }

/* ---- 33: directory list --------------------------------------------------
   Three columns of mark, name and a couple of lines. Denser than the tile
   directory at entry 30: that one is a glance, this one is for a reader who
   has decided to actually read the list. */
.dr { background: var(--color-paper); padding: var(--space-12) 0; }
.dr-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.dr-h {
  margin: 0 auto var(--space-4); text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.dr-lede {
  margin: 0 auto var(--space-16); max-width: var(--measure-lede); text-align: center;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.dr-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-10) var(--space-8);
}
.dr-item { display: flex; align-items: flex-start; gap: var(--space-4); }
.dr-mark {
  display: grid; place-items: center; flex: 0 0 auto;
  width: var(--space-10); height: var(--space-10);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--dr-hue) 20%, var(--color-surface));
  color: var(--dr-ink);
}
.dr-ico { width: var(--space-5); height: var(--space-5); }
.dr-ico path { fill: currentColor; }
.dr-name {
  margin: 0 0 var(--space-2);
  font-size: var(--text-ui); font-weight: var(--weight-bold); color: var(--color-ink);
}
.dr-text p {
  margin: 0; font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
@media (max-width: 900px) {
  .dr { padding: var(--space-16) 0; }
  .dr-h { font-size: var(--text-h1); }
  .dr-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-8) var(--space-6); }
}
@media (max-width: 600px) { .dr-grid { grid-template-columns: minmax(0, 1fr); } }

/* ---- 34: alternating rows ------------------------------------------------
   Six cells in one grid on the container width, copy and capture swapping
   sides row by row.

   The rules are elements, not the grid gap. Gap-as-rule draws a line wherever
   two cells meet, which puts one down the outside edges as soon as you close
   the block with a border, and it cannot fade. The reference has no outer
   rule and its horizontals fade to nothing at both ends. So: one line element
   per row boundary spanning both columns, one vertical spanning every row,
   each 1px of the rule colour, which makes the thickness consistent by
   construction rather than by three declarations agreeing with each other.

   The flip is explicit placement, so the copy is first in the markup on every
   row and read first whichever side it is drawn on. */
.al { background: var(--color-paper); padding: var(--space-12) 0; }
.al-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.al-grid {
  position: relative;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  /* Explicit, so the vertical rule's 1 / -1 spans all three rows rather than
     stopping at the end of an implicit grid. */
  grid-template-rows: repeat(3, auto);
}

/* --- the rules --- */
.al-line, .al-vline {
  position: relative; z-index: 1; pointer-events: none;
  background: var(--color-rule);
}
.al-line {
  grid-column: 1 / -1; grid-row: var(--al-row);
  align-self: start; height: 1px;
  /* Fades to nothing at both ends, which is what stops the block reading as a
     boxed table. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent);
}
.al-line--last { align-self: end; }
.al-vline {
  grid-column: 1 / -1; grid-row: 1 / -1;
  justify-self: center; width: 1px;
}

.al-cell {
  display: flex; flex-direction: column; justify-content: center;
  grid-row: var(--al-row);
}
.al-cell--copy { grid-column: 1; padding: var(--space-12); }
.al-cell--media {
  grid-column: 2; overflow: hidden;
  /* Inset, so the vertical rule has paper either side of it. */
  padding: var(--space-10);
}
.al-cell--copy.al-cell--flip { grid-column: 2; }
.al-cell--media.al-cell--flip { grid-column: 1; }

.al-eyebrow {
  margin: 0 0 var(--space-4);
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.al-h {
  margin: 0 0 var(--space-4); max-width: 18ch;
  font-size: var(--text-h2); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.al-p {
  margin: 0 0 var(--space-8); max-width: 42ch;
  font-size: var(--text-body); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.al-btn {
  align-self: flex-start;
  display: inline-flex; padding: var(--space-3) var(--space-5);
  border: 1px solid var(--color-rule); border-radius: var(--radius-btn);
  background: var(--color-surface); color: var(--color-ink);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: border-color var(--dur-fast) var(--ease-standard);
}
.al-btn:hover, .al-btn:focus-visible { border-color: var(--lb-teal); }
.al-cell--media img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: top left;
  border-radius: var(--radius-md);
}
/* The shape the reference gives its photograph. */
.al-cell--blob { padding: var(--space-10); }
.al-cell--blob img {
  height: auto; object-position: center;
  border-radius: 42% 58% 46% 54% / 52% 44% 56% 48%;
  background: color-mix(in srgb, var(--lb-teal) 10%, var(--color-surface));
}

@media (max-width: 900px) {
  .al { padding: var(--space-16) 0; }
  .al-grid { grid-template-columns: minmax(0, 1fr); }
  /* Stacked, the placement has to go or the cells fight over rows, and the
     drawn rules have nothing to sit between. The copy cell carries its own. */
  .al-cell, .al-cell--copy, .al-cell--media,
  .al-cell--copy.al-cell--flip, .al-cell--media.al-cell--flip {
    grid-column: auto; grid-row: auto;
  }
  .al-line, .al-vline { display: none; }
  .al-cell--copy { padding: var(--space-10) var(--space-6) var(--space-8); border-top: 1px solid var(--color-rule); }
  .al-h { font-size: var(--text-h3); max-width: none; }
  .al-cell--media img { max-height: calc(var(--space-32) * 2.5); }
  .al-cell--media { padding: var(--space-6); }
  .al-cell--blob { padding: var(--space-6); }
}
@media (prefers-reduced-motion: reduce) { .al-btn { transition: none; } }

/* ---- 35: left hero with a gradient stage ---------------------------------
   The other hero arrangement: type ranged left, one capture carrying the page,
   and a gradient stage under it so a single screenshot does not sit on bare
   paper. The floating card is the product's own answer, pulled out so the
   claim in the heading is visible without opening anything. */
.lh { background: var(--color-paper); padding: var(--space-12) 0; }
.lh-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.lh-eyebrow { margin: 0 0 var(--space-4); }
.lh-h {
  margin: 0 0 var(--space-5); max-width: 20ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.lh-h-quiet { color: var(--color-ink-muted); }
.lh-lede {
  margin: 0 0 var(--space-8); max-width: var(--measure-lede);
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.lh-cta-row {
  display: flex; align-items: center; gap: var(--space-5);
  margin: 0 0 var(--space-16);
}
.lh-cta {
  display: inline-flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-btn);
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.lh-cta:hover, .lh-cta:focus-visible { transform: translateY(-2px); }
.lh-cta-ico { width: var(--space-4); height: var(--space-4); }
.lh-cta-ico path { fill: currentColor; }
.lh-fine {
  font-size: var(--text-small); line-height: var(--leading-snug);
  color: var(--color-ink-muted);
}

/* The stage: the capture never touches paper directly. */
.lh-stage {
  position: relative; isolation: isolate;
  padding: var(--space-10);
  border-radius: var(--radius-xl);
  background: linear-gradient(135deg, var(--lb-blue), var(--lb-purple) 45%,
              var(--lb-magenta) 75%, var(--lb-orange));
}
.lh-shot { margin: 0; border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-4); }
.lh-shot img { display: block; width: 100%; height: auto; }
.lh-float {
  position: absolute; right: var(--space-16); bottom: var(--space-16); z-index: 1;
  display: flex; gap: var(--space-3); max-width: 26rem; margin: 0;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  background: var(--color-surface); box-shadow: var(--shadow-4);
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.lh-float b { display: block; font-weight: var(--weight-bold); color: var(--color-ink); }
.lh-float-ico { width: var(--space-5); height: var(--space-5); flex: 0 0 auto; }
.lh-float-ico path { fill: var(--lb-teal-ink); }

@media (max-width: 900px) {
  .lh { padding: var(--space-16) 0; }
  .lh-h { font-size: var(--text-h1); }
  .lh-cta-row { flex-direction: column; align-items: flex-start; gap: var(--space-3); }
  .lh-stage { padding: var(--space-4); }
  .lh-float { position: static; max-width: none; margin-top: var(--space-4); }
}
@media (prefers-reduced-motion: reduce) { .lh-cta { transition: none; } }

/* ---- 36: without and with ------------------------------------------------
   Two columns and one rule between them. The rule is an element rather than a
   border, for the same reason as entry 34: it fades at both ends, and a border
   cannot.

   The left column is not a straw man. Every line on it is what the brief says
   the buyer does today, and every line on the right is verified. A comparison
   that overstates the left is the fastest way to lose the reader who lives
   there. */
.ww { background: var(--color-paper); padding: var(--space-12) 0; }
.ww-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.ww-h {
  margin: 0 auto var(--space-16); max-width: 22ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.ww-h-quiet { color: var(--color-ink-muted); }
.ww-grid {
  position: relative;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-16);
}
.ww-rule {
  position: absolute; left: 50%; top: 0; bottom: 0; width: 1px;
  background: var(--color-rule);
  -webkit-mask-image: linear-gradient(transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(transparent, #000 12%, #000 88%, transparent);
}
.ww-col-h {
  margin: 0 0 var(--space-6);
  font-size: var(--text-h3); font-weight: var(--weight-bold); color: var(--color-ink);
}
.ww-col-h--off { color: var(--color-ink-muted); }
.ww-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); }
.ww-list li {
  display: flex; align-items: flex-start; gap: var(--space-3);
  font-size: var(--text-body); line-height: var(--leading-normal);
  color: var(--color-ink);
}
.ww-list--off li { color: var(--color-ink-muted); }
.ww-ico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; margin-top: 0.35em; }
.ww-ico path { fill: currentColor; }
.ww-list li .ww-ico { color: var(--lb-green-ink); }
.ww-list--off li .ww-ico { color: var(--lb-red-ink); }
.ww-cta {
  display: inline-flex; align-items: center; gap: var(--space-3);
  margin-top: var(--space-8); padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-btn);
  background: color-mix(in srgb, var(--color-ink) 6%, var(--color-surface));
  color: var(--color-ink); text-decoration: none;
  font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: background-color var(--dur-fast) var(--ease-standard);
}
/* The ask sits on the section's own centre line, the way every other
   section closes, not tucked inside whichever column it happened to be
   written in. */
.ww-cta-row { display: flex; justify-content: center; margin-top: var(--space-10); }
.ww-cta:hover, .ww-cta:focus-visible { background: color-mix(in srgb, var(--color-ink) 10%, var(--color-surface)); }
.ww-cta-ico { width: var(--space-4); height: var(--space-4); }
.ww-cta-ico path { fill: currentColor; }
@media (max-width: 900px) {
  .ww { padding: var(--space-16) 0; }
  .ww-h { font-size: var(--text-h1); }
  .ww-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-10); }
  .ww-rule { display: none; }
}
@media (prefers-reduced-motion: reduce) { .ww-cta { transition: none; } }

/* ---- 37: diagram and linked list -----------------------------------------
   A figure on the left, three linked points on the right. The reference draws
   interlocking puzzle pieces over a field of faded third-party marks, because
   its argument is that it sits among a thousand other tools. Ours connects to
   one thing, so the field would be a lie: the pieces are the three parts of
   our own chain instead, drawn as three overlapping blocks. */
.dg { background: var(--color-paper); padding: var(--space-12) 0; }
.dg-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.dg-eyebrow {
  margin: 0 0 var(--space-4); text-align: center;
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.dg-h {
  margin: 0 auto var(--space-4); text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.dg-lede {
  margin: 0 auto var(--space-16); max-width: var(--measure-lede); text-align: center;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.dg-body {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: var(--space-16); align-items: center;
}
/* Three blocks, overlapped, each carrying its own hue. */
.dg-figure { position: relative; aspect-ratio: 1; }
.dg-piece {
  position: absolute; display: grid; place-items: start center; text-align: center;
  width: 47%; height: 47%; padding: var(--space-5) var(--space-4);
  border-radius: var(--radius-lg);
  background: linear-gradient(150deg,
              color-mix(in srgb, var(--dg-hue) 68%, var(--color-ink-deep)),
              color-mix(in srgb, var(--dg-hue) 52%, var(--color-ink-deep)));
  color: var(--color-ink-on-dark);
  font-size: var(--text-ui); font-weight: var(--weight-bold);
  box-shadow: var(--shadow-3);
}
.dg-piece--1 { left: 1%; top: 2%; }
.dg-piece--2 { right: 1%; top: 14%; }
.dg-piece--3 { left: 26%; bottom: 2%; }
/* Each piece carries a real capture under its name, or the diagram is three
   coloured rectangles standing where the product should be. */
.dg-piece { grid-template-rows: auto 1fr; gap: var(--space-3); }
.dg-shot {
  width: 100%; height: 100%; min-height: 0; overflow: hidden;
  border-radius: var(--radius-md);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 22%, transparent);
}
.dg-shot img { width: 100%; height: 100%; object-fit: cover; object-position: left top; display: block; }

.dg-links { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-8); }
.dg-link { display: flex; align-items: flex-start; gap: var(--space-4); }
.dg-link-mark {
  display: grid; place-items: center; flex: 0 0 auto;
  width: var(--space-10); height: var(--space-10);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--dg-hue) 20%, var(--color-surface));
  color: var(--dg-ink);
}
.dg-link-ico { width: var(--space-5); height: var(--space-5); }
.dg-link-ico path { fill: currentColor; }
.dg-link-h {
  margin: 0 0 var(--space-2);
  font-size: var(--text-h4); font-weight: var(--weight-bold); color: var(--dg-ink);
}
.dg-link p {
  margin: 0; font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
@media (max-width: 900px) {
  .dg { padding: var(--space-16) 0; }
  .dg-h { font-size: var(--text-h1); }
  .dg-body { grid-template-columns: minmax(0, 1fr); gap: var(--space-10); }
  /* LB-15: auto margins on a grid item absorb the free space instead of
     stretching it, and a box whose only children are absolutely positioned
     then measures zero. Explicit width, then the margins centre it. */
  .dg-figure { width: 100%; max-width: calc(var(--space-32) * 4); margin-inline: auto; }
}

/* ---- 38: capture bento ---------------------------------------------------
   The same rhythm as entry 21, led by product captures rather than drawn
   figures. Different job: 21 explains things that have no screen, this one
   shows screens that need no explaining.

   Cards are copy over capture, and the capture is clipped by the card rather
   than fitted inside it, so six different aspect ratios still line up. */
.cb { background: var(--color-paper); padding: var(--space-12) 0; }
.cb-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.cb-eyebrow {
  margin: 0 0 var(--space-4); text-align: center;
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold);
  color: var(--lb-teal-ink);
}
.cb-h {
  margin: 0 auto var(--space-4); max-width: 24ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.cb-h-quiet { color: var(--color-ink-muted); }
.cb-lede {
  margin: 0 auto var(--space-12); max-width: var(--measure-lede); text-align: center;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.cb-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--space-4);
}
.cb-card {
  display: flex; flex-direction: column; gap: var(--space-5); overflow: hidden;
  padding: var(--space-6);
  border: 1px solid var(--color-rule); border-radius: var(--radius-lg);
  background: var(--color-surface);
}
/* No card runs the full row. One horizontal capture cannot fill six columns,
   so the grid is dealt in twos and threes and every card is a picture over
   its own caption. */
.cb-card--wide { grid-column: span 3; }
.cb-card--s { grid-column: span 2; }
.cb-card--half { grid-column: span 3; }
/* A lone card sits on the page axis instead of hanging off the left edge. */
.cb-grid > .cb-card:only-child { grid-column: 2 / span 4; }
.cb-h {} /* the section heading; card headings below */
.cb-card .cb-h { font-size: var(--text-h4); margin: 0 0 var(--space-2); text-align: left; max-width: none; }
.cb-p { margin: 0; font-size: var(--text-small); line-height: var(--leading-normal); color: var(--color-ink-muted); }
.cb-shot {
  /* One border everywhere, at the token colour, rather than whatever edge
     each capture happened to have baked into its own pixels: with `cover`
     cropping to a fixed 16:10 box, a capture at any other ratio (three at
     16:9, seven narrower still) had its right or bottom edge cut off, which
     is what read as a missing border on some cards and not others. `contain`
     never crops; the fill colour behind it stands in for the cut corners. */
  margin: 0; overflow: hidden; border-radius: var(--radius-md);
  border: 1px solid var(--color-rule);
  aspect-ratio: 16 / 10;
  background: color-mix(in srgb, var(--color-ink) 4%, var(--color-surface));
}
.cb-shot img { display: block; width: 100%; height: 100%; object-fit: contain; object-position: center; }
@media (max-width: 900px) {
  .cb { padding: var(--space-16) 0; }
  .cb-h { font-size: var(--text-h1); }
  .cb-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cb-card--wide { grid-column: span 2; flex-direction: column; align-items: stretch; }
  .cb-card--s, .cb-card--half { grid-column: span 1; }
}
@media (max-width: 600px) {
  .cb-grid { grid-template-columns: minmax(0, 1fr); }
  .cb-card--wide, .cb-card--s, .cb-card--half { grid-column: span 1; }
}

/* ---- 39: compact row -----------------------------------------------------
   Four items, mark over two lines, no heading. The smallest of the three
   directory variants: 30 is a grid of sixteen, 33 is three columns of prose,
   this is a strip that sits under something bigger and adds four facts without
   asking for a section of its own. */
.cr { background: var(--color-paper); padding: var(--space-6) 0; }
.cr-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.cr-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-6);
}
.cr-item { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-1) var(--space-4); }
.cr-mark {
  grid-row: 1 / 3; display: grid; place-items: center;
  width: var(--space-10); height: var(--space-10);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--lb-purple) 14%, var(--color-surface));
  color: var(--lb-purple-ink);
}
.cr-ico { width: var(--space-5); height: var(--space-5); }
.cr-ico path { fill: currentColor; }
.cr-name { font-size: var(--text-ui); font-weight: var(--weight-bold); color: var(--color-ink); }
.cr-line { font-size: var(--text-fine); line-height: var(--leading-snug); color: var(--color-ink-muted); }
@media (max-width: 900px) {
  .cr-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
}
@media (max-width: 600px) { .cr-grid { grid-template-columns: minmax(0, 1fr); } }

/* ---- 41: download section ------------------------------------------------
   Heading, two actions with an "or" between them, one capture, then a card per
   thing you can install.

   The reference puts two QR codes here, one per app store. Ours puts the two
   plugins, because that is what we ship: each card carries the component name
   an administrator will type, what it does, and where it goes. No QR, because
   a QR that scans to nothing is worse than none.

   The row is built to grow. A marketplace entry, an integration or a mobile
   build drops in as another card with no layout change, and the two-up becomes
   three-up by changing one number in the grid. */
.dl { background: var(--color-paper); padding: var(--space-12) 0; }
.dl-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.dl-h {
  margin: 0 auto var(--space-4); max-width: 22ch; text-align: center;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.dl-h-quiet { color: var(--color-ink-muted); }
.dl-lede {
  margin: 0 auto var(--space-8); max-width: var(--measure-lede); text-align: center;
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}

.dl-actions {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
  margin: 0 0 var(--space-12);
}
.dl-btn {
  display: inline-flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-btn);
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.dl-btn:hover, .dl-btn:focus-visible { transform: translateY(-2px); }
.dl-btn--quiet {
  background: color-mix(in srgb, var(--color-ink) 6%, var(--color-surface));
  color: var(--color-ink);
}
.dl-btn-ico { width: var(--space-4); height: var(--space-4); }
.dl-btn-ico path { fill: currentColor; }
/* The rule either side of the word, drawn rather than bordered so it fades. */
.dl-or {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: var(--space-4); width: min(100%, 22rem);
  font-size: var(--text-small); color: var(--color-ink-muted);
}
.dl-or::before, .dl-or::after {
  content: ""; height: 1px; background: var(--color-rule);
}
.dl-or::before {
  -webkit-mask-image: linear-gradient(to right, transparent, #000);
  mask-image: linear-gradient(to right, transparent, #000);
}
.dl-or::after {
  -webkit-mask-image: linear-gradient(to left, transparent, #000);
  mask-image: linear-gradient(to left, transparent, #000);
}

.dl-shot {
  margin: 0 0 var(--space-16); border-radius: var(--radius-xl); overflow: hidden;
  box-shadow: var(--shadow-4);
}
.dl-shot img { display: block; width: 100%; height: auto; }

.dl-sub {
  margin: 0 0 var(--space-6);
  font-size: var(--text-h3); font-weight: var(--weight-bold); color: var(--color-ink);
}
.dl-grid {
  list-style: none; margin: 0 0 var(--space-6); padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6);
}
.dl-card {
  display: flex; flex-direction: column;
  padding: var(--space-6);
  border: 1px solid var(--color-rule); border-radius: var(--radius-lg);
  background: var(--color-surface);
}
.dl-card-head { display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-5); }
.dl-mark {
  display: grid; place-items: center; flex: 0 0 auto;
  width: var(--space-12); height: var(--space-12);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--dl-hue) 20%, var(--color-surface));
  color: var(--dl-ink);
}
.dl-mark-ico { width: var(--space-6); height: var(--space-6); }
.dl-mark-ico path { fill: currentColor; }
.dl-card-h { margin: 0 0 var(--space-1); font-size: var(--text-h4); font-weight: var(--weight-bold); }
/* The string an administrator actually types, so it is set as one. */
.dl-code {
  display: inline-block; padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-xs);
  background: color-mix(in srgb, var(--color-ink) 6%, transparent);
  font-family: inherit; font-size: var(--text-fine); color: var(--color-ink-muted);
}
.dl-card-p {
  margin: 0 0 var(--space-5);
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.dl-path {
  display: flex; align-items: flex-start; gap: var(--space-3);
  margin: 0 0 var(--space-6); padding: var(--space-4);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--color-ink) 3%, var(--color-surface));
  font-size: var(--text-fine); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.dl-path-ico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; margin-top: 0.15em; }
.dl-path-ico path { fill: var(--dl-ink); }
.dl-card-btn {
  margin-top: auto;
  display: flex; align-items: center; justify-content: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border: 1px solid var(--color-rule); border-radius: var(--radius-btn);
  background: var(--color-surface); color: var(--color-ink);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: border-color var(--dur-fast) var(--ease-standard);
}
.dl-card-btn:hover, .dl-card-btn:focus-visible { border-color: var(--dl-hue); }
.dl-note {
  margin: 0; text-align: center;
  font-size: var(--text-small); color: var(--color-ink-muted);
}

@media (max-width: 900px) {
  .dl { padding: var(--space-16) 0; }
  .dl-h { font-size: var(--text-h1); }
  .dl-grid { grid-template-columns: minmax(0, 1fr); }
  .dl-shot { margin-bottom: var(--space-10); }
}
@media (prefers-reduced-motion: reduce) {
  .dl-btn, .dl-card-btn { transition: none; }
}

/* ---- 42: demo request form -----------------------------------------------
   Fields on the left, the argument on the right. The form is real markup: every
   field carries a label, a name and an autocomplete token, so a browser can
   fill it and a screen reader can read it. Nothing here is a styled div
   pretending to be an input.

   The fields are the ones we would actually use. The reference asks for a phone
   number and a headcount band; we ask for learner count because that is what
   the plans are sized on, and we do not ask for a phone number for a call that
   happens over a link. */
.df { background: var(--color-paper); padding: var(--space-12) 0; }
.df-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.df-panel {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: var(--space-16); align-items: start;
  padding: var(--space-10);
  border-radius: var(--radius-xl);
  background: var(--color-surface); box-shadow: var(--shadow-3);
}
.df-form {
  padding: var(--space-8);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--color-ink) 3%, var(--color-surface));
}
.df-field { margin: 0 0 var(--space-5); }
.df-field label {
  display: block; margin-bottom: var(--space-2);
  font-size: var(--text-small); font-weight: var(--weight-bold); color: var(--color-ink);
}
.df-field input, .df-field select {
  display: block; width: 100%; padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-rule); border-radius: var(--radius-md);
  background: var(--color-surface); color: var(--color-ink);
  font: inherit; font-size: var(--text-body);
}
.df-field input:focus-visible, .df-field select:focus-visible, .df-field textarea:focus-visible {
  outline: 2px solid var(--lb-teal); outline-offset: 1px;
}
.df-field textarea {
  display: block; width: 100%; padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-rule); border-radius: var(--radius-md);
  background: var(--color-surface); color: var(--color-ink);
  font: inherit; font-size: var(--text-body); line-height: var(--leading-normal);
  resize: vertical; min-height: 7.5em;
}
/* The honeypot: present in the DOM, absent from the page. */
.df-hp { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
/* One message under the field it is about. Colour is never the only
   signal: the words say what to fix, and the control is aria-invalid. */
.df-err {
  margin: var(--space-2) 0 0;
  font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--lb-red-ink);
}
.df-field input[aria-invalid="true"], .df-field select[aria-invalid="true"],
.df-field textarea[aria-invalid="true"], .df-digit[aria-invalid="true"] {
  border-color: var(--lb-red-ink);
}
/* Cloudflare's widget renders its own iframe at a fixed size; this only
   gives it room and keeps it on the form's rhythm. */
.df-turnstile { width: 100%; margin: 0 0 var(--space-5); min-height: 65px; }
.df-turnstile:empty { display: none; }
/* Cloudflare's flexible widget fills whatever it is given; this keeps its
   own iframe from overflowing the column on a narrow phone. */
.df-turnstile iframe { width: 100% !important; max-width: 100%; }
/* One line of feedback under the form. Colour is backed by an icon-free
   sentence, so the state is readable without it. */
.df-status {
  margin: var(--space-4) 0 0; padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: var(--text-small); line-height: var(--leading-normal);
}
.df-status--info { background: color-mix(in srgb, var(--lb-blue) 10%, var(--color-surface)); color: var(--lb-blue-ink); }
.df-status--ok { background: color-mix(in srgb, var(--lb-green) 12%, var(--color-surface)); color: var(--lb-green-ink); }
.df-status--error { background: color-mix(in srgb, var(--lb-red) 10%, var(--color-surface)); color: var(--lb-red-ink); }
.df-code-lead, .df-code-help {
  margin: 0 0 var(--space-5); font-size: var(--text-small); line-height: var(--leading-normal); color: var(--color-ink-muted);
}
.df-code-help { margin: var(--space-4) 0 0; text-align: center; }
.df-code-help a { color: var(--color-ink); }
/* Six one-digit boxes, the pattern the product uses to confirm an email. */
.df-digits { border: 0; margin: 0 0 var(--space-5); padding: 0; min-width: 0; }
.df-digits legend {
  display: block; margin-bottom: var(--space-2); padding: 0;
  font-size: var(--text-small); font-weight: var(--weight-bold); color: var(--color-ink);
}
.df-digits-row { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--space-2); }
.df-digit {
  display: block; width: 100%; height: calc(var(--space-10) + var(--space-4)); padding: 0;
  border: 1px solid var(--color-rule); border-radius: var(--radius-md);
  background: var(--color-surface); color: var(--color-ink);
  font-family: var(--font-mono); font-size: var(--text-h3); font-weight: var(--weight-semibold);
  text-align: center;
}
.df-digit:focus-visible { outline: 2px solid var(--lb-teal); outline-offset: 1px; }
.df-submit:disabled { opacity: 0.6; cursor: wait; transform: none; }
.df-check { display: flex; gap: var(--space-3); margin: 0 0 var(--space-6); }
.df-check input { margin-top: 0.2em; width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
.df-check label {
  font-size: var(--text-small); line-height: var(--leading-normal); color: var(--color-ink-muted);
}
.df-submit {
  width: 100%; padding: var(--space-4) var(--space-6);
  border: 0; border-radius: var(--radius-btn);
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  font: inherit; font-size: var(--text-ui); font-weight: var(--weight-bold);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-standard);
}
.df-submit:hover, .df-submit:focus-visible { transform: translateY(-2px); }
.df-legal {
  margin: var(--space-4) 0 0; text-align: center;
  font-size: var(--text-fine); line-height: var(--leading-normal); color: var(--color-ink-muted);
}
.df-legal a { color: var(--color-ink); }

.df-copy { padding-top: var(--space-6); }
.df-h {
  margin: 0 0 var(--space-4);
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.df-lede {
  margin: 0 0 var(--space-8); max-width: 42ch;
  font-size: var(--text-body); line-height: var(--leading-normal); color: var(--color-ink-muted);
}
.df-points { list-style: none; margin: 0 0 var(--space-10); padding: 0; display: grid; gap: var(--space-4); }
.df-points li {
  display: flex; align-items: flex-start; gap: var(--space-3);
  font-size: var(--text-small); line-height: var(--leading-normal); color: var(--color-ink);
}
.df-ico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; margin-top: 0.3em; color: var(--lb-teal-ink); }
.df-ico path { fill: currentColor; }
.df-trust {
  margin: 0 0 var(--space-5);
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold); color: var(--color-ink-muted);
}
.df-logos {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6); align-items: center;
}
.df-logos img { display: block; width: var(--space-20); height: auto; opacity: 0.6; filter: grayscale(1); }
@media (max-width: 900px) {
  .df { padding: var(--space-16) 0; }
  .df-panel { grid-template-columns: minmax(0, 1fr); gap: var(--space-10); padding: var(--space-5); }
  .df-h { font-size: var(--text-h1); }
  .df-copy { padding-top: 0; order: -1; }
}
@media (prefers-reduced-motion: reduce) { .df-submit { transition: none; } }

/* ---------- the form pages' icons, chips, in-column marquee and spectrum button ---------- */
/* Each point carries its own mark in a chip tinted with the point's hue. */
.df-points li { align-items: center; }
.df-point-ico {
  display: grid; place-items: center; flex: 0 0 auto;
  width: var(--space-8); height: var(--space-8);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--df-hue, var(--lb-teal)) 12%, var(--color-surface));
}
.df-point-ico .mi { background-color: var(--df-hue-ink, var(--lb-teal-ink)); }
/* A small mark before each label, and an arrow on the button. */
.df-label-ico {
  width: var(--space-3); height: var(--space-3); margin-right: var(--space-2);
  vertical-align: -1px; background-color: var(--color-ink-muted);
}
.df-submit-ico {
  width: var(--space-4); height: var(--space-4); margin-left: var(--space-2);
  vertical-align: -3px; background-color: var(--color-ink-on-dark);
}
/* The framework marquee where the roster used to sit, in the copy column
   of a form page:
   scaled to the column, title left, no card of its own. */
.df-trusted { padding: var(--space-6) 0 0; background: none; }
.df-trusted .trustbar-title {
  margin: 0 0 var(--space-4); text-align: left;
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold); color: var(--color-ink-muted);
}
.df-trusted .marquee { gap: var(--space-4); }
.df-trusted .mq-track { gap: var(--space-8); }
.df-trusted .mq-item img { height: var(--space-10); }
/* The button wears the same spectrum as Start free in the header, and
   drifts with it. Declared here because .df-submit's own background
   shorthand comes later in the shared sheet and would blank the image. */
.df-submit.btn--spectrum {
  color: var(--color-icon-glyph);
  background-color: transparent;
  background-image: linear-gradient(100deg,
    var(--lb-teal), var(--lb-blue), var(--lb-purple), var(--lb-magenta),
    var(--lb-red), var(--lb-orange), var(--lb-gold),
    var(--lb-orange), var(--lb-red), var(--lb-magenta),
    var(--lb-purple), var(--lb-blue), var(--lb-teal));
  background-size: 1200px 100%; background-repeat: repeat-x;
  animation: spectrum-drift calc(var(--dur-ambient) * 2) linear infinite;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .28);
}
.df-submit.btn--spectrum:hover { filter: saturate(1.15); }
.df-submit-ico { background-color: var(--color-icon-glyph); }
@media (prefers-reduced-motion: reduce) { .df-submit.btn--spectrum { animation: none; } }

/* ---- 43: auth screen -----------------------------------------------------
   One card on a soft ground. Checked against the API before drawing it: there
   is a Sign in with Moodle provider and a password form, and there is no
   Google sign-in, so the reference's Google row is not here. Drawing a button
   that cannot work is worse than one fewer option.

   Real form markup, labels included: an auth screen with placeholder-only
   fields is the most common accessibility failure on the web. */
.au {
  display: grid; place-items: center; padding: var(--space-24) var(--gutter);
  background:
    radial-gradient(60% 50% at 20% 0%, color-mix(in srgb, var(--lb-orange) 18%, transparent), transparent 70%),
    radial-gradient(60% 50% at 80% 0%, color-mix(in srgb, var(--lb-blue) 20%, transparent), transparent 70%),
    var(--color-paper);
}
.au-card {
  width: min(100%, 26rem); text-align: center;
  padding: var(--space-10);
  border: 1px solid var(--color-rule); border-radius: var(--radius-xl);
  background: var(--color-surface); box-shadow: var(--shadow-3);
}
.au-mark { display: block; width: var(--space-24); height: auto; margin: 0 auto var(--space-6); }
.au-h { margin: 0 0 var(--space-2); font-size: var(--text-h2); font-weight: var(--weight-bold); }
.au-sub { margin: 0 0 var(--space-8); font-size: var(--text-small); color: var(--color-ink-muted); }
.au-sub a { color: var(--lb-teal-ink); }
.au-alt {
  display: flex; align-items: center; justify-content: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border: 1px solid var(--color-rule); border-radius: var(--radius-btn);
  background: var(--color-surface); color: var(--color-ink);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: border-color var(--dur-fast) var(--ease-standard);
}
.au-alt:hover, .au-alt:focus-visible { border-color: var(--lb-teal); }
.au-alt-ico { width: var(--space-4); height: var(--space-4); }
.au-alt-ico path { fill: currentColor; }
.au-or {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--space-4);
  margin: var(--space-6) 0;
  font-size: var(--text-small); color: var(--color-ink-muted);
}
.au-or::before, .au-or::after { content: ""; height: 1px; background: var(--color-rule); }
.au-field { margin: 0 0 var(--space-4); text-align: left; }
.au-field label {
  display: block; margin-bottom: var(--space-2);
  font-size: var(--text-small); font-weight: var(--weight-bold);
}
.au-field input {
  display: block; width: 100%; padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-rule); border-radius: var(--radius-md);
  background: var(--color-surface); color: var(--color-ink); font: inherit;
}
.au-field input:focus-visible { outline: 2px solid var(--lb-teal); outline-offset: 1px; }
.au-submit {
  width: 100%; margin-top: var(--space-2); padding: var(--space-3) var(--space-5);
  border: 0; border-radius: var(--radius-btn);
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  font: inherit; font-size: var(--text-ui); font-weight: var(--weight-bold); cursor: pointer;
}
.au-forgot { margin: var(--space-5) 0 0; font-size: var(--text-small); }
.au-forgot a { color: var(--color-ink-muted); }

/* ---- 44: dark radial hero ------------------------------------------------
   The figure is the argument, so it moves: raw records travel in from the
   left, drawn charts travel out to the right, and a radar beam turns over the
   rings in the middle where the mark sits.

   Both sides use the roll-call pill from entry 05, which is what makes the two
   halves read as one idea rather than two lists: the same object, grey going
   in and in its own hue coming out.

   Three rows a side, all travelling left to right. Each side is a masked
   window rather than a set of individually timed items: the left group fades
   toward the core and the right group fades away from it, so nothing has to be
   timed against the middle by hand and each side is one keyframe rather than
   fourteen. Only transform and opacity move, and the whole figure is
   aria-hidden because the heading above it already says this in words. */
.rh {
  --rh-cycle: calc(var(--dur-ambient) * 5);
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  padding: var(--space-12) 0; overflow: clip;
}
/* The eyebrow above this heading read as barely moving against a hero this
   busy. Scoped to this hero only: every other page's eyebrow keeps the
   ambient pace. */
.rh .eyebrow { animation-duration: calc(var(--dur-ambient) * 0.3); }
.rh-in {
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
  text-align: center;
}
.rh-h {
  margin: 0 auto var(--space-6);
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
  color: var(--color-ink-on-dark);
}
.rh-points {
  list-style: none; margin: 0 0 var(--space-8); padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-6);
}
.rh-points li { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-small); }
.rh-ico { width: var(--space-4); height: var(--space-4); }
.rh-ico path { fill: var(--lb-teal); }
.rh-actions { display: flex; justify-content: center; gap: var(--space-4); margin: 0 0 var(--space-12); }
.rh-btn {
  display: inline-flex; padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-btn);
  background: var(--lb-teal); color: var(--color-ink-deep);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
}
.rh-btn--quiet {
  background: color-mix(in srgb, var(--color-ink-on-dark) 12%, transparent);
  color: var(--color-ink-on-dark);
}

/* --- the figure --- */
.rh-figure {
  position: relative; isolation: isolate;
  width: 100%; height: calc(var(--space-32) * 3.1);
  margin-inline: auto;
}

/* --- the two sides --- */
.rh-side {
  position: absolute; top: 50%; translate: 0 -50%;
  list-style: none; margin: 0; padding: 0;
  width: 46%;
  display: grid; gap: var(--space-4);
}
.rh-side--in { left: 0; }
.rh-side--out { right: 0; }
/* Each side fades toward the middle, so a pill dissolves as it arrives at the
   core rather than driving over it. */
.rh-side--in {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 18%, #000 62%, transparent 96%);
  mask-image: linear-gradient(to right, transparent, #000 18%, #000 62%, transparent 96%);
}
.rh-side--out {
  -webkit-mask-image: linear-gradient(to right, transparent 4%, #000 34%, #000 66%, transparent);
  mask-image: linear-gradient(to right, transparent 4%, #000 34%, #000 66%, transparent);
}
.rh-row { overflow: hidden; }
.rh-track {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: center; gap: var(--space-4); width: max-content;
  /* Left to right on both sides: the list is present twice and the keyframe
     runs from minus one half back to zero. */
  animation: rh-run var(--rh-cycle) var(--ease-linear) infinite;
  /* Each row is offset a beat, so the three never march in step. */
  animation-delay: calc(var(--rh-cycle) / 3 * var(--i) * -1);
  will-change: transform;
}
@keyframes rh-run {
  from { transform: translate3d(-50%, 0, 0); }
  to { transform: translate3d(0, 0, 0); }
}

/* The pill from entry 05, in two states. */
.rh-pill {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-btn);
  border: 1px solid color-mix(in srgb, var(--rh-hue, var(--color-ink-on-dark)) 26%, transparent);
  background: color-mix(in srgb, var(--rh-hue, var(--color-ink-on-dark)) 10%, var(--color-ink-deep));
  color: var(--color-ink-on-dark);
  font-size: var(--text-fine); white-space: nowrap;
}
.rh-pill-ico { width: var(--space-4); height: var(--space-4); flex: 0 0 auto; }
.rh-pill-ico path { fill: var(--rh-hue, currentColor); }
/* Going in: no hue at all. The colour is what the middle adds. */
.rh-pill--raw {
  border-color: color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
  background: color-mix(in srgb, var(--color-ink-on-dark) 4%, var(--color-ink-deep));
  color: color-mix(in srgb, var(--color-ink-on-dark) 58%, transparent);
}
.rh-pill--raw .rh-pill-ico path { fill: color-mix(in srgb, var(--color-ink-on-dark) 40%, transparent); }

/* --- the middle --- */
.rh-ring, .rh-core {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  border-radius: var(--radius-pill);
}
/* A pulse, not a sweep: each ring is born at the logo's own size and grows
   to the old sweep's outer edge while it fades, so the wave reads as coming
   from the mark rather than scanning around it. All three share one
   keyframe and only their delay differs, which is what keeps the ripple
   continuous instead of three rings breathing in sync. */
.rh-ring {
  z-index: 1;
  width: calc(var(--space-32) * 2.4); height: calc(var(--space-32) * 2.4);
  border: 1px solid color-mix(in srgb, var(--lb-teal) 55%, transparent);
  opacity: 0;
  animation: rh-pulse calc(var(--dur-ambient) * 0.55) var(--ease-standard) infinite;
  will-change: transform, opacity;
}
.rh-ring--1 { animation-delay: 0s; }
.rh-ring--2 { animation-delay: calc(var(--dur-ambient) * 0.55 / -3); }
.rh-ring--3 { animation-delay: calc(var(--dur-ambient) * 0.55 / -3 * 2); }
@keyframes rh-pulse {
  0% { transform: scale(0.48); opacity: 0.6; }
  65% { opacity: 0.14; }
  100% { transform: scale(1); opacity: 0; }
}
.rh-core {
  display: grid; place-items: center; z-index: 2;
  width: calc(var(--space-32) * 1.15); height: calc(var(--space-32) * 1.15);
  background: radial-gradient(circle at 42% 34%,
    color-mix(in srgb, var(--lb-teal) 26%, var(--color-ink-deep)),
    var(--color-ink-deep) 72%);
  border: 1px solid color-mix(in srgb, var(--lb-teal) 40%, transparent);
  box-shadow: 0 0 var(--space-16) color-mix(in srgb, var(--lb-teal) 24%, transparent);
}
.rh-core img { display: block; width: 68%; height: auto; }

.rh-label {
  position: absolute; top: 50%; z-index: 3;
  translate: 0 -50%;
  margin-top: calc(var(--space-32) * -1.6);
  font-size: var(--text-fine); letter-spacing: var(--tracking-caption);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-ink-on-dark) 44%, transparent);
}
.rh-label--left { left: 0; }
.rh-label--right { right: 0; }

@media (max-width: 900px) {
  .rh { padding: var(--space-16) 0; }
  .rh-h { font-size: var(--text-h1); }
  .rh-actions { flex-direction: column; align-items: center; }
  .rh-figure { height: calc(var(--space-32) * 2.4); }
  .rh-side { width: 49%; gap: var(--space-3); }
  /* All three share one size, same as desktop: they are the same pulse at
     every width, just born from a smaller mark. */
  .rh-ring { width: calc(var(--space-24) * 2.4); height: calc(var(--space-24) * 2.4); }
  .rh-core { width: calc(var(--space-24) * 1.15); height: calc(var(--space-24) * 1.15); }
  .rh-label { margin-top: calc(var(--space-24) * -1.7); }
  .rh-pill { padding: var(--space-1) var(--space-3); font-size: 0.625rem; }
}
@media (prefers-reduced-motion: reduce) {
  /* The duration token is 0ms here, so the animation comes off entirely and
     both sides simply sit where they are. The rings settle on one faint,
     static ring rather than three stacked at full opacity. */
  .rh-track, .rh-ring { animation: none; }
  .rh-ring { opacity: 0.22; transform: scale(0.7); }
}
/* ---- 45: dark compliance band --------------------------------------------
   Badge circles over three cards, on the dark. The reference's badges are SOC
   2, ISO, HIPAA and GDPR certifications; brief.md puts all of those on the
   must-not-appear list, so these are the framework marks already approved on
   the homepage strip and the lede says plainly what they are. A compliance
   band that overstates is the one place on a site where it is not a marketing
   problem but a legal one. */
.pb { background: var(--color-ink-deep); color: var(--color-ink-on-dark); padding: var(--space-12) 0; }
.pb-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); text-align: center; }
.pb-eyebrow {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  margin: 0 0 var(--space-4);
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
   font-weight: var(--weight-bold); color: var(--lb-teal);
}
.pb-eyebrow-ico { width: var(--space-4); height: var(--space-4); }
.pb-eyebrow-ico path { fill: currentColor; }
.pb-h {
  margin: 0 auto var(--space-4); max-width: 22ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.pb-lede {
  margin: 0 auto var(--space-16); max-width: var(--measure-lede);
  font-size: var(--text-body); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 74%, transparent);
}
.pb-badges {
  list-style: none; margin: 0 0 var(--space-16); padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-8);
}
.pb-badge { display: grid; justify-items: center; gap: var(--space-3); }
.pb-badge img {
  width: var(--space-16); height: var(--space-16); padding: var(--space-3);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--color-ink-on-dark) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
  object-fit: contain;
  /* Drawn dark on transparent, so they vanish on this ground until knocked. */
  filter: brightness(0) invert(1);
  opacity: 0.85;
}
.pb-badge span { font-size: var(--text-fine); color: color-mix(in srgb, var(--color-ink-on-dark) 70%, transparent); }
.pb-cards {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-5);
}
.pb-card {
  padding: var(--space-8) var(--space-6);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 12%, transparent);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--color-ink-on-dark) 4%, transparent);
}
.pb-mark { display: grid; place-items: center; margin: 0 auto var(--space-4);
  width: var(--space-10); height: var(--space-10); border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--lb-teal) 20%, transparent); color: var(--lb-teal); }
.pb-ico { width: var(--space-5); height: var(--space-5); }
.pb-ico path { fill: currentColor; }
.pb-card-h { margin: 0 0 var(--space-2); font-size: var(--text-h4); font-weight: var(--weight-bold); }
.pb-card p {
  margin: 0; font-size: var(--text-small); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 70%, transparent);
}
@media (max-width: 900px) {
  .pb { padding: var(--space-16) 0; }
  .pb-h { font-size: var(--text-h1); }
  .pb-cards { grid-template-columns: minmax(0, 1fr); }
  .pb-badges { gap: var(--space-6); }
}

/* ---- 46: dark carousel ---------------------------------------------------
   Cover Flow: the slides overlap, the middle one faces you square and the ones
   either side turn almost edge on, each with a reflection under it and its
   caption on the image rather than beneath. Nothing here is interactive: it is
   a thing to watch, so the slides are plain elements and add no tab stops.

   Stacking is depth, not z-index. The track is preserve-3d and the centre
   slide sits at z 0 while its neighbours are pushed back, so the middle is in
   front for free and the order stays right at every position. A z-index would
   have needed rounding a fractional custom property, which CSS cannot do
   portably.

   --ca-f is 1 at the middle of the rail and 0 at the edges; --ca-s is the same
   signed. The script writes both and nothing else; every visual consequence is
   declared here. */
.ca { background: var(--color-ink-deep); color: var(--color-ink-on-dark); padding: var(--space-10) 0; }
.ca-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); text-align: center; }
.ca-h {
  margin: 0 auto var(--space-4);
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.ca-lede {
  margin: 0 auto var(--space-8); max-width: var(--measure-lede);
  font-size: var(--text-body); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 74%, transparent);
}
.ca-actions { margin: 0 0 var(--space-6); }
.ca-btn {
  display: inline-flex; padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-btn);
  background: var(--color-surface); color: var(--color-ink);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
}

/* --- the rail --- */
.ca-rail {
  /* On the container like every other section, and faded at both ends rather
     than cut, the same treatment as the marquees at entries 19 and 23. */
  max-width: var(--container); margin-inline: auto;
  overflow: hidden; padding-block: var(--space-8) var(--space-4);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
  perspective: calc(var(--space-32) * 9);
  perspective-origin: 50% 42%;
  cursor: ew-resize;
}
.ca-track {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: flex-start; gap: 0; width: max-content;
  transform-style: preserve-3d;
  will-change: transform;
}
/* Overlapped rather than spaced: the negative margin is what makes it a stack
   seen edge on instead of a row. */
.ca-slide {
  /* Trimmed from min(58vw, 30rem): smaller cards put more of the rail on
     screen at once instead of one giant card and two slivers. */
  flex: 0 0 auto; width: min(40vw, 21rem);
  margin-inline: calc(var(--space-8) * -1);
  transform-style: preserve-3d;
}

.ca-card {
  --ca-f: 0; --ca-s: 0;
  display: block; width: 100%;
  transform-style: preserve-3d;
  /* Hard turn on the ones either side, square on in the middle. Depth is what
     puts the middle in front. */
  transform:
    rotateY(calc(var(--ca-s) * -48deg))
    translateZ(calc((var(--ca-f) - 1) * var(--space-32)))
    scale(calc(0.9 + 0.1 * var(--ca-f)));
  opacity: calc(0.5 + 0.5 * var(--ca-f));
}

.ca-frame { position: relative; display: block; }
.ca-shot {
  display: block; overflow: hidden; aspect-ratio: 16 / 10;
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--color-ink-on-dark) 4%, transparent);
  box-shadow: 0 var(--space-5) var(--space-12)
              color-mix(in srgb, #000 calc(55% * var(--ca-f)), transparent);
}
.ca-shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top left; }

/* The caption lives on the image, over a scrim that only exists where the type
   is, so the artwork is not dimmed for the sake of two lines. */
.ca-cap {
  position: absolute; inset: auto 0 0 0;
  display: block; padding: var(--space-10) var(--space-5) var(--space-4);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  background: linear-gradient(transparent, color-mix(in srgb, #000 82%, transparent) 55%);
  font-size: var(--text-small); line-height: var(--leading-snug);
  color: color-mix(in srgb, var(--color-ink-on-dark) 76%, transparent);
}
.ca-cap b {
  display: block; font-size: var(--text-ui); font-weight: var(--weight-bold);
  color: var(--color-ink-on-dark);
}

/* The reflection: the same picture flipped, faded out and dimmed. */
/* A squat window onto the flipped picture. It cannot carry a percentage
   height: the parent's height is auto, so the box collapses to nothing, which
   is what happened the first time. */
.ca-reflect {
  display: block; overflow: hidden; margin-top: var(--space-2);
  aspect-ratio: 16 / 4.5;
  border-radius: var(--radius-lg);
  opacity: calc(0.1 + 0.22 * var(--ca-f));
  -webkit-mask-image: linear-gradient(#000, transparent 78%);
  mask-image: linear-gradient(#000, transparent 78%);
}
.ca-reflect img {
  display: block; width: 100%; height: auto; aspect-ratio: 16 / 10;
  object-fit: cover; object-position: top left;
  /* Flipped about its own centre: the box then shows what was the foot of the
     picture at the head of the reflection. */
  transform: scaleY(-1);
}

@media (max-width: 900px) {
  .ca { padding: var(--space-16) 0; }
  .ca-h { font-size: var(--text-h1); }
  .ca-slide { width: 72vw; margin-inline: calc(var(--space-12) * -1); }
}
@media (prefers-reduced-motion: reduce) {
  .ca-rail { overflow-x: auto; perspective: none; cursor: auto; }
  .ca-track { transform: none; gap: var(--space-5); }
  .ca-slide { margin-inline: 0; }
  .ca-card { transform: none; opacity: 1; }
  .ca-reflect { display: none; }
}
/* ---- 47: dark capability grid --------------------------------------------
   Six cards, each a wireframe figure over a name, a line and a tag. The
   figures are drawn from gradients and borders rather than illustration files,
   which is the same decision as the bento at entry 21 and for the same reason:
   six small drawings are not worth six requests. */
.dc { background: var(--color-ink-deep); color: var(--color-ink-on-dark); padding: var(--space-12) 0; }
.dc-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
/* Hero copy on the page axis: eyebrow, heading, lede and CTA row all centred,
   like the rest of the hero family. The card grid below keeps its own text. */
.dc-in > .sec-eyebrow { text-align: center; }
.dc-h {
  text-align: center;
  margin: 0 0 var(--space-2);
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
.dc-lede {
  text-align: center;
  margin: 0 auto var(--space-12); max-width: var(--measure-lede);
  font-size: var(--text-body-lg); color: color-mix(in srgb, var(--color-ink-on-dark) 70%, transparent);
}
.dc-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px; background: color-mix(in srgb, var(--color-ink-on-dark) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 12%, transparent);
  border-radius: var(--radius-lg); overflow: hidden;
}
.dc-card { padding: var(--space-8); background: var(--color-ink-deep); }
.dc-fig {
  display: block; margin-bottom: var(--space-6);
  aspect-ratio: 16 / 9; border-radius: var(--radius-md);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
  background-color: color-mix(in srgb, var(--color-ink-on-dark) 3%, transparent);
  background-repeat: no-repeat;
}
/* Each figure is one or two gradients, sized and placed. Cheap, and it reads at
   a glance, which is all a wireframe has to do. */
.dc-fig--bars {
  background-image:
    linear-gradient(var(--lb-purple), var(--lb-purple)),
    linear-gradient(color-mix(in srgb, var(--color-ink-on-dark) 30%, transparent) 0 0),
    linear-gradient(color-mix(in srgb, var(--color-ink-on-dark) 20%, transparent) 0 0),
    linear-gradient(color-mix(in srgb, var(--color-ink-on-dark) 24%, transparent) 0 0);
  background-size: 10% 34%, 10% 54%, 10% 22%, 56% 1px;
  background-position: 30% 74%, 44% 74%, 58% 74%, 50% 74%;
}
.dc-fig--line {
  background-image:
    linear-gradient(72deg, transparent 47%, var(--lb-purple) 48% 52%, transparent 53%),
    linear-gradient(108deg, transparent 47%, var(--lb-purple) 48% 52%, transparent 53%),
    linear-gradient(color-mix(in srgb, var(--color-ink-on-dark) 24%, transparent) 0 0);
  background-size: 28% 34%, 28% 34%, 56% 1px;
  background-position: 36% 46%, 62% 52%, 50% 74%;
}
.dc-fig--grid {
  background-image:
    linear-gradient(color-mix(in srgb, var(--color-ink-on-dark) 24%, transparent) 0 0),
    linear-gradient(color-mix(in srgb, var(--color-ink-on-dark) 18%, transparent) 0 0);
  background-size: 30% 26%, 30% 26%;
  background-position: 30% 34%, 62% 62%;
}
.dc-fig--doc {
  background-image:
    linear-gradient(color-mix(in srgb, var(--color-ink-on-dark) 26%, transparent) 0 0),
    linear-gradient(var(--lb-purple), var(--lb-purple));
  background-size: 46% 6%, 30% 6%;
  background-position: 28% 38%, 28% 56%;
}
.dc-fig--bell {
  background-image: radial-gradient(circle, var(--lb-purple) 40%, transparent 42%);
  background-size: 22% 38%; background-position: 50% 50%;
}
.dc-fig--window {
  background-image:
    linear-gradient(color-mix(in srgb, var(--color-ink-on-dark) 26%, transparent) 0 0),
    linear-gradient(color-mix(in srgb, var(--lb-purple) 60%, transparent) 0 0);
  background-size: 64% 8%, 64% 30%;
  background-position: 50% 26%, 50% 62%;
}
.dc-card-h {
  margin: 0 0 var(--space-2);
  font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold);
}
.dc-card p {
  margin: 0 0 var(--space-4); font-size: var(--text-small); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 70%, transparent);
}
.dc-card p.dc-tag {
  margin: 0; font-size: var(--text-fine); letter-spacing: var(--tracking-caption);
  color: color-mix(in srgb, var(--color-ink-on-dark) 46%, transparent);
}
@media (max-width: 900px) {
  .dc { padding: var(--space-16) 0; }
  .dc-h { font-size: var(--text-h1); }
  .dc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) { .dc-grid { grid-template-columns: minmax(0, 1fr); } }
/* ---- 48: dark search hero ------------------------------------------------
   The reference's row of app logos is nine integrations we do not have, so the
   tiles here are the eight surfaces a learner profile opens with instead, and
   the mock is the one search the product really ships.

   The panel's lit edge is a single conic gradient rotating behind a clipped
   rounded rect: one element, no border image, and nothing to redraw but a
   rotation.

   Nothing in the mock is interactive. It is a picture of a screen, so it is
   spans and list items and the section adds exactly two tab stops. */
.se {
  background: var(--color-ink-deep); color: var(--color-ink-on-dark);
  padding: var(--space-10) 0; text-align: center;
}
.se-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.se-eyebrow {
  margin: 0 0 var(--space-3);
  font-size: var(--text-ui); font-weight: var(--weight-bold);
}
.se-h {
  margin: 0 auto var(--space-5);
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
}
/* Painted type, so the fallback matters: the colour underneath is a real one,
   and only the clip takes it away. */
.se-grad {
  color: var(--lb-teal);
  background: linear-gradient(100deg, var(--lb-teal), var(--lb-blue) 45%, var(--lb-purple));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.se-lede {
  margin: 0 auto var(--space-10); max-width: var(--measure-lede);
  font-size: var(--text-body-lg); line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--color-ink-on-dark) 76%, transparent);
}

/* --- the tiles --- */
.se-tiles {
  list-style: none; margin: 0 auto var(--space-10); padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3);
}
.se-tile {
  position: relative;
  display: grid; place-items: center;
  width: var(--space-14); height: var(--space-14);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 12%, transparent);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--color-ink-on-dark) 5%, transparent);
}
/* The lit ring travels along the row rather than pulsing in unison, which is
   what the reference does and why it reads as a system waking up. */
.se-tile::after {
  content: ""; position: absolute; inset: -1px;
  border: 1px solid var(--lb-teal); border-radius: inherit;
  opacity: 0;
  animation: se-lit calc(var(--dur-ambient) * 2) var(--ease-standard) infinite;
  animation-delay: calc(var(--i) * var(--dur-ambient) / -4);
}
.se-tile:nth-child(3n)::after { border-color: var(--lb-purple); }
.se-tile:nth-child(3n + 2)::after { border-color: var(--lb-blue); }
@keyframes se-lit {
  0%, 78%, 100% { opacity: 0; }
  84%, 92% { opacity: 1; }
}
.se-tile-ico {
  width: var(--space-5); height: var(--space-5);
  fill: color-mix(in srgb, var(--color-ink-on-dark) 82%, transparent);
}

/* --- the panel --- */
.se-panel {
  position: relative; overflow: hidden;
  max-width: 62rem; margin: 0 auto var(--space-10); padding: 1px;
  border-radius: var(--radius-xl);
  background: color-mix(in srgb, var(--color-ink-on-dark) 14%, transparent);
  box-shadow: 0 0 var(--space-16) color-mix(in srgb, var(--lb-purple) 22%, transparent);
  text-align: left;
}
/* One square gradient, larger than the panel, turning behind it. Clipped by the
   panel's own rounded rect, all that shows is a lit arc running the border. */
.se-panel::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 150%; aspect-ratio: 1; translate: -50% -50%;
  background: conic-gradient(from 0deg,
    transparent 0 50%, var(--lb-teal) 66%, var(--lb-blue) 76%,
    var(--lb-purple) 84%, transparent 96%);
  animation: se-turn calc(var(--dur-ambient) * 1.5) var(--ease-linear) infinite;
}
@keyframes se-turn { to { rotate: 360deg; } }
.se-body {
  position: relative;
  border-radius: calc(var(--radius-xl) - 1px);
  background: var(--color-surface-dark);
}

.se-bar {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-4);
}
.se-ico {
  flex: 0 0 auto; width: var(--space-4); height: var(--space-4);
  fill: color-mix(in srgb, var(--color-ink-on-dark) 55%, transparent);
}
.se-ico--sm { width: var(--space-3); height: var(--space-3); }
.se-ico--find { fill: color-mix(in srgb, var(--color-ink-on-dark) 72%, transparent); }
.se-q {
  flex: 1 1 auto; margin: 0; min-width: 0;
  font-size: var(--text-h4); line-height: var(--leading-snug);
}
.se-caret {
  display: inline-block; width: 2px; height: 1.1em; margin-left: 2px;
  background: var(--lb-teal); vertical-align: -0.16em;
  animation: se-blink calc(var(--dur-sweep) * 2) steps(1) infinite;
}
@keyframes se-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }
.se-clear { display: inline-flex; }
/* The reference puts a Search with AI button here. There is no AI search, so
   the slot carries the key that does work. */
.se-key {
  flex: 0 0 auto; margin: 0;
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-small);
  color: color-mix(in srgb, var(--color-ink-on-dark) 62%, transparent);
}
.se-key kbd {
  padding: var(--space-1) var(--space-2);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 18%, transparent);
  border-radius: var(--radius-xs);
  background: color-mix(in srgb, var(--color-ink-on-dark) 8%, transparent);
  font: inherit; font-size: var(--text-fine); color: var(--color-ink-on-dark);
}

.se-tabs {
  list-style: none; margin: 0; padding: 0 var(--space-5);
  display: flex; gap: var(--space-5);
  border-bottom: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 10%, transparent);
  font-size: var(--text-small);
  color: color-mix(in srgb, var(--color-ink-on-dark) 62%, transparent);
}
.se-tab { padding-bottom: var(--space-3); border-bottom: 2px solid transparent; }
.se-tab--on { color: var(--color-ink-on-dark); border-bottom-color: var(--lb-teal); }

.se-out { padding: var(--space-4) var(--space-5) var(--space-2); min-height: var(--space-32); }
.se-rlabel, .se-short {
  margin: 0 0 var(--space-3);
  font-size: var(--text-small);
  color: color-mix(in srgb, var(--color-ink-on-dark) 62%, transparent);
}
/* Below two characters the endpoint returns nothing, so the mock shows nothing
   either and says why. */
.se-short { display: none; }
.se[data-len="0"] .se-short, .se[data-len="1"] .se-short { display: block; }
.se[data-len="0"] .se-results, .se[data-len="1"] .se-results { display: none; }

.se-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-1); }
.se-row {
  display: grid; align-items: center; gap: var(--space-3);
  grid-template-columns: auto minmax(0, 1fr) auto;
  padding: var(--space-2);
  border-radius: var(--radius-md);
}
.se-row:first-child { background: color-mix(in srgb, var(--color-ink-on-dark) 7%, transparent); }
.se-av {
  display: grid; place-items: center;
  width: var(--space-8); height: var(--space-8);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--lb-blue) 44%, var(--color-surface-dark));
  font-size: var(--text-fine); font-weight: var(--weight-bold);
  color: var(--color-ink-on-dark);
}
.se-who { min-width: 0; }
.se-name {
  display: block; font-size: var(--text-ui); font-weight: var(--weight-semibold);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The matched run, underlined the way the reference marks it. */
.se-name em {
  font-style: normal; color: var(--lb-teal);
  text-decoration: underline; text-underline-offset: 3px;
}
.se-meta {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  font-size: var(--text-small);
  color: color-mix(in srgb, var(--color-ink-on-dark) 62%, transparent);
}
.se-meta span + span::before { content: "\00b7"; margin-right: var(--space-2); }
.se-pill {
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
  font-size: var(--text-fine); font-weight: var(--weight-semibold);
  white-space: nowrap;
}
.se-pill--on {
  background: color-mix(in srgb, var(--lb-teal) 22%, transparent);
  color: color-mix(in srgb, var(--lb-teal) 62%, var(--color-ink-on-dark));
}
.se-pill--off {
  background: color-mix(in srgb, var(--color-ink-on-dark) 10%, transparent);
  color: color-mix(in srgb, var(--color-ink-on-dark) 72%, transparent);
}

.se-foot {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 10%, transparent);
  font-size: var(--text-small);
  color: color-mix(in srgb, var(--color-ink-on-dark) 62%, transparent);
}
.se-arrows { display: inline-flex; gap: var(--space-3); }
.se-hint { flex: 1 1 auto; margin: 0; }
.se-set { margin: 0; display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }

.se-actions { margin: 0; display: flex; justify-content: center; gap: var(--space-3); flex-wrap: wrap; }
.se-btn {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-btn);
  text-decoration: none; font-size: var(--text-ui); font-weight: var(--weight-bold);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.se-btn:hover, .se-btn:focus-visible { transform: translateY(-2px); }
.se-btn--go {
  background: linear-gradient(100deg, var(--lb-teal), var(--lb-purple));
  color: var(--color-ink-deep);
}
.se-btn--quiet {
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 22%, transparent);
  color: var(--color-ink-on-dark);
}
.se-btn-ico { width: var(--space-4); height: var(--space-4); fill: currentColor; }

/* The rows arrive after the query has been typed, which is the order it would
   happen in. Guarded on js-reveal so they are simply there without script. */
.js-reveal .se-row { opacity: 0; transform: translateY(6px); }
.js-reveal .se-results.is-in .se-row {
  opacity: 1; transform: none;
  transition: opacity var(--dur-base) var(--ease-linear),
              transform var(--dur-base) var(--ease-assemble);
  transition-delay: calc(var(--i) * var(--stagger-step));
}

@media (max-width: 900px) {
  .se { padding: var(--space-16) 0; }
  .se-h { font-size: var(--text-h1); }
  .se-key { display: none; }
  .se-hint { font-size: var(--text-fine); }
  .se-tile { width: var(--space-12); height: var(--space-12); }
}
@media (max-width: 560px) {
  .se-meta span + span { display: none; }
  .se-foot { gap: var(--space-3); }
  .se-set { font-size: var(--text-fine); }
  .se-tabs { gap: var(--space-4); overflow-x: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .se-panel::before { animation: none; }
  .se-caret { animation: none; }
  .se-tile::after { animation: none; }
  .js-reveal .se-row { opacity: 1; transform: none; }
  .ai-shot img { filter: none; transition: none; }
}

/* ---- entry types ---------------------------------------------------------
   Every entry carries one primary role and any number of alternates, from a
   closed set of thirteen. Closed is the point: it is what lets a page plan be
   checked before it is built, rather than discovered to be four Range sections
   in a row afterwards.

   The filter hides label and section together. The script copies each label's
   data-roles onto the section beneath it, so this is one rule over both
   instead of a pairing walk on every click. */
.lib-filter { margin: 0 0 var(--space-6); }
.lib-filter-h {
  margin: 0 0 var(--space-3); font-size: var(--text-caption);
  letter-spacing: var(--tracking-caption); text-transform: uppercase;
  font-weight: var(--weight-bold);
  color: color-mix(in srgb, var(--color-ink-on-dark) 56%, transparent);
}
.lib-filter-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.lib-f {
  display: inline-flex; align-items: baseline; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 18%, transparent);
  border-radius: var(--radius-pill);
  background: none; color: var(--color-ink-on-dark);
  font: inherit; font-size: var(--text-small); cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.lib-f b {
  font-variant-numeric: tabular-nums; font-size: var(--text-fine);
  font-weight: var(--weight-bold);
  color: color-mix(in srgb, var(--color-ink-on-dark) 52%, transparent);
}
.lib-f:hover, .lib-f:focus-visible {
  background: color-mix(in srgb, var(--color-ink-on-dark) 10%, transparent);
  border-color: color-mix(in srgb, var(--color-ink-on-dark) 38%, transparent);
}
.lib-f--on {
  background: var(--color-surface); color: var(--color-ink);
  border-color: var(--color-surface);
}
.lib-f--on b { color: var(--color-ink-muted); }

.lib-toc a { justify-content: flex-start; }
.lib-toc-role {
  margin-left: auto; padding-left: var(--space-3);
  font-size: var(--text-fine); white-space: nowrap;
  color: color-mix(in srgb, var(--color-ink-on-dark) 50%, transparent);
}

/* The use block sits in the description column, under the description. */
.lib-use { grid-column: 3; display: grid; gap: var(--space-2); margin-top: var(--space-3); }
.lib-use-h {
  margin: 0; font-size: var(--text-caption); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; font-weight: var(--weight-bold);
  color: var(--color-ink-muted);
}
.lib-roles { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.lib-role {
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
  border: 1px solid var(--color-rule-strong);
  font-size: var(--text-fine); color: var(--color-ink-muted);
}
/* The primary role is the one you scan for, so it is the only filled chip. */
.lib-role--is {
  border-color: var(--lb-teal-ink);
  background: color-mix(in srgb, var(--lb-teal) 14%, var(--color-surface));
  /* The hue carries the meaning, so it stays; the tint under it costs half a
     point of contrast, which the text pays back by sitting nearer the ink. */
  color: color-mix(in srgb, var(--lb-teal-ink) 74%, var(--color-ink-deep));
  font-weight: var(--weight-semibold);
}
.lib-once {
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--lb-gold) 22%, var(--color-surface));
  border: 1px solid var(--lb-gold-ink);
  font-size: var(--text-fine); font-weight: var(--weight-semibold);
  color: color-mix(in srgb, var(--lb-gold-ink) 74%, var(--color-ink-deep));
}
.lib-never {
  margin: 0; font-size: var(--text-fine); line-height: var(--leading-snug);
  color: var(--color-ink-muted);
}
.lib-never::before { content: "Never: "; font-weight: var(--weight-bold); color: var(--lb-red-ink); }

/* Filtering. Hiding is display:none rather than a height animation: the page is
   a catalogue and a reader filtering it wants the result, not a transition. */
.lib-filtering .lib-label[data-roles]:not(.is-match),
.lib-filtering section[data-roles]:not(.is-match),
.lib-filtering .lib-toc li[data-roles]:not(.is-match) { display: none; }

@media (max-width: 900px) {
  .lib-use { grid-column: 2; }
  /* The type is the reason to read the contents grid, so it wraps under the
     name rather than being dropped. */
  .lib-toc a { flex-wrap: wrap; }
  .lib-toc-role { flex-basis: 100%; margin-left: 0; padding-left: calc(var(--space-6) + var(--space-1)); }
}


/* The step grid's plain heading. These four rules were written on the Moodle
   plugin page and never made it back into the catalogue, so entry 08 rendered
   unstyled anywhere else it was used. */
.qa-head-plain {
  max-width: var(--container); margin: 0 auto var(--space-12);
  padding-inline: var(--gutter); text-align: center;
}
.qa-head-plain .qa-h { margin: 0 auto var(--space-3); }
.qa-head-plain .qa-lede { margin: 0 auto; max-width: var(--measure); }
.qa-ico path { fill: currentColor; }

/* Headings are one colour. The two-tone treatment muted the second half of
   every heading; the owner wants the whole line in the darker shade, so the
   quiet spans inherit the heading's own ink everywhere, on light and dark
   grounds alike. */
.ar-h-quiet, .bn-h-quiet, .cb-h-quiet, .cp-h-quiet, .dl-h-quiet, .fd-h-quiet,
.ig-h-quiet, .lh-h-quiet, .ql-h-quiet, .rs-h-quiet, .st-h-quiet, .uc-h-quiet,
.ww-h-quiet, .cw-h-quiet, .dr-h-quiet, .sd-h-quiet, .aw-h-quiet, .oa-h-quiet,
.tn-h-quiet, .se-h-quiet { color: inherit; }

/* ---- plan cards ----------------------------------------------------------
   A price list as a table is how it is audited, not how it is chosen: fifteen
   rows of small text with the buy button at the bottom. Five cards instead,
   one hue each, the four limits that actually decide the choice, and the
   button in each card next to the number it buys. */
.pl { background: var(--color-paper); padding: var(--space-12) 0 var(--space-10); }
.pl-in { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.pl-eyebrow { text-align: center; margin: 0 0 var(--space-4); }
.pl-h {
  margin: 0 0 var(--space-3); text-align: center;
  font-size: var(--text-h2); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-heading); color: var(--color-ink);
}
.pl-h-quiet { color: var(--color-ink-muted); }
.pl-lede {
  margin: 0 auto var(--space-8); max-width: 62ch; text-align: center;
  font-size: var(--text-body); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.pl-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-4); align-items: stretch;
}
.pl-card {
  position: relative; display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-6) var(--space-5) var(--space-5);
  border-radius: var(--radius-xl);
  border: 1px solid color-mix(in srgb, var(--pl-hue) 30%, var(--color-rule));
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--pl-hue) 12%, var(--color-surface)) 0%,
      var(--color-surface) 58%);
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-base) var(--ease-standard),
              box-shadow var(--dur-base) var(--ease-standard);
}
.pl-card:hover { transform: translateY(calc(var(--space-1) * -1)); box-shadow: var(--shadow-2); }
.pl-card--us {
  border-color: var(--pl-hue); box-shadow: var(--shadow-3);
  transform: translateY(calc(var(--space-2) * -1));
}
.pl-card--us:hover { transform: translateY(calc(var(--space-3) * -1)); }
.pl-badge {
  position: absolute; top: calc(var(--space-3) * -1); left: var(--space-5);
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
  background: var(--pl-hue); color: var(--color-ink-on-dark);
  font-size: var(--text-fine); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-caption); text-transform: uppercase;
}
.pl-name {
  margin: 0; font-size: var(--text-small); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-caption); text-transform: uppercase;
  color: var(--pl-ink);
}
.pl-price { margin: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.pl-price b {
  font-size: var(--text-h2); line-height: 1; letter-spacing: var(--tracking-heading);
  color: var(--color-ink); font-variant-numeric: tabular-nums;
}
.pl-under { font-size: var(--text-fine); color: var(--color-ink-muted); }
.pl-for {
  margin: 0; font-size: var(--text-small); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.pl-open {
  margin: 0; padding: var(--space-3) 0 0;
  border-top: 1px solid color-mix(in srgb, var(--pl-hue) 22%, var(--color-rule));
  display: flex; flex-direction: column; gap: var(--space-1);
  font-size: var(--text-small);
}
.pl-open b { color: var(--pl-ink); font-weight: var(--weight-bold); }
.pl-packs { color: var(--color-ink-muted); font-size: var(--text-fine); }
.pl-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.pl-line {
  display: grid; grid-template-columns: var(--space-5) 1fr; gap: var(--space-2);
  align-items: start; font-size: var(--text-fine); color: var(--color-ink);
}
.pl-ico { width: var(--space-4); height: var(--space-4); margin-top: 0.15em; }
.pl-ico path { fill: var(--pl-ink); }
.pl-cta { margin-top: auto; align-self: stretch; text-align: center; }

/* The three facts that used to be amber bands above and below the table. */
.pl-facts {
  list-style: none; margin: var(--space-8) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4);
}
.pl-fact {
  display: grid; grid-template-columns: var(--space-8) 1fr;
  grid-template-rows: auto auto; column-gap: var(--space-3);
  padding: var(--space-5); border-radius: var(--radius-lg);
  border: 1px solid var(--color-rule); background: var(--color-surface);
}
.pl-fico {
  grid-row: 1 / 3; width: var(--space-6); height: var(--space-6);
  margin-top: 0.1em;
}
.pl-fico path { fill: var(--lb-teal-ink); }
.pl-fact b { font-size: var(--text-small); font-weight: var(--weight-bold); color: var(--color-ink); }
.pl-fact span {
  font-size: var(--text-fine); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
@media (max-width: 1100px) {
  .pl-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pl-card--us { transform: none; }
}
@media (max-width: 820px) {
  .pl-grid, .pl-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .pl-grid, .pl-facts { grid-template-columns: minmax(0, 1fr); }
}

/* ---- the matrix, restyled ------------------------------------------------
   Same rows, without the grid of borders that made it read as a form: the
   head carries each plan's hue, the recommended column is tinted the whole
   way down, and a yes or a no is a mark rather than a word in a cell. */
.cp-table {
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--color-rule); background: var(--color-surface);
}
.cp-table thead th {
  background: color-mix(in srgb, var(--cp-hue, var(--color-ink)) 10%, var(--color-surface));
  color: var(--cp-hue, var(--color-ink));
  border-bottom: 2px solid color-mix(in srgb, var(--cp-hue, var(--color-rule)) 45%, transparent);
}
.cp-table tbody tr:nth-child(even) td,
.cp-table tbody tr:nth-child(even) th { background: var(--color-paper); }
.cp-cell { border-bottom: 0; }
.cp-table tbody th, .cp-table tbody td {
  border-bottom: 1px solid color-mix(in srgb, var(--color-rule) 60%, transparent);
}
.cp-flag {
  display: inline-flex; align-items: center; gap: var(--space-1);
  min-width: var(--space-10); padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-fine); font-weight: var(--weight-semibold);
}
.cp-flag-ico { width: var(--space-3); height: var(--space-3); flex: 0 0 auto; fill: currentColor; }
.cp-flag--yes {
  background: color-mix(in srgb, var(--lb-green) 18%, transparent);
  color: var(--lb-green-ink);
}
.cp-flag--no {
  background: color-mix(in srgb, var(--color-ink) 7%, transparent);
  color: var(--color-ink-muted);
}

/* ---- the framework band --------------------------------------------------
   The same marks as the footer, but this is the page a reader opens looking
   for them, so they are full colour and full size rather than greyed into a
   strip, and the sentence beside them says what a mark here does and does not
   mean. */
.fwb { background: var(--color-surface); padding: var(--space-12) 0; }
.fwb-in {
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
  text-align: center;
}
.fwb-eyebrow { margin: 0 0 var(--space-4); }
.fwb-h {
  /* Matches every other section heading site-wide (cp-h, uc-h, ww-h): this
     one was left on an older, smaller scale. */
  margin: 0 auto var(--space-3); max-width: 26ch;
  font-size: var(--text-display); line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display); font-weight: var(--weight-bold);
  color: var(--color-ink);
}
.fwb-h-quiet { color: var(--color-ink-muted); }
.fwb-lede {
  margin: 0 auto var(--space-8); max-width: 68ch;
  font-size: var(--text-body); line-height: var(--leading-normal);
  color: var(--color-ink-muted);
}
.fwb-marks {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: var(--space-5) var(--space-8);
}
.fwb-mark {
  display: grid; place-items: center;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-rule); border-radius: var(--radius-md);
  background: var(--color-paper);
  transition: box-shadow var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard);
}
.fwb-mark:hover { box-shadow: var(--shadow-2); transform: translateY(calc(var(--space-1) * -0.5)); }
.fwb-mark img { display: block; width: auto; height: var(--space-10); }
@media (max-width: 700px) {
  .fwb-marks { gap: var(--space-3) var(--space-4); }
  .fwb-mark img { height: var(--space-8); }
}

/* The orbit and the morph were written without an eyebrow, so the pill needs
   its own room in both rather than sitting on the headline. */
.oa-copy .sec-eyebrow { margin: 0 0 var(--space-5); }
.mh-copy .sec-eyebrow { margin: 0 0 var(--space-4); display: flex; justify-content: center; }

/* A capability tile that carries a real capture rather than a wireframe: the
   gradient figures are fine in the catalogue, and on a sector page a reader
   who pressed "explore" has to see the actual screens. */
.dc-fig--shot {
  padding: 0; overflow: hidden;
  background-color: color-mix(in srgb, var(--color-ink-on-dark) 6%, transparent);
}
.dc-fig--shot img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
  transition: transform var(--dur-base) var(--ease-standard);
}
.dc-fig--shot:hover img { transform: scale(1.03); }
.dc-card-h a { color: inherit; text-decoration: none; }
.dc-card-h a:hover { text-decoration: underline; }

/* ---- comparison tables, second pass -------------------------------------
   A table of fifteen unlabelled rows is read by counting. Named groups, a
   head that stays while the rows move, and the column being argued for lit
   the whole way down rather than tinted a shade off its neighbours. */
.cp-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.cp-table thead th {
  position: sticky; top: 0; z-index: 2;
  background: color-mix(in srgb, var(--cp-hue, var(--color-ink)) 12%, var(--color-surface));
}
.cp-table thead th.cp-corner { background: var(--color-surface); }
.cp-cap {
  display: block; margin: 0 auto var(--space-1); width: max-content;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--cp-hue, var(--lb-teal)); color: var(--color-ink-on-dark);
  font-size: var(--text-caption); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-caption); text-transform: uppercase;
}
/* A mark per column, in the column's own hue, and a mark per claim: the
   table stops reading as a grid of plain words. */
.cp-th-ico {
  display: grid; place-items: center;
  width: var(--space-8); height: var(--space-8);
  margin: 0 auto var(--space-2);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--cp-hue, var(--lb-teal)) 18%, var(--color-surface));
  color: var(--cp-hue, var(--lb-teal));
}
.cp-th-ico-svg { width: var(--space-4); height: var(--space-4); fill: currentColor; }
/* The icon and the claim's text sit in a wrapper of their own, inside the
   cell, rather than on the cell: a <th> that is itself a flex container
   stops taking part in the table's row layout, which is what carried the
   row's own height, its background and its bottom border along with it and
   left it floating loose from the row next to it. The cell stays a plain
   table-cell; only the row-icon wrapper flexes. */
.cp-claim-row {
  display: flex; align-items: center; gap: var(--space-3);
}
.cp-row-ico {
  display: grid; place-items: center; flex: 0 0 auto;
  width: var(--space-9); height: var(--space-9);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--lb-teal) 12%, var(--color-surface));
  color: var(--lb-teal-ink);
}
.cp-row-ico-svg { width: var(--space-4); height: var(--space-4); fill: currentColor; }
.cp-claim-t { min-width: 0; }
/* The recommended column, lit from the head to the button. */
.cp-us, .cp-cell--us {
  background: color-mix(in srgb, var(--lb-teal) 9%, var(--color-surface));
  box-shadow:
    inset 1px 0 0 color-mix(in srgb, var(--lb-teal) 40%, transparent),
    inset -1px 0 0 color-mix(in srgb, var(--lb-teal) 40%, transparent);
}
.cp-table tbody tr:nth-child(even) td.cp-cell--us {
  background: color-mix(in srgb, var(--lb-teal) 13%, var(--color-surface));
}
.cp-table tbody tr:hover td, .cp-table tbody tr:hover th {
  background: color-mix(in srgb, var(--lb-teal) 5%, var(--color-surface));
}
.cp-table tbody tr:hover td.cp-cell--us {
  background: color-mix(in srgb, var(--lb-teal) 16%, var(--color-surface));
}
/* A named group of rows, so a reader looking for one line knows which third
   of the table to look in. */
.cp-group th {
  text-align: left; padding: var(--space-5) var(--space-4) var(--space-2);
  background: var(--color-paper);
  font-size: var(--text-caption); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-caption); text-transform: uppercase;
  color: var(--color-ink-muted);
  border-bottom: 1px solid var(--color-rule);
}
.cp-table tbody tr.cp-group:hover th { background: var(--color-paper); }
.cp-claim { position: sticky; left: 0; z-index: 1; background: var(--color-surface); }
.cp-table tbody tr:nth-child(even) th.cp-claim { background: var(--color-paper); }

/* The answer block: the question a reader types, and the answer in one
   paragraph, above the cards that prove it. */
.pl-answer {
  max-width: var(--measure-lede); margin: 0 auto var(--space-8);
  padding: var(--space-5) var(--space-6);
  border: 1px solid color-mix(in srgb, var(--lb-teal) 24%, var(--color-rule));
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--lb-teal) 6%, var(--color-surface));
  box-shadow: var(--shadow-1);
}
.pl-answer-q {
  margin: 0 0 var(--space-2); font-size: var(--text-small);
  font-weight: var(--weight-bold); letter-spacing: var(--tracking-caption);
  text-transform: uppercase; color: var(--lb-teal-ink);
}
.pl-answer-a {
  margin: 0; font-size: var(--text-body); line-height: var(--leading-normal);
  color: var(--color-ink);
}
.pl-answer-a b { font-weight: var(--weight-bold); font-variant-numeric: tabular-nums; }

/* The flight path carrying whole screens: a dashboard is 16:9, so the panel
   is too, or every capture would be letterboxed inside a card-shaped box. */
.tn--wide {
  /* The band under the flight was mostly air. Shorter section, same type,
     same flight: the space below the animation closes up. */
  min-height: 78vh;
  padding-bottom: var(--space-12);
}
.tn--wide .tn-panel {
  aspect-ratio: 16 / 9;
  /* Bigger, not lower: the panel is absolutely placed on the flight band, so
     growing it grows the pictures without adding a pixel under the section. */
  width: calc(var(--space-32) * 2.5);
  margin: calc(var(--space-32) * -0.7) 0 0 calc(var(--space-32) * -1.25);
}
.tn--wide .tn-panel img { object-fit: cover; object-position: top center; }

/* The flight panel's mirrored floor: the same device as the rail's, so the
   two dark heroes read as one family. The panel stops clipping, the picture
   takes the frame styling itself, and the mirror hangs under it, upside
   down and fading out. */
.tn--wide .tn-panel {
  overflow: visible; border-radius: 0; background: transparent;
  box-shadow: none;
}
.tn--wide .tn-panel img {
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-3);
}
.tn--wide .tn-mirror {
  position: absolute; left: 0; top: calc(100% + var(--space-1));
  width: 100%; height: 100%;
  transform: scaleY(-1);
  opacity: 0.3; box-shadow: none;
  -webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent 55%);
  mask-image: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent 55%);
  pointer-events: none;
}

/* ---- the CTA pair --------------------------------------------------------
   Start free and Instant demo travel together: the reader who is not ready
   to start gets the demo, and neither ever stands alone in a hero. */
.oa-cta--quiet {
  background: transparent;
  border: 1px solid var(--color-rule-strong);
  color: var(--color-ink);
  margin-left: var(--space-3);
}
.oa-cta--quiet:hover { border-color: var(--color-ink-muted); }
.ca-btn--quiet {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--color-ink-on-dark) 40%, transparent);
  color: var(--color-ink-on-dark);
  margin-left: var(--space-3);
}
.ca-btn--quiet:hover {
  border-color: var(--color-ink-on-dark);
  background: color-mix(in srgb, var(--color-ink-on-dark) 8%, transparent);
}
.im-actions {
  margin: var(--space-6) 0 var(--space-4);
  display: flex; flex-wrap: wrap; gap: var(--space-3);
}
.dc-actions {
  margin: var(--space-6) 0 var(--space-2);
  display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: center;
}


.lh-cta--quiet {
  background: transparent;
  border: 1px solid var(--color-rule-strong);
  color: var(--color-ink);
  margin-left: var(--space-3);
}
.lh-cta--quiet:hover { border-color: var(--color-ink-muted); background: var(--color-surface); }

/* The alternating row's quiet partner: same shape as its Start free, the
   surface's own colours. */
.al-actions {
  margin: 0; display: flex; flex-wrap: wrap; gap: var(--space-3);
  align-self: flex-start;
}
.al-actions .al-btn { align-self: auto; margin: 0; }
.al-btn--quiet {
  background: transparent;
  border: 1px solid var(--color-rule-strong);
  color: var(--color-ink);
}
.al-btn--quiet:hover { border-color: var(--color-ink-muted); }

/* =====================================================================
   The QA sheet, LB-01 to LB-39. Each block is one finding.
   ===================================================================== */

/* LB-21, LB-28: a heading is one colour. The quiet span keeps its size and
   its line break and loses its second ink. */
.ar-h-quiet, .aw-h-quiet, .bn-h-quiet, .cb-h-quiet, .cp-h-quiet, .cw-h-quiet, .dl-h-quiet, .dr-h-quiet, .fd-h-quiet, .fwb-h-quiet, .ig-h-quiet, .lh-h-quiet, .oa-h-quiet, .pl-h-quiet, .ql-h-quiet, .rs-h-quiet, .sd-h-quiet, .se-h-quiet, .st-h-quiet, .tn-h-quiet, .uc-h-quiet, .ww-h-quiet { color: inherit; }

/* LB-04: on a phone the eyebrow pill centres, everywhere. */
@media (max-width: 760px) {
  p[class*="eyebrow"], .sec-eyebrow {
    display: flex; justify-content: center; text-align: center;
  }
  /* And the pill itself, for the heroes that place it without a wrapper. */
  .eyebrow { display: flex; width: max-content; max-width: 100%; margin-inline: auto; }
}

/* LB-05: a client logo with a white plate melts into the light card. */
.cs-tab img { mix-blend-mode: multiply; }

/* LB-06: the orbit's decorative bubbles land on the copy at phone width. */
@media (max-width: 760px) {
  .oa-field { display: none; }
}

/* LB-07: the flight band rode up into the headline. It sits lower, and the
   section keeps enough height for both at any viewport. */
.tn--wide { min-height: 84vh; }
.tn--wide .tn-panel { top: 58%; }

/* LB-24: on a phone the flight band is small, so the reserved gap under the
   headline was mostly air. */
@media (max-width: 760px) {
  .tn { min-height: 0; }
  .tn-gap { min-height: var(--space-10); }
  .tn-lede { margin-bottom: var(--space-6); }
}

/* LB-11, LB-27: the Instant demo button is the animated outline everywhere.
   On a dark ground the outline stays an outline instead of a white plate. */
.cx-cta-row .btn--gradient-quiet,
.im-actions .btn--gradient-quiet,
.dc-actions .btn--gradient-quiet,
.mm-copy .btn--gradient-quiet {
  --btn-quiet-fill: transparent;
  color: var(--color-ink-on-dark);
}

/* LB-17: a component card always shows its own four-sided outline, and never
   doubles up where the slot already draws a frame. */
img[src*="/components/"] {
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.ca-shot img[src*="/components/"], .ca-reflect img[src*="/components/"],
.mh-tile img[src*="/components/"], .cw-tile img[src*="/components/"],
.tn-panel img[src*="/components/"], .pd-card img[src*="/components/"],
.ai-shot img[src*="/components/"] {
  border: 0; background: transparent;
}

/* LB-18: media cells present their picture the same way in every row. */
.al-cell--media { box-shadow: none; }
.al-cell--media img { display: block; width: 100%; height: auto; }

/* LB-19, LB-30: the CTA pair keeps its gap when it stacks on a phone. */
.mm-copy > .btn + .btn { margin-left: var(--space-3); }
@media (max-width: 760px) {
  .mm-copy > .btn { width: 100%; justify-content: center; margin: 0 0 var(--space-3); }
  .mm-copy > .btn + .btn { margin-left: 0; }
  .cx-cta-row { flex-direction: column; align-items: stretch; }
  .cx-cta-row .btn { width: 100%; justify-content: center; text-align: center; }
  .im-actions, .dc-actions { flex-direction: column; align-items: stretch; }
  .im-actions .btn, .dc-actions .btn { width: 100%; justify-content: center; text-align: center; }
  .lh-cta-row { width: 100%; }
  .lh-cta { width: 100%; justify-content: center; }
  .lh-cta--quiet { margin-left: 0; }
}

/* LB-20: the answer block runs the same width as the five cards under it. */
.pl-answer { max-width: none; }

/* LB-22: the matrix continues the cards above it. Each column wears its
   plan's wash the whole way down, not only in the head. */
.cp-table td[data-plan] {
  background: color-mix(in srgb, var(--cp-hue, transparent) 0%, var(--color-surface));
}
#plan-table td[data-plan="free"]       { background: color-mix(in srgb, var(--lb-green) 5%, var(--color-surface)); }
#plan-table td[data-plan="pro"]        { background: color-mix(in srgb, var(--lb-blue) 5%, var(--color-surface)); }
#plan-table td[data-plan="business"]   { background: color-mix(in srgb, var(--lb-teal) 10%, var(--color-surface)); }
#plan-table td[data-plan="scale"]      { background: color-mix(in srgb, var(--lb-purple) 5%, var(--color-surface)); }
#plan-table td[data-plan="enterprise"] { background: color-mix(in srgb, var(--lb-magenta) 5%, var(--color-surface)); }

/* LB-25: the comparison table scrolls inside its own box on a phone and the
   claim column stops eating half the screen. */
@media (max-width: 760px) {
  .cp-table { min-width: 38rem; }
  .cp-corner, .cp-claim { width: 44%; }
  .cp-claim { font-size: var(--text-small); }
}

/* LB-29: below phone width a card grid is one column, not two slivers. */
@media (max-width: 560px) {
  .uc-grid, .uc-grid.uc-grid--four { grid-template-columns: minmax(0, 1fr); }
}

/* LB-31: a linked card heading reads as a heading, not as a hyperlink. */
.cb-h a { color: inherit; text-decoration: none; }
.cb-h a:hover, .cb-h a:focus-visible { text-decoration: underline; }

/* LB-10: the questions block everywhere wears the homepage's card look. */
.ql-list { gap: var(--space-4); align-items: start; }
/* LB-10. The mark that says a row opens, and the half turn when it does.
   The rule was here from the start; the pages had never printed the mark. */
.ql-q { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.ql-qt { min-width: 0; }
.ql-row {
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  padding: 0 var(--space-5);
  transition: border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
.ql-row[open] { border-color: var(--color-rule-strong); box-shadow: var(--shadow-2); }
.ql-row .ql-q { padding-block: var(--space-4); }

/* LB-34, LB-35: the morph pill's action is the brand button with white text,
   docked or not, and its dot stays white. */
.mh-pill-cta {
  /* The docked pill's action is the same ask as everywhere else, so it wears
     the same drifting gradient rather than a two-hue one of its own. */
  background:
    linear-gradient(100deg, var(--lb-teal), var(--lb-blue), var(--lb-purple),
      var(--lb-magenta), var(--lb-gold), var(--lb-teal));
  background-size: 300% 100%;
  animation: eyebrow-drift var(--dur-ambient) linear infinite;
  color: var(--color-ink-on-dark);
}
.mh-pill-cta .mh-dot { background: var(--color-ink-on-dark); }
.mh.is-nav .mh-pill-cta {
  background:
    linear-gradient(100deg, var(--lb-teal), var(--lb-blue), var(--lb-purple),
      var(--lb-magenta), var(--lb-gold), var(--lb-teal));
  background-size: 300% 100%;
  color: var(--color-ink-on-dark);
}
.mh.is-nav .mh-dot { background: var(--color-ink-on-dark); }

/* LB-36: on a phone the docked pill keeps only its action; the menu lives in
   the sheet, and the pill no longer runs off the screen. */
@media (max-width: 760px) {
  .mh-nav { display: none; }
}

/* LB-37 reworked: the hero is centred, not left-hung. Eyebrow, heading, lede,
   CTA row and fine print all sit on the page axis; the lede keeps its measure
   so a centred paragraph stays readable. */
.lh-in { text-align: center; }
.lh-h { max-width: none; margin-inline: auto; }
.lh-lede { max-width: var(--measure-lede); margin-inline: auto; }
.lh-cta-row { justify-content: center; }

/* LB-39: the shapes explorer stacks cleanly on a phone: copy first at its
   own height, the picture under it, nothing squeezed into a sliver. */
@media (max-width: 1000px) {
  .sx--edge .sx-panel-in { display: block; padding: var(--space-6); }
  .sx--edge .sx-side { display: block; padding: 0; }
  .sx--edge .sx-side .sx-panel-top { padding: 0 0 var(--space-3); }
  .sx--edge .sx-shot {
    display: block; margin-top: var(--space-5); padding: var(--space-4);
    border: 1px solid var(--color-rule); border-radius: var(--radius-lg);
    border-left: 1px solid var(--color-rule);
  }
  .sx--edge .sx-shot img { width: 100%; height: auto; }
}

/* LB-07, LB-24 on a phone: every flight hero is copy first and a flight
   strip second, never a backdrop behind the words. */
@media (max-width: 760px) {
  .tn { display: flex; flex-direction: column; min-height: 0;
        padding-top: var(--space-16); }
  .tn .tn-copy { order: 1; position: static; }
  .tn .tn-stage { position: relative; inset: auto; order: 2;
                  height: 10rem; overflow: hidden; margin-top: var(--space-4); }
  .tn .tn-panel { top: 45%; width: 40vw; margin: -14vw 0 0 -20vw;
                  aspect-ratio: 4 / 3; }
  .tn--wide .tn-panel { aspect-ratio: 16 / 9; margin: -11vw 0 0 -20vw; }
  .tn .tn-gap { display: none; }
  .tn .tn-lede { margin-bottom: var(--space-5); }
}

/* =====================================================================
   The ask, one shape everywhere: the homepage hero's pair.
   Start free carries the drifting gradient fill, Instant demo the drifting
   gradient border, and both take the same width so neither reads as the
   lesser option.
   ===================================================================== */
.cta-free, .cta-demo { min-width: 11rem; justify-content: center; text-align: center; }
.cta-pair {
  /* The pair carries its own gap. Relying on the container's meant the Log
     in Monday band, which is a plain block, showed the two buttons touching. */
  display: inline-flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-4); vertical-align: middle;
}
/* The row a pair sits in, whatever the section calls it. */
.hero-cta, .cx-cta-row, .im-actions, .dc-actions, .al-actions, .lh-cta-row,
.mm-copy, .oa-copy, .ca-actions, .tn-copy {
  --cta-gap: var(--space-4);
}
/* the pair's own wrapper owns the spacing; no margins to fight */

/* On a dark ground the outline stays an outline. The fill layer is what
   hides the border gradient everywhere except the border, so it has to be
   the ground's own colour: made transparent, the gradient shows through the
   whole button and the outline reads as a fill, which is exactly what was
   reported. The closing panel paints its own hue, so it takes that. */
.im .cta-demo, .tn .cta-demo, .dc .cta-demo, .mm .cta-demo, .rh .cta-demo,
.gh .cta-demo, .ww .cta-demo, .pd .cta-demo, .dl .cta-demo, .se .cta-demo,
.ca .cta-demo {
  --btn-quiet-fill: var(--color-ink-deep);
  color: var(--color-ink-on-dark);
}
.cx .cta-demo {
  /* Opaque, like every other ground. A translucent plate lets the border
     gradient through the middle, which is precisely the fault reported: the
     outline read as a filled teal-blue button on the mesh. */
  --btn-quiet-fill: var(--color-ink-deep);
  color: var(--color-ink-on-dark);
}
/* Start free is the gradient on every ground, so a section that used to
   paint its own primary button stops repainting it. */
.cx-cta.cta-free, .ww-cta.cta-free, .gh-cta.cta-free, .rh-btn.cta-free,
.oa-cta.cta-free, .lh-cta.cta-free, .ca-btn.cta-free, .al-btn.cta-free,
.se-btn.cta-free {
  background:
    linear-gradient(100deg, var(--lb-teal), var(--lb-blue), var(--lb-purple),
      var(--lb-magenta), var(--lb-gold), var(--lb-teal)) border-box;
  background-size: 300% 100%;
  animation: eyebrow-drift var(--dur-ambient) linear infinite;
  border: 0; color: var(--color-ink-on-dark);
}
@media (prefers-reduced-motion: reduce) {
  .cta-free, .cta-demo { animation: none; }
}
/* On a phone the pair stacks and both run the full width. */
@media (max-width: 760px) {
  .cta-free, .cta-demo { width: 100%; min-width: 0; }
  .cta-pair { display: flex; width: 100%; flex-direction: column; }
}

/* The plan card's ask: the same gradient, sized to the card rather than to
   the hero, because five of them share a row. */
.pl-cta.btn--spectrum { color: var(--color-ink-on-dark); }


/* ---------------------------------------------------------------------------
   The header at 320px.

   320 is the narrowest viewport WCAG 1.4.10 asks a page to reflow into, and
   the header did not fit: the "Start free" action and the toggle together
   pushed the document 21px wider than the screen on the home page and 52px on
   the demo page, so EVERY page scrolled sideways. A horizontal scrollbar on a
   marketing site reads as broken before anybody reads a word.

   Below 360px the header keeps the one thing it must have - the way into the
   navigation - and lets the CTA go. It is one tap away inside the sheet, and
   a CTA that forces the page off screen converts nobody.
--------------------------------------------------------------------------- */
@media (max-width: 359px) {
  .header-actions .btn--primary,
  .header-actions .btn--cta { display: none; }

  .header-inner { gap: var(--space-2); }

  /* Belt and braces: the header must never be the thing that widens the
     document, whatever ends up inside it. */
  .site-header, .header-inner { max-width: 100%; overflow-x: clip; }
}

/* And nothing anywhere may push the document sideways at the reflow width. */
@media (max-width: 400px) {
  html, body { overflow-x: hidden; }
}
