/* ==========================================================================
   Fonts: self-hosted latin subsets, SIL Open Font License (see assets/fonts/).
   ========================================================================== */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("assets/fonts/inter-latin-var.woff2") format("woff2");
}

@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("assets/fonts/space-grotesk-latin-var.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/ibm-plex-mono-latin-400.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/ibm-plex-mono-latin-400-italic.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("assets/fonts/ibm-plex-mono-latin-500.woff2") format("woff2");
}

/* ==========================================================================
   Design tokens
   Colors: Figma "Branding Guide" variable collection.
   Type: Figma text styles and the inspected text layers.
   ========================================================================== */
:root {
  color-scheme: light;

  /* color/primary (Light mode) */
  --brand-background: #f1eee5;
  --brand-text-dark: #292927;
  --brand-text-light: rgb(41 41 39 / 0.75);
  --brand-text-flipped: #f1eee5;
  --brand-text-flipped-light: rgb(241 238 229 / 0.75);
  --brand-accent-light: #d7882b;
  --brand-accent-dark: #a65e1e;

  /* color/signal (same in both modes) */
  --brand-mint: #45e8a2;
  --brand-magenta: #ed24c7;
  --brand-blue: #4c76ff;
  --brand-red: #e6533c;
  --brand-amber: #ff9a00;
  --brand-yellow: #ffe44d;

  /* color/supporting (same in both modes) */
  --brand-stone: #d8d3c9;
  --brand-slate: #6b6b63;
  --brand-silver: #9a9992;
  --brand-charcoal: #1a1a1a;

  /* Roles. Dark panels stay charcoal in both themes, so their ink is fixed. */
  --color-bg: var(--brand-background);
  --color-ink: var(--brand-text-dark);
  --color-ink-muted: var(--brand-text-light);
  --color-rule: var(--brand-silver);
  --color-slot: var(--brand-stone);
  --color-panel: var(--brand-charcoal);
  --color-panel-ink: #f1eee5;
  --color-panel-ink-muted: rgb(241 238 229 / 0.75);
  --color-panel-rule: var(--brand-slate);
  --color-tag-primary: var(--brand-accent-light);
  /* Text on the orange link buttons is near-black: cream on #d7882b is only 2.4:1,
     near-black is 6.2:1 (WCAG AA 4.5:1). Pre-launch audit, ADR 0012. */
  --color-tag-primary-ink: #1a1a1a;
  /* The accent-dark tag background, darkened slightly so cream text passes AA:
     #a65e1e gives 4.27:1, #9c581c gives 4.73:1. */
  --color-tag: #9c581c;
  --color-tag-ink: #f1eee5;
  --color-focus: var(--brand-blue);
  /* Current-position cues (nav underline, active rail dot) and the other rail dots.
     Each keeps 3:1 against the page and the charcoal banner (WCAG 1.4.11, ADR 0012). */
  --color-marker: var(--brand-accent-dark);
  --color-rail: var(--brand-slate);

  /* Families */
  --font-display: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-sub: "Space Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;

  /* Sizes: Figma desktop values are the maximums. */
  --text-name: clamp(2.25rem, 1rem + 5vw, 4rem);         /* h1: Inter Bold 64 */
  --text-subtitle: clamp(1.125rem, 0.9rem + 1vw, 1.5rem); /* SubTitle: 24 */
  --text-header: clamp(1.5rem, 1.1rem + 1.5vw, 2rem);     /* Header: 32 */
  --text-subheader: clamp(1rem, 0.85rem + 0.6vw, 1.25rem); /* SubHeader: 20 */
  --text-banner: clamp(3rem, 1rem + 5vw, 4.5rem);
  --text-body: 1rem;
  --text-small: 0.875rem;
  --text-xs: 0.75rem;

  /* Space and shape */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;
  --space-16: 4rem;
  --space-24: 6rem;
  --radius-sm: 4px;
  --radius-md: 16px;
  --content-max: 72rem; /* 1152px, the Figma desktop content width */
  --gutter: clamp(1rem, 5vw, 4rem);
}

/* Figma Branding Guide, Dark mode. Applied by the toggle or the system setting. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --brand-background: #292927;
  --brand-text-dark: #f1eee5;
  --brand-text-light: rgb(241 238 229 / 0.75);
  --brand-text-flipped: #292927;
  --brand-text-flipped-light: rgb(41 41 39 / 0.75);
  --color-slot: #3a3a37;
  --color-marker: var(--brand-accent-light);
  --color-rail: var(--brand-silver);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --brand-background: #292927;
    --brand-text-dark: #f1eee5;
    --brand-text-light: rgb(241 238 229 / 0.75);
    --brand-text-flipped: #292927;
    --brand-text-flipped-light: rgb(41 41 39 / 0.75);
    --color-slot: #3a3a37;
    --color-marker: var(--brand-accent-light);
    --color-rail: var(--brand-silver);
  }
}

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

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

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-mono);
  font-size: var(--text-body);
  line-height: 1.5;
}

/* Page-wide drafting grid (ADR 0008), fixed behind the page; dark panels cover it.
   While motion is on it drifts one cell diagonally every 40 seconds, which loops
   seamlessly, and it moves slower than the page while scrolling. The two use the
   separate `translate` and `transform` properties, so they combine. */
body::before {
  content: "";
  position: fixed;
  top: -48px;
  left: 0;
  right: -48px;
  z-index: -1;
  height: calc(100vh + 192px);
  background:
    linear-gradient(var(--color-rule) 1px, transparent 1px) 0 0 / 48px 48px,
    linear-gradient(90deg, var(--color-rule) 1px, transparent 1px) 0 0 / 48px 48px;
  opacity: 0.14;
  pointer-events: none;
}

html.js-ready:not(.motion-off) body::before {
  animation: grid-ambient 40s linear infinite;
}

@keyframes grid-ambient {
  from {
    translate: 0 0;
  }
  to {
    translate: -48px 48px;
  }
}

@media screen and (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    html:not(.motion-off) body::before {
      animation: grid-drift linear both;
      animation-timeline: scroll(root);
    }

    html.js-ready:not(.motion-off) body::before {
      animation: grid-drift linear both, grid-ambient 40s linear infinite;
      animation-timeline: scroll(root), auto;
    }
  }
}

@keyframes grid-drift {
  to {
    transform: translateY(-96px);
  }
}

img,
svg,
canvas {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
}

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

h1,
h2,
h3 {
  margin: 0;
  line-height: 1.15;
}

p {
  margin: 0;
}

button {
  font: inherit;
  color: inherit;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -3rem;
  padding: var(--space-2) var(--space-4);
  background: var(--color-panel);
  color: var(--color-panel-ink);
  z-index: 10;
}

.skip-link:focus {
  top: var(--space-4);
}

/* Reading progress (ADR 0009): visibility of system status. It reports where the
   reader is, so it is a status display rather than decorative motion: it follows
   scrolling under reduced motion and with motion off too. Browsers without scroll
   timelines simply don't show it. */
.read-progress {
  display: none;
}

@supports (animation-timeline: scroll()) {
  .read-progress {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 20;
    display: block;
    height: 3px;
    background: linear-gradient(90deg, var(--brand-red), var(--brand-amber), var(--brand-yellow));
    transform-origin: 0 50%;
    pointer-events: none;
    animation: read-progress linear both;
    animation-timeline: scroll(root);
  }
}

@keyframes read-progress {
  from {
    transform: scaleX(0);
  }
}

/* Print (pre-launch audit, ADR 0012). Browsers drop page backgrounds when printing,
   so cream text (dark theme, dark panels) would vanish on white paper. Print is
   always black on white, decoration and controls are left out, and outside links
   show their address, so a printed About & CV page stays useful. The roles are set
   with a high-specificity selector to beat both theme blocks. */
@media print {
  :root:root:root {
    color-scheme: light;
    --color-bg: #fff;
    --color-ink: #000;
    --color-ink-muted: #333;
    --color-rule: #999;
    --color-slot: #eee;
    --color-panel: #fff;
    --color-panel-ink: #000;
    --color-panel-ink-muted: #333;
    --color-panel-rule: #999;
    /* Signal colours (value headings, labels) print as plain black. */
    --brand-mint: #000;
    --brand-magenta: #000;
    --brand-blue: #000;
    --brand-red: #000;
    --brand-amber: #000;
    --brand-yellow: #000;
    /* Tags lose their coloured background on paper, so they print as outlines. */
    --color-tag: #fff;
    --color-tag-ink: #000;
    --color-tag-primary: #fff;
    --color-tag-primary-ink: #000;
  }

  .tag {
    border: 1px solid #000;
  }

  body::before,
  .read-progress,
  .ambient-scene,
  .skip-link,
  .site-header,
  .section-rail,
  .section-bar,
  .banner,
  .model,
  .toggle,
  .action,
  .barcode-strip,
  .ticks,
  .footer-motion {
    display: none !important;
  }

  :is(.prose, .entry-list, .socials, .card-body) a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.85em;
    word-break: break-all;
  }

  .card,
  .entry-list li {
    break-inside: avoid;
  }
}

.container {
  width: 100%;
  max-width: calc(var(--content-max) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Empty image slot shown until real artwork is supplied. */
.slot {
  display: grid;
  place-items: center;
  aspect-ratio: 544 / 432;
  border-radius: var(--radius-md);
  background: var(--color-slot);
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
}

/* Small bordered control: theme and motion toggles, copy and share actions. */
.toggle,
.action {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid currentColor;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}

/* Toggles need js/main.js, so hide them until it has run.
   Canvases stay empty without JavaScript, so hide them in that case too. */
html:not(.js-ready) .toggle,
html:not(.ui-ready) .action,
html:not(.ui-ready) .section-rail,
html:not(.ui-ready) .section-bar,
html:not(.js) canvas[data-scene],
html:not(.js) .model {
  display: none;
}

/* Copy feedback, in the reference boards' status-chip style: a mint dot carries
   the state, the text stays ink for contrast. */
.copy-status {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.copy-status:not(:empty)::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brand-mint);
}

/* Reserves its line so feedback never shifts the layout. */
.copy-status {
  min-height: 1.6em;
  margin-top: var(--space-4);
}

/* Wayfinding: a dot rail on wide screens, a sticky section bar on phones. */
.section-rail,
.section-bar {
  display: none;
}

@media (min-width: 1280px) {
  .section-rail {
    position: fixed;
    top: 50%;
    left: 1rem;
    z-index: 5;
    display: flex;
    flex-direction: column;
    transform: translateY(-50%);
  }

  /* 6px dots inside 24px targets (WCAG 2.5.8). Their colours keep 3:1 against
     the page in either theme and the charcoal banner (tested, ADR 0012). */
  .section-rail a {
    width: 24px;
    height: 24px;
    padding: 9px;
    background: var(--color-rail);
    background-clip: content-box;
    border-radius: 12px;
    transition: height 0.2s;
  }

  .section-rail a[aria-current="true"] {
    height: 40px;
    background-color: var(--color-marker);
  }
}

@media (max-width: 799px) {
  .section-bar {
    position: fixed;
    inset: 0 0 auto;
    z-index: 10;
    display: flex;
    justify-content: space-between;
    padding: var(--space-2) var(--gutter);
    border-bottom: 1px solid var(--color-rule);
    background: var(--color-bg);
    font-size: var(--text-xs);
    letter-spacing: 0.12em;
    text-decoration: none;
    text-transform: uppercase;
    visibility: hidden;
    transform: translateY(-100%);
    transition: transform 0.2s, visibility 0.2s;
  }

  .section-bar.is-visible {
    visibility: visible;
    transform: none;
  }

  .section-bar-menu {
    color: var(--color-ink-muted);
  }

  /* Anchors and keyboard focus land below the bar, not under it. */
  html {
    scroll-padding-top: 3rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .section-rail a,
  .section-bar {
    transition: none;
  }
}

/* ==========================================================================
   Header and nav. Mobile: stacked list, as in the Figma mobile frame.
   ========================================================================== */
.site-header {
  border-bottom: 1px solid var(--color-rule);
}

.site-header .container {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding-block: var(--space-6);
}

.monogram {
  font-weight: 500;
  text-decoration: none;
}

.nav-list,
.socials {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Phones: a compact two-column grid keeps the hero on the first screen. */
.nav-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2) var(--space-4);
}

.nav-index {
  margin-right: var(--space-1);
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
}

.nav-list a[aria-current] {
  text-decoration: underline;
  text-decoration-color: var(--color-marker);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.35em;
}

.nav-list a {
  text-decoration: none;
}

.nav-list a:hover {
  text-decoration: underline;
}

.header-tools {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.socials {
  gap: var(--space-3);
}

/* Outline icons as in Figma (ADR 0017), each a 24px target at least (WCAG 2.5.8);
   the link's aria-label names it. */
.socials a {
  display: inline-flex;
  padding: 2px;
  border-radius: 4px;
  text-decoration: none;
}

.socials .icon {
  display: block;
}

.socials a:hover {
  color: var(--color-marker);
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  padding-block: var(--space-16);
  text-align: center;
}

.hero h1 {
  font-family: var(--font-display);
  font-size: var(--text-name);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.025em;
}

.hero p {
  max-width: 36rem;
  margin: var(--space-4) auto 0;
  font-family: var(--font-sub);
  font-size: var(--text-subtitle);
  line-height: 1.3;
}

/* ==========================================================================
   Dark panels
   ========================================================================== */
.panel {
  background: var(--color-panel);
  color: var(--color-panel-ink);
}

/* Banner: full-height console on the left, wireframe scene, the "Bridging"
   head with a cube behind it, and technical labels. */
.banner {
  position: relative;
  overflow: hidden;
}

.banner .container {
  position: relative;
  display: grid;
  grid-template-areas:
    "head"
    "art"
    "term"
    "labels";
  gap: var(--space-6);
  padding-block: var(--space-8);
}

/* Dot-matrix columns behind the right side, after the "Illusion" reference. */
.dot-field {
  position: absolute;
  inset: 0 0 0 auto;
  width: 38%;
  /* The dimming lives in the dot color, not opacity, so the scan band can be brighter. */
  background: radial-gradient(circle, color-mix(in srgb, var(--brand-mint) 18%, transparent) 1px, transparent 1.6px) 0 0 / 10px 10px;
  -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 10px, transparent 10px 50px),
    linear-gradient(90deg, transparent, #000 60%);
  mask-image: repeating-linear-gradient(90deg, #000 0 10px, transparent 10px 50px),
    linear-gradient(90deg, transparent, #000 60%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
  pointer-events: none;
}

/* A brighter band of the same dots scans up and down the columns while motion is on. */
.dot-field::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle, color-mix(in srgb, var(--brand-mint) 55%, transparent) 1px, transparent 1.6px) 0 0 / 10px 10px;
  -webkit-mask: linear-gradient(transparent 40%, #000 50%, transparent 60%) 0 100% / 100% 300% no-repeat;
  mask: linear-gradient(transparent 40%, #000 50%, transparent 60%) 0 100% / 100% 300% no-repeat;
  opacity: 0;
}

/* mask-position repaints, so the shimmer runs only on wider screens. */
@media (min-width: 800px) {
  html.js-ready:not(.motion-off) .dot-field::before {
    opacity: 1;
    animation: dot-scan 7s ease-in-out infinite alternate;
  }
}

@keyframes dot-scan {
  from {
    -webkit-mask-position: 0 100%;
    mask-position: 0 100%;
  }
  to {
    -webkit-mask-position: 0 0;
    mask-position: 0 0;
  }
}

/* On phones the banner stacks, so the dots sit only behind the headline, fading out
   before the wireframe, console, and labels below it. */
@media (max-width: 799px) {
  .dot-field {
    inset: 0 0 auto auto;
    width: 70%;
    height: 17rem;
    -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 10px, transparent 10px 50px),
      linear-gradient(90deg, transparent, #000 60%), linear-gradient(#000 55%, transparent);
    mask-image: repeating-linear-gradient(90deg, #000 0 10px, transparent 10px 50px),
      linear-gradient(90deg, transparent, #000 60%), linear-gradient(#000 55%, transparent);
  }
}

.terminal {
  grid-area: term;
  min-width: 0;
  height: 18em;
  margin: 0;
  overflow: hidden;
  font-size: 0.6875rem;
  line-height: 1.5;
  color: var(--color-panel-ink-muted);
  white-space: pre;
}

.terminal .cursor {
  display: inline-block;
  width: 0.6em;
  height: 1em;
  vertical-align: text-bottom;
  background: var(--brand-mint);
  animation: blink 1s steps(1) infinite;
}

.motion-off .terminal .cursor {
  animation: none;
}

@keyframes blink {
  50% {
    opacity: 0;
  }
}

.banner-scene {
  grid-area: art;
  width: 100%;
  aspect-ratio: 4 / 3;
  color: var(--brand-mint);
}

/* The banner container is isolated, so the desktop canvas's z-index -1 (ADR 0014)
   stays above the banner's own background. */
.banner .container {
  isolation: isolate;
}

.banner-head {
  grid-area: head;
  position: relative;
  padding-block: var(--space-4);
}

.cube-scene {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  color: var(--brand-mint);
  opacity: 0.35;
}

.banner-words,
.norm-label {
  position: relative;
}

.banner-words {
  font-size: var(--text-banner);
  line-height: 1.05;
}

.banner-words span:not(.visually-hidden) {
  display: block;
}

.banner-words .w-tech,
.banner-words .w-eng,
.banner-words .w-art {
  font-size: 0.45em;
}

.banner-words .w-tech {
  color: var(--brand-red);
}

.banner-words .w-eng {
  color: var(--brand-amber);
}

.banner-words .w-art {
  color: var(--brand-yellow);
}

/* Norm label with a gradient bar, after the "10918-1" mark in the references. */
.norm-label {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.norm-code {
  padding: 0 var(--space-1);
  border: 1px solid var(--color-panel-ink);
  font-size: var(--text-small);
  line-height: 1.4;
}

.norm-bar {
  width: min(10rem, 40%);
  height: 6px;
  background: linear-gradient(90deg, var(--brand-red), var(--brand-amber), var(--brand-yellow));
}

.tech-labels {
  grid-area: labels;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
  font-size: 0.625rem;
  line-height: 1.4;
  text-transform: uppercase;
}

/* Figma's "Technicals labels" (ADR 0017): one bordered grid of cells in mint, the
   globe in its own cell, the system lines beside it, and Batch over Serial on the
   right. Cells share their borders, so the grid reads as one instrument plate. */
.tech-grid {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  border: 1px solid var(--color-panel-rule);
  color: var(--brand-mint);
}

.tech-cell {
  padding: var(--space-2) var(--space-3);
}

.tech-cell strong {
  font-weight: 400;
}

.tech-globe {
  display: grid;
  place-items: center;
  border-right: 1px solid var(--color-panel-rule);
}

.tech-core {
  display: grid;
  align-content: center;
}

.tech-side {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-top: 1px solid var(--color-panel-rule);
}

.tech-side .tech-cell {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  white-space: nowrap;
}

.tech-side .tech-cell + .tech-cell {
  border-left: 1px solid var(--color-panel-rule);
}

.globe-scene {
  flex: none;
  width: 2.25rem;
  min-width: 2.25rem; /* an auto-sized grid cell would otherwise shrink it to nothing */
  height: 2.25rem;
  color: var(--brand-mint);
}

.tech-labels .toggle {
  color: var(--brand-mint);
}

/* Values strip */
.values .container {
  display: grid;
  gap: var(--space-8);
  padding-block: var(--space-12) var(--space-6);
}

/* Figma's ID strip (ADR 0017): one bordered row of cells, each with a small label
   above its value and a crossed-box marker in its corner, ending in the barcode. */
.barcode-strip {
  grid-column: 1 / -1;
  justify-self: start;
  display: flex;
  max-width: 100%;
  border: 1px solid var(--color-panel-rule);
  font-size: 0.75rem;
  line-height: 1.3;
  text-transform: uppercase;
}

.strip-cell {
  position: relative;
  display: grid;
  flex: 1 1 auto;
  align-content: center;
  min-width: 0;
  padding: var(--space-1) var(--space-4) var(--space-2) var(--space-2);
}

.strip-cell + .strip-cell {
  border-left: 1px solid var(--color-panel-rule);
}

.strip-cell strong {
  color: var(--color-panel-ink-muted);
  font-size: 0.625rem;
  font-weight: 400;
}

.strip-cell::after {
  content: "";
  position: absolute;
  right: 5px;
  bottom: 4px;
  width: 7px;
  height: 7px;
  border: 1px solid var(--color-panel-rule);
  background: linear-gradient(45deg, transparent 44%, var(--color-panel-rule) 44% 56%, transparent 56%);
}

.strip-mark {
  min-width: 0;
  padding-right: var(--space-2);
  font-size: 0.875rem;
  letter-spacing: 0.04em;
}

.strip-mark::after,
.barcode::after {
  content: none;
}

.barcode {
  flex: 0 0 4rem;
  padding: 5px 6px;
  background: repeating-linear-gradient(
    90deg,
    var(--color-panel-ink) 0 1px,
    transparent 1px 3px,
    var(--color-panel-ink) 3px 5px,
    transparent 5px 6px,
    var(--color-panel-ink) 6px 7px,
    transparent 7px 10px
  );
  background-clip: content-box;
}

.ticks {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  color: var(--brand-slate);
  font-size: 0.625rem;
  letter-spacing: 0.6em;
}

.value {
  border-top: 1px solid var(--color-panel-rule);
  padding-top: var(--space-4);
}

.value h3 {
  margin-bottom: var(--space-3);
  font-size: var(--text-header);
  font-weight: 500;
}

.value p {
  font-size: var(--text-subheader);
}

.barcode-strip + .value h3 {
  color: var(--brand-mint);
}

.barcode-strip + .value + .value h3 {
  color: var(--brand-magenta);
}

.barcode-strip + .value + .value + .value h3 {
  color: var(--brand-blue);
}

/* ==========================================================================
   Scroll reveals (ADR 0008). Each section's blocks slide up and fade in as they
   enter the screen, driven by scrolling itself, so no script is needed. The
   values arrive one after another, and their headings wipe on like a scan line.
   Browsers without view timelines, reduced motion, the motion toggle, and print
   all get the page as is: nothing is hidden outside these rules. Every range ends
   by "entry 100%", which any block with content below it always reaches.
   ========================================================================== */
@media screen and (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    html:not(.motion-off) :is(.section > .container > :not(.project-list), .project-list > .card, .values .value) {
      animation: reveal linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 70%;
    }

    html:not(.motion-off) .barcode-strip + .value + .value {
      animation-range: entry 20% entry 90%;
    }

    html:not(.motion-off) .barcode-strip + .value + .value + .value {
      animation-range: entry 40% entry 100%;
    }

    html:not(.motion-off) .section > .container > .section-title {
      animation: reveal linear both, rule-draw linear both;
      animation-timeline: view(), view();
      animation-range: entry 0% entry 70%, entry 10% entry 100%;
    }

    html:not(.motion-off) .values .value h3 {
      animation: wipe linear both;
      animation-timeline: view();
      animation-range: entry 40% entry 100%;
    }
  }
}

@keyframes reveal {
  from {
    opacity: 0;
    transform: translateY(2rem);
  }
}

@keyframes rule-draw {
  from {
    background-size: 0 1px;
  }
}

@keyframes wipe {
  from {
    clip-path: inset(0 100% 0 0);
  }
}

/* ==========================================================================
   Content sections and cards
   ========================================================================== */
.section {
  padding-block: var(--space-12);
}

/* Numbered mono section labels: "01 / ABOUT". */
/* Section markers (ADR 0009): wayfinding in the FIG-label style. Each title ends in
   a registration mark, and its rule draws in during the reveal. (A counted section
   code was tried and dropped: unnumbered panels made it disagree with the "04 /"
   index beside it, and a wrong number is worse than none.) */
.section-title {
  position: relative;
  margin-bottom: var(--space-8);
  padding-bottom: var(--space-3);
  background: linear-gradient(var(--color-rule), var(--color-rule)) left bottom / 100% 1px no-repeat;
  font-family: var(--font-mono);
  font-size: var(--text-small);
  font-weight: 500;
  color: var(--color-ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.16em;
}

.section-index {
  margin-right: var(--space-2);
  color: var(--color-ink);
}

/* The line between the home page's sections (ADR 0017): the rule the numbered
   headings used to carry, without their text. A border, so high-contrast mode and
   print keep it. */
.section-rule {
  margin: 0 0 var(--space-8);
  border: 0;
  border-top: 1px solid var(--color-rule);
}

/* High-contrast mode and print drop backgrounds, so the rule falls back to a border. */
@media (forced-colors: active), print {
  .section-title {
    border-bottom: 1px solid;
  }
}

.section-title::after {
  content: "+";
  content: "+" / "";
  position: absolute;
  right: 0;
  bottom: -0.55em;
  color: var(--color-ink-muted);
  font-size: var(--text-body);
  line-height: 1;
}

.card {
  display: grid;
  gap: var(--space-6);
  align-items: center;
}

.card + .card {
  margin-top: var(--space-16);
}

.card h3 {
  margin-bottom: var(--space-4);
  font-size: var(--text-header);
  font-weight: 500;
}

.card-media {
  margin: 0;
}

.card-media figcaption {
  margin-top: var(--space-2);
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* Corner brackets and a crosshair mark around media, after the references. */
.frame {
  position: relative;
  padding: var(--space-3);
  --bracket: linear-gradient(var(--brand-silver), var(--brand-silver));
  background:
    var(--bracket) top left / 14px 1px no-repeat,
    var(--bracket) top left / 1px 14px no-repeat,
    var(--bracket) bottom right / 14px 1px no-repeat,
    var(--bracket) bottom right / 1px 14px no-repeat,
    var(--bracket) bottom left / 0 1px no-repeat,
    var(--bracket) bottom left / 1px 0 no-repeat,
    var(--bracket) top right / 0 1px no-repeat,
    var(--bracket) top right / 1px 0 no-repeat;
  transition: background-size 0.25s ease-out;
}

/* Affordance and feedback (ADR 0009): pointing at or tabbing into a card closes its
   frame brackets in, so it is clear the card responds. */
.card:hover .frame,
.card:focus-within .frame {
  background-size: 28px 1px, 1px 28px, 28px 1px, 1px 28px, 28px 1px, 1px 28px, 28px 1px, 1px 28px;
}

.frame::after {
  content: "+";
  content: "+" / "";
  position: absolute;
  top: -0.6em;
  right: -0.25em;
  color: var(--brand-silver);
  font-size: var(--text-body);
  line-height: 1;
}

.card-media img,
.card-media video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-md);
}

.card-media video {
  background: var(--color-slot);
}

.card-body > p {
  color: var(--color-ink-muted);
}

.card-body > p + p {
  margin-top: var(--space-3);
}

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-6) 0 0;
  padding: 0;
  list-style: none;
}

.tag {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--color-tag);
  color: var(--color-tag-ink);
  font-family: var(--font-display);
  font-size: var(--text-small);
  line-height: 1.4;
}

.tag-link {
  padding: var(--space-2) var(--space-3);
  background: var(--color-tag-primary);
  color: var(--color-tag-primary-ink);
  text-decoration: none;
}

/* Decorative arrow; the empty alt text keeps screen readers from announcing it. */
.tag-link::before {
  content: "\2192\00a0";
  content: "\2192\00a0" / "";
  display: inline-block;
  transition: transform 0.2s ease-out;
}

.tag-link:hover {
  text-decoration: underline;
}

/* The arrow nudges forward on hover or keyboard focus: this goes somewhere. */
.tag-link:hover::before,
.tag-link:focus-visible::before {
  transform: translateX(3px);
}

.contact-list {
  display: grid;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ==========================================================================
   Subpages: About & CV, R&D, posts, 404 (ADR 0004)
   ========================================================================== */
.page-hero {
  padding-block: var(--space-16) var(--space-8);
  border-bottom: 1px solid var(--color-rule);
}

.page-hero h1,
.post h1 {
  font-family: var(--font-display);
  font-size: var(--text-name);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.025em;
}

.eyebrow {
  margin-bottom: var(--space-4);
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.lede {
  max-width: 40rem;
  margin-top: var(--space-4);
  font-family: var(--font-sub);
  font-size: var(--text-subtitle);
  line-height: 1.35;
}

/* Long-form text: a readable measure and quiet structure. */
.prose {
  max-width: 68ch;
}

.prose > * + * {
  margin-top: var(--space-4);
}

.prose h2 {
  margin-top: var(--space-12);
  font-size: var(--text-header);
  font-weight: 500;
}

/* A numbered section label inside prose keeps its small label style. */
.prose h2.section-title {
  margin-top: 0;
  font-size: var(--text-small);
}

.prose code {
  padding: 0 0.25em;
  border-radius: var(--radius-sm);
  background: var(--color-slot);
}

.prose blockquote {
  margin: var(--space-6) 0;
  padding-left: var(--space-4);
  border-left: 2px solid var(--brand-accent-dark);
  color: var(--color-ink-muted);
}

.entry-list,
.post-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.entry-list li {
  display: grid;
  gap: var(--space-2);
  padding-block: var(--space-4);
  border-top: 1px solid var(--color-rule);
}

.entry-list h3,
.list-heading {
  margin-bottom: var(--space-2);
  font-size: var(--text-subheader);
  font-weight: 500;
}

.entry-when {
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.post-card {
  padding-block: var(--space-6);
  border-top: 1px solid var(--color-rule);
}

/* An empty list never shows; the empty-state line explains instead. */
.post-list:not(:has(li)) {
  display: none;
}

.post-list:has(li) + .empty-state {
  display: none;
}

.empty-state {
  color: var(--color-ink-muted);
}

/* Generated project cards and skills (ADR 0006). */
.project-list:not(:has(article)),
.skill-list:not(:has(li)) {
  display: none;
}

.project-list:has(article) + .empty-state {
  display: none;
}

/* The underline grows in from the left on hover or keyboard focus. */
.card h3 a {
  text-decoration: none;
  background: linear-gradient(currentColor, currentColor) left bottom / 0 1px no-repeat;
  transition: background-size 0.25s ease-out;
}

.card h3 a:hover,
.card h3 a:focus-visible {
  background-size: 100% 1px;
}

@media (prefers-reduced-motion: reduce) {
  .frame,
  .tag-link::before,
  .card h3 a {
    transition: none;
  }
}

.card-more {
  margin-top: var(--space-4);
  font-size: var(--text-small);
}

/* The home page previews two projects, then this link to the rest (ADR 0019). */
.all-projects {
  margin-top: var(--space-6);
}

/* "Opinion" on R&D posts labelled "opinion" (ADR 0013): an outlined tag in the date
   line, so readers know a post argues a view. Ink on the page passes AA. */
.tag-opinion {
  display: inline-block;
  padding: 0 var(--space-2);
  border: 1px solid currentColor;
  border-radius: var(--radius-sm);
  color: var(--color-ink);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.tag-count {
  margin-left: 0.25em;
}

/* Comments and guestbook entries (ADR 0005). Usernames get a mono initial badge
   instead of an avatar, so no third-party images load. */
.comment-list,
.comment-replies,
.guestbook-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.comment,
.guestbook-entry {
  padding-block: var(--space-4);
  border-top: 1px solid var(--color-rule);
}

.comment-replies {
  margin-top: var(--space-4);
  padding-left: var(--space-6);
  border-left: 1px solid var(--color-rule);
}

.comment-meta,
.guestbook-meta {
  margin-bottom: var(--space-2);
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
}

.badge {
  display: inline-grid;
  place-items: center;
  width: 1.6em;
  height: 1.6em;
  margin-right: var(--space-1);
  border: 1px solid var(--color-rule);
  border-radius: 50%;
  color: var(--color-ink);
}

.guestbook-number {
  color: var(--color-ink);
  font-weight: 500;
}

.guestbook-list:not(:has(li)) {
  display: none;
}

.guestbook-list:has(li) + .empty-state {
  display: none;
}

.note {
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
}

.footer-links {
  margin-bottom: var(--space-4);
  font-size: var(--text-small);
}

.prose img {
  height: auto;
  border-radius: var(--radius-sm);
}

/* Clips (ADR 0007) keep their aspect ratio from width and height before they load. */
.prose video {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  background: var(--color-slot);
}

.post-footer {
  margin-top: var(--space-12);
  padding-top: var(--space-6);
  border-top: 1px solid var(--color-rule);
}

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer .container {
  display: grid;
  gap: var(--space-8);
  padding-block: var(--space-16);
}

.footer-rule {
  width: min(100%, 20rem);
  margin: 0 0 var(--space-6);
  border: 0;
  border-top: 1px solid var(--color-rule);
}

.footer-brand {
  font-weight: 500;
}

.footer-tagline {
  margin-block: var(--space-2) var(--space-4);
  color: var(--color-ink-muted);
  font-size: var(--text-small);
}

/* The AI-use disclosure (ADR 0013): small, but on every page. */
.footer-ai {
  margin-block: 0 var(--space-4);
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
}

/* Contact sits first in every page's footer (ADR 0015). The QR card shows its own
   contact actions, so its footer skips the duplicate. */
.footer-contact .tags {
  margin-top: var(--space-4);
}

:root:has(.card-page) .footer-contact {
  display: none;
}

/* The teapot ends every page (ADR 0015); the footer grid's gap already spaces it
   from the brand. */
.site-footer .model-teapot {
  margin-top: 0;
}

/* Hands-on models (ADR 0009). The grab cursor and focus ring say "you can move
   this"; sideways drags turn the model while vertical swipes still scroll. */
.model {
  position: relative;
  width: 100%;
  margin: 0;
}

.model canvas {
  display: block;
  cursor: grab;
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
  border-radius: 4px;
}

.model canvas:active {
  cursor: grabbing;
}

.model canvas:focus-visible {
  outline-offset: 4px;
}

/* The hint and Reset sit under the model, never over it. Where a pointer can hover
   they appear on hover or focus (their space is kept, so nothing jumps); on touch
   screens they always show, since there is no hover to discover them. */
.model-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  margin: var(--space-2) 0 0;
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 0.2s;
}

.model:hover .model-controls,
.model:focus-within .model-controls {
  opacity: 1;
}

.model-reset {
  min-height: 44px;
}

@media (hover: none) {
  .model-controls {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .model-controls {
    transition: none;
  }
}

.teapot-scene,
.headset-scene,
.tesseract-scene {
  width: 100%;
  aspect-ratio: 4 / 3;
  color: var(--color-ink);
}

/* The teapot is wide and low, so its frame is too: no empty band above and below. */
.teapot-scene {
  aspect-ratio: 3 / 2;
}

/* The headset can face any way, and its strap reaches as far up as sideways, so its
   frame is square: the largest size that never clips at any angle. The tesseract
   turns in four dimensions, so its frame is square too (ADR 0019). */
.headset-scene,
.tesseract-scene {
  aspect-ratio: 1;
}

.model-teapot,
.model-headset,
.model-tesseract {
  max-width: 24rem;
  margin-top: var(--space-8);
}

/* 404 (ADR 0009): ways back sized as real targets, at least 44px tall. */
.way-back {
  margin-top: var(--space-6);
}

.way-back .tag-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--space-4);
}

/* A heading with a model beside it: the R&D headset, the Projects tesseract. */
@media (min-width: 800px) {
  .hero-split .container {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 22rem);
    gap: var(--space-12);
    align-items: center;
  }

  .hero-split .model {
    margin-top: 0;
  }
}

.model figcaption {
  margin-top: var(--space-2);
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* ==========================================================================
   Tablet: 800px and up
   ========================================================================== */
@media (min-width: 800px) {
  /* The five-item nav doesn't fit beside the name and tools below about 1000px; there
     the tools wrap to a second row instead of squeezing every label (ADR 0019). */
  .site-header .container {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3) var(--space-8);
  }

  .nav-list {
    display: flex;
    gap: var(--space-4);
  }

  .nav-list a {
    white-space: nowrap;
  }

  .header-tools {
    margin-left: auto;
  }

  /* The console fills the whole left side, edge to edge vertically. */
  .banner .container {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas:
      "term head"
      "term art"
      "term labels";
    align-items: start;
  }

  /* contain: size keeps the typed lines from growing the row they fill. */
  .terminal {
    contain: size;
    align-self: stretch;
    height: auto;
    min-height: 20em;
    margin-block: calc(-1 * var(--space-8));
    padding: var(--space-8) var(--space-4) var(--space-8) 0;
    border-right: 1px solid var(--color-panel-rule);
  }

  .banner-words {
    text-align: right;
  }

  .norm-label {
    justify-content: flex-end;
  }

  .terminal {
    font-size: 0.75rem;
  }

  .card {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-12);
  }

  /* Alternate the image side on every other card, as in the design. */
  .card:nth-of-type(even) .card-media {
    order: -1;
  }

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

  .entry-list li {
    grid-template-columns: 10rem 1fr;
    gap: var(--space-8);
  }

  /* Footer (ADR 0015): contact above the brand on the left, the teapot beside both,
     in the wider column and at a generous size (ADR 0016). */
  .site-footer .container {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
    column-gap: var(--space-12);
    row-gap: var(--space-12);
    align-items: start;
  }

  .site-footer .model-teapot {
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: center;
    max-width: 40rem;
    margin-top: 0;
  }
}

/* ==========================================================================
   Desktop: 1280px and up
   ========================================================================== */
@media (min-width: 1280px) {
  .banner .container {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.2fr) minmax(0, 1fr);
    grid-template-areas:
      "term art head"
      "term art labels";
    align-items: center;
    /* The banner fills most of the screen, so the console runs its full height. */
    min-height: min(calc(100svh - 4rem), 48rem);
  }

  /* As in Figma, the art spans the whole banner behind the text, the terrain running
     under the console (ADR 0014). The art column stays as the gap between console
     and headline. The container is
     isolated (above), so the canvas stays over the banner's own background, and
     grid-area: auto frames it by the whole container, not its old grid cell. */
  .banner-scene {
    grid-area: auto;
    position: absolute;
    inset: 0;
    z-index: -1;
    width: 100%;
    height: 100%;
    aspect-ratio: auto;
    pointer-events: none;
  }

  .tech-labels {
    justify-content: flex-end;
    align-self: end;
  }

  /* The ID strip's cells keep Figma's widths once there is room. */
  .strip-cell {
    flex: none;
    min-width: 6.5rem;
    padding-right: var(--space-6);
  }

  .strip-mark {
    min-width: 0;
  }

  .barcode {
    flex-basis: 6rem;
    min-width: 0;
  }

  /* The labels as Figma's single row on desktop: globe, system lines, then Batch over Serial. */
  .tech-grid {
    grid-template-columns: auto auto auto;
  }

  .tech-core {
    white-space: nowrap;
    border-right: 1px solid var(--color-panel-rule);
  }

  .tech-side {
    grid-column: auto;
    grid-template-columns: none;
    grid-template-rows: 1fr 1fr;
    border-top: 0;
  }

  .tech-side .tech-cell + .tech-cell {
    border-top: 1px solid var(--color-panel-rule);
    border-left: 0;
  }
}

/* ==========================================================================
   Big screens: 1600px and up (ADR 0009). The content column and type grow with
   the screen, so a full-screen 16:9 display isn't 40% empty margin; the banner
   spans the full width; sections get margin labels; and one faint wireframe
   drifts in the margins.
   ========================================================================== */
@media (min-width: 1600px) {
  :root {
    --content-max: 80rem; /* 1360px at 17px, 1440px at 18px */
  }

  html {
    font-size: 106.25%;
  }

  /* The banner is the showcase: its console pins to the left edge of the screen
     and the headline to the right, with the art taking the extra width. */
  .banner .container {
    max-width: none;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) minmax(0, 1fr);
  }
}

@media (min-width: 1920px) {
  html {
    font-size: 112.5%;
  }
}

/* Margin labels: a drawing sheet's margin note beside each named section. It stays
   put (sticky) while its section is read and leaves with it. It is a real box of
   fixed height but zero width, floated against the screen's right edge, so it
   takes no width from anything beside it (a grid such as Contact's narrows for
   any float in its row), then slid into the margin with `translate`, which
   layout ignores. Its text overflows the zero-width box; the longest label fits
   the 13rem height. The section, a flow root, always contains it, so a label can never
   slide into the next section's. Decorative, so screen readers skip it. */
@media screen and (min-width: 1600px) {
  .section[data-label] {
    display: flow-root;
  }

  .section[data-label]::before {
    content: "+ " attr(data-label);
    content: "+ " attr(data-label) / "";
    position: sticky;
    top: 30vh;
    float: right;
    width: 0;
    height: 13rem;
    translate: calc(var(--content-max) / 2 + 3rem + 1.5em - 50vw) 0;
    color: var(--color-ink-muted);
    font-size: var(--text-xs);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    white-space: nowrap;
    writing-mode: vertical-rl;
    opacity: 0.7;
    pointer-events: none;
  }
}

/* The ambient wireframe: one oversized, very faint geodesic sphere fixed behind the
   page, masked out behind the content column so it shows only in the margins.
   Dark panels cover it, like the page grid. */
.ambient-scene {
  display: none;
}

@media screen and (min-width: 1600px) {
  .ambient-scene {
    position: fixed;
    inset: 0;
    z-index: -1;
    display: block;
    width: 100%;
    height: 100vh;
    color: var(--color-ink);
    opacity: 0.07;
    pointer-events: none;
    -webkit-mask-image: linear-gradient(90deg, #000 calc(50% - var(--content-max) / 2 - 3rem), transparent calc(50% - var(--content-max) / 2 + 3rem), transparent calc(50% + var(--content-max) / 2 - 3rem), #000 calc(50% + var(--content-max) / 2 + 3rem));
    mask-image: linear-gradient(90deg, #000 calc(50% - var(--content-max) / 2 - 3rem), transparent calc(50% - var(--content-max) / 2 + 3rem), transparent calc(50% + var(--content-max) / 2 - 3rem), #000 calc(50% + var(--content-max) / 2 + 3rem));
  }
}

/* ==========================================================================
   Conference QR card (ADR 0011): /card/. On a phone held up at a booth the code
   comes first and as large as the screen allows; its own white quiet zone keeps it
   scannable in either theme. Printed, it becomes a 4 x 3 in badge insert.
   ========================================================================== */
.card-page .container {
  display: grid;
  gap: var(--space-8);
  justify-items: center;
  text-align: center;
}

.qr-code {
  width: min(100%, 70vh, 26rem);
  margin: 0;
}

.qr-code img {
  width: 100%;
  height: auto;
  border-radius: 6px;
}

.qr-code figcaption {
  margin-top: var(--space-3);
  color: var(--color-ink-muted);
  font-size: var(--text-small);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.card-id .tags {
  justify-content: center;
}

.card-url {
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  letter-spacing: 0.04em;
}

@media (min-width: 800px) {
  .card-page .container {
    grid-template-columns: minmax(0, 26rem) minmax(0, 1fr);
    align-items: center;
    justify-items: start;
    text-align: left;
  }

  .card-id .tags {
    justify-content: flex-start;
  }
}

@media print {
  :root:has(.card-page) body {
    background: #fff;
    color: #000;
  }

  :root:has(.card-page) :is(.site-header, .site-footer, .skip-link, .section-rail, .section-bar) {
    display: none;
  }

  /* The page grid prints if "background graphics" is on; the badge stays plain. */
  :root:has(.card-page) body::before {
    display: none;
  }

  .card-page {
    padding: 0;
    border: 0;
  }

  .card-page .container {
    grid-template-columns: 1.6in 1fr;
    gap: 0.2in;
    width: 4in;
    height: 3in;
    max-width: none;
    margin: 0;
    padding: 0.2in;
    border: 1px solid #000;
    align-items: center;
    justify-items: start;
    text-align: left;
  }

  .qr-code {
    width: 1.6in;
  }

  .qr-code figcaption {
    color: #000;
    font-size: 7pt;
  }

  .card-page :is(.eyebrow, .tags, .copy-status, .note) {
    display: none;
  }

  .card-page h1 {
    font-size: 15pt;
  }

  .card-page .lede {
    font-size: 9pt;
  }

  .card-url {
    font-size: 8pt;
  }
}
