/* d-ma.be — shared stylesheet.
 *
 * Extracted from the per-page <style> blocks once the site grew past three
 * pages: five copies of the same tokens is five places for the identity to
 * drift, and drift is the failure mode this site has already had.
 *
 * Same-origin by construction. /privacy asserts these pages make no
 * third-party transfers, so this file — like the webfonts — is served from
 * here and never from a CDN.
 *
 * Palette: the v3 design system (docs/branding/tokens.md records the handoff
 * and what it supersedes). Warm-shifted near-blacks and a bone white; no pure
 * black or white. Dark is the source of truth.
 *
 * NOTE: v3 introduces a chromatic accent, which the previous identity
 * deliberately did not have. `--link` is the ONLY interactive hue, and the
 * three status hues are confined to dots, 1px rules and text. Everything else
 * is still set apart with a rule and space rather than with colour.
 */

@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;          /* variable: one binary covers the range */
  font-display: swap;
  src: url('/space-grotesk.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/ibm-plex-mono-400.woff2') format('woff2');
}

:root {
  /* Dark is the source of truth, not a preference. The v3 design system defines
     the palette dark-first and treats light as an INVERSION used for the PDF,
     the OG image and print — so this is `dark`, not `light dark`, and there is
     no prefers-color-scheme branch. A light-preference visitor gets the dark
     site, which is the intended identity. */
  color-scheme: dark;

  /* Four surface steps. Depth comes from these, never from shadow — there is no
     elevation scale in this system. */
  --surface-inset:  #131216;   /* code, specimens, recessed blocks */
  --surface-page:   #17161a;   /* page background */
  --surface-panel:  #1e1d21;   /* cards and panels */
  --surface-raised: #26252a;   /* emphasised cards */

  /* One border weight only: 1px. */
  --line:        #2c2b30;
  --line-strong: #3a3941;      /* emphasised panels */

  --text-primary:   #f2f1ee;   /* headings, emphasis      13.4:1 */
  --text-bright:    #d6d3cb;   /* hero lede, contrast rows */
  --text-secondary: #a5a29a;   /* body prose               6.4:1 */
  --text-tertiary:  #7d7a73;   /* labels, meta             3.9:1 — 11px and up ONLY */

  --bone:     #f5f4f1;         /* primary button, light inversion */
  --bone-ink: #141311;         /* text on bone */

  /* Status hues, muted set. NEVER a background fill and never a panel tint:
     a 10/8/7px dot, a 1px rule, or text. Scope is build state and generator
     verdicts only. A brighter `vivid` set exists in the design system; ship
     this one unless asked. */
  --status-ok:     #7fb08a;    /* built and running */
  --status-warn:   #d9ab55;    /* specified, not built */
  --status-danger: #dd7f6e;    /* blocked, rejected */

  /* The only interactive hue. */
  --link:       #7fd1d8;
  --link-hover: #a9e6eb;

}

/* Light inversion — for print and PDF export, per the design system's own
   "Light inversion" row: background #f5f4f1, line #e3e0d9, body text #55524c,
   headings #141311. Not a user-facing theme.

   infra#321: this used to reassign a layer of legacy ALIASES, which meant it
   only inverted the base element rules. Every v3 component reads the real
   tokens directly, so `.v3-h1 { color: var(--text-primary) }` stayed
   near-white and printed white-on-bone — invisible. Overriding the tokens
   themselves inverts the whole page, which is what the design system
   describes.

   Surfaces collapse to white rather than the four dark steps: on paper the
   depth cue is the 1px line, and four near-white greys would just band. */
@media print {
  :root {
    --surface-page:   var(--bone);
    --surface-panel:  #ffffff;
    --surface-inset:  #ffffff;
    --surface-raised: #ffffff;

    --line:           #e3e0d9;
    --line-strong:    #e3e0d9;

    --text-primary:   var(--bone-ink);
    --text-bright:    var(--bone-ink);
    --text-secondary: #55524c;
    --text-tertiary:  #55524c;

    /* The interactive hue is a light cyan chosen for a dark ground; on bone it
       is close to unreadable. Print has no hover state to lose, so links take
       the ink colour and keep their underline as the affordance. */
    --link:           var(--bone-ink);
    --link-hover:     var(--bone-ink);
  }
}

html, body {
  margin: 0;
  font-family: 'Space Grotesk', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  background: var(--surface-page);
  color: var(--text-primary);
  -webkit-text-size-adjust: 100%;
}
main { max-width: 40rem; margin: 0 auto; padding: 3rem 1.5rem 5rem; line-height: 1.6; }
main.wide { max-width: 46rem; }

a { color: var(--text-primary); overflow-wrap: anywhere; }
a[href^="mailto:"] { overflow-wrap: normal; white-space: nowrap; }
a:focus-visible { outline: 2px solid var(--text-primary); outline-offset: 2px; }

h1 { font-size: 2rem; font-weight: 600; margin: 0 0 0.35rem; letter-spacing: -0.02em; line-height: 1.15; text-wrap: balance; }
h2 { font-size: 1.3rem; font-weight: 600; margin: 2.75rem 0 0.6rem; letter-spacing: -0.012em; line-height: 1.25; text-wrap: balance; }
h3 { font-size: 1.02rem; font-weight: 600; margin: 1.75rem 0 0.35rem; letter-spacing: -0.005em; }
/* Body copy is ink, not grey. Muted is for labels and metadata only — the
   previous inversion (dark headings, washed-out prose) is what made every page
   read as an undifferentiated slab. */
p, ul, ol { color: var(--text-primary); margin: 0.6rem 0; }
li { margin: 0.35rem 0; }
strong { color: var(--text-primary); font-weight: 600; }
code { font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.9em; }

/* Marks a consulting engagement as distinct from employment. A prospective
   client is looking for "who has hired this person as a consultant", and that
   list is much shorter than the career. The last class surviving from the
   pre-v3 stylesheet; kept because it is still used (portfolio, research) and
   reads correctly on v3 tokens. infra#321 retired the alias layer around it. */
.eng {
  font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.62rem;
  letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-secondary);
  border: 1px solid var(--line-strong); border-radius: 3px; padding: 0.05rem 0.3rem;
  margin-left: 0.4rem; white-space: nowrap; vertical-align: 0.08rem;
}

/* ─────────────────────────────────────────────────────────────────────────────
   v3 layout primitives (infra#302)

   OPT-IN BY CLASS, deliberately (infra#319 finished the migration this
   enabled: all eight pages now use .shell). Element rules would have reflowed
   every page in a single unreviewable change; opt-in let each page move on
   its own commit instead.

   Spacing is the design system's scale — multiples of 4, and the specific values
   the handoff names: 48 page gutter, 56 below a section rule, 88 section break,
   24 card padding, 16 card gutter.

   No shadows anywhere: depth is the four surface steps plus a 1px line. There is
   no elevation scale in this system, and e2e/tests/design.spec.ts enforces that.
   ───────────────────────────────────────────────────────────────────────────── */

/* Page shell. Gutter drops 48 -> 24 below ~720px, where the design says the
   two-column grids collapse. */
.shell { max-width: 1180px; margin-inline: auto; padding-inline: 48px; }

/* Section: 1px top rule, 56px lead before the heading, 88px after the content.
   The first section on a page has no rule above it — the header already
   provides one. */
.section { border-top: 1px solid var(--line); padding-top: 56px; padding-bottom: 88px; }
.section:first-of-type { border-top: 0; }

/* Anchor targets sit under a sticky header; without this the heading lands
   behind it. 80px per the handoff. */
[id] { scroll-margin-top: 80px; }

/* Panels. radius 10 for a content card, 12–14 when the panel owns a page
   region. --raised is for the cards that carry the argument (pipeline stages
   4–5), which the design marks with both a lighter surface and a stronger line. */
.panel { background: var(--surface-panel); border: 1px solid var(--line);
         border-radius: 10px; padding: 24px; }
.panel--region { border-radius: 14px; }
.panel--raised { background: var(--surface-raised); border-color: var(--line-strong); }

/* Inset: recessed block for specimens and code. Nested containers drop to this
   surface rather than gaining a shadow. */
.inset { background: var(--surface-inset); border-radius: 6px; padding: 16px; }

/* Chip: mono metadata, 1px border, radius 4. --strong is the emphasised column
   (stronger line, brighter text) the comparison panels use. */
.chip { display: inline-block; font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
        font-size: 11px; line-height: 1.5; color: var(--text-secondary);
        border: 1px solid var(--line); border-radius: 4px; padding: 4px 8px; }
.chip--strong { border-color: var(--line-strong); color: var(--text-bright); }

/* Mono label / eyebrow. text-tertiary is 3.9:1 and permitted here precisely
   because this is mono at 11px — the documented exception, enforced by
   design.spec.ts. Section eyebrows take the wider +0.16em. */
.label { font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
         font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
         color: var(--text-tertiary); }
.label--section { letter-spacing: 0.16em; }

@media (max-width: 720px) {
  .shell { padding-inline: 24px; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   v3 header and footer (infra#302)

   Scoped to .v3-* rather than bare `header`/`footer` elements — infra#302's
   opt-in-by-class reasoning applies here too, and did the job: all eight
   pages now use these.

   THE MARK IS INLINED, not <img>. The design ships one path with
   fill="currentColor" precisely so light/dark needs no second file — but that
   only works when the SVG is in the document and can inherit. The served
   /d-ma-mark.svg has currentColor PINNED to ink #141311, because Authentik
   loads it as an <img> where nothing inherits; using that file on this dark
   header would render the mark near-invisible. Inline the size-appropriate cut.

   Cuts are chosen by RENDERED SIZE, never by scaling one down — the frame
   stroke differs per cut. Header 26px -> the 25-32px cut. Footer 24px -> the
   17-24px cut, which is a solid tile with no open frame.
   ───────────────────────────────────────────────────────────────────────────── */

.v3-header {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
  padding: 16px 48px;
  border-bottom: 1px solid var(--line);
  /* d9 alpha over surface-page, so content scrolling under it stays legible. */
  background: color-mix(in srgb, var(--surface-page) 85%, transparent);
  backdrop-filter: blur(12px);
}
/* Where backdrop-filter is unsupported the panel must not be see-through. */
@supports not (backdrop-filter: blur(12px)) {
  .v3-header { background: var(--surface-page); }
}

.v3-lockup { display: flex; align-items: center; gap: 10px; text-decoration: none; color: var(--text-primary); }
.v3-lockup svg { width: 26px; height: 26px; flex: none; }
.v3-wordmark { font-size: 14px; font-weight: 600; letter-spacing: -0.02em; }

.v3-nav { display: flex; flex-wrap: wrap; gap: 20px; }
.v3-nav a {
  font-size: 13.5px; color: var(--text-secondary); text-decoration: none;
  padding-bottom: 2px; border-bottom: 1px solid transparent;
}
.v3-nav a:hover { color: var(--text-primary); }
/* Active carries a 1px underline in text-primary — the only nav affordance. */
.v3-nav a[aria-current="page"] { color: var(--text-primary); border-bottom-color: var(--text-primary); }

.v3-contact {
  margin-left: auto;
  font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px; color: var(--text-tertiary); text-decoration: none;
}
.v3-contact:hover { color: var(--text-secondary); }

.v3-footer {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px;
  padding: 30px 48px;
  border-top: 1px solid var(--line);
  max-width: none; margin: 0;
}
.v3-footer-meta {
  font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px; color: var(--text-tertiary);
}
.v3-footer-meta a { color: var(--text-tertiary); }
.v3-footer-meta a:hover { color: var(--text-secondary); }
.v3-footer svg { width: 24px; height: 24px; flex: none; color: var(--text-tertiary); }

@media (max-width: 720px) {
  .v3-header, .v3-footer { padding-inline: 24px; }
  .v3-contact { margin-left: 0; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   v3 hero + checks (infra#302)

   .v3-main resets the pre-v3 `main { max-width: 40rem }`, which otherwise
   squeezes the whole page into a 640px column inside the 1180px shell. Third
   time a bare element rule has leaked into a v3 component (after `footer` and
   the pinned mark) — this stylesheet styles main/footer/h1/h2/p/a, and v3
   markup inherits all of them unless scoped over.
   ───────────────────────────────────────────────────────────────────────────── */

.v3-main { max-width: none; margin: 0; padding: 0; }

/* Hero: text column and the "Where this sits" card, collapsing below ~720px
   with the card under the text. */
.v3-hero { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(300px, 0.8fr);
           gap: 56px; align-items: start; padding-top: 84px; }
@media (max-width: 720px) { .v3-hero { grid-template-columns: 1fr; gap: 32px; } }

.v3-h1 { font-size: clamp(36px, 4.4vw, 56px); font-weight: 600; letter-spacing: -0.038em;
         line-height: 1.05; text-wrap: balance; margin: 12px 0 0; color: var(--text-primary); }
.v3-h2 { font-size: 34px; font-weight: 600; letter-spacing: -0.03em; line-height: 1.12;
         text-wrap: balance; margin: 12px 0 0; color: var(--text-primary); }
.v3-lede { font-size: 18px; line-height: 1.55; color: var(--text-bright);
           max-width: 640px; margin: 20px 0 0; text-wrap: pretty; }
.v3-sub  { font-size: 15px; line-height: 1.65; color: var(--text-secondary);
           max-width: 640px; margin: 16px 0 0; text-wrap: pretty; }
.v3-heading { max-width: 720px; }

/* Buttons. Primary is bone; secondary is a 1px line. Nothing moves on hover —
   no lift, no shadow, no scale. */
.v3-actions { display: flex; flex-wrap: wrap; gap: 20px; margin: 28px 0 0; }
.v3-btn { font-size: 14px; font-weight: 500; border-radius: 6px; padding: 11px 18px;
          text-decoration: none; background: var(--bone); color: var(--bone-ink);
          border: 1px solid var(--bone); }
.v3-btn:hover { background: #fff; border-color: #fff; }
.v3-btn--ghost { background: transparent; color: var(--text-secondary); border-color: var(--line); }
.v3-btn--ghost:hover { background: transparent; color: var(--text-primary); border-color: var(--line-strong); }

/* Contrast rows: 2px left border, grey line states what others do, bone line
   states what we do. */
.v3-card-title { font-size: 17px; font-weight: 600; letter-spacing: -0.025em; margin: 0 0 16px; color: var(--text-primary); }
.v3-contrast { border-left: 2px solid var(--line); padding-left: 14px; margin: 0 0 16px; }
.v3-contrast--key { border-left-color: var(--text-primary); }
.v3-contrast:last-child { margin-bottom: 0; }
/* text-secondary, not the tertiary the component spec names: at 13px the
   handoff's own acceptance check forbids #7d7a73 (3.9:1, below AA), and the
   contrast row is prose a reader has to actually read. Decided 2026-08-24. */
.v3-them { font-size: 13px; line-height: 1.5; color: var(--text-secondary); margin: 0; }
.v3-us   { font-size: 13.5px; line-height: 1.5; color: var(--text-bright); margin: 4px 0 0; }

/* The checks grid. */
.v3-checks { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
             gap: 16px; margin-top: 32px; }
.v3-rules { border-color: var(--line-strong); }
.v3-rule { margin-bottom: 20px; }
.v3-rule:last-child { margin-bottom: 0; }
.v3-rule-title { font-size: 14.5px; font-weight: 500; margin: 4px 0 0; color: var(--text-primary); }
.v3-fine { font-size: 13px; line-height: 1.55; color: var(--text-secondary); margin: 6px 0 0; }

/* Ad specimen. */
.v3-ad { display: flex; flex-direction: column; gap: 7px; padding: 20px; }
.v3-ad p { margin: 0; }
.v3-ad-tag { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 10.5px;
             letter-spacing: 0.12em; text-transform: uppercase; color: var(--bone-ink);
             background: var(--text-secondary); border-radius: 3px; padding: 2px 6px; }
.v3-ad-url { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 12px; color: var(--text-secondary); }
.v3-ad-headline { font-size: 18px; line-height: 1.3; font-weight: 500; letter-spacing: -0.015em; color: var(--text-primary); }
.v3-ad-body { font-size: 13.5px; line-height: 1.55; color: var(--text-secondary); }

/* Status row + the caveat, which sits OUTSIDE the inset and is not optional. */
.v3-mono { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 11px; color: var(--text-secondary); }
.v3-status { display: flex; align-items: center; gap: 8px; margin: 14px 0 0; }
/* infra#307. The dots are accents, not the message: in every place one appears
   the state is already written next to it in words — "passed 01 · 02 · 03",
   "no API writes", "specified", "Failures are logged with the rule they broke".
   So they are aria-hidden rather than labelled, which keeps them out of the
   accessibility tree instead of inventing a meaning for them. That is also what
   satisfies WCAG 1.4.1: colour is not the only visual carrier of the state,
   because the text carries it.
   forced-colors is handled explicitly — a background-only dot disappears
   entirely in Windows high-contrast mode, so it falls back to a border. */
.v3-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; display: inline-block; }
@media (forced-colors: active) {
  .v3-dot { border: 1px solid currentColor; background: currentColor; }
}
.v3-dot--ok { background: var(--status-ok); }
/* Same decision as .v3-them: the caveat is a disclaimer that must be legible,
   and at 12.5px tertiary fails the handoff's own contrast rule. */
.v3-caveat { font-size: 12.5px; line-height: 1.5; color: var(--text-secondary); margin: 12px 0 0; }

/* Rejected lines: offending copy in mono, verdict in the failure hue. */
.v3-reject { border-bottom: 1px solid var(--line); padding: 12px 0; }
.v3-reject:first-child { padding-top: 0; }
.v3-reject:last-child { border-bottom: 0; padding-bottom: 0; }
.v3-reject-copy { font-size: 11.5px; color: var(--text-bright); margin: 0; }
.v3-verdict { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 11px;
              color: var(--status-danger); margin: 6px 0 0; }

/* ── v3 remaining sections (infra#302) ────────────────────────────────────── */

/* Two-column prose + panel. */
.v3-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, 0.85fr); gap: 48px; align-items: start; }
@media (max-width: 720px) { .v3-two { grid-template-columns: 1fr; gap: 28px; } }
.v3-h2--minor { font-size: 26px; line-height: 1.2; }

/* Rule-separated list inside a panel. */
.v3-item { border-bottom: 1px solid var(--line); padding: 13px 0; }
.v3-item:first-of-type { padding-top: 0; }
.v3-item:last-child { border-bottom: 0; padding-bottom: 0; }
.v3-item-title { font-size: 13.5px; font-weight: 500; color: var(--text-primary); margin: 0; }
.v3-item .v3-fine { margin-top: 4px; }

/* Five pipeline stages; 4 and 5 are raised via panel--raised in the markup. */
.v3-stages { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; margin-top: 32px; }
.v3-stage-title { font-size: 15px; font-weight: 600; letter-spacing: -0.02em; color: var(--text-primary); margin: 4px 0 0; }
.v3-stage-title .v3-dot { margin-right: 8px; vertical-align: middle; }
.v3-dot--danger { background: var(--status-danger); width: 8px; height: 8px; }
.v3-dot--warn { background: var(--status-warn); }

/* Today vs API comparison. */
.v3-compare { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; margin-top: 32px; }
.v3-col { padding: 0; overflow: hidden; }
.v3-col--next { border-color: var(--line-strong); }
.v3-colhead { display: flex; align-items: center; gap: 10px; padding: 16px 24px; border-bottom: 1px solid var(--line); }
.v3-col-title { font-size: 17px; font-weight: 600; margin: 0; color: var(--text-primary); }
.v3-colstate { margin-left: auto; }
.v3-colbody { padding: 0 24px 8px; }
.v3-block { border-bottom: 1px solid var(--line); padding: 16px 0; }
.v3-block:last-child { border-bottom: 0; }
.v3-chips { display: flex; flex-wrap: wrap; gap: 7px; margin: 8px 0 0; }
.v3-note { margin-top: 16px; }
.v3-note .v3-dot { margin-right: 10px; vertical-align: middle; }

/* API operation cards + the reviewer's panel. */
.v3-ops { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; margin-top: 32px; }
.v3-op-title { font-size: 19px; font-weight: 600; letter-spacing: -0.025em; margin: 0 0 8px; color: var(--text-primary); }
.v3-small { font-size: 13.5px; line-height: 1.6; color: var(--text-secondary); margin: 0; }
.v3-scope { border-top: 1px solid var(--line); margin: 14px 0 0; padding-top: 12px; }

.v3-review { margin-top: 16px; padding: 0; border-color: var(--line-strong); }
.v3-reviewhead { padding: 16px 24px; border-bottom: 1px solid var(--line); }
.v3-reviewhead .label { margin: 0; }
.v3-reviewbody { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
                 gap: 22px 32px; padding: 24px; }
.v3-reviewbody .label { margin: 0 0 6px; }

.v3-links { font-size: 13.5px; line-height: 1.9; margin: 16px 0 0; color: var(--text-secondary); }
.v3-attrib { margin: 14px 0 0; }

/* The consent-screen logo must be SHOWN, not merely served: brand verification
   rejected the app while /logo-120.png was declared in schema.org and rendered
   on no page. It sits in the reviewer's panel because that is where a reviewer
   comparing the consent screen against the site will look. Enforced by
   e2e/rules.json -> required_logo_image. */
.v3-reviewhead { display: flex; align-items: center; gap: 14px; }
.v3-applogo { width: 40px; height: 40px; flex: none; border: 1px solid var(--line); border-radius: 8px; }
/* infra#304. The consent-screen logo moved out of the reviewer panel and into
   the company section. Rendered at 48px: large enough that a reviewer comparing
   it against the consent screen sees the same mark, small enough that the
   section still reads as prose rather than as a badge. */
.v3-companymark { margin: 0 0 14px; }
.v3-companymark img { width: 48px; height: 48px; border: 1px solid var(--line); border-radius: 8px; display: block; }
