/* ==========================================================================
   SD Development design tokens — curated from branding_style-main/colors_and_type.css
   Single, hand-authored, self-contained stylesheet: no preprocessor, no CDN
   imports, no third-party runtime dependency. Everything a page needs to read
   as the SD Development brand family (sddevelopment.be) lives in this file:
   tokens, self-hosted fonts, base typography, page components, responsive
   layer. Class names under "COMPONENTS" are the markup contract with WP03 —
   keep them exact.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. RESET / NORMALIZE — small, hand-written, not an imported library
   -------------------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html,
body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
figure,
blockquote,
dl,
dd {
  margin: 0;
}
ul[class],
ol[class] {
  list-style: none;
  padding: 0;
  margin: 0;
}
img,
picture,
svg {
  display: block;
  max-width: 100%;
}
/* WP03 round-1 rework: img needs height:auto so its aspect ratio is
   preserved once max-width:100% constrains its rendered width — this is
   the root cause of the hero cover stretching. Kept off svg: dossier props
   size themselves via explicit width/height attrs on the wrapping .stamp/
   .clip usages (see §6.3), which already resolve height correctly without
   this rule; adding it there was not needed and risked changing their
   intrinsic sizing. */
img {
  height: auto;
}
button,
input,
textarea,
select {
  font: inherit;
  color: inherit;
}
a {
  color: inherit;
}

/* --------------------------------------------------------------------------
   1. DESIGN TOKENS (T005)
   Names preserved from colors_and_type.css; curated to what this file uses.
   Every colour used below resolves to one of these tokens — no stray hex
   outside this block (logo-adjacent one-offs would be excepted with a
   comment; there are none in this file).
   -------------------------------------------------------------------------- */
:root {
  /* — Brand cores (the four logo hues) — */
  --sd-navy-900: #061325;
  --sd-navy-800: #0a1d36;
  --sd-navy-700: #122947;
  --sd-navy-600: #1a3458; /* brand-dark, clean navy */
  --sd-navy-500: #25457a;
  --sd-navy-400: #3d5e8c; /* em / italic ink */
  --sd-navy-300: #6481a6;
  --sd-navy-200: #b3c2d7;

  --sd-blue-700: #255787; /* info */
  --sd-blue-500: #1e75c0; /* brand-primary, sky blue */
  --sd-blue-300: #6181a8;

  --sd-orange-900: #9e5200; /* AA-safe link hover */
  --sd-orange-800: #ad5a00; /* AA-safe link */
  --sd-orange-700: #c45e00;
  --sd-orange-600: #de7600;
  --sd-orange-500: #f39200; /* brand-secondary, accent/CTA only */

  --sd-gold-500: #e0b73a; /* brand-gold, logo gold */

  --sd-ink-900: #030d12;
  --sd-ink-700: #1f2933;

  --sd-status-info: #255787;
  --sd-status-tip: #126fbe;
  --sd-status-warning: #ffad33;
  --sd-status-danger: #cd0c1a;
  --sd-status-success: #0786a6;

  /* — Brand aliases — */
  --brand-dark: var(--sd-navy-600); /* #1a3458 */
  --brand-primary: var(--sd-blue-500); /* #1e75c0 */
  --brand-secondary: var(--sd-orange-500); /* #f39200 — accent only, never a background field */
  --brand-gold: var(--sd-gold-500); /* #e0b73a */

  /* — Backgrounds — */
  --bg-page: #fefefe; /* paper, never grey-90 */
  --bg-surface-warm: rgba(255, 248, 244, 0.55); /* warm tint for content blocks */
  --bg-elevated: #ffffff; /* cards / nav surface */
  --bg-dark-pattern: linear-gradient(rgba(11, 28, 42, 0.92), rgba(1, 13, 24, 0.98)); /* hero + footer, full-bleed only */

  /* — Foregrounds — */
  --fg-1: var(--sd-navy-900); /* main reading ink */
  --fg-2: var(--sd-ink-700); /* secondary body */
  --fg-3: var(--sd-navy-400); /* muted / captions */
  --fg-on-dark: var(--bg-page); /* text on dark sections (hero/footer) */
  --fg-on-orange: var(--bg-page); /* text on the CTA fill */
  --fg-link: var(--sd-orange-800); /* AA-safe on all light surfaces */
  --fg-link-hover: var(--sd-orange-900); /* AA-safe hover */

  /* — Borders — */
  --border-1: rgba(243, 146, 0, 0.27); /* warm hairline, used on hr */
  --border-2: rgba(30, 117, 192, 0.31); /* cool hairline, used on navbar */
  --border-card: rgba(224, 205, 193, 0.99);

  /* — Shadows — warm-tinted; no grey ambient halos, no neon — */
  --shadow-md: 0 5px 14px rgba(11, 23, 35, 0.1);
  --shadow-orange:
    0 14px 26px -12px rgba(243, 146, 0, 0.42), 0 4px 23px rgba(0, 0, 0, 0.1),
    0 8px 10px -5px rgba(243, 146, 0, 0.2);
  --shadow-blue:
    0 14px 26px -12px rgba(30, 117, 192, 0.42), 0 4px 23px rgba(0, 0, 0, 0.1),
    0 8px 10px -5px rgba(30, 117, 192, 0.2);

  /* — Spacing scale (8-step rem-based) — */
  --space-3xs: 0.125rem;
  --space-2xs: 0.25rem;
  --space-xs: 0.5rem;
  --space-sm: 0.75rem;
  --space-md: 1rem;
  --space-lg: 1.25rem;
  --space-xl: 1.5rem;
  --space-2xl: 1.75rem;
  --space-3xl: 2.75rem; /* body sections breathe here */
  --space-4xl: 3.75rem;
  --space-hero: 4.5rem; /* heroes */

  /* — Radii — tight, technical. Never 16–24px. — */
  --radius-xs: 3px; /* cards, buttons */
  --radius-sm: 6px; /* card max */
  --radius-pill: 999px; /* pill button variant */

  /* — Motion — fades only, no bounce/scale — */
  --easing-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --motion-slow: 500ms;

  /* — Type scale — modular ~1.2, 16px root — */
  --fs-14: 0.875rem;
  --fs-16: 1rem;
  --fs-18: 1.125rem;
  --fs-20: 1.25rem;
  --fs-26: 1.625rem; /* h3 */
  --fs-32: 2rem; /* h2 */
  --fs-40: 2.5rem; /* h1 mobile floor */
  --fs-52: 3.25rem; /* h1 desktop */
  --fs-72: 4.5rem; /* hero display ceiling */

  --lh-tight: 1.1;
  --lh-snug: 1.25;
  --lh-normal: 1.5;
  --lh-relaxed: 1.65;
  --lh-loose: 1.75;

  --ls-tight: -0.01em;
  --ls-normal: 0;
  --ls-wide: 0.02em;
  --ls-widest: 0.08em; /* uppercase eyebrows */
  --ls-display: 0.125rem; /* h3 tracking */

  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  /* — Layout — */
  --layout-max-width: 1200px; /* contained body sections */
  --layout-content-width: 70ch; /* capped reading measure */

  /* — Font stacks (T006) — D-DIN first so real files can drop in later — */
  --font-sans: "D-DIN", "Barlow", "Gill Sans", system-ui, sans-serif;
  --font-serif: "Literata", Georgia, serif;
  --font-condensed: "D-DIN Condensed", "Barlow Condensed", "Gill Sans", sans-serif;
}

/* --------------------------------------------------------------------------
   2. SELF-HOSTED FONTS (T006)
   Files ship at site/assets/fonts/ (WP01 asset contract). Same-origin only —
   no CDN, no Google Fonts runtime request.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Barlow";
  src: url("../assets/fonts/barlow-400-normal.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Barlow";
  src: url("../assets/fonts/barlow-400-italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Barlow";
  src: url("../assets/fonts/barlow-500-normal.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Barlow";
  src: url("../assets/fonts/barlow-600-normal.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Barlow";
  src: url("../assets/fonts/barlow-700-normal.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Barlow Condensed";
  src: url("../assets/fonts/barlow-condensed-600-normal.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("../assets/fonts/literata-400-normal.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("../assets/fonts/literata-400-italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   3. BASE TYPOGRAPHY & PAGE RULES (T007)
   -------------------------------------------------------------------------- */
html {
  font-size: 16px;
  scroll-behavior: smooth;
}

body {
  background: var(--bg-page);
  color: var(--fg-1);
  font-family: var(--font-sans);
  font-size: var(--fs-16);
  font-weight: var(--fw-regular);
  line-height: var(--lh-loose);
  letter-spacing: var(--ls-wide);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  padding-top: 6rem; /* clears the fixed .site-nav */
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-sans);
  color: var(--brand-dark);
  line-height: var(--lh-snug);
  text-wrap: balance;
  text-indent: 0; /* headings never inherit the paragraph indent */
  clear: both; /* headings clear floats */
  overflow-wrap: break-word; /* long words wrap instead of forcing horizontal scroll */
  margin: 0 0 var(--space-md);
}

h1 {
  font-size: clamp(2.25rem, 4vw + 1.25rem, var(--fs-72));
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-tight);
}
h2 {
  font-size: clamp(1.75rem, 2vw + 1.25rem, var(--fs-32));
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-normal);
  margin-top: var(--space-4xl);
}
h3 {
  font-size: clamp(1.375rem, 1.5vw + 1rem, var(--fs-26));
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-display);
  margin-top: var(--space-3xl);
}

/* Eyebrow / kicker — the one deliberately-uppercase heading-adjacent label */
.eyebrow {
  display: block;
  font-family: var(--font-condensed);
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--ls-widest);
  color: var(--brand-secondary);
  margin-bottom: var(--space-sm);
}

/* Paragraphs — justified, book-page indent, scoped first-letter tint.
   Scoped to `main p` so nav/footer/small print are unaffected. No drop-cap. */
main p {
  margin: 0 0 var(--space-md);
  text-align: justify;
  text-justify: inter-word;
  text-indent: 0.5em;
  max-width: var(--layout-content-width);
  overflow-wrap: break-word;
}
main p:first-of-type,
.no-indent p {
  text-indent: 0;
}
main p::first-letter {
  font-size: 1.125em;
  color: var(--brand-dark);
}

/* Italic → serif handoff — a quiet aside, not louder text */
em,
i {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 0.975em;
  color: var(--sd-navy-400);
  letter-spacing: var(--ls-wide);
}

a {
  color: var(--fg-link);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  transition: color var(--motion-slow) var(--easing-standard);
}
a:hover {
  color: var(--fg-link-hover);
}
a:focus-visible,
button:focus-visible,
.button:focus-visible {
  outline: 2px solid var(--brand-primary);
  outline-offset: 2px;
}

hr {
  border: 0;
}
/* Section-break rule — line, never a fill */
hr.warm-rule {
  border-top: 1px solid var(--border-1);
  margin-block: var(--space-xl);
}
hr.warm-rule--heavy {
  border-top-width: 2px;
  border-top-color: var(--brand-secondary);
}

/* --------------------------------------------------------------------------
   4. COMPONENTS (T008)
   Class names are the markup contract with WP03 — keep them exact.
   -------------------------------------------------------------------------- */

/* Fixed top navbar */
.site-nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 999;
  height: 6rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xl);
  padding-inline: var(--space-xl);
  background: var(--bg-elevated);
  border-bottom: 2px solid var(--border-2);
}
.site-nav ul {
  display: flex;
  align-items: center;
  gap: var(--space-xl);
}
.site-nav a {
  font-family: var(--font-sans);
  font-weight: var(--fw-medium);
  color: var(--fg-1);
  transition: color var(--motion-slow) var(--easing-standard);
}
.site-nav a:hover {
  color: var(--fg-link-hover);
}
.site-nav img {
  height: 2.25rem;
}

/* Buttons — fade-only hover, never a transform on press */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  padding: var(--space-sm) var(--space-xl);
  border-radius: var(--radius-xs);
  border: 1px solid transparent;
  font-family: var(--font-sans);
  font-size: var(--fs-16);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  cursor: pointer;
  transition:
    opacity var(--motion-slow) var(--easing-standard),
    background-color var(--motion-slow) var(--easing-standard),
    color var(--motion-slow) var(--easing-standard),
    box-shadow var(--motion-slow) var(--easing-standard),
    border-color var(--motion-slow) var(--easing-standard);
}
.button:active {
  /* Press = colour/opacity only. No transform, no scale, no bounce. */
  opacity: 0.9;
}
.button--pill {
  border-radius: var(--radius-pill);
}

.button--cta {
  background: var(--brand-secondary);
  color: var(--fg-on-orange);
  border-color: var(--brand-secondary);
}
.button--cta:hover {
  opacity: 0.8;
  box-shadow: var(--shadow-orange);
}

.button--ghost {
  background: transparent;
  color: var(--brand-primary);
  border-color: var(--brand-primary);
}
.button--ghost:hover {
  background: var(--brand-primary);
  color: var(--fg-on-orange);
  box-shadow: var(--shadow-blue);
}

/* Hero — full-bleed dark section, two-column (copy + cover), collapses on mobile */
.hero {
  background: var(--bg-dark-pattern);
  color: var(--fg-on-dark);
  padding-block: var(--space-hero);
  padding-inline: max(var(--space-xl), calc((100% - var(--layout-max-width)) / 2));
  display: grid;
  grid-template-columns: 1fr; /* mobile-first: single column, cover falls below copy */
  gap: var(--space-2xl);
  align-items: center;
}
.hero h1,
.hero h2,
.hero h3 {
  color: var(--fg-on-dark);
}
.hero p {
  color: var(--fg-on-dark);
  /* Hero copy is a lede, not book prose: ragged, not justified/indented */
  text-align: left;
  text-indent: 0;
  max-width: 60ch;
}
.hero .eyebrow {
  color: var(--brand-secondary);
}

/* Contained body sections; full-bleed only for .hero + .site-footer */
.section {
  padding-block: var(--space-3xl);
  padding-inline: max(var(--space-xl), calc((100% - var(--layout-max-width)) / 2));
}
.section--tint {
  background: var(--bg-surface-warm);
}

/* Card — testimonials / highlights. No coloured left border. */
.card {
  background: var(--bg-elevated);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-md);
  padding: var(--space-xl);
}

/* Optional responsive grouping helper for collections of .card elements
   (highlights / testimonials); not part of the required class contract. */
.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-xl);
}

/* Footer — full-bleed dark pattern, accent bottom border, link list + logo */
.site-footer {
  background: var(--bg-dark-pattern);
  color: var(--fg-on-dark);
  border-bottom: 4px solid var(--brand-secondary);
  padding-block: var(--space-4xl) var(--space-2xl);
  padding-inline: max(var(--space-xl), calc((100% - var(--layout-max-width)) / 2));
}
.site-footer h5 {
  font-family: var(--font-condensed);
  font-size: var(--fs-14);
  text-transform: uppercase;
  letter-spacing: var(--ls-widest);
  color: var(--brand-secondary);
  margin-bottom: var(--space-sm);
}
.site-footer ul {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.site-footer a {
  color: var(--fg-on-dark);
  transition: color var(--motion-slow) var(--easing-standard);
}
.site-footer a:hover {
  color: var(--brand-secondary);
}
.site-footer img {
  height: 2.5rem;
}

/* --------------------------------------------------------------------------
   5. RESPONSIVE LAYER + FIXED-HEADER COMPENSATION (T009)
   Mobile-first; breakpoints ~640 / 960 / 1200px. NFR-001: 320–1920px, no
   horizontal scroll at any width.
   -------------------------------------------------------------------------- */

/* Fixed 6rem header: pre-compensate in-page anchor targets */
section[id] {
  scroll-margin-top: 7rem; /* 6rem header + breathing room */
}

@media (max-width: 640px) {
  .site-nav {
    padding-inline: var(--space-md);
    gap: var(--space-md);
  }
  .site-nav ul {
    gap: var(--space-md);
  }
  .site-nav a {
    font-size: var(--fs-14);
  }
}

@media (min-width: 640px) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 960px) {
  .hero {
    grid-template-columns: 1.1fr 1fr;
  }
  .card-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* Note: .hero / .section / .site-footer already self-center above 1200px via
   the `max(gutter, calc((100% - 1200px) / 2))` padding formula — no extra
   breakpoint override needed; one was tried here and removed for regressing
   the minimum gutter at exactly 1200px. */

/* ==========================================================================
   6. DOSSIER LAYER (WP02, T006–T010)
   Extends the file above — mission-1 tokens/components/classes above this
   point are UNCHANGED. Everything below is additive: the "released dossier"
   interaction layer (plan.md Design Concept). Class names in this section
   are the markup contract with WP03/WP04 — keep them exact.
   ========================================================================== */

/* --------------------------------------------------------------------------
   6.1 DOSSIER TOKENS + JETBRAINS MONO @FONT-FACE (T006)
   -------------------------------------------------------------------------- */
:root {
  /* — Ink & stamp accents — */
  --ink-stamp: var(--brand-dark);
  --stamp-accent: var(--brand-secondary);

  /* — Sheet rotation steps — cycled across .sheet instances, ≥960px only
     (rotation is disabled below that breakpoint; see §6.5) — */
  --tilt-1: 0.6deg;
  --tilt-2: -1.1deg;
  --tilt-3: 1.4deg;

  /* — Sheet shadow depths (rest / lift) — warm-tinted, same family as
     --shadow-md, just deeper for the "paper lifting off the desk" read — */
  --shadow-sheet-rest: 0 6px 18px -8px rgba(6, 19, 37, 0.28), 0 2px 6px rgba(6, 19, 37, 0.12);
  --shadow-sheet-lift: 0 16px 32px -12px rgba(6, 19, 37, 0.38), 0 6px 14px rgba(6, 19, 37, 0.18);

  /* — Redaction bar colour (navy, P-3) — */
  --redaction-bar: var(--sd-navy-800);

  /* — Desk backdrop — deep navy, derived from the --bg-dark-pattern family — */
  --desk-bg: linear-gradient(165deg, var(--sd-navy-900) 0%, var(--sd-ink-900) 100%);
  --desk-gutter: var(--space-xl);

  /* — Paper sheet surface — warm paper + a subtle two-layer gradient
     "texture" (no image files, no repeating hard stops → no zebra banding) — */
  --paper-warm: #f6ecd9;
  --paper-texture:
    linear-gradient(115deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 42%),
    radial-gradient(ellipse at 15% 10%, rgba(255, 255, 255, 0.4), transparent 60%);

  /* — Motion — */
  --motion-fast: 150ms; /* feedback-onset budget for hover/focus state changes (NFR-004) */
  --reveal-distance: 24px; /* scroll-reveal travel distance, JS-gated only */
}

/* JetBrains Mono 400 — prop-scoped typewriter accent (R-4). File ships at
   the fixed path contract from WP01's lane: site/assets/fonts/
   jetbrains-mono-400-normal.woff2. Same-origin only, one weight. */
@font-face {
  font-family: "JetBrains Mono";
  src: url("../assets/fonts/jetbrains-mono-400-normal.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
:root {
  /* INV-7: --font-prop may be referenced ONLY by prop classes — .stamp,
     .file-label, .memo-meta. Do not use it on body copy, headings, nav,
     buttons, or anywhere else. This is the sole place the mono face may
     leak into the page; grep for --font-prop usage to audit INV-7. */
  --font-prop: "JetBrains Mono", "Courier New", monospace;
}

/* --------------------------------------------------------------------------
   6.2 PAPER SHEET, FOLDER CHROME, DESK BACKDROP (T007)
   -------------------------------------------------------------------------- */

/* Page/section-level backdrop — content sections float as paper on top */
.dossier-desk {
  background: var(--desk-bg);
  padding-block: var(--space-3xl);
  padding-inline: max(var(--desk-gutter), calc((100% - var(--layout-max-width)) / 2));
}

/* Paper sheet — the case-file surface. Rotation is applied only ≥960px
   (§6.5); below that the sheet is flat and only lifts on hover/focus. */
.sheet {
  --tilt: var(--tilt-1);
  position: relative;
  background-color: var(--paper-warm);
  background-image: var(--paper-texture);
  color: var(--fg-1);
  max-width: var(--layout-max-width);
  margin-inline: auto;
  padding: var(--space-2xl);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-sheet-rest);
  transform: rotate(0deg);
  transition:
    transform 280ms var(--easing-standard),
    box-shadow 280ms var(--easing-standard);
}
.sheet + .sheet {
  margin-top: var(--space-2xl);
}

/* Rotation cycling — nth-of-type default, modifier classes take precedence
   (source order: nth-of-type first, modifiers after) for hand-authored
   control when sibling counts don't line up with the desired pattern. */
.sheet:nth-of-type(3n + 2) {
  --tilt: var(--tilt-2);
}
.sheet:nth-of-type(3n) {
  --tilt: var(--tilt-3);
}
.sheet--tilt-b {
  --tilt: var(--tilt-2);
}
.sheet--tilt-c {
  --tilt: var(--tilt-3);
}

/* Smaller, card-like sheet for grids (Exhibit A/B). Combine with .sheet:
   class="sheet sheet--exhibit" */
.sheet--exhibit {
  padding: var(--space-xl);
  max-width: none;
}

/* Folder-tab chrome for the page header — pure CSS trapezoid via clip-path.
   If a photographic/SVG tab proves necessary, WP01's folder-tab.svg fallback
   applies instead (coordinate via commit note) — this rule is the default. */
.folder-header {
  position: relative;
  background: var(--sd-navy-800);
  color: var(--fg-on-dark);
  padding-block: var(--space-sm);
  padding-inline: max(var(--space-xl), calc((100% - var(--layout-max-width)) / 2));
}
.folder-header::before {
  content: "";
  position: absolute;
  top: -0.9rem;
  left: max(var(--space-xl), calc((100% - var(--layout-max-width)) / 2));
  width: 7rem;
  height: 1rem;
  background: var(--sd-navy-800);
  clip-path: polygon(10% 100%, 0 0, 100% 0, 90% 100%);
}

/* Bottom-edge "deckled" paper treatment (P-6). A pseudo-element strip below
   the sheet, not a clip on the sheet itself, so real content is never cut. */
.torn-edge {
  position: relative;
  padding-bottom: var(--space-xl);
}
.torn-edge::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.4rem;
  height: 0.55rem;
  background-color: var(--paper-warm);
  clip-path: polygon(
    0% 0%,
    5% 100%,
    10% 10%,
    15% 100%,
    20% 10%,
    25% 100%,
    30% 10%,
    35% 100%,
    40% 10%,
    45% 100%,
    50% 10%,
    55% 100%,
    60% 10%,
    65% 100%,
    70% 10%,
    75% 100%,
    80% 10%,
    85% 100%,
    90% 10%,
    95% 100%,
    100% 0%
  );
}

/* Mono-face metadata line ("FILE NO. CB/INT/77-FG-MM · RELEASED UNDER
   DIRECTIVE 12" — file number is the cover-sourced fact, round 6) */
.memo-meta {
  font-family: var(--font-prop);
  font-size: var(--fs-14);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: inherit;
}

/* Bordered mono label chip (P-2: "EYES ONLY", "ITEM 01") */
.file-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-prop);
  font-size: var(--fs-14);
  letter-spacing: var(--ls-wide);
  padding: var(--space-3xs) var(--space-sm);
  border: 1px solid currentColor;
  border-radius: 2px;
  color: var(--ink-stamp);
}

/* --------------------------------------------------------------------------
   6.3 PROP CLASSES: REDACTION, STAMPS, CLIPS (T008)
   -------------------------------------------------------------------------- */

/* .redacted (IX-1, P-3, research R-1). The real text is plain DOM content —
   the bar is a decorative pseudo-element only, so screen readers read the
   sentence identically with or without JS, with or without hover/focus.
   WP03 must add tabindex="0" to each .redacted element — that is purely a
   visual trigger for sighted keyboard users. Do NOT add an ARIA role or
   label to this class; it is not a control, just a styled span.
   INV-4: only satirical flourishes/teasers may use this class — never
   facts, prices, or CTAs (those must always be readable at rest). */
.redacted {
  position: relative;
  display: inline-block;
  color: inherit;
}
.redacted::after {
  content: "";
  position: absolute;
  inset: -0.05em -0.2em;
  background: var(--redaction-bar);
  transform: rotate(-0.3deg);
  clip-path: inset(0 0 0 0);
  transition: clip-path 350ms var(--easing-standard);
  pointer-events: none;
}
.redacted:hover::after,
.redacted:focus-visible::after {
  clip-path: inset(0 0 0 100%);
}
/* Touch/no-hover: revealed at rest — nothing hover-locked and unreachable */
@media (hover: none) {
  .redacted::after {
    clip-path: inset(0 0 0 100%);
  }
}

/* .stamp — sizing/rotation wrapper for inline-SVG or <img> props (P-1).
   Ink colour via currentColor so props recolour with the surrounding text
   colour/token. font-family included per INV-7 (.stamp is an allowed
   prop class) in case markup nests real text inside a stamp wrapper. */
.stamp {
  --stamp-rotate: -6deg;
  display: inline-block;
  width: var(--stamp-size, 6rem);
  color: var(--ink-stamp);
  font-family: var(--font-prop);
  transform: rotate(var(--stamp-rotate));
}
.stamp img,
.stamp svg {
  width: 100%;
  height: auto;
  color: inherit;
}
.stamp--corner {
  position: absolute;
  top: var(--space-sm);
  right: var(--space-sm);
}
.stamp--rotate-a {
  --stamp-rotate: -8deg;
}
.stamp--rotate-b {
  --stamp-rotate: 4deg;
}
.stamp--rotate-c {
  --stamp-rotate: -3deg;
}

/* .stamp--settle (IX-3). Rest = faint, ONLY once html.js is present — the
   settle trigger is hovering/focusing an ancestor flagged
   [data-stamp-trigger], or the ancestor [data-reveal] gaining
   .is-revealed from enhance.js. Without the js flag there is no reliable
   trigger (esp. for touch), so the no-JS/no-flag fallback is fully visible
   at rest — never permanently faint or hidden. */
.stamp--settle {
  opacity: 1;
  transform: scale(1) rotate(var(--stamp-rotate));
  transition:
    transform 280ms var(--easing-standard),
    opacity 280ms var(--easing-standard);
}
html.js .stamp--settle {
  opacity: 0.18;
  transform: scale(1.1) rotate(var(--stamp-rotate));
}
html.js [data-stamp-trigger]:hover .stamp--settle,
html.js [data-stamp-trigger]:focus-within .stamp--settle,
html.js [data-reveal].is-revealed .stamp--settle {
  opacity: 1;
  transform: scale(1) rotate(var(--stamp-rotate));
}
@media (hover: none) {
  html.js .stamp--settle {
    opacity: 1;
    transform: scale(1) rotate(var(--stamp-rotate));
  }
}

/* .clip — paperclip prop positioning (P-4): top-corner overlap, slight
   rotation. Requires a `position: relative` ancestor (.sheet already is). */
.clip {
  position: absolute;
  top: -0.9rem;
  left: 1.5rem;
  width: 2rem;
  color: var(--ink-stamp);
  transform: rotate(-9deg);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   6.4 INTERACTION STATES: THE FULL IX MATRIX (T009)
   .sheet/.sheet--exhibit/.redacted/.stamp--settle hover+focus twins are
   defined in §6.2/§6.3 above (co-located with their rest state). This block
   covers the remaining IX rows: CTA halo+lift, nav/footer ink-sweep.
   -------------------------------------------------------------------------- */

.sheet:hover,
.sheet:focus-within {
  transform: translateY(-4px) rotate(0deg);
  box-shadow: var(--shadow-sheet-lift);
}
.sheet--exhibit:hover,
.sheet--exhibit:focus-within {
  transform: translateY(-3px) rotate(0deg);
}

/* IX-5: Buy CTA — halo intensify + 2px lift on hover/focus only. Press
   stays transform-free (brand rule); redeclares the full mission-1
   transition list plus transform so the lift animates too. */
.button--cta {
  transition:
    opacity var(--motion-slow) var(--easing-standard),
    background-color var(--motion-slow) var(--easing-standard),
    color var(--motion-slow) var(--easing-standard),
    box-shadow var(--motion-fast) var(--easing-standard),
    border-color var(--motion-slow) var(--easing-standard),
    transform var(--motion-fast) var(--easing-standard);
}
.button--cta:hover,
.button--cta:focus-visible {
  transform: translateY(-2px);
  box-shadow: var(--shadow-orange), 0 0 0 6px rgba(243, 146, 0, 0.14);
}
.button--cta:active {
  transform: none; /* press stays transform-free, per brand rule */
}

/* IX-7: nav/footer links — mission-1 colour fade stays; optional ink-sweep
   underline added on top (hover + focus-visible twin). */
.site-nav a,
.site-footer a {
  position: relative;
}
.site-nav a::after,
.site-footer a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 100%;
  bottom: -2px;
  height: 2px;
  background: currentColor;
  transition: right var(--motion-fast) var(--easing-standard);
}
.site-nav a:hover::after,
.site-nav a:focus-visible::after,
.site-footer a:hover::after,
.site-footer a:focus-visible::after {
  right: 0;
}

/* --------------------------------------------------------------------------
   6.5 RESPONSIVE + REDUCED-MOTION + JS-FLAG GATING (T010)
   -------------------------------------------------------------------------- */

/* 320px safety net (NFR-001). Rotation (§6.2) only ever applies ≥960px, so
   the layout box and the rendered/painted box match at every width below
   that — this rule is a defensive backstop against the "rotated elements
   widen the bounding box" trap, not the primary mechanism. */
html,
body {
  overflow-x: hidden;
}

@media (max-width: 640px) {
  .dossier-desk {
    --desk-gutter: var(--space-md);
  }
  .folder-header {
    padding-inline: max(var(--space-md), calc((100% - var(--layout-max-width)) / 2));
  }
  .folder-header::before {
    left: max(var(--space-md), calc((100% - var(--layout-max-width)) / 2));
    width: 5rem;
  }
}

@media (min-width: 960px) {
  /* Rotation only ever switches on here — flat + lift-only below (T010 #1) */
  .sheet {
    transform: rotate(var(--tilt));
  }
}

/* js-flag gating (R-2, INV-6): content is only hidden/offset pre-reveal
   under html.js [data-reveal]. No-JS visitors — and any browser where
   enhance.js fails to run — see everything, visible, at rest. */
html.js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-distance));
  transition:
    opacity 500ms var(--easing-standard),
    transform 500ms var(--easing-standard);
  transition-delay: var(--reveal-delay, 0ms);
}
html.js [data-reveal].is-revealed {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  /* Scroll reveals: disabled entirely, content stays visible (T010 #2) */
  html.js [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }

  /* Sheets: hover/focus collapses to shadow-only change, no lift/tilt.
     Static rest-state tilt is left alone — it is a fixed design detail,
     not an animation, so it is unaffected by this media query. */
  .sheet,
  .sheet--exhibit {
    transition: box-shadow 200ms linear;
  }
  .sheet:hover,
  .sheet:focus-within,
  .sheet--exhibit:hover,
  .sheet--exhibit:focus-within {
    transform: none;
  }

  /* Redaction: instant opacity reveal, geometry stays static (no wipe) */
  .redacted::after {
    transition: opacity 200ms linear;
  }
  .redacted:hover::after,
  .redacted:focus-visible::after {
    clip-path: inset(0 0 0 0);
    opacity: 0;
  }

  /* Stamps: opacity fade only, no scale-settle */
  .stamp--settle {
    transition: opacity 200ms linear;
  }
  html.js .stamp--settle,
  html.js [data-stamp-trigger]:hover .stamp--settle,
  html.js [data-stamp-trigger]:focus-within .stamp--settle,
  html.js [data-reveal].is-revealed .stamp--settle {
    transform: none;
  }

  /* CTA: no lift, halo/colour change only */
  .button--cta:hover,
  .button--cta:focus-visible {
    transform: none;
  }

  /* Nav/footer underline: fade instead of sweep */
  .site-nav a::after,
  .site-footer a::after {
    right: 0;
    opacity: 0;
    transition: opacity 200ms linear;
  }
  .site-nav a:hover::after,
  .site-nav a:focus-visible::after,
  .site-footer a:hover::after,
  .site-footer a:focus-visible::after {
    opacity: 1;
  }
}

/* --------------------------------------------------------------------------
   6.6 WP03 OUT-OF-MAP MINIMAL ADDITIONS
   Two small layout-only rules the §6 contract doesn't define, needed by the
   index.html rebuild. Not new props/interactions — pure spacing/grid.
   -------------------------------------------------------------------------- */

/* Rationale: the memo hero needs a two-column (copy + stamped cover) layout
   at wide viewports, matching mission-1's .hero ratio, but .sheet itself is
   single-column by design (max-width + padding only) — no grid utility in
   the §6 contract covers this pairing. Mobile-first single column; the
   [data-reveal]/rotation gate at ≥960px is untouched. */
.memo-hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2xl);
  align-items: start;
}
@media (min-width: 960px) {
  .memo-hero {
    grid-template-columns: 1.1fr 1fr;
    align-items: center;
  }
}

/* Rationale: §6.2 only defines spacing between adjacent .sheet siblings
   (.sheet + .sheet). WP03's page rebuild stacks <section> "chapters" on the
   desk and pairs an intro .sheet with a following .card-grid (not itself a
   .sheet) — both need the same paper-sheet rhythm, or content collapses
   flush against the previous block. */
.dossier-desk > section + section,
.dossier-desk > .sheet + section {
  margin-top: var(--space-4xl);
}
.dossier-desk .card-grid {
  margin-top: var(--space-2xl);
}

/* ==========================================================================
   6.7 WP03 ROUND-1 REWORK (human change request)
   Everything in this section is a documented out-of-map edit to WP02's
   surface, made per the round-1 human browser-check change request
   (decision plan.design.human-check-round1-changes). Grouped here rather
   than scattered inline so a reviewer can audit the whole change in one
   pass. See the commit body for the full file-by-file rationale.

   NOTE ON CURSOR-FOLLOWING EFFECTS (§6.7.4 below): the plan's original
   motion vocabulary banned continuous cursor-tracking effects. The human
   explicitly overrode that rule during the round-1 check and asked for
   cover tilt, a magnetic CTA, and a card spotlight. All three are gated to
   hover-capable/fine-pointer devices, fully disabled under
   prefers-reduced-motion, and are pointer-only enrichments that add no
   information (the equivalent content/controls are always reachable
   without them) — see js/enhance.js for the matching gating logic.

   NOTE ON --font-prop (INV-7): §6.1 restricts --font-prop to .stamp,
   .file-label, .memo-meta. Round 1 added .sign-here-tab to that allowed
   list; round 2 (decision plan.design.human-check-round2-changes) removed
   the SIGN HERE tab entirely, so the allowed list is back to the original
   three plus §6.8's .photo-frame__label (see the §6.8 header note).
   ========================================================================== */

/* --------------------------------------------------------------------------
   6.7.1 THEME CALM — subtler folder chrome, quieter desk backdrop
   The book's own aesthetic is a military field manual (olive, file
   numbers, typewriter, a restrained red stamp), not a manila office
   folder. No new colours are introduced here — same SD brand tokens,
   just less chrome density and a quieter backdrop so paper sheets read
   as the focal surface.
   -------------------------------------------------------------------------- */

/* Drop the trapezoid tab flap — folder-header reads as a plain fixed bar
   with just the memo-meta line, per the "or plain fixed nav" alternative
   offered in the change request. */
.folder-header::before {
  content: none;
}

/* Quieter desk gradient: both stops moved one step up the navy scale so
   the backdrop never bottoms out at near-black ink-900, which read as too
   heavy/competing against the paper sheets. Still full-bleed dark navy,
   still the same token family — register shift only, no new colour. */
:root {
  --desk-bg: linear-gradient(165deg, var(--sd-navy-800) 0%, var(--sd-navy-900) 100%);
}

/* --------------------------------------------------------------------------
   6.7.2 REDACTED LINE — replacement copy
   The original "[which of your meetings count as an incident]" flourish is
   replaced in index.html; the .redacted mechanic itself (approved) is
   unchanged. No CSS change needed here — listed for completeness.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   6.7.3 PAPERCLIPS — repositioned clear of text
   Root cause of the original overlap: the paperclip SVG's intrinsic aspect
   ratio (40:90) means a 2rem-wide clip renders ~4.5rem tall; the old
   top:-0.9rem offset only pushed ~0.9rem of that above the sheet, leaving
   ~3.6rem hanging *inside* the card — well past the 1.5–1.75rem padding
   zone and into the first line of copy. These modifiers push the clip
   further up/aside so only a small hook ever falls inside the padding
   zone, never over text.
   -------------------------------------------------------------------------- */

/* Used on testimonial cards + the personnel-file sheet: clip hooks the top
   edge, mostly hanging above the sheet; only ~1.1rem dips inside, which
   stays inside the card's own padding (never reaches the first text line). */
.clip--top-edge {
  top: -3.4rem;
  left: auto;
  right: 1.1rem;
  transform: rotate(8deg);
}

/* .clip--side (hero-cover paperclip) was removed in round 2: the human
   change request drops the paperclip from the hero entirely (decision
   plan.design.human-check-round2-changes, point 1). The .clip--top-edge
   modifier above remains in use on Exhibit B and the personnel file, with
   its round-2 position adjustment applied in §6.8.3. */

/* --------------------------------------------------------------------------
   6.7.4 NEW MOTION: cover tilt, magnetic CTA, card spotlight
   All three are driven from js/enhance.js (pointermove listeners gated to
   hover-capable/fine-pointer + !prefers-reduced-motion — see that file's
   "ROUND-1 REWORK" block). The rules below are the CSS half: rest state +
   the transition that makes the JS-driven updates feel eased rather than
   snapping. Nothing here renders differently if the JS never runs — the
   cover keeps its static rotation, the CTA sits still, cards show no
   spotlight (INV-6, additive only).
   -------------------------------------------------------------------------- */

.cover-frame {
  position: relative;
  transform: rotate(-1.5deg);
  transition: transform 400ms var(--easing-standard);
}

/* Card spotlight — Exhibit A only. Custom properties default to a centred
   fallback so nothing looks broken if JS never sets them. Low-opacity
   white radial per spec (5–8%); pointer-events:none so it never blocks
   clicks/keyboard focus on the card's real content. */
#inside .sheet--exhibit {
  --spot-x: 50%;
  --spot-y: 50%;
}
#inside .sheet--exhibit::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(circle 220px at var(--spot-x) var(--spot-y), rgba(255, 255, 255, 0.07), transparent 70%);
  opacity: 0;
  transition: opacity 200ms var(--easing-standard);
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  #inside .sheet--exhibit:hover::before,
  #inside .sheet--exhibit:focus-within::before {
    opacity: 1;
  }
}

/* --------------------------------------------------------------------------
   6.7.5 UNDERLINE INK-SWEEP — extended site-wide (IX-7 generalised)
   WP02 shipped the ink-sweep underline on .site-nav/.site-footer links
   only. The round-1 request asks for it on every text link, including
   in-copy links (personnel-file bio links). Buttons are excluded — they
   already have their own dedicated hover treatment (§6.4 IX-5).
   -------------------------------------------------------------------------- */
main a:not(.button) {
  position: relative;
}
main a:not(.button)::after {
  content: "";
  position: absolute;
  left: 0;
  right: 100%;
  bottom: -2px;
  height: 2px;
  background: currentColor;
  transition: right var(--motion-fast) var(--easing-standard);
}
main a:not(.button):hover::after,
main a:not(.button):focus-visible::after {
  right: 0;
}

/* --------------------------------------------------------------------------
   6.7.6 THEME CHERRIES (f–j)
   -------------------------------------------------------------------------- */

/* f. Cover-red EYES ONLY settle stamp — the ONLY red element on the page.
   Hex sampled directly from the printed CLASSIFIED/EYES ONLY stamp ink on
   assets/covers/csfg-cover.jpg (average of the most colour-saturated
   stamp-ink pixels, sampled programmatically): #6e1d10 — a muted brick
   red, not a pure/bright red. Recoloured via a CSS mask rather than an
   approximated filter() chain, so the exact hex lands as a real
   background-color: .stamp-mask is an empty span whose visible shape is
   cut from stamp-eyes-only.svg via mask-image, then filled with
   --stamp-red. This replaces the closing-directive DECLASSIFIED stamp. */
:root {
  --stamp-red: #6e1d10;
}
.stamp--alert {
  color: var(--stamp-red);
}
.stamp--alert .stamp-mask {
  display: block;
  width: 100%;
  aspect-ratio: 300 / 90;
  background-color: var(--stamp-red);
  -webkit-mask-image: url("../assets/props/stamp-eyes-only.svg");
  mask-image: url("../assets/props/stamp-eyes-only.svg");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: center;
  mask-position: center;
}

/* g. Punch holes — REMOVED in round 3 (decision
   plan.design.human-check-round3-changes, point 1). Round 1 introduced 3
   faint binder holes down the left margin of the main sheets
   (.sheet:not(.sheet--exhibit)::before radial-gradient strip); round 2
   reduced the count to exactly 2 and dropped them from the personnel
   file; round 3 removes them everywhere — the rule, its ≤640px hide, and
   §6.8.1's per-section opt-out are all deleted. The prop was CSS-only (no
   markup) and is not used by styleguide.html, so nothing is orphaned. */

/* h. Typewriter caret — REWORKED by mission stijn-review-refinements-
   01KX8H6R WP02 (FR-008): carets are animation-scoped now. The round-1
   caret blinked forever on .memo-meta itself ("the eternal blink"); the
   caret now exists ONLY while enhance.js's type-once animation is running
   and has flagged the line with `.is-typing` (contracts/dom-behavior.md
   §2). At rest — no JS, reduced motion, repeat visits, crawlers — the
   memo line is plain static text: no caret, no running animation, zero
   extra rules needed. The keyframes keep their name, but may only ever
   be referenced from the .is-typing scope. */
.memo-meta.is-typing::after {
  content: "▍";
  display: inline-block;
  margin-left: 0.35em;
  animation: caret-blink 1s steps(1) infinite;
}
@keyframes caret-blink {
  50% {
    opacity: 0;
  }
}

/* i. SIGN HERE tab — REMOVED in round 2 (decision
   plan.design.human-check-round2-changes, point 1). The .sign-here-tab
   rule and its .cta-row anchor rule are deleted; the markup span is gone
   from index.html. */

/* j. Coffee ring — REMOVED in round 5 (decision
   plan.design.human-check-round5-changes, point 3): the "random circle"
   bottom-right of the personnel file read as noise rather than a stain
   prop. The #author .sheet::after bordered-circle rule is deleted; the
   section's paperclip is likewise removed (markup-side, index.html). */

/* --------------------------------------------------------------------------
   6.7.7 REDUCED MOTION — round-1 additions
   Consolidated here rather than merged into §6.5's block so this whole
   rework stays reviewable as one unit. All of §6.7.4's JS-driven effects
   already never run when JS detects prefers-reduced-motion (see
   enhance.js); the rules below are the defensive CSS-only backstop plus
   the two purely-CSS animations added in this section (caret, ink-sweep).
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .cover-frame {
    transition: none;
  }
  #inside .sheet--exhibit::before {
    display: none;
  }
  /* WP02 (stijn-review-refinements, FR-008): the caret is scoped to
     .is-typing, which enhance.js never adds under reduced motion — this
     backstop removes the caret outright even if that JS guard ever
     regresses. Was: animation:none; opacity:1 on the unscoped caret. */
  .memo-meta.is-typing::after {
    content: none;
  }
  main a:not(.button)::after {
    right: 0;
    opacity: 0;
    transition: opacity 200ms linear;
  }
  main a:not(.button):hover::after,
  main a:not(.button):focus-visible::after {
    opacity: 1;
  }
}

/* ==========================================================================
   6.8 WP03 ROUND-2 REWORK (human change request)
   Documented out-of-map edits to WP02's surface, made per the round-2 human
   browser-check change request (decision plan.design.human-check-round2-
   changes). Grouped here for one-pass review; round-1 rules this round
   deletes (SIGN HERE tab, hero side-clip) carry tombstone notes in §6.7
   instead of overrides here. See the commit body for the file-by-file
   rationale.

   NOTE ON --font-prop (INV-7): this section adds one prop class to the
   allowed list — .photo-frame__label, the mono "PHOTO" caption inside the
   aria-hidden author-photo attachment frame. It is a decorative prop label
   in the same family as .stamp/.file-label/.memo-meta, never body copy.
   ========================================================================== */

/* --------------------------------------------------------------------------
   6.8.1 HERO — only the cover reacts (point 1)
   The hero sheet itself must not lift/tilt on hover or focus: these rules
   pin .memo-hero's hover/focus-within state to its rest state (source
   order beats §6.4's .sheet:hover at equal specificity). The cover's
   cursor tilt lives on .cover-frame alone — driven by enhance.js;
   strengthened to ±12° + scale in round 2, then retuned to ±9° + a
   subtler scale in round 3 (see that file) — so hovering the cover moves
   only the cover.
   -------------------------------------------------------------------------- */
.memo-hero:hover,
.memo-hero:focus-within {
  transform: rotate(0deg);
  box-shadow: var(--shadow-sheet-rest);
}
@media (min-width: 960px) {
  .memo-hero:hover,
  .memo-hero:focus-within {
    transform: rotate(var(--tilt));
  }
}

/* Personnel-file punch-hole opt-out (round-2 point 4) — REMOVED in round 3:
   punch holes are gone site-wide (§6.7.6 g tombstone), so the
   #author .sheet::before { content: none } rule became moot. */

/* --------------------------------------------------------------------------
   6.8.2 SITE-WIDE TITLE / KEY-ELEMENT HIERARCHY (points 2, 3, 5)
   Titles and key elements must stand out more, within the dossier design
   language: bigger/bolder section h2s carrying a heavy orange rule (the
   same accent family as hr.warm-rule--heavy and the footer border), a
   stamp-bar prefix on eyebrows, orange-ink file labels, and heavier
   exhibit-card h3s. Sentence case is preserved (INV-3).
   -------------------------------------------------------------------------- */

/* Section titles on the desk: larger, bolder, with an orange ink rule. */
.dossier-desk h2 {
  font-size: clamp(2rem, 2.5vw + 1.25rem, 2.75rem);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-tight);
}
.dossier-desk h2::after {
  content: "";
  display: block;
  width: 4.5rem;
  height: 0.3rem;
  margin-top: var(--space-xs);
  background: var(--brand-secondary);
}

/* Eyebrows: slightly larger, with a short stamp-bar prefix. */
.dossier-desk .eyebrow {
  font-size: var(--fs-16);
}
.dossier-desk .eyebrow::before {
  content: "";
  display: inline-block;
  width: 1.6rem;
  height: 2px;
  margin-right: 0.5em;
  vertical-align: middle;
  background: currentColor;
}

/* File labels: orange stamp ink (AA-safe orange-800 on paper) instead of
   navy, so the ITEM markers read as key elements at a glance. */
.sheet--exhibit .file-label {
  color: var(--sd-orange-800);
  border-width: 1.5px;
}

/* Exhibit-card titles: heavier, tighter to their file label. */
.sheet--exhibit h3 {
  font-weight: var(--fw-semibold);
  margin-top: var(--space-md);
}

/* --------------------------------------------------------------------------
   6.8.3 EXHIBIT B — quote dominance + paperclips off the text (point 3)
   The quote is the star: serif, larger, reading ink. The attribution drops
   to a small muted line. Quote TEXT is untouched (INV-2 byte-binding —
   styling only). Paperclips sit slightly lower on the cards; the cards get
   extra top padding so the clip's tail ends above the first text line.
   -------------------------------------------------------------------------- */
#testimonials blockquote {
  font-family: var(--font-serif);
  font-size: var(--fs-20);
  line-height: var(--lh-normal);
  color: var(--fg-1);
}
#testimonials figcaption {
  margin-top: var(--space-md);
  font-size: var(--fs-14);
  letter-spacing: var(--ls-wide);
  color: var(--fg-3);
}

/* Round-2 clip position: down from -3.4rem to -2.5rem, so the clip visibly
   grips the card instead of floating above it. At 2rem wide the clip is
   4.5rem tall (40:90 ratio) → 2rem now dips inside the card; the padding
   bump below keeps the first text line clear of it. */
.clip--top-edge {
  top: -2.5rem;
}
#testimonials .sheet--exhibit {
  padding-top: 2.75rem;
}

/* --------------------------------------------------------------------------
   6.8.4 PERSONNEL FILE — author photo frame, B&W treatment (point 4)
   Left column reserves space for an author photo as a dossier "attached
   photograph": white print matte, dashed empty well with a mono PHOTO
   label, slight rotation. When Lars drops a real <img> into .photo-frame
   it covers the well absolutely (no :has() dependency) and automatically
   receives the black-and-white paper-palette treatment via CSS filter.
   -------------------------------------------------------------------------- */
.author-file {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-xl);
}
@media (min-width: 640px) {
  .author-file {
    grid-template-columns: minmax(9rem, 12rem) 1fr;
    gap: var(--space-2xl);
    align-items: start;
  }
}
.photo-frame {
  position: relative;
  width: 100%;
  max-width: 12rem;
  aspect-ratio: 3 / 4;
  margin: 0;
  padding: 0.45rem; /* the photo print's white matte */
  background: var(--bg-elevated);
  border: 1px solid var(--border-card);
  box-shadow: var(--shadow-md);
  transform: rotate(-2deg);
}
.photo-frame__well {
  position: absolute;
  inset: 0.45rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.5px dashed var(--sd-navy-300);
  background: rgba(6, 19, 37, 0.05);
}
.photo-frame__label {
  font-family: var(--font-prop); /* allowed prop class — see §6.8 header */
  font-size: var(--fs-14);
  letter-spacing: var(--ls-widest);
  color: var(--sd-navy-400);
}
.photo-frame img {
  position: absolute;
  inset: 0.45rem;
  width: calc(100% - 0.9rem);
  height: calc(100% - 0.9rem);
  object-fit: cover;
  /* Automatic B&W dossier treatment for the dropped-in author photo:
     grayscale plus a touch of contrast and sepia to sit in the warm
     paper palette. */
  filter: grayscale(1) contrast(1.06) sepia(0.14);
}

/* --------------------------------------------------------------------------
   6.8.5 CLOSING DIRECTIVE — the page's call to action (point 5)
   Bolder presence within the design system: centered final memo page with
   a heavy orange frame (same accent family as hr.warm-rule--heavy / the
   footer's bottom border — border accent, not an orange field), the
   largest h2 on the page, and an enlarged CTA that carries its orange halo
   at rest. Canonical Leanpub URL untouched.
   -------------------------------------------------------------------------- */
.closing-directive {
  text-align: center;
  border: 2px solid var(--brand-secondary);
}
.closing-directive h2 {
  font-size: clamp(2.25rem, 3vw + 1.25rem, 3.25rem);
  margin-top: var(--space-xl);
}
.closing-directive h2::after {
  margin-inline: auto;
}
.closing-directive p {
  text-align: center;
  text-indent: 0;
  max-width: 55ch;
  margin-inline: auto;
}
.button--lg {
  padding: var(--space-md) var(--space-3xl);
  font-size: var(--fs-18);
}
.closing-directive .button--cta {
  box-shadow: var(--shadow-orange); /* halo at rest, intensifies on hover per §6.4 */
  margin-block: var(--space-sm) var(--space-md);
}
/* Round-4 browser check (2026-07-12): "Where to buy" merged into this closing
   sheet — the ticket rail (§7.3) now lives inside the directive. The ticket
   paragraphs opt back out of the 55ch centring cap above. */
.closing-directive .channel-list {
  margin-top: var(--space-xl);
  text-align: initial; /* tickets manage their own centring */
}
.closing-directive .channel-list p {
  max-width: none;
}

/* --------------------------------------------------------------------------
   6.8.6 FOOTER COLOPHON (point 6)
   The "(c) copyright 2026 | built by Astronic" line at the very bottom:
   small, muted, separated by a faint hairline. Mission-1 footer content
   above it is untouched.
   -------------------------------------------------------------------------- */
.site-footer__colophon {
  margin-top: var(--space-xl);
  padding-top: var(--space-md);
  border-top: 1px solid rgba(254, 254, 254, 0.14);
  font-size: var(--fs-14);
  opacity: 0.78;
}

/* --------------------------------------------------------------------------
   6.8.7 REDUCED MOTION — round-2 backstop
   Nothing in §6.8 introduces a new animation: the hero-freeze rules REMOVE
   motion, and the photo filter, hierarchy styles, and colophon are static.
   The only round-2 motion change is the stronger cover tilt, which lives
   in enhance.js behind its existing prefers-reduced-motion early return.
   The rules below just keep the §6.5 reduced-motion contract intact for
   the new/changed selectors (hover pinned to rest, halo without lift).
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .memo-hero:hover,
  .memo-hero:focus-within {
    transform: none;
  }
  .closing-directive .button--cta:hover,
  .closing-directive .button--cta:focus-visible {
    transform: none;
  }
}

/* ==========================================================================
   6.9 WP03 ROUND-3 REWORK (human change request)
   Documented out-of-map edits per the round-3 human browser-check change
   request (decision plan.design.human-check-round3-changes), grouped for
   one-pass review. Point 1 (punch holes removed everywhere) is a deletion
   — see the §6.7.6 g and §6.8.1 tombstones. Point 2 (cover tilt toned
   down) lives in js/enhance.js. Points 3 and 4 are the rules below.
   Everything here is static layout — no new motion, so no reduced-motion
   additions are needed.
   ========================================================================== */

/* --------------------------------------------------------------------------
   6.9.1 SPACING RHYTHM (point 3) — more space BETWEEN sections, less
   WITHIN them: each section reads as a compact unit with clear separation
   from its neighbours.
   -------------------------------------------------------------------------- */

/* Between: widen the inter-section gap on the desk (was --space-4xl). */
.dossier-desk > section + section,
.dossier-desk > .sheet + section {
  margin-top: 6rem;
}
@media (max-width: 640px) {
  .dossier-desk > section + section,
  .dossier-desk > .sheet + section {
    margin-top: 4rem;
  }
}

/* Within: headings sit tight on their section content. h2's global
   --space-4xl top margin was inherited from mission-1's flat page flow;
   inside a sheet the eyebrow already provides the lead-in. */
.dossier-desk h2 {
  margin-top: 0;
  margin-bottom: var(--space-sm);
}
.closing-directive h2 {
  margin-top: var(--space-xs);
}

/* Within: intro-sheet → grid handoff and card gaps tightened one step. */
.dossier-desk .card-grid {
  margin-top: var(--space-lg);
  gap: var(--space-lg);
}

/* Within: sheet interiors one step tighter (§6.2 set 2xl / xl). The
   testimonial cards' 2.75rem clip-clearance padding-top (§6.8.3) has
   higher specificity and is intentionally unaffected. */
.sheet {
  padding: var(--space-xl);
}
.sheet--exhibit {
  padding: var(--space-lg);
}
.sheet--exhibit h3 {
  margin-top: var(--space-sm);
  margin-bottom: var(--space-sm);
}

/* Within: hero internals — tighter column gap and copy rhythm. */
.memo-hero {
  gap: var(--space-xl);
}
.memo-hero h1 {
  margin-bottom: var(--space-sm);
}
.memo-hero p {
  margin-bottom: var(--space-sm);
}

/* --------------------------------------------------------------------------
   6.9.2 CENTERED EXHIBITS (point 4) — Exhibit A ("What's inside") and
   Exhibit B ("What readers are saying"): section headers and card content
   centered; grids already sit centered in the page column via the desk's
   gutter formula. Copy blocks get text-indent 0 + auto margins so their
   capped reading measure centers too (overrides `main p`'s justified
   book-prose rules at higher specificity). Decorative corner props
   (paperclips, stamps) stay absolutely positioned and are unaffected.
   -------------------------------------------------------------------------- */
#inside .sheet,
#testimonials .sheet {
  text-align: center;
}
#inside h2::after,
#testimonials h2::after {
  margin-inline: auto;
}
#inside p,
#testimonials blockquote,
#testimonials figcaption {
  text-align: center;
  text-indent: 0;
  margin-inline: auto;
}

/* ==========================================================================
   6.10 WP03 ROUND-4 REWORK (human change request)
   Documented out-of-map edits per the round-4 human browser-check change
   request (decision plan.design.human-check-round4-changes), grouped for
   one-pass review. Static layout/typography only — no new motion, so no
   reduced-motion additions are needed. The nav restyle (point 4) follows
   the live sddevelopment.be navbar (fetched 2026-07-09): brand left,
   plain horizontal links pushed to the far right, minimal bar without
   heavy chrome.
   ========================================================================== */

/* --------------------------------------------------------------------------
   6.10.1 TESTIMONIAL PAPERCLIPS (point 1) — the clip hangs almost entirely
   over the card's paper (only ~0.5rem of the 4.5rem-tall clip stays above
   the edge) and tilts slightly to the RIGHT across the corner. The card's
   top padding grows to 4.5rem so the clip's tail (reaching ~4rem into the
   card) stays clear of the first text line. Scoped to Exhibit B per the
   change request; the personnel-file clip keeps its round-3 position.
   -------------------------------------------------------------------------- */
#testimonials .clip--top-edge {
  top: -0.5rem;
  right: 1.25rem;
  transform: rotate(14deg);
}
#testimonials .sheet--exhibit {
  padding-top: 4.5rem; /* overrides §6.8.3's 2.75rem clip clearance */
}

/* --------------------------------------------------------------------------
   6.10.2 EXHIBIT GRID ROW GAP (point 2) — row gap must equal column gap.
   Root cause of the unequal gap: §6.2's stacked-sheet rhythm rule
   (.sheet + .sheet { margin-top }) also matches adjacent grid items, so
   every card after the first carried a 1.75rem top margin that inflated
   the row track (and nudged cards 2+ down within row 1). Inside a grid
   the gap property owns spacing; the margin is zeroed here.
   -------------------------------------------------------------------------- */
.dossier-desk .card-grid > .sheet + .sheet {
  margin-top: 0;
}

/* --------------------------------------------------------------------------
   6.10.3 FOLDER HEADER (point 3) — memo metadata line centered.
   -------------------------------------------------------------------------- */
.folder-header {
  text-align: center;
}

/* --------------------------------------------------------------------------
   6.10.4 SITE NAV (point 4) — styled after sddevelopment.be's navbar:
   brand (logo + wordmark) left, nav links right (margin-auto push, like
   their .navbar-end), CTA at the far edge, and a minimal warm hairline
   instead of the heavy 2px blue border. Same links, same canonical CTA.
   -------------------------------------------------------------------------- */
.site-nav {
  justify-content: flex-start;
  border-bottom: 1px solid var(--border-1);
}
.site-nav__brand {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.site-nav__wordmark {
  font-family: var(--font-condensed);
  font-size: var(--fs-18);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  color: var(--brand-dark);
  white-space: nowrap; /* the brand name never breaks (see also 6.10.5) */
}
.site-nav ul {
  margin-inline-start: auto; /* links sit right, sddevelopment.be-style */
}
@media (max-width: 760px) {
  .site-nav__wordmark {
    display: none; /* logo-only brand keeps the bar intact down to 320px */
  }
}

/* --------------------------------------------------------------------------
   6.10.5 "SD DEVELOPMENT" ON ONE LINE (point 5) — the two in-content
   occurrences (personnel-file link, footer link list) must never wrap
   mid-name. Done via white-space on the existing anchors (attribute
   selector) so the mission-1 link text stays byte-identical. The anchor
   is ~8.5rem wide at fs-16 — comfortably inside a 320px viewport.
   -------------------------------------------------------------------------- */
#author a[href="https://sddevelopment.be/"],
.site-footer a[href="https://sddevelopment.be/"] {
  white-space: nowrap;
}

/* Round-6 browser check (2026-07-12): the global drop-cap first-letter tint
   (main p::first-letter, §6.2) painted the "S" of "SD Development" a different
   colour from the rest of the link. Make that first letter match the link
   colour so "SD Development" reads as one uniform orange word. */
.author-file__bio p:last-of-type::first-letter {
  color: var(--fg-link);
  font-size: 1em;
}

/* ==========================================================================
   6.11 WP03 ROUND-5 REWORK (human change request)
   Documented out-of-map edits per the round-5 human browser-check change
   request (decision plan.design.human-check-round5-changes), grouped for
   one-pass review. Most of this round is markup/asset-side (author photo
   drop-in via the §6.8.4 pattern, webp logo swap, footer copyright-line
   removal — see index.html); the CSS half is only the two items below.
   Deletions carry tombstones in place: coffee ring §6.7.6 j; the
   personnel-file paperclip removal also retires §6.10.1's "keeps its
   round-3 position" remark — .clip--top-edge now appears in Exhibit B
   only. Static styling — no new motion, no reduced-motion impact.
   ========================================================================== */

/* Colophon centered (point 4); it is now the footer's only copyright line
   (the mission-1 "© 2026 Stijn Dejongh / SD Development" line was removed
   by explicit human decision — see the FR-007 comment in index.html). */
.site-footer__colophon {
  text-align: center;
}

/* The dropped-in author photo replaces the placeholder visually: it paints
   over the .photo-frame__well (later DOM sibling, both absolutely
   positioned in the frame) — declared here for the reviewer's benefit; no
   new rule is needed, §6.8.4's .photo-frame img styling (cover fit + B&W
   filter) applies as designed. */

/* ==========================================================================
   6.12 WP03 ROUND-6 REWORK (human change request)
   Documented out-of-map edits per the round-6 human browser-check change
   request (decision plan.design.human-check-round6-changes), grouped for
   one-pass review. Point 1 (real file number, cover-sourced) and the bio/
   photo DOM order are markup-side (index.html); point 2 (cover tilt down
   to ±4°, scale dropped) lives in js/enhance.js. Static layout only — no
   new motion, no reduced-motion impact.
   ========================================================================== */

/* --------------------------------------------------------------------------
   6.12.1 PERSONNEL FILE LAYOUT (point 3) — centered title; photo RIGHT of
   the bio; the text+photo pair centered as a group. The bio now precedes
   the photo in the DOM, so the grid column order below puts the photo in
   the right-hand column; below 640px the pair stacks (text first, photo
   below, photo centered via its auto margins).
   -------------------------------------------------------------------------- */
#author .sheet {
  text-align: center; /* centers the eyebrow + h2 (title) */
}
#author h2::after {
  margin-inline: auto; /* the orange ink rule centers under the title */
}
#author .author-file {
  max-width: 52rem; /* the pair reads as one centered composition… */
  margin-inline: auto; /* …instead of hugging the sheet's left edge */
  text-align: left; /* bio copy keeps its reading alignment */
}
@media (min-width: 640px) {
  .author-file {
    /* overrides §6.8.4's photo-left order: text 1fr left, photo right */
    grid-template-columns: 1fr minmax(9rem, 12rem);
    align-items: center;
  }
}
.photo-frame {
  margin-inline: auto; /* centered in its column, and when stacked <640px */
}

/* ==========================================================================
   6.13 WP04 OUT-OF-MAP MINIMAL FIX — nav compaction below 375px
   Finding from the T019 final viewport sweep (quickstart item 1, NFR-001):
   at ≤374px the fixed nav's "Buy on Leanpub" CTA was pushed past the right
   edge (masked by the §6.5 overflow-x guard, so no scrollbar — but the
   page's always-reachable CTA, INV-1, rendered truncated). The human-checked
   layout at ≥375px (round-4 approved bar) is untouched: this query only
   tightens chrome gaps/padding and lets the link list wrap in the window
   375px never reaches. No new colours, no new motion.
   ========================================================================== */
@media (max-width: 374px) {
  .site-nav {
    padding-inline: var(--space-xs);
    gap: var(--space-xs);
  }
  .site-nav ul {
    gap: var(--space-xs);
    flex-wrap: wrap;
    row-gap: var(--space-3xs);
  }
  .site-nav .button {
    padding-inline: var(--space-sm);
    font-size: var(--fs-14);
  }
}

/* ==========================================================================
   7. MISSION stijn-review-refinements-01KX8H6R — WP02
   Stijn's review refinements, stylesheet half. All additive to the dossier
   layer above — §1–§6 rules are only overridden from here, never edited,
   except the two documented FR-008 caret edits in §6.7.6 h / §6.7.7:
     7.1  .visually-hidden utility (the nav toggle's "Menu" label needs it)
     7.2  Mobile header unsquash + hamburger dropdown (FR-001/002/004,
          NFR-001). Menu collapse is gated on html.js AND ≤640px only;
          no-JS visitors keep visible links at every width; >640px nav
          rendering is untouched.
     7.3  Purchase-channel cards (FR-005, NFR-004/005)
     7.4  Reading-sample slots (FR-007, C-004)
     7.5  Reduced-motion backstop for the one new animation
   No new colours, fonts, or assets: SD brand tokens plus the established
   dossier idiom (paper sheets, stamp red, mono prop labels) only.

   NOTE ON --font-prop (INV-7): this section adds two prop classes to the
   allowed list — .channel__badge (the RECOMMENDED stamp chip) and
   .sample__stamp (the SPECIMEN placeholder stamp). Both are stamp-family
   prop labels in the same spirit as .stamp/.file-label/.memo-meta, never
   body copy.
   ========================================================================== */

/* --------------------------------------------------------------------------
   7.1 VISUALLY-HIDDEN UTILITY
   Standard clip/position pattern: removes content from the visual layout
   while keeping it in the accessibility tree. Used by the nav toggle's
   "Menu" label (index.html, WP01) — without this rule the label would
   render as visible text the moment enhance.js unhides the button.
   -------------------------------------------------------------------------- */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   7.2 MOBILE HEADER + HAMBURGER DROPDOWN (FR-001/002/004, NFR-001)
   Gate matrix (contracts/dom-behavior.md §1):
     - default (no html.js), any width → menu visible (no-JS fallback)
     - html.js AND ≤640px             → menu hidden; dropdown panel while
                                         the nav carries .site-nav--open
     - >640px                          → menu always inline as before; the
                                         toggle can never render (FR-004)
   -------------------------------------------------------------------------- */

/* Toggle chrome. display:none here is the desktop guarantee (FR-004):
   even after enhance.js removes [hidden], nothing >640px ever shows the
   button — the show rule lives inside the ≤640px html.js branch below.
   Focus ring: the global button:focus-visible rule (§3) already covers it. */
.site-nav__toggle {
  display: none;
  align-items: center;
  justify-content: center;
  min-width: 44px; /* NFR-001 touch target */
  min-height: 44px;
  margin-inline-start: auto;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border-1);
  border-radius: var(--radius-xs);
  color: var(--brand-dark);
  cursor: pointer;
}

/* Hamburger glyph: three 2px ink bars from one span and its pseudo twins —
   no images, no new assets (NFR-005). */
.site-nav__toggle-icon {
  position: relative;
  display: block;
  width: 1.25rem;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
}
.site-nav__toggle-icon::before,
.site-nav__toggle-icon::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
}
.site-nav__toggle-icon::before {
  top: -6px;
}
.site-nav__toggle-icon::after {
  top: 6px;
}

/* Open state: the bars fold into an X. A state swap, not motion — no
   transition, so it is inherently reduced-motion-safe. */
.site-nav--open .site-nav__toggle-icon {
  background: transparent;
}
.site-nav--open .site-nav__toggle-icon::before {
  top: 0;
  transform: rotate(45deg);
}
.site-nav--open .site-nav__toggle-icon::after {
  top: 0;
  transform: rotate(-45deg);
}

@media (max-width: 640px) {
  /* T007 unsquash: the bar becomes a single brand-left/toggle-right row;
     the menu leaves the row entirely (dropdown panel below under JS, or
     stacked in flow without it). 4.5rem bar instead of the desktop 6rem,
     with the body offset and anchor scroll margins re-tuned to match. */
  .site-nav {
    height: auto;
    min-height: 4.5rem;
    flex-wrap: wrap;
    justify-content: space-between;
    padding-block: var(--space-xs);
  }
  body {
    padding-top: 4.5rem;
  }
  section[id] {
    scroll-margin-top: 5.5rem; /* 4.5rem bar + breathing room */
  }

  /* No-JS fallback (INV-6): the toggle can never appear, so the bar must
     not stay fixed over a menu it cannot collapse — it goes static and
     the menu stacks in flow, readable and navigable at every width. */
  html:not(.js) .site-nav {
    position: static;
  }
  html:not(.js) body {
    padding-top: 0;
  }
  html:not(.js) section[id] {
    scroll-margin-top: var(--space-md);
  }

  /* Shared ≤640px menu presentation (both the no-JS stacked flow and the
     JS dropdown): dossier paper panel, one link per row, every row ≥44px
     tall (NFR-001). ul.site-nav__menu ties §4/§5/§6.13's `.site-nav ul`
     on specificity and wins on source order. */
  ul.site-nav__menu {
    flex-basis: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin-inline-start: 0;
    background-color: var(--paper-warm);
    background-image: var(--paper-texture);
    border-top: 1px solid var(--border-1);
  }
  .site-nav__menu li + li {
    border-top: 1px solid var(--border-1);
  }
  .site-nav__menu a:not(.button) {
    display: flex;
    align-items: center;
    min-height: 44px; /* NFR-001 row target */
    padding: var(--space-xs) var(--space-md);
    font-size: var(--fs-16); /* overrides §5's fs-14 inline-nav shrink */
  }
  .site-nav__menu .button {
    margin: var(--space-sm) var(--space-md);
  }

  /* Menu collapse gate — exactly the contract logic: hidden only when JS
     is present to provide the toggle; shown as a dropdown panel under the
     fixed bar while the nav carries WP03's open-state class. Absolute
     positioning keeps the closed bar at 4.5rem and lets the open panel
     span the bar's full width; it overlays the memo banner only WHILE
     open (never permanently — closing restores it), and page scroll is
     never trapped: a too-tall panel scrolls itself via max-height. */
  html.js .site-nav__toggle:not([hidden]) {
    display: inline-flex;
  }
  html.js .site-nav__menu {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    max-height: calc(100vh - 4.5rem);
    max-height: calc(100dvh - 4.5rem);
    overflow-y: auto;
    box-shadow: var(--shadow-md);
  }
  html.js .site-nav--open .site-nav__menu {
    display: flex;
    animation: menu-drop 200ms var(--easing-standard);
  }

  /* T007 memo banner: let the cover-sourced line wrap to ~2 legible rows
     at 320px — tuned via mono size/tracking only; the text itself is a
     cover-sourced fact and is never edited (C-001). */
  .memo-meta {
    font-size: clamp(0.6875rem, 1.9vw + 0.3rem, var(--fs-14));
    letter-spacing: normal;
    line-height: var(--lh-normal);
  }
}
@media (max-width: 480px) {
  /* A step more line width for the memo banner at phone sizes */
  .folder-header {
    padding-inline: var(--space-xs);
  }
}

/* One-shot fade for the dropdown opening (fades only — brand motion rule);
   forced instant under reduced motion in §7.5. Closing is instant by
   design (plain display flip). */
@keyframes menu-drop {
  from {
    opacity: 0;
  }
}

/* --------------------------------------------------------------------------
   7.2b CENTRED INTRO SHEETS — round-2 browser check (2026-07-12)
   The human check asked for centred titles, standfirsts, and exhibit
   labels on the two new sections. The modifier lives on the intro sheet
   only, so Exhibit A/B and the personnel file keep their filed, left-set
   look while #sample and #buy open like printed cover pages.
   -------------------------------------------------------------------------- */
.sheet--centered {
  text-align: center;
}
.sheet--centered h2::after {
  margin-inline: auto; /* same centred title rule as #inside/#testimonials */
}
.sheet--centered p {
  margin-inline: auto;
  text-indent: 0;
}

/* --------------------------------------------------------------------------
   7.3 PURCHASE CHANNELS (#buy, FR-005, NFR-004/005)
   ROUND-2 REWORK (human browser check, 2026-07-12): the first pass drew the
   channels as three more paper sheets — flagged as "too much of the same",
   titles to be centred. Channels are now requisition TICKETS: a double-rule
   ticket frame (border + negative-offset outline), centred condensed
   headings, and a dashed perforation line that separates the ticket body
   from the counterfoil (CTA / status stub). Leanpub is the full-width
   master ticket; the Amazon entries are half-width pending stubs.
   Recommended vs pending is still carried by border weight/style +
   badge/status text, never colour alone (NFR-004, grayscale-safe). Muted
   text stays --fg-3 (~5.6:1 on paper, AA-clear).
   -------------------------------------------------------------------------- */
.channel-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
  max-width: var(--layout-max-width);
  margin-inline: auto;
}
.dossier-desk .channel-list {
  margin-top: var(--space-lg); /* same intro-sheet handoff as .card-grid */
}
@media (min-width: 640px) {
  /* Round-3 browser check (2026-07-12): tickets equal size on one row — the
     grid's stretch alignment plus each ticket's margin-top:auto perforation
     keeps the counterfoils on a shared baseline. Three columns: Leanpub,
     Amazon, and a coming-soon placeholder (round-6). */
  .channel-list {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* Ticket body: centred flex column. The ::after pseudo-element is the
   perforation — it participates in the flex order (order 1) with
   margin-top auto, so the "tear here" line always sits directly above the
   counterfoil (order 2: CTA or status chip) whatever the body height. */
.channel {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-xs);
  padding: var(--space-lg) var(--space-xl);
  background-color: var(--paper-warm);
  background-image: var(--paper-texture);
  border: 1px solid var(--sd-navy-300);
  outline: 1px solid var(--sd-navy-300);
  outline-offset: -6px; /* inner rule of the double ticket frame */
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-sheet-rest);
  color: var(--fg-1);
}
.channel h3 {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  margin: 0;
  font-family: var(--font-condensed);
  font-size: var(--fs-20);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--ls-widest);
}
.channel p {
  margin: 0;
  text-align: center;
  text-indent: 0;
}
.channel__formats {
  font-size: var(--fs-14);
  color: var(--fg-2);
}
.channel::after {
  content: "";
  order: 1;
  width: 100%;
  margin-top: auto;
  margin-bottom: var(--space-2xs);
  border-top: 2px dashed var(--sd-navy-300);
}
/* Counterfoil row: CTA (live ticket) or status chip (pending stub) */
.channel .button,
.channel .channel__status {
  order: 2;
  margin-top: 0;
}

.channel__badge {
  display: inline-block;
  padding: var(--space-3xs) var(--space-xs);
  font-family: var(--font-prop); /* allowed prop class — §7 header note */
  font-size: var(--fs-14);
  font-weight: var(--fw-regular);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--stamp-red);
  border: 2px solid var(--stamp-red);
  border-radius: 2px;
  transform: rotate(-2deg);
}
.channel--recommended {
  border: 2px solid var(--brand-secondary);
  outline-color: var(--brand-secondary);
  box-shadow: var(--shadow-sheet-lift);
}

.channel--coming-soon {
  background-image: none; /* flatter paper: a stub nobody has torn yet */
  border: 1px dashed var(--sd-navy-300);
  outline: none; /* pending stubs lose the inner rule */
  cursor: default; /* obviously not clickable */
}
.channel--coming-soon h3 {
  color: var(--fg-3);
}
.channel--coming-soon .channel__formats {
  color: var(--fg-3);
}
.channel__status {
  display: inline-block;
  padding: var(--space-3xs) var(--space-xs);
  font-family: var(--font-condensed);
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--ls-widest);
  color: var(--fg-3);
  border: 1px dashed var(--sd-navy-300);
  border-radius: 2px;
}

/* --------------------------------------------------------------------------
   7.4 READING-SAMPLE SLOTS (#sample, FR-007, C-004)
   ROUND-2 REWORK (human browser check, 2026-07-12): the three excerpts were
   full-width sheets — same "more of the same" flag. They are now CATALOGUE
   INDEX CARDS in a .sample-catalog rail: ruled card faces (stamp-red head
   rule + cool hairline lines from --border-2), centred condensed headings,
   a punched reinforcement ring at the card foot, and a light fan of
   rotations ≥960px reusing the §6.5 tilt tokens. The placeholder state
   stays loud: rotated SPECIMEN stamp, dashed pending frame, muted
   filing-note ink (data-model invariant). Dropping .sample--placeholder
   plus the stamp line still yields a clean live-excerpt card with zero
   CSS edits.
   -------------------------------------------------------------------------- */
.sample-catalog {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
  max-width: var(--layout-max-width);
  margin-inline: auto;
}
.dossier-desk .sample-catalog {
  margin-top: var(--space-lg); /* intro-sheet handoff, as .channel-list */
}
@media (min-width: 960px) {
  .sample-catalog {
    grid-template-columns: repeat(3, 1fr);
    align-items: start;
  }
  /* Fan the drawer: hand-set tilts, no transition (static state) */
  .sample-catalog .sample:nth-child(1) {
    transform: rotate(var(--tilt-2));
  }
  .sample-catalog .sample:nth-child(2) {
    transform: rotate(var(--tilt-1));
  }
  .sample-catalog .sample:nth-child(3) {
    transform: rotate(var(--tilt-3));
  }
}

.sample {
  position: relative;
  width: 100%;
  margin-inline: auto;
  padding: var(--space-xl) var(--space-lg) var(--space-3xl);
  background-color: var(--paper-warm);
  /* Ruled index-card face: cool hairlines over the paper grain */
  background-image:
    repeating-linear-gradient(
      to bottom,
      transparent 0,
      transparent calc(1.6rem - 1px),
      var(--border-2) calc(1.6rem - 1px),
      var(--border-2) 1.6rem
    ),
    var(--paper-texture);
  border-top: 3px solid var(--stamp-red); /* index-card head rule */
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-sheet-rest);
  color: var(--fg-1);
  text-align: center;
}
/* Punched reinforcement ring at the card foot (catalogue-drawer rod hole) */
.sample::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: var(--space-md);
  width: 14px;
  height: 14px;
  transform: translateX(-50%);
  border: 2px solid var(--sd-navy-300);
  border-radius: 50%;
  background-color: var(--bg-page);
}
.sample h3 {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-condensed);
  font-size: var(--fs-20);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--ls-widest);
}
/* Round-3 browser check (2026-07-12): the SPECIMEN stamp is retired — the
   placeholder cards now stay deliberately blank until real excerpts land.
   The dashed pending frame plus the min-height below keep an empty card
   reading as a waiting index card rather than a rendering glitch. */
/* Card body reads left-aligned under the centred head (index-card idiom) */
.sample__body {
  max-width: none;
  text-align: left;
}
.sample__body p {
  text-indent: 0;
}
.sample--placeholder {
  border: 1px dashed var(--sd-navy-300); /* pending-file frame */
  border-top: 3px solid var(--stamp-red); /* head rule survives the state */
  min-height: 11rem; /* an empty card still reads as a ruled index card */
}
.sample--placeholder .sample__body p {
  color: var(--fg-3); /* muted filing-note ink, AA-clear on paper */
}
.sample__cta {
  max-width: none;
  margin: var(--space-xl) 0 0;
  text-align: center;
  text-indent: 0;
}


/* --------------------------------------------------------------------------
   7.5 REDUCED MOTION — §7 additions
   The only new animation in this mission's CSS is the dropdown's one-shot
   fade; open/close must be instant under reduced motion (contract:
   functionality is not motion). The icon fold and all §7.3/§7.4 styling
   are static state swaps with no transition. The FR-008 caret backstop
   lives in §6.7.7, alongside the rule it replaces.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html.js .site-nav--open .site-nav__menu {
    animation: none;
  }
}
