
:root{
  --ground:#F7F1E6;   /* lightened Tatami, the page ground */
  --surface:#EFE6D6;  /* card + panel fill */
  --text:#395145;     /* Pine Needle */
  --link:#474072;     /* Meteorite */
  --meta:#5A6C5F;     /* Ecological, re-picked 2026-09-18: the old #6F8474 was
                         carried into both themes unchanged when light became
                         the default and was the one token nobody re-checked;
                         it failed AA on light ground/surface. New value is
                         4.99:1 on --ground, 4.53:1 on --surface (light). Rules
                         and meta only, never small body. */
  --meta2:#8A5A2B;    /* warm second, for eyebrows */
  --display:#474072;
  /* --hot / --hot-ink split, 2026-09-18: --hot is the accent as FILL (buttons,
     rules, borders, backgrounds) and stays at its original saturation, which
     does not clear AA as small text. --hot-ink is the same hue re-lightened
     for use as TEXT (4.99:1 on --ground, 4.53:1 on --surface, light). Do not
     merge these back: every current text use of --hot below was moved to
     --hot-ink on purpose. */
  --hot:#C5346B;      /* fill only: buttons, rules, borders, backgrounds */
  --hot-ink:#BB3166;  /* text only: the accent hue as type */
  --cairn:#3D6B86;    /* river-stone blue: Cairn's own colour, not the site's */
  --cairn-wash:rgba(61,107,134,.07);
  --rule:rgba(57,81,69,.20);

  /* THE TYPE SCALE, 2026-09-18, Kai's ruling. Six rungs at roughly a 1.25 ratio,
     built around the 17px body. Before this there were fifteen fixed sizes with
     no relationship between them, so picking a size meant inventing one, and
     16px, 17px and 18px were all doing body copy on different pages.

     Adding a rung is a decision to make out loud, not a convenience: the whole
     value of a ladder is that the next size is already chosen. If something
     genuinely does not fit, it is an ornament and belongs off the scale with a
     comment saying so (see .agent .glyph). */
  --t-micro:11px;                  /* caps meta: eyebrows, badges, nav, footer,
                                      note numbers, table heads, captions */
  --t-small:14px;                  /* captions, the draft note, code, tables */
  --t-body:17px;                   /* every paragraph, .prose included */
  --t-lead:21px;                   /* deks, leads, h3 */
  --t-h2:27px;                     /* every h2 */
  --t-h1:clamp(30px,6vw,52px);     /* page titles: fluid, and already right */

  /* Three letter-spacings instead of twelve (.20 .18 .16 .14 .12 .11 .10 .06
     .04 .02 .01 -.005 -.015). Uppercase runs need tracking, display sizes need
     a little negative, body needs none. The wordmark keeps its own .20em: that
     spacing is part of how the logo reads, not a text style. */
  --ls-caps:.14em;
  --ls-tight:-.015em;

  /* Dark palette, single source, 2026-09-18: these --dark-* values are the
     ONLY place the dark hexes are written. Both dark triggers below (the OS
     media query and the explicit data-theme toggle) just reference these
     vars, so changing a dark colour means editing one line instead of two
     that have to be kept in sync by hand. They had drifted out of order
     (never out of value, but nothing would have caught it if they had). A
     light-dark()+color-scheme rewrite was considered instead; skipped because
     this toggle needs an explicit override of the OS preference via
     data-theme, which light-dark() does not remove the need for and this
     indirection expresses just as directly with wider legacy support. */
  --dark-ground:#0D0F10; --dark-surface:#16201B; --dark-text:#DFCCAF; --dark-link:#DB6716;
  --dark-meta:#758B7A; --dark-meta2:#DB6716; --dark-display:#EEE16B;
  --dark-hot:#D50C7A; --dark-hot-ink:#F32F9A;
  --dark-cairn:#8FB4C9; --dark-cairn-wash:rgba(143,180,201,.09);
  --dark-rule:rgba(223,204,175,.18);
}
/* The dark set is still here, now as the alternate rather than the identity.
   Both blocks below just point at the --dark-* values above; see the comment
   on that block for why. */
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --ground:var(--dark-ground); --surface:var(--dark-surface);
    --text:var(--dark-text); --link:var(--dark-link);
    --meta:var(--dark-meta); --meta2:var(--dark-meta2); --display:var(--dark-display);
    --hot:var(--dark-hot); --hot-ink:var(--dark-hot-ink);
    --cairn:var(--dark-cairn); --cairn-wash:var(--dark-cairn-wash);
    --rule:var(--dark-rule);
  }
}
:root[data-theme="dark"]{
  --ground:var(--dark-ground); --surface:var(--dark-surface);
  --text:var(--dark-text); --link:var(--dark-link);
  --meta:var(--dark-meta); --meta2:var(--dark-meta2); --display:var(--dark-display);
  --hot:var(--dark-hot); --hot-ink:var(--dark-hot-ink);
  --cairn:var(--dark-cairn); --cairn-wash:var(--dark-cairn-wash);
  --rule:var(--dark-rule);
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
@media (prefers-reduced-motion: no-preference){
  html{scroll-behavior:smooth}
  /* The nav is not sticky, so no scroll-margin is needed; if it ever
     becomes sticky, every section needs scroll-margin-top or the heading
     lands under it. */
  header.hero h1, header.hero p.sub, header.hero .cta,
  header.hero .secondary, header.hero .coil{
    animation:rise .6s cubic-bezier(.2,.7,.3,1) both}
  header.hero .coil{animation-delay:.02s}
  header.hero h1{animation-delay:.10s}
  header.hero p.sub{animation-delay:.18s}
  header.hero .cta{animation-delay:.26s}
  header.hero .secondary{animation-delay:.30s}
}
/* Sizes across all three stylesheets are px, not rem: html sets no font-size of
   its own, so 1rem silently means the UA default (usually 16px) rather than the
   19px body actually renders at, and a subhead set in rem came out smaller than
   its own paragraph (call.css .callbody h3 was 1.05rem = 16.8px against a 19px
   body). Picking px keeps every size self-explanatory without cross-checking html. */
body{
  margin:0; background:var(--ground); color:var(--text);
  /* Atkinson Hyperlegible Next has a much larger x-height than Newsreader did,
     so the old 19px/1.65 read visibly bigger under the new face. Re-tuned by eye
     at desktop and phone widths: 17px/1.6 lands Atkinson at roughly the same
     visual size and measure Newsreader held at 19px/1.65, on both. */
  font-family:'Atkinson Hyperlegible Next','Atkinson Hyperlegible',system-ui,sans-serif;
  font-size:var(--t-body); line-height:1.6;
  /* Short pages (/talks/, a one-entry list) ended with the footer floating
     mid-viewport over a dead band of ground, because nothing set a minimum
     height. svh not vh: on iOS vh is the tallest-possible viewport, which
     leaves the footer pushed below the fold behind the browser chrome. */
  min-height:100svh; display:flex; flex-direction:column;
}
main{flex:1 0 auto}
.wrap{max-width:880px; margin:0 auto; padding:0 16px}
/* --link on --text is 1.09:1: colour alone never distinguished a prose link
   from surrounding text, and nothing pinned the underline explicitly, so the
   only thing carrying it was whatever the browser's UA stylesheet happened to
   do by default. Pinned here so it can't silently go missing. */
a{color:var(--link); text-decoration:underline}
a:focus-visible{outline:2px solid var(--link); outline-offset:3px; border-radius:2px}

nav{border-bottom:1px solid var(--rule); position:relative; z-index:2}
nav .wrap{display:flex; align-items:baseline; gap:22px; padding:18px 16px; flex-wrap:wrap}
.mark{font-family:"Archivo",system-ui,sans-serif; font-weight:700; font-size:var(--t-small);
  letter-spacing:.20em; text-transform:uppercase; color:var(--text);
  text-decoration:none; margin-right:auto}
.mark b{color:var(--display)}
nav a.navlink{font-family:"Archivo",system-ui,sans-serif; font-size:var(--t-micro);
  letter-spacing:var(--ls-caps); text-transform:uppercase; text-decoration:none}
nav a.navlink:hover{text-decoration:underline}
/* .badge (below), .soon (below) and .themetoggle were three near-identical
   pills, each differing from the others by one unit in about six places (a
   size, a letter-spacing, a padding, a margin) with no shared definition
   anywhere they could have been kept in step. This grouped selector is the
   one shape now: shared shell here, and each rule below keeps only the
   numbers that are genuinely its own. */
.badge, .soon, .themetoggle{
  display:inline-block; font-family:"Archivo",system-ui,sans-serif;
  text-transform:uppercase; border-radius:99px; border:1px solid;
}
/* .themetoggle's border used --rule (1.37:1 light, 1.48:1 dark), which fails
   WCAG 1.4.11 (non-text contrast, 3:1) for the site's one interactive control.
   --meta clears 3:1 in both themes at its re-picked value (see the --meta
   comment in :root) and it is already the "rule-ish, not decorative" role, so
   it takes over the border here instead of introducing a fourth token. */
.themetoggle{background:none; border-color:var(--meta); color:var(--meta2);
  font-size:var(--t-micro); letter-spacing:var(--ls-caps); padding:5px 10px; cursor:pointer}

header.hero{position:relative; overflow:hidden; border-bottom:1px solid var(--rule)}
.beam{position:absolute; top:0; left:0; width:100%; height:3px;
  pointer-events:none; opacity:.95;
  background:linear-gradient(90deg,
    #D50C7A 0%,#D50C7A 16%,#DB6716 16%,#DB6716 33%,#EEE16B 33%,#EEE16B 50%,
    #3A9360 50%,#3A9360 66%,#85BDE8 66%,#85BDE8 83%,#124C83 83%,#124C83 100%)}
header.hero .wrap{position:relative; z-index:1; padding:72px 16px 56px; text-align:left}
/* The halo is a separate background behind the coil's own SVG line art (the
   line art is unaffected either way). The yellow-to-green bloom was drawn to
   read as light against space, which is what dark ground is; on cream it
   read as a stain instead of a glow, so light theme gets its own much
   quieter version: the same two hues, far lower alpha, no attempt to "glow"
   against a ground that's already bright. Dark keeps the original. */
.coil{width:min(380px,40vw); height:auto; display:block; float:right;
  margin:-50px -100px 20px 44px; padding:42px;
  background:radial-gradient(closest-side,
    rgba(138,138,42,.07), rgba(58,147,96,.05) 58%, transparent 80%);
  border-radius:62% 38% 46% 54% / 55% 48% 52% 45%}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .coil{
    background:radial-gradient(closest-side,
      rgba(238,225,107,.26), rgba(58,147,96,.16) 58%, transparent 80%);
  }
}
:root[data-theme="dark"] .coil{
  background:radial-gradient(closest-side,
    rgba(238,225,107,.26), rgba(58,147,96,.16) 58%, transparent 80%);
}
@media (max-width:720px){
  .coil{float:none; width:64vw; padding:26px; margin:0 auto 20px}}
h1{font-family:"Archivo",system-ui,sans-serif; font-weight:700;
  font-size:var(--t-h1); line-height:1.08; letter-spacing:var(--ls-tight);
  margin:0 0 20px; text-wrap:balance}
.hero p.sub{margin:0 0 30px; max-width:56ch; font-size:var(--t-lead); opacity:.92}
/* The label was the literal light-ground hex (#F7F1E6), which looked like a
   theme-neutral constant but wasn't: the fill flips to --hot's dark value
   (#D50C7A) while this stayed the light one, landing at 4.49:1 against a 4.5
   bar in dark mode. #FFFFFF clears both fills (5.15:1 light, 5.04:1 dark) as
   one value with no token needed. */
.cta{display:inline-block; background:var(--hot); color:#FFFFFF; text-decoration:none;
  padding:13px 26px; border-radius:2px; font-family:"Archivo",system-ui,sans-serif;
  font-size:var(--t-small); font-weight:700; letter-spacing:var(--ls-caps); text-transform:uppercase}
.cta:hover{filter:brightness(1.12)}
.secondary{font-family:"Archivo",system-ui,sans-serif; font-size:var(--t-small);
  letter-spacing:var(--ls-caps); text-transform:uppercase; margin-left:18px; white-space:nowrap}

section{padding:66px 0; position:relative; z-index:1}
.eyebrow{font-family:"Archivo",system-ui,sans-serif; font-size:var(--t-micro); letter-spacing:.20em;
  text-transform:uppercase; color:var(--meta); margin:0 0 10px}
h2{font-family:"Archivo",system-ui,sans-serif; font-size:var(--t-h2); font-weight:700;
  margin:0 0 12px; letter-spacing:var(--ls-tight)}
.lede{margin:0 0 34px; max-width:62ch; opacity:.9}

.notes{border-top:1px solid var(--rule)}
/* The old homepage index's rows. Scoped under .notes since 2026-10-18: a bare
   .note also matched the callouts two field notes call .note (four-max,
   agent-visual-audit) once the notes came inside the site frame, and the grid
   below squeezed them into a 3px column. */
.notes .note{border-bottom:1px solid var(--rule); padding:22px 0;
  display:grid; grid-template-columns:3px 34px 1fr; gap:0 18px; align-items:start}
.notes .note .bar{width:3px; align-self:stretch; border-radius:2px; background:var(--c)}
.notes .note .num{font-family:"Archivo",system-ui,sans-serif; font-size:var(--t-micro);
  letter-spacing:var(--ls-caps); color:var(--c); padding-top:6px}
.notes .note h3{font-family:"Archivo",system-ui,sans-serif; font-size:var(--t-lead); font-weight:500;
  margin:0 0 6px; line-height:1.34}
.notes .note h3 a{text-decoration:none}
.notes .note h3 a:hover{text-decoration:underline; text-underline-offset:3px}
.notes .note p{margin:0; font-size:var(--t-body); opacity:.8; max-width:68ch}
/* shared pill shape is above, at .themetoggle. text use of the accent moves
   to --hot-ink (see the split comment in :root); the border stays a rule, so
   it stays --hot. */
.badge{border-color:var(--hot); color:var(--hot-ink); font-size:var(--t-micro);
  letter-spacing:var(--ls-caps); padding:2px 8px; margin-left:10px; vertical-align:2px}

@media (prefers-reduced-motion: no-preference){
  .notes .note{animation:rise .5s cubic-bezier(.2,.7,.3,1) both}
  .notes .note:nth-child(n+1){animation-delay:calc(.02s * var(--i,0))}
  @keyframes rise{from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:none}}
}

/* the agent's own section: Cairn's typography, not Kai's, and no animation */
.agent{border:1px solid var(--rule); border-left:3px solid var(--cairn);
  border-radius:3px; padding:34px 34px 34px 38px; position:relative;
  background:linear-gradient(180deg,var(--cairn-wash),transparent)}
.agent .eyebrow{color:var(--cairn)}
/* The mark at section scale. Decorative, so it is hidden from the a11y tree;
   the heading already says whose pages these are. */
.agent .glyph{font-family:"Marcellus",Georgia,serif; font-size:40px;
  line-height:1; color:var(--cairn); opacity:.55; float:right; margin:0 0 8px 20px}
.agent h2{font-family:"Marcellus",Georgia,serif; font-weight:400;}
.agent .lede{font-family:"Literata",Georgia,serif; font-size:var(--t-body)}
.agent-note{border-top:1px solid var(--rule); padding:22px 0 4px}
.agent-note h3{font-family:"Marcellus",Georgia,serif; font-weight:400; font-size:var(--t-lead);
  margin:0 0 8px; line-height:1.3}
.agent-note h3 a{color:var(--cairn); text-decoration:none}
.agent-note h3 a:hover{text-decoration:underline; text-underline-offset:3px}
.agent-note p{font-family:"Literata",Georgia,serif; font-size:var(--t-body); margin:0 0 8px; opacity:.85}
.agent-note p.byline{font-family:"Archivo",system-ui,sans-serif; font-size:var(--t-micro);
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--cairn);
  opacity:1; margin-top:10px}

/* The panel was a navy gradient chosen against the dark ground. Light became the
   default and it rendered as a grey wash sitting on cream, the one dead rectangle
   on the page. It now takes the surface fill and a left accent rule, the same
   treatment the agent block uses, so the two feature blocks read as one system. */
.panel{border:1px solid var(--rule); border-left:3px solid var(--hot);
  border-radius:3px; padding:34px 34px 34px 38px; background:var(--surface)}
.panel h2{margin-top:0}
/* This was declared three times (here, again inside the max-width:720 query
   below, and a third time unconditionally further down the file). Media
   queries add no specificity, so the last one in source order always won, at
   every width, which made the 720px override dead code.
   One base declaration now, plus its own dedicated breakpoint (see the
   max-width:800 rule further down, deliberately NOT the shared 720px mobile
   one): with three 210px-minimum tiers in this panel, auto-fit has a band
   where exactly two columns fit and the third wraps alone at half width. That
   band runs from where a second column first fits (measured ~526px viewport)
   to where a third does (measured ~782px viewport, using this panel's own
   padding, which is wider than the 34px desktop padding above 720px would
   suggest, because it's still 24px in the mobile query's own range) -- past
   the site's general 720px mobile breakpoint, not inside it, which is why a
   shared breakpoint alone doesn't cover it. Screenshotted at 600/680/750px to
   confirm 750 still orphaned before adding the dedicated 800px cutoff below. */
.tiers{display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:26px; margin-top:28px}
.tier h4{font-family:"Archivo",system-ui,sans-serif; font-size:var(--t-micro); letter-spacing:var(--ls-caps);
  text-transform:uppercase; margin:0 0 8px}
.tier.free h4{color:var(--meta)} .tier.paid h4{color:var(--meta2)}
.tier p{margin:0; font-size:var(--t-body); opacity:.9}
/* shared pill shape is up at .themetoggle. */
.soon{border-color:var(--hot); color:var(--hot-ink); font-size:var(--t-micro);
  letter-spacing:var(--ls-caps); padding:3px 9px; margin-left:12px; vertical-align:4px}

footer{border-top:1px solid var(--rule); padding:38px 0 70px; position:relative; z-index:1}
footer p{font-family:"Archivo",system-ui,sans-serif; font-size:var(--t-micro); letter-spacing:var(--ls-caps);
  color:var(--meta); margin:0 0 8px}

@media (max-width:720px){
  header.hero .wrap{padding:76px 16px 50px}
  .secondary{display:block; margin:18px 0 0}
  .notes .note{grid-template-columns:3px 28px 1fr; gap:0 14px}
  .agent,.panel{padding:24px}
}
/* .tiers' own breakpoint, wider than the 720px one above on purpose: three
   210px-minimum tiers don't reliably get a third column until the panel is
   about 782px of viewport wide (with its own 24px/34px padding switch
   factored in), so a cutoff shared with the rest of the mobile styles would
   still leave the two-column-plus-orphan band open between 720 and ~782. */
@media (max-width:800px){
  .tiers{grid-template-columns:1fr}
}

/* --- accent contrast -------------------------------------------------------
   The nineteen index accents were chosen against the dark ground. Light became
   the default afterwards and seventeen of them fell below WCAG AA on #F7F1E6,
   worst #EDDA66 at 1.26:1. The 3px bar keeps the pure accent, because it is a
   graphic; the number is text, so it takes a per-theme variant that holds hue
   and saturation and moves only lightness. Computed, not eyeballed: every value
   clears 4.6:1 on its own ground.
   Renamed 2026-09-18: --c-light/--c-dark read as "the lighter accent" and "the
   darker accent" just as fluently as their intended "the accent FOR light
   theme" / "the accent FOR dark theme" -- and for seventeen of nineteen notes
   those two readings pick opposite hex values. That ambiguity, hand-checked
   one theme at a time, is how the AA failures shipped. --accent-on-light and
   --accent-on-dark name the theme, not the lightness, so there is only one
   reading left. layouts/index.html now emits the new names directly (done by
   the session owning that file, verified in this worktree's working tree);
   --c-light/--c-dark are kept as a second-rung fallback here for any caller
   still on the old names, and --c as the last rung for a note with no
   per-theme variant set at all. */
.notes .note .num{color:var(--accent-on-light,var(--c-light,var(--c)))}
:root[data-theme="dark"] .note .num{color:var(--accent-on-dark,var(--c-dark,var(--c)))}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .note .num{color:var(--accent-on-dark,var(--c-dark,var(--c)))}
}

/* The nav mark for the page you are already on. Was defined a second time,
   identically, in talks.css, with a comment there claiming it had been moved
   out to here -- it hadn't been deleted, just duplicated. This is the one
   copy now (talks.css's copy is gone).
   It also used to be effectively color-invisible in light mode: --display and
   --link render the exact same hex there (both #474072), so the underline was
   the ONLY thing marking the current page; only in dark mode do the two hexes
   differ enough to read as a second cue. Weight is not a themed token, so
   bolding the current item is a cue that reads identically in both themes
   without borrowing a hue (in particular, not --cairn's wash, which is Cairn's
   own colour per the comment on .agent above, not the site's to spend here). */
.navlink--here{color:var(--display); font-weight:700; text-decoration:underline;
  text-underline-offset:5px}

/* The panel's way out to the call page. It was a paragraph of prose tacked under
   the two tiers, which read as an afterthought; it is a third tier now, in the same
   grid, so the panel has three parallel things in it instead of two plus a remark.
   (The grid-template-columns declaration this comment used to introduce is now
   the single one up in .tiers, above; a third copy here was the dead-code bug.) */
.tier.call h4{color:var(--hot-ink)}
.tier.call p a{font-weight:600}
