/* ai-wall.com — one stylesheet. No external requests: fonts are self-hosted WOFF2.
   Direction: a printed technical document, not a dark SaaS dashboard. Paper, ink,
   one signal colour. The audience arrives from a DOI and a methods paper.

   Mobile-first: base rules are the small-screen values; larger screens opt in at 26em. */

/* ---------- fonts (self-hosted, SIL OFL) ---------- */

@font-face {
  font-family: "Archivo";
  src: url("/fonts/Archivo-Variable.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Atkinson";
  src: url("/fonts/AtkinsonHyperlegible-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Atkinson";
  src: url("/fonts/AtkinsonHyperlegible-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Metric-matched fallbacks. Measured in the browser: the system fallback renders the
   headline 1.82% wider than Archivo and body text 1.91% wider than Atkinson. That gap is
   what produced ~0.05 CLS when the webfont swapped in — the line COUNT was identical
   (3 lines either way), so this is metric drift, not a reflow. `size-adjust` scales the
   fallback so the two occupy the same space and the swap costs nothing. */
@font-face {
  font-family: "Archivo Fallback";
  src: local("Helvetica Neue"), local("Arial"), local("Liberation Sans");
  size-adjust: 98.2%;
  font-weight: 400 700;
}
@font-face {
  font-family: "Atkinson Fallback";
  src: local("Helvetica Neue"), local("Arial"), local("Liberation Sans");
  size-adjust: 98.1%;
  font-weight: 400 700;
}

:root {
  /* colour */
  --paper: #e9e2d4;
  --panel: #f4efe4;
  --ink: #14120d;
  --muted: #4a4236;
  --rule: #cfc4ae;
  --rule-strong: #a89a80;
  --accent: #7a2913;
  --accent-wash: #e0d3c2;

  /* type — one modular scale, ratio 1.25, base 1rem = the user's own setting.
     Named by ROLE, not by value, so a size change stays a one-line edit. */
  --t-label: 0.8rem;    /* uppercase eyebrows, table labels          */
  --t-small: 0.9rem;    /* captions, footer, tool lists              */
  --t-body: 1rem;       /* everything meant to be read               */
  --t-lead: 1.125rem;   /* opening paragraph, sub-headings           */
  --t-strong: 1.25rem;  /* names, figures                            */

  --display: "Archivo", "Archivo Fallback", "Helvetica Neue", Arial, sans-serif;
  --body: "Atkinson", "Atkinson Fallback", "Helvetica Neue", Arial, sans-serif;

  --maxw: 66rem;
  --measure: 62ch;

  /* spacing — one scale. Tight inside a group, generous between sections;
     that difference is what creates rhythm, not the absolute values. */
  --space-2xs: 0.25rem;
  --space-xs: 0.4rem;
  --space-sm: 0.75rem;
  --space-md: 1.1rem;
  --space-lg: 1.75rem;
  --space-xl: 2.5rem;
  --space-section: clamp(2rem, 4.5vw, 3rem);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--t-body);
  line-height: 1.6;
  font-kerning: normal;
  overflow-wrap: break-word;
}

/* Background. The map is NOT washed out — it is tone-mapped into the palette, which is
   a different and better way to protect the text.

   A photographic basemap spans luminance 10..254; laid under body copy at full strength
   its deep ocean would put dark text on near-black. The earlier answer was to dilute it
   to 15% opacity, and at that strength it stopped reading as a map at all.

   So the strip is a DUOTONE: every pixel keeps its shape and position, but its luminance
   is remapped into 179..235 — bounded by construction. Contrast is then guaranteed
   everywhere at once rather than measured spot by spot: ink 9.4:1, secondary text 4.9:1
   and links 4.9:1 against the DARKEST tone the map can produce. Sea pixels are nudged
   0.55 of a step cooler at the same luminance, so a coastline still reads as a coastline
   and not as sepia texture.

   Geometry unchanged: a 760px band from the platform's own basemap at native resolution,
   pinned to the right edge, stacked with its own vertical mirror so `repeat-y` runs down
   a 5000px page with no seam (measured: 0/255 at the join AND at the wrap). Its left
   200px ramps into the paper colour, so the ocean side of the page stays plain. */
body {
  background-image:
    url("/img/coast.webp?v=c2adf7508a"),
    url("/img/contours.svg?v=5495b359c9");
  background-repeat: repeat-y, repeat;
  background-size: auto 3000px, 900px auto;
  background-position: right top, top center;
}

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

/* Atkinson Hyperlegible declares a very high underline position, which drew the rule
   through the middle of digits — it read as a strikethrough on exactly the two strings
   that carry credibility (ORCID, DOI). `text-underline-offset` alone does NOT fix this:
   it is added to the font's own metric, so a bad metric stays bad. `text-underline-position:
   under` ignores the metric and places the rule below the whole content box. */
a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-position: under;
  text-underline-offset: 0.02em;
  text-decoration-thickness: 1px;
  text-decoration-skip-ink: auto;
}
a:hover { text-decoration-thickness: 2px; }
a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---------- header ---------- */

.topbar { border-bottom: 1px solid var(--rule-strong); }
.topbar .wrap {
  display: flex; flex-wrap: wrap; gap: var(--space-2xs) var(--space-md);
  align-items: center; justify-content: space-between;
  padding-top: var(--space-xs); padding-bottom: var(--space-xs);
  font-size: var(--t-small);
}
.brand {
  display: inline-flex; align-items: center; min-height: 44px;
  font-family: var(--display); font-weight: 700; font-size: var(--t-body);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink); text-decoration: none;
}

/* Tap area comes from padding, not from type size: WCAG 2.2 AA (2.5.8) wants 24x24 CSS px
   minimum and the inline-in-a-sentence exemption does not apply to standalone nav links.
   The language toggle measured 18x19 px before this. 44px is the AAA target (2.5.5). */
.topnav { display: flex; gap: var(--space-2xs); align-items: center; color: var(--muted); }
.topnav a,
.topnav .here {
  display: inline-flex; align-items: center;
  min-height: 44px; padding: 0 var(--space-sm);
}
.topnav a { color: var(--ink); }
.topnav .here { font-weight: 700; }

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



.hero { padding: clamp(1.75rem, 5vw, 2.5rem) 0 var(--space-lg); }
.hero h1 {
  font-family: var(--display); font-weight: 700;
  /* Size and measure trade against each other: capping the measure at 18ch pushed the
     headline to five lines at 56px, which is taller than four lines at 48px. Fewer,
     longer lines win. `text-wrap: balance` evens the ragged edge where supported. */
  font-size: clamp(2rem, 5.2vw, 3rem); line-height: 1.08;
  letter-spacing: -0.02em; margin: 0 0 var(--space-sm); max-width: 26ch;
  text-wrap: balance;
}
.role {
  font-family: var(--display); font-weight: 600;
  font-size: var(--t-lead); line-height: 1.3;
  margin: 0 0 var(--space-md); max-width: 46ch; color: var(--accent);
}
.lede { font-size: var(--t-lead); line-height: 1.6; max-width: var(--measure); margin: 0 0 var(--space-lg); }

/* proof bar — the credibility payload sits above the fold, not four sections down */
.proofbar {
  list-style: none; margin: 0; padding: var(--space-md) 0 0;
  border-top: 2px solid var(--ink);
  /* Tried and REJECTED: forcing two columns at 360px (minmax 8rem). It saved 18px of
     height and broke the DOI across two lines. The mobile hero runs ~1.26 screens
     because the lede is three sentences — that is a copy decision, not a grid one. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--space-md) var(--space-lg);
}
.proofbar b {
  display: block; font-family: var(--display); font-weight: 700;
  font-size: var(--t-strong); line-height: 1.15; letter-spacing: -0.01em;
}
.proofbar span { display: block; color: var(--muted); font-size: var(--t-small); line-height: 1.45; }
.proofbar a { color: var(--ink); }

/* ---------- sections ---------- */

section { padding: var(--space-section) 0; border-top: 1px solid var(--rule); }

h2 {
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.5rem, 4vw, 2rem); line-height: 1.15; letter-spacing: -0.02em;
  margin: 0 0 var(--space-xs);
}
.sub { color: var(--muted); font-size: var(--t-body); margin: 0 0 var(--space-lg); max-width: var(--measure); }
p { max-width: var(--measure); }

/* group label — three groups of at most three, instead of one wall of seven */
.group { margin-bottom: var(--space-xl); }
.group:last-child { margin-bottom: 0; }
.group > h3 {
  font-family: var(--display); font-weight: 700; font-size: var(--t-label);
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent);
  font-variant-numeric: tabular-nums;
  margin: 0 0 var(--space-md); padding-bottom: var(--space-xs); border-bottom: 1px solid var(--rule);
}
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1.75rem 2.25rem; }
.card p { margin: 0 0 var(--space-sm); font-size: var(--t-body); max-width: var(--measure); }
.card p:last-child { margin-bottom: 0; }
.stack { color: var(--muted); font-size: var(--t-small); line-height: 1.55; }

/* evidence block — the claim next to it is worth nothing without this */
.card p.result {
  background: var(--accent-wash);
  padding: var(--space-md) var(--space-md);
  color: var(--ink);
  font-size: var(--t-body);
}
.result b {
  display: block;
  font-family: var(--display); font-weight: 700; font-size: var(--t-label);
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent);
  margin-bottom: var(--space-xs);
}

/* numbered principles — replaces the accent side-stripe.
   Each <li> has THREE grid children (::before, <strong>, <span>), not two, so the
   placement must be explicit: without it the description auto-flows into column 1,
   which is 2rem wide, and wraps one syllable per line. */
.rows { list-style: none; margin: 0; padding: 0; counter-reset: rule; display: grid; gap: var(--space-lg); }
.rows > li {
  counter-increment: rule;
  display: grid;
  grid-template-columns: 2rem minmax(0, 1fr);
  gap: var(--space-2xs) var(--space-md);
  align-items: baseline;
}
.rows > li::before {
  content: counter(rule, decimal-leading-zero);
  grid-column: 1; grid-row: 1;
  font-family: var(--display); font-weight: 700; font-size: var(--t-small);
  font-variant-numeric: tabular-nums;
  color: var(--accent); letter-spacing: 0.02em;
}
.rows strong { grid-column: 2; grid-row: 1; font-family: var(--display); font-weight: 700; font-size: var(--t-lead); }
.rows span { grid-column: 2; grid-row: 2; color: var(--muted); font-size: var(--t-body); }

/* selected work */
.work { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-lg); }
.work li { border-top: 1px solid var(--rule); padding-top: var(--space-md); }
.work li:first-child { border-top: 0; padding-top: 0; }
.work b { font-family: var(--display); font-weight: 700; font-size: var(--t-lead); display: block; margin-bottom: var(--space-2xs); }
.work span { color: var(--muted); font-size: var(--t-body); display: block; max-width: var(--measure); }

.proof { background: var(--panel); border: 1px solid var(--rule-strong); padding: var(--space-lg); margin-top: var(--space-xl); }
.proof h3 { font-family: var(--display); font-weight: 700; font-size: var(--t-strong); line-height: 1.2; margin: 0 0 var(--space-sm); }
.tag {
  display: inline-block; font-family: var(--display); font-weight: 700;
  font-size: var(--t-label); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--accent); background: var(--accent-wash); padding: var(--space-2xs) var(--space-sm); margin-bottom: var(--space-sm);
}
.proof dl { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 1rem; margin: 1.25rem 0 0; }
.proof dt { font-family: var(--display); font-weight: 700; font-size: var(--t-strong); line-height: 1.15; font-variant-numeric: tabular-nums; }
.proof dd { margin: var(--space-2xs) 0 0; color: var(--muted); font-size: var(--t-small); }

/* who does the work — a list, so hiring is one more <li>, not a rewrite */
.people { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-xl); }
.people > li { border-top: 2px solid var(--ink); padding-top: var(--space-md); max-width: var(--measure); }
.people b { font-family: var(--display); font-weight: 700; font-size: var(--t-strong); line-height: 1.2; letter-spacing: -0.01em; display: block; }
.person-role { display: block; color: var(--accent); font-size: var(--t-small); margin-bottom: var(--space-sm); }
.people p { margin: 0; font-size: var(--t-body); }

/* contact */
.contact { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); gap: 1.5rem; margin: 0; }
.contact dt {
  font-family: var(--display); font-weight: 700; font-size: var(--t-label);
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted);
  padding-bottom: var(--space-xs); border-bottom: 1px solid var(--rule);
}
.contact dd { margin: var(--space-sm) 0 0; font-size: var(--t-body); }

footer { padding: var(--space-xl) 0 3.5rem; border-top: 1px solid var(--rule); color: var(--muted); font-size: var(--t-small); }
footer p { margin: 0 0 var(--space-xs); max-width: none; }
footer .legal { font-family: var(--display); font-weight: 600; color: var(--ink); letter-spacing: 0.01em; }

/* Mobile-first: the small-screen values above are the BASE; larger screens opt in.
   The previous single max-width override made the desktop values the default and
   shipped them to phones first. */
@media (min-width: 26em) {
  .wrap { padding: 0 var(--space-lg); }
  .rows > li { grid-template-columns: 2.5rem minmax(0, 1fr); }
}

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

/* screenshot of the platform — evidence, not decoration, so it carries a caption and
   real dimensions. width/height stop it reserving the wrong box before it loads. */
.shot { margin: var(--space-lg) 0 0; }
.shot img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--rule-strong);
}
.shot figcaption { font-size: var(--t-small); color: var(--muted); margin-top: var(--space-xs); }
