/* Alikely — one stylesheet for every page. The brand values and the rules for using them
   are documented in ../README.md (Design profile); contrast pairs in ../docs/contrast.md.
   Self-hosted fonts, no images in the layout, no JavaScript. */

@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 600 700;
  font-display: swap;
  src: url('/fonts/plus-jakarta-sans-latin.woff2') format('woff2');
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/inter-latin.woff2') format('woff2');
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Metric-matched fallback while Inter loads, so the swap does not shift the layout. */
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  ascent-override: 90.44%;
  descent-override: 22.52%;
  line-gap-override: 0%;
  size-adjust: 107.12%;
}

:root {
  /* Brand palette — the only colours on the site. */
  --alk-cream: #f6e5d3;
  --alk-apricot: #e09c77;
  --alk-sand: #d4a78a;
  --alk-clay: #ab704e;
  --alk-ember: #d4651f;
  --alk-brick: #8c2315;
  --alk-oxblood: #5a170f;
  --alk-saffron: #f9c841;
  --alk-brass: #d2b789;
  --alk-ink: #2b1410;
  --alk-ember-deep: #b4501a;
  --alk-white: #ffffff;

  /* Field status — only ever on white, always with a symbol and a word. */
  --status-match: #2f7d4f;
  --status-partial: #9a6412;
  --status-miss: #b42318;

  /* Roles (light). */
  --page: var(--alk-cream);
  --text: var(--alk-ink);
  --heading: var(--alk-brick);
  --link: var(--alk-brick);
  --line: var(--alk-brass);
  --card: var(--alk-white);
  --card-text: var(--alk-ink);
  --card-heading: var(--alk-brick);
  --hero: var(--alk-apricot);
  --hero-text: var(--alk-ink);
  --hero-heading: var(--alk-brick);
  --band: var(--alk-sand);
  --band-heading: var(--alk-ink);
  --dark: var(--alk-brick);
  --dark-text: var(--alk-cream);
  --footer: var(--alk-oxblood);
  --footer-text: var(--alk-cream);
  --focus: var(--alk-brick);
  --secondary-text: var(--alk-brick);
  --secondary-line: var(--alk-brick);
  --button-edge: var(--alk-brick);
  --seam-line-same: transparent;
  --secondary-hover-fill: var(--alk-cream);
  --secondary-hover-text: var(--alk-ink);
  --card-shadow: 0 14px 28px -16px rgb(90 23 15 / 0.45);

  --font-heading: 'Plus Jakarta Sans', 'Inter Fallback', Arial, sans-serif;
  --font-body: 'Inter', 'Inter Fallback', Arial, sans-serif;
  --radius: 16px;
  --measure: 68ch;
  --width: 1080px;
  --gutter: 16px;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --page: var(--alk-oxblood);
    --text: var(--alk-cream);
    --heading: var(--alk-cream);
    --link: var(--alk-cream);
    --card: transparent;
    --card-text: var(--alk-cream);
    --card-heading: var(--alk-cream);
    --hero: var(--alk-brick);
    --hero-text: var(--alk-cream);
    --hero-heading: var(--alk-cream);
    --band: var(--alk-oxblood);
    --band-heading: var(--alk-cream);
    /* A brick ring disappears on oxblood and brick; dark mode uses cream. */
    --focus: var(--alk-cream);
    --secondary-text: var(--alk-cream);
    --secondary-line: var(--alk-cream);
    --button-edge: var(--alk-cream);
    --seam-line-same: var(--alk-brass);
    --secondary-hover-fill: var(--alk-oxblood);
    --secondary-hover-text: var(--alk-cream);
    --card-shadow: 0 14px 28px -14px rgb(43 20 16 / 0.85);
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--text);
  font: 400 1.0625rem/1.6 var(--font-body);
}

a {
  color: var(--link);
  text-underline-offset: 0.18em;
}

a:hover {
  text-decoration-thickness: 2px;
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 4px;
}

h1,
h2,
h3 {
  margin: 0 0 0.5em;
  color: var(--heading);
  font-family: var(--font-heading);
  line-height: 1.12;
  letter-spacing: -0.02em;
}

h1 {
  font-size: clamp(2.3rem, 7.5vw, 3.9rem);
  font-weight: 700;
  letter-spacing: -0.03em;
}

h2 {
  font-size: clamp(1.65rem, 4.6vw, 2.35rem);
  font-weight: 700;
}

h3 {
  font-size: 1.18rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

p {
  margin: 0 0 1em;
}

.skip {
  position: absolute;
  left: -9999px;
}

.skip:focus {
  left: var(--gutter);
  top: var(--gutter);
  z-index: 10;
  padding: 0.5em 0.9em;
  background: var(--alk-white);
  color: var(--alk-ink);
}

.wrap {
  max-width: var(--width);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* ---- header: sits on the hero's colour field ---- */

.site-header {
  background: var(--hero);
  color: var(--hero-text);
}

.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 68px;
}

/* Wordmark: lower-case "alikely" in Plus Jakarta Sans, after the provisional symbol. */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  color: var(--hero-heading);
  font-family: var(--font-heading);
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  text-decoration: none;
}

/* Provisional symbol, not a logo: a large cream circle with a small saffron circle. */
.symbol {
  position: relative;
  display: inline-block;
  flex: none;
  width: 0.95em;
  height: 0.95em;
  border-radius: 50%;
  background: var(--alk-cream);
}

.symbol::after {
  position: absolute;
  right: -0.14em;
  top: -0.06em;
  width: 0.38em;
  height: 0.38em;
  border-radius: 50%;
  background: var(--alk-saffron);
  content: '';
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  font-size: 0.97rem;
}

.site-nav a:not(.button) {
  color: var(--hero-text);
  text-decoration: none;
}

.site-nav a:not(.button):hover,
.site-nav a[aria-current='page'] {
  text-decoration: underline;
}

.nav-optional {
  display: none;
}

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

/* Primary: ember fill with ink text (4.69:1) — never white on ember (3.7:1). Ember against
   apricot is only 1.6:1, so a 2 px edge carries the button's outline on every ground: brick in
   light mode (3.9:1 on apricot, 7.2:1 on cream), cream in dark mode (7.2:1 on brick). */
.button {
  display: inline-block;
  padding: 0.72em 1.2em;
  border-radius: 999px;
  background: var(--alk-ember);
  box-shadow: inset 0 0 0 2px var(--button-edge);
  color: var(--alk-ink);
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
}

/* Hover and keyboard focus warm the fill to saffron (ink on saffron 11.0:1); the 2 px edge
   keeps outlining the button on every ground. Colour changes stay under reduced motion; the
   pop (scale) lives in the motion block at the end of this file. */
.button:hover,
.button:focus-visible {
  background: var(--alk-saffron);
}

.button.small {
  padding: 0.52em 1em;
  font-size: 0.93rem;
}

.button.secondary {
  background: transparent;
  color: var(--secondary-text);
  box-shadow: inset 0 0 0 2px var(--secondary-line);
}

.button.secondary:hover,
.button.secondary:focus-visible {
  background: var(--secondary-hover-fill);
  color: var(--secondary-hover-text);
}

.button,
.button.secondary {
  transition:
    background-color 200ms ease-out,
    color 200ms ease-out;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 1.6rem 0 1rem;
}

/* ---- sections: one colour field each ---- */

.section {
  padding: 3.5rem 0;
}

.section-hero {
  padding: 2.5rem 0 3.5rem;
  background: var(--hero);
  color: var(--hero-text);
}

.section-hero h1 {
  color: var(--hero-heading);
}

/* On apricot, small brick text is only 3.9:1, so the secondary button's label is ink there. */
.section-hero .button.secondary {
  color: var(--hero-text);
}

.section-hero .button.secondary:hover,
.section-hero .button.secondary:focus-visible {
  color: var(--secondary-hover-text);
}

.section-band {
  background: var(--band);
}

.section-band h2 {
  color: var(--band-heading);
}

.section-dark {
  background: var(--dark);
  color: var(--dark-text);
}

.section-dark h2,
.section-dark .lede {
  color: var(--dark-text);
}

.eyebrow {
  display: inline-block;
  margin-bottom: 1rem;
  padding: 0.28em 0.8em;
  border-radius: 999px;
  background: var(--alk-cream);
  color: var(--alk-ink);
  font-size: 0.88rem;
  font-weight: 600;
}

.lede {
  max-width: var(--measure);
  font-size: clamp(1.1rem, 2.6vw, 1.28rem);
}

.prose {
  max-width: var(--measure);
}

.status-note {
  font-size: 0.95rem;
}

/* The one saffron highlight on a page. An underline, not a fill: the text keeps its own
   contrast against the field in both light (brick on apricot) and dark (cream on brick). */
.highlight {
  text-decoration: underline;
  text-decoration-color: var(--alk-saffron);
  text-decoration-thickness: 0.14em;
  text-decoration-skip-ink: none;
  text-underline-offset: 0.1em;
}

/* ---- hero ---- */

.hero {
  position: relative;
  display: grid;
  gap: 2.5rem;
}

.hero > :not(.hero-orbit) {
  position: relative;
  z-index: 1;
}

.section-hero {
  overflow: hidden;
  overflow: clip;
}

/* The provisional symbol, large and quiet, behind the top-right of the hero. Hidden unless
   motion is allowed (see the motion block). Decoration: aria-hidden, no pointer events. */
.hero-orbit {
  display: none;
}

.orbit-big {
  fill: var(--alk-cream);
  opacity: 0.55;
}

.orbit-small {
  fill: var(--alk-saffron);
  opacity: 0.9;
}

.orbit-spin {
  transform-box: view-box;
  transform-origin: 200px 200px;
}

@media (prefers-color-scheme: dark) {
  .orbit-big {
    opacity: 0.16;
  }

  .orbit-small {
    opacity: 0.7;
  }
}

/* ---- concept illustration (not a screenshot): white, so the status colours hold ---- */

.concept {
  margin: 0;
  padding: 1.15rem;
  border-radius: var(--radius);
  background: var(--alk-white);
  color: var(--alk-ink);
}

.concept-head {
  margin: 0 0 0.8rem;
  font-size: 0.9rem;
}

.concept ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

.concept li + li {
  margin-top: 0.8rem;
  padding-top: 0.8rem;
  border-top: 1px solid var(--alk-brass);
}

.match-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  font-weight: 600;
}

.score {
  position: relative;
  display: inline-block;
  font-family: var(--font-heading);
  font-size: 1.2rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

.fields {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.9rem;
  margin: 0.45rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.88rem;
  font-weight: 500;
}

.concept .fields li,
.concept .fields li + li {
  margin: 0;
  padding: 0;
  border: 0;
}

.f {
  position: relative;
}

.f-label::before {
  margin-right: 0.35em;
  font-weight: 700;
}

.f-match {
  color: var(--status-match);
}

.f-match .f-label::before {
  content: '✓';
}

.f-partial {
  color: var(--status-partial);
}

.f-partial .f-label::before {
  content: '≈';
}

.f-miss {
  color: var(--status-miss);
}

.f-miss .f-label::before {
  content: '✕';
}

.concept figcaption {
  margin-top: 0.9rem;
  font-size: 0.82rem;
}

/* ---- grids and cards ---- */

.steps,
.cases,
.facts {
  display: grid;
  gap: 1rem;
  margin: 1.75rem 0 0;
  padding: 0;
  list-style: none;
}

.card {
  position: relative;
  padding: 1.3rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--card-text);
}

@media (hover: hover) {
  .card:hover {
    border-color: var(--alk-ember);
  }

  .card:hover .step-n,
  .card:hover .role::before {
    background: var(--alk-saffron);
  }
}

/* Keyboard focus inside a card (the "What we do keep" link) is at least as visible as hover. */
.card:focus-within {
  border-color: var(--alk-ember);
}

.card:focus-within .step-n,
.card:focus-within .role::before {
  background: var(--alk-saffron);
}

.card,
.step-n,
.role::before {
  transition:
    border-color 200ms ease-out,
    background-color 200ms ease-out;
}

.card h3 {
  color: var(--card-heading);
}

.card p:last-child {
  margin-bottom: 0;
}

.step-n {
  display: inline-grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  margin-bottom: 0.8rem;
  border-radius: 50%;
  background: var(--alk-ember);
  color: var(--alk-ink);
  font-family: var(--font-heading);
  font-weight: 700;
}

.nowrap {
  white-space: nowrap;
}

.cases-note {
  max-width: var(--measure);
  margin: 1.5rem 0 0;
}

.role {
  position: relative;
  display: block;
  margin-bottom: 0.35rem;
  color: var(--card-heading);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.role::before,
.role::after {
  display: inline-block;
  width: 0.55em;
  height: 0.55em;
  margin-right: 0.5em;
  border-radius: 50%;
  background: var(--alk-ember);
  content: '';
  vertical-align: 0.08em;
}

/* A saffron twin laid exactly over the ember dot; its opacity is driven by scroll in the
   motion block. Hidden otherwise. */
.role::after {
  position: absolute;
  top: 50%;
  left: 0;
  margin-top: -0.32em;
  background: var(--alk-saffron);
  opacity: 0;
}

/* ---- text pages ---- */

.page-head {
  padding: 2rem 0 2.5rem;
  background: var(--hero);
  color: var(--hero-text);
}

.page-head h1 {
  color: var(--hero-heading);
}

.page-head .lede {
  margin-bottom: 0;
}

.doc {
  max-width: var(--measure);
  padding: 2.5rem 0 3.5rem;
}

.doc h2 {
  margin-top: 2.2rem;
  font-size: 1.45rem;
}

.doc h3 {
  margin-top: 1.5rem;
}

.doc ul {
  padding-left: 1.2rem;
}

.doc li {
  margin-bottom: 0.4rem;
}

.table-scroll {
  overflow-x: auto;
  margin: 0 0 1.2rem;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}

th,
td {
  padding: 0.55rem 0.6rem;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}

th {
  font-weight: 600;
}

/* Drafts: the saffron banner is that page's one highlight. */
.draft-banner {
  max-width: var(--measure);
  margin: 1.25rem 0 0;
  padding: 0.9rem 1rem;
  border-radius: 12px;
  background: var(--alk-saffron);
  color: var(--alk-ink);
}

.draft-banner strong {
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.version {
  font-size: 0.92rem;
}

.mail {
  font-family: var(--font-heading);
  font-size: 1.35rem;
  font-weight: 700;
}

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

.site-footer {
  padding: 2.25rem 0 2.75rem;
  background: var(--footer);
  color: var(--footer-text);
  font-size: 0.92rem;
}

.site-footer a {
  color: var(--footer-text);
}

.site-footer .wordmark {
  color: var(--footer-text);
  font-size: 1.3rem;
}

.site-footer nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  margin: 1rem 0;
  padding-top: 1rem;
  border-top: 1px solid var(--alk-brass);
}

.site-footer p {
  margin: 0;
}

.site-footer :focus-visible {
  outline-color: var(--alk-cream);
}

/* ---- seams: soft transitions between colour fields ----
   A seam is an empty, aria-hidden block between two fields, holding one inline SVG:
     <div class="seam seam-a seam-<from>-<to>" aria-hidden="true"><svg viewBox="0 0 1440 64"
       preserveAspectRatio="none" focusable="false"><path class="seam-tint" d="…"/>
       <path class="seam-fill" d="…"/><path class="seam-line" d="…"/></svg></div>
   The block's background is the upper field; the paths draw the lower field rising into it
   as a low wave, with a translucent second wave (the tint) for a short blend. The colours are
   the section role variables, so the seam follows light and dark mode by itself. Inline SVG
   is not fetched, so the CSP's img-src does not apply; fills come from this stylesheet, so
   style-src 'self' holds. The height is fixed here, so a seam never shifts the layout.
   Nothing is ever placed on a seam (check-site.py enforces that it is empty). */

.seam {
  --seam-from: var(--page);
  --seam-to: var(--page);
  --seam-line: transparent;
  display: block;
  height: clamp(28px, 4.4vw, 64px);
  margin-bottom: -1px; /* no hairline of the upper colour between seam and field */
  background: var(--seam-from);
}

.seam svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* The seam under the hero: about 1.6× the amplitude of the others, and twice as wide as the
   page — two periods of a wave that tiles at 1440 units — so it can drift sideways in a
   seamless loop (motion block). Static, it shows exactly the first period. */
.seam-hero {
  height: clamp(44px, 7.6vw, 112px);
  overflow: hidden;
}

.seam-hero svg {
  width: 200%;
}

.seam-flip svg {
  transform: scaleX(-1);
}

.seam-fill {
  fill: var(--seam-to);
}

.seam-tint {
  fill: var(--seam-to);
  opacity: 0.45;
}

.seam-line {
  fill: none;
  stroke: var(--seam-line);
  stroke-width: 1.5px;
  vector-effect: non-scaling-stroke;
}

/* Which fields meet. Same-colour neighbours (in dark mode several fields are oxblood) get a
   thin brass wave instead of an invisible fill — the curved successor of the old straight
   brass rule. */
.seam-hero-page {
  --seam-from: var(--hero);
  --seam-to: var(--page);
}

.seam-page-band {
  --seam-from: var(--page);
  --seam-to: var(--band);
  --seam-line: var(--seam-line-same);
}

.seam-band-page {
  --seam-from: var(--band);
  --seam-to: var(--page);
  --seam-line: var(--seam-line-same);
}

.seam-page-dark {
  --seam-from: var(--page);
  --seam-to: var(--dark);
}

.seam-dark-page {
  --seam-from: var(--dark);
  --seam-to: var(--page);
}

.seam-page-footer {
  --seam-from: var(--page);
  --seam-to: var(--footer);
  --seam-line: var(--seam-line-same);
}

/* ---- wider screens ---- */

@media (min-width: 640px) {
  :root {
    --gutter: 24px;
  }

  .nav-optional {
    display: inline;
  }

  .steps,
  .facts {
    grid-template-columns: repeat(3, 1fr);
  }

  .cases {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .section {
    padding: 5rem 0;
  }

  .section-hero {
    padding: 3.5rem 0 5rem;
  }

  .hero {
    grid-template-columns: 1.15fr 1fr;
    align-items: center;
  }

  .cases {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ==========================================================================================
   MOTION — everything that moves lives below, inside prefers-reduced-motion: no-preference.
   Under "reduce" none of it applies: the page is the static page, with the hover colours
   above. Only transform-family properties (transform, translate, scale) and opacity are
   animated, plus the registered --score integer for the counter. Nothing hides content
   unless its animation runs: initial states come only from animation fill-mode backwards.
   check-site.py fails if an animation or a non-colour transition appears outside this block.
   ========================================================================================== */

@property --score {
  syntax: '<integer>';
  inherits: true;
  initial-value: 0;
}

/* Two-digit counter, padded with a figure space (U+2007: the width of a tabular digit), so
   the score's text keeps one width from 0 to 92 and never shifts while it counts. */
@counter-style alk-two {
  system: extends decimal;
  pad: 2 '\2007';
}

@keyframes alk-rise {
  from {
    opacity: 0;
    translate: 0 14px;
  }
}

@keyframes alk-row-in {
  from {
    opacity: 0;
    translate: 0 -8px;
  }
}

@keyframes alk-chip-on {
  0% {
    opacity: 0;
    scale: 0.85;
  }

  60% {
    opacity: 1;
    scale: 1.08;
  }

  100% {
    opacity: 1;
    scale: 1;
  }
}

@keyframes alk-ghost-out {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

@keyframes alk-count {
  from {
    --score: 0;
  }
}

@keyframes alk-draw {
  from {
    scale: 0 1;
  }
}

@keyframes alk-orbit {
  to {
    transform: rotate(360deg);
  }
}

@keyframes alk-drift {
  to {
    transform: translateX(-1440px);
  }
}

@keyframes alk-bounce {
  0% {
    translate: 0 0;
  }

  45% {
    translate: 0 -5px;
  }

  75% {
    translate: 0 1px;
  }

  100% {
    translate: 0 0;
  }
}

@keyframes alk-dot-shift {
  0%,
  100% {
    opacity: 0;
  }

  50% {
    opacity: 0.95;
  }
}

@media (prefers-reduced-motion: no-preference) {
  /* ---- micro-interactions (pointer devices; keyboard focus gets the same) ---- */

  .button,
  .button.secondary {
    transition:
      background-color 200ms ease-out,
      color 200ms ease-out,
      transform 180ms ease-out;
  }

  .button:focus-visible {
    transform: scale(1.03);
  }

  .card,
  .card .step-n,
  .card .role::before,
  .card .role::after {
    transition:
      border-color 200ms ease-out,
      background-color 200ms ease-out,
      transform 220ms ease-out;
  }

  /* The soft shadow is its own layer, so only its opacity animates. */
  .card::after {
    position: absolute;
    inset: 0;
    border-radius: inherit;
    box-shadow: var(--card-shadow);
    content: '';
    opacity: 0;
    pointer-events: none;
    transition: opacity 220ms ease-out;
  }

  .card:focus-within {
    transform: translateY(-3px);
  }

  .card:focus-within::after {
    opacity: 1;
  }

  .card:focus-within .role::before,
  .card:focus-within .role::after {
    transform: scale(1.6);
  }

  /* Header links: an underline drawn in from the left replaces the plain hover underline. */
  .site-nav a:not(.button) {
    position: relative;
  }

  .site-nav a:not(.button):hover,
  .site-nav a[aria-current='page'] {
    text-decoration: none;
  }

  .site-nav a:not(.button)::after {
    position: absolute;
    right: 0;
    bottom: -0.2em;
    left: 0;
    height: 2px;
    background: currentColor;
    content: '';
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 220ms ease-out;
  }

  .site-nav a:not(.button):hover::after,
  .site-nav a:not(.button):focus-visible::after,
  .site-nav a[aria-current='page']::after {
    transform: scaleX(1);
  }

  @media (hover: hover) {
    .button:hover {
      transform: scale(1.03);
    }

    .card:hover {
      transform: translateY(-3px);
    }

    .card:hover::after {
      opacity: 1;
    }

    .card:hover .step-n {
      animation: alk-bounce 300ms ease-out;
    }

    .card:hover .role::before,
    .card:hover .role::after {
      transform: scale(1.6);
    }
  }

  /* ---- the hero moment: runs once on load, ~2.7 s ---- */

  /* "best reference": the saffron underline is drawn per word, after the heading. */
  .highlight {
    text-decoration: none;
  }

  .hl-word {
    position: relative;
    display: inline-block;
  }

  .hl-word::after {
    position: absolute;
    right: 0;
    bottom: 0.02em;
    left: 0;
    height: 0.14em;
    background: var(--alk-saffron);
    content: '';
    transform-origin: left;
    animation: alk-draw 420ms ease-out 450ms backwards;
  }

  .hl-word + .hl-word::after {
    animation-delay: 750ms;
  }

  /* Rows fade in from the top, one after the other. */
  .concept ol > li {
    animation: alk-row-in 450ms ease-out backwards;
  }

  .concept ol > li:nth-child(1) {
    animation-delay: 150ms;
  }

  .concept ol > li:nth-child(2) {
    animation-delay: 450ms;
  }

  .concept ol > li:nth-child(3) {
    animation-delay: 750ms;
  }

  /* Chips start neutral (the ghost) and light up one at a time in their status colour. */
  .f-label {
    display: inline-block;
    animation: alk-chip-on 340ms ease-out backwards;
  }

  /* The neutral copy is generated content with empty alt text, so neither a screen reader
     nor a contrast checker meets the half-faded text. */
  .f::after {
    position: absolute;
    top: 0;
    left: 0;
    color: var(--alk-clay);
    content: '•  ' attr(data-ghost) / '';
    font-weight: 500;
    white-space: pre;
    opacity: 0;
    pointer-events: none;
    animation: alk-ghost-out 340ms ease-out backwards;
  }

  .concept ol > li:nth-child(1) .f:nth-child(1) > *,
  .concept ol > li:nth-child(1) .f:nth-child(1)::after {
    animation-delay: 550ms;
  }

  .concept ol > li:nth-child(1) .f:nth-child(2) > *,
  .concept ol > li:nth-child(1) .f:nth-child(2)::after {
    animation-delay: 770ms;
  }

  .concept ol > li:nth-child(1) .f:nth-child(3) > *,
  .concept ol > li:nth-child(1) .f:nth-child(3)::after {
    animation-delay: 990ms;
  }

  .concept ol > li:nth-child(2) .f:nth-child(1) > *,
  .concept ol > li:nth-child(2) .f:nth-child(1)::after {
    animation-delay: 1210ms;
  }

  .concept ol > li:nth-child(2) .f:nth-child(2) > *,
  .concept ol > li:nth-child(2) .f:nth-child(2)::after {
    animation-delay: 1430ms;
  }

  .concept ol > li:nth-child(2) .f:nth-child(3) > *,
  .concept ol > li:nth-child(2) .f:nth-child(3)::after {
    animation-delay: 1650ms;
  }

  .concept ol > li:nth-child(3) .f:nth-child(1) > *,
  .concept ol > li:nth-child(3) .f:nth-child(1)::after {
    animation-delay: 1870ms;
  }

  .concept ol > li:nth-child(3) .f:nth-child(2) > *,
  .concept ol > li:nth-child(3) .f:nth-child(2)::after {
    animation-delay: 2090ms;
  }

  .concept ol > li:nth-child(3) .f:nth-child(3) > *,
  .concept ol > li:nth-child(3) .f:nth-child(3)::after {
    animation-delay: 2310ms;
  }

  /* Scores count up from 0. Gated on transition-behavior: every engine that has it also has
     @property (Chrome 117 / Firefox 129 / Safari 17.4 vs 85 / 128 / 16.4), so without
     @property the real number is simply shown. The number in the HTML stays the accessible
     text; the counter is an empty-alt pseudo-element laid over it. */
  @supports (transition-behavior: allow-discrete) {
    .score-92 {
      --score: 92;
    }

    .score-74 {
      --score: 74;
    }

    .score-51 {
      --score: 51;
    }

    .score-num {
      opacity: 0;
    }

    /* Spans the whole score box (right-aligned) so its box never moves while the digits
       change: no layout shift. */
    .score::after {
      position: absolute;
      top: 0;
      right: 0;
      left: 0;
      text-align: right;
      content: counter(alk-score, alk-two) / '';
      counter-reset: alk-score var(--score);
    }

    .concept ol > li .score {
      animation: alk-count 1600ms cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
    }

    .concept ol > li:nth-child(1) .score {
      animation-delay: 350ms;
    }

    .concept ol > li:nth-child(2) .score {
      animation-delay: 650ms;
    }

    .concept ol > li:nth-child(3) .score {
      animation-delay: 950ms;
    }
  }

  /* The symbol exists to move, so it appears only when motion is allowed (and only from 900 px,
     where it sits behind the card column, never behind the hero text). */
  @media (min-width: 900px) {
    .hero-orbit {
      position: absolute;
      top: -50px;
      right: -170px;
      z-index: 0;
      display: block;
      width: 340px;
      height: 340px;
      pointer-events: none;
    }
  }

  /* The small ball circles the big one: one turn every 18 s. Decoration, never on text. */
  .orbit-spin {
    animation: alk-orbit 18s linear infinite;
  }

  /* The seam under the hero drifts sideways, its two layers at different speeds. */
  .seam-hero .seam-fill {
    animation: alk-drift 26s linear infinite;
  }

  .seam-hero .seam-tint {
    animation: alk-drift 38s linear infinite;
  }

  /* ---- scroll: headings and card rows rise into view (scroll-driven, where supported) ---- */

  @supports (animation-timeline: view()) {
    .section h2,
    .section .lede,
    .section .card,
    .cases-note {
      animation: alk-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% entry 55%;
    }

    .section .card:nth-child(3n + 2) {
      animation-range: entry 12% entry 62%;
    }

    .section .card:nth-child(3n) {
      animation-range: entry 19% entry 69%;
    }

    /* While "Your objects. Your properties." scrolls through, the dots warm from ember to
       saffron and back. */
    .section-cases {
      view-timeline: --alk-cases block;
    }

    .section-cases .role::after {
      animation: alk-dot-shift linear both;
      animation-timeline: --alk-cases;
      animation-range: cover 10% cover 90%;
    }
  }
}
