/* ==========================================================================
   NEIGHBOURHOOD - the design language of Vector Databases.

   Named for the idea the cover was drawn to express: eight arms reaching
   into eight distinct clusters of related points. Many simultaneous reaches
   into distinct neighbourhoods. The site is that idea in motion.

   Five pillars:
     1. Printed cream, not screen white. Every surface colour is sampled from
        the actual printed cover (artwork/cover-layout.json), so the site and
        the book object are the same ink.
     2. The Lattice. A faint coordinate grid plus hairline rails and corner
        registration ticks, so a page reads like a plotted field.
     3. The Distance Ramp. One accent ramp that runs the way a similarity
        score does: far (teal) to near (copper) to nearest (amber).
     4. Reaching type. Space Grotesk set large and tight for display, Inter
        for reading. Labels are the display face in small caps, never mono.
     5. Live neighbourhoods. Motion that means something: a drifting point
        field, connections that form on proximity, counts that resolve.

   One stylesheet, no build step, no framework. The CSP is style-src 'self'
   with no 'unsafe-inline', so nothing here may ever move into a style
   attribute: every one-off in a template needs a class in this file.
   ========================================================================== */

/* ------------------------------------------------------------------ tokens */
:root {
  color-scheme: light;

  /* Sampled from the printed cover. cream / linework / teal / subtitle ink. */
  --paper:          #f6eddc;
  --paper-sunk:     #f0e4cd;
  --paper-raised:   #fbf6ea;
  --paper-veil:     rgba(251, 246, 234, .72);

  /* The inverted band. Same paper, photographed at night. */
  --deep:           #12100f;
  --deep-raised:    #1d1917;
  --deep-ink:       #f4ead8;
  --deep-ink-soft:  #b4a692;
  --deep-line:      rgba(244, 234, 216, .15);
  --deep-line-hi:   rgba(244, 234, 216, .34);

  --ink:            #1f0e0f;
  --ink-soft:       #60514c;
  --ink-faint:      #8d7d73;

  --line:           #ded0b4;
  --line-strong:    #c9b591;
  --line-hair:      rgba(31, 14, 15, .10);

  /* The distance ramp: far, near, nearest. */
  --far:            #265360;
  --near:           #c16b5a;
  --nearest:        #e8a33d;

  /* Teal carries interaction because it clears 7:1 on cream; copper carries
     display and decoration, where contrast is not load bearing. */
  --accent:         #265360;
  --accent-strong:  #17383f;
  --accent-ink:     #f6eddc;
  --accent-soft:    #e4e2cf;
  --accent-line:    #a9bcbf;
  --copper:         #c16b5a;
  --copper-ink:     #9a4634;

  --good:           #2f6b45;
  --warn:           #8a5b12;
  --bad:            #a33528;
  --good-bg:        #e6ebd9;
  --warn-bg:        #f7e9cb;
  --bad-bg:         #f7ded4;

  --radius:         14px;
  --radius-sm:      9px;
  --radius-lg:      26px;
  --radius-pill:    999px;

  --shadow-sm:      0 1px 2px rgba(31, 14, 15, .07);
  --shadow:         0 2px 6px rgba(31, 14, 15, .06), 0 18px 44px rgba(31, 14, 15, .09);
  --shadow-lift:    0 4px 12px rgba(31, 14, 15, .10), 0 30px 70px rgba(31, 14, 15, .16);

  --display: "Space Grotesk", "Segoe UI", system-ui, sans-serif;
  --sans:    Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono:    ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
             "Liberation Mono", monospace;
  --serif:   Charter, "Bitstream Charter", "Iowan Old Style", Georgia, serif;

  --wrap:        1240px;
  --wrap-wide:   1440px;
  --wrap-narrow: 660px;
  --gutter:      clamp(20px, 4vw, 44px);
  --header-h:    68px;

  --ease:      cubic-bezier(.22, .61, .36, 1);
  --ease-out:  cubic-bezier(.16, 1, .3, 1);
}

/* Dark is a real second art direction, not an inversion filter: the same
   cream ink printed on a night field. Both the media query (system default)
   and the explicit data-theme (the toggle) define the whole set, so neither
   can leave a colour undefined. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --paper:         #12100f;
    --paper-sunk:    #0a0908;
    --paper-raised:  #1d1917;
    --paper-veil:    rgba(29, 25, 23, .74);
    --deep:          #0a0908;
    --deep-raised:   #16130f;
    --ink:           #f4ead8;
    --ink-soft:      #b4a692;
    --ink-faint:     #877a6c;
    --line:          #322c26;
    --line-strong:   #4a4137;
    --line-hair:     rgba(244, 234, 216, .12);
    --accent:        #86b9c6;
    --accent-strong: #a7d2dd;
    --accent-ink:    #0a0908;
    --accent-soft:   #16262b;
    --accent-line:   #2f4950;
    --copper:        #dd8c76;
    --copper-ink:    #e5a08d;
    --good:          #7bc294;
    --warn:          #dfae55;
    --bad:           #e8917f;
    --good-bg:       #14251a;
    --warn-bg:       #291f0e;
    --bad-bg:        #2a1410;
    --shadow-sm:     0 1px 2px rgba(0, 0, 0, .5);
    --shadow:        0 2px 6px rgba(0, 0, 0, .45), 0 18px 44px rgba(0, 0, 0, .5);
    --shadow-lift:   0 4px 12px rgba(0, 0, 0, .5), 0 30px 70px rgba(0, 0, 0, .6);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --paper:         #12100f;
  --paper-sunk:    #0a0908;
  --paper-raised:  #1d1917;
  --paper-veil:    rgba(29, 25, 23, .74);
  --deep:          #0a0908;
  --deep-raised:   #16130f;
  --ink:           #f4ead8;
  --ink-soft:      #b4a692;
  --ink-faint:     #877a6c;
  --line:          #322c26;
  --line-strong:   #4a4137;
  --line-hair:     rgba(244, 234, 216, .12);
  --accent:        #86b9c6;
  --accent-strong: #a7d2dd;
  --accent-ink:    #0a0908;
  --accent-soft:   #16262b;
  --accent-line:   #2f4950;
  --copper:        #dd8c76;
  --copper-ink:    #e5a08d;
  --good:          #7bc294;
  --warn:          #dfae55;
  --bad:           #e8917f;
  --good-bg:       #14251a;
  --warn-bg:       #291f0e;
  --bad-bg:        #2a1410;
  --shadow-sm:     0 1px 2px rgba(0, 0, 0, .5);
  --shadow:        0 2px 6px rgba(0, 0, 0, .45), 0 18px 44px rgba(0, 0, 0, .5);
  --shadow-lift:   0 4px 12px rgba(0, 0, 0, .5), 0 30px 70px rgba(0, 0, 0, .6);
}

/* -------------------------------------------------------------------- base */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

/* An <img> lays out at its width and height attributes until a rule says
   otherwise, and those attributes are CSS pixels, not a hint. Nothing may
   outgrow the column it sits in, including during the first paint. */
img, picture, video, canvas { max-width: 100%; height: auto; }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 16.5px;
  line-height: 1.65;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* The lattice. A coordinate field under everything, faint enough to be felt
   rather than seen, and pinned so it does not swim while the page scrolls. */
body::before {
  content: "";
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(circle at 1px 1px, var(--line-hair) 1px, transparent 0);
  background-size: 28px 28px;
  opacity: .5;
}
body > * { position: relative; z-index: 1; }

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -.03em;
  margin: 0 0 .5em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.5rem, 1.5rem + 4.2vw, 4.9rem); letter-spacing: -.038em; line-height: .99; }
h2 { font-size: clamp(1.95rem, 1.35rem + 2.5vw, 3.3rem); letter-spacing: -.033em; line-height: 1.02; }
h3 { font-size: clamp(1.12rem, 1.04rem + .35vw, 1.34rem); letter-spacing: -.016em; line-height: 1.25; }
h4 { font-size: 1.02rem; letter-spacing: -.01em; }
p  { margin: 0 0 1rem; }

/* THE INK DOES NOT MOVE.
   Nothing on this site recolours its own type to signal a state. Hover,
   focus and selection are expressed by the ground behind the words, by a
   rule under them, or by the shape around them, never by repainting the
   letters. Two reasons: a hue swap reads as a weight change, so the word
   appears to flicker rather than respond; and every hover colour is a second
   contrast ratio to defend, which is how the header CTA ended up at 2.5:1.
   Keep new states to background, border, underline, shadow and transform. */
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* The selection keeps the reader's ink and tints the paper under it, so a
   highlighted passage is still the same text at the same weight. */
::selection { background: color-mix(in srgb, var(--near) 34%, transparent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--ink); color: var(--paper); padding: 12px 18px;
  font-weight: 600; border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; text-decoration: none; }

/* ------------------------------------------------------------------ layout */
.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }
.wrap.wide   { max-width: var(--wrap-wide); }
.wrap.narrow { max-width: var(--wrap-narrow); }

.section       { padding: clamp(58px, 7vw, 104px) 0; position: relative; }
.section-tight { padding: clamp(38px, 5vw, 64px) 0; position: relative; }

/* The rails. Two hairlines that bracket the content column and read as the
   axes of the field the page is plotted on. */
.railed { position: relative; }
.railed::before, .railed::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 1px;
  background: var(--line); pointer-events: none;
}
.railed::before { left:  calc(50% - min(var(--wrap), 100vw - 2 * var(--gutter)) / 2); }
.railed::after  { right: calc(50% - min(var(--wrap), 100vw - 2 * var(--gutter)) / 2); }
@media (max-width: 700px) { .railed::before, .railed::after { display: none; } }

/* Registration ticks. Small crop marks that pin a block to the lattice the
   way a plotted chart registers its data. */
.ticked { position: relative; }
.ticked::before, .ticked::after {
  content: ""; position: absolute; width: 7px; height: 7px; pointer-events: none;
  border: 0 solid var(--line-strong);
}
.ticked::before { top: -1px; left: -1px;  border-top-width: 1px; border-left-width: 1px; }
.ticked::after  { bottom: -1px; right: -1px; border-bottom-width: 1px; border-right-width: 1px; }

.grid   { display: grid; gap: clamp(16px, 2vw, 24px); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(258px, 1fr)); }
.stack > * + * { margin-top: 14px; }
.row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.row-end { justify-content: flex-end; }
.row-between { justify-content: space-between; }
.center { text-align: center; }

/* Spacing utilities exist because the CSP forbids the style attribute these
   one-offs used to live in. They are deliberately few. */
.mt-l  { margin-top: clamp(26px, 3.4vw, 44px); }
.mt-m  { margin-top: 22px; }
.mt-s  { margin-top: 14px; }
.mb-0  { margin-bottom: 0; }
.measure       { max-width: 62ch; }
.measure-tight { max-width: 46ch; }

/* -------------------------------------------------------------------- type */
.muted { color: var(--ink-soft); }
.small { font-size: .89rem; }
.tiny  { font-size: .8rem; }
.mono  { font-family: var(--mono); font-size: .9em; letter-spacing: -.01em; }
.lead {
  font-size: clamp(1.06rem, 1rem + .35vw, 1.24rem);
  line-height: 1.55; color: var(--ink-soft); text-wrap: pretty;
}
.num { font-variant-numeric: tabular-nums; }

/* The label. Bracketed like a coordinate, set in the display face rather
   than a monospace, which would read as a terminal instead of a legend. */
.label {
  display: inline-block;
  font-family: var(--display);
  font-size: .72rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .17em;
  color: var(--copper-ink);
  margin: 0 0 1.1rem;
}
.label::before { content: "["; margin-right: .45em; opacity: .5; }
.label::after  { content: "]"; margin-left:  .35em; opacity: .5; }
.label-far { color: var(--accent); }

/* A headline word promoted onto the ramp. */
.hot {
  background: linear-gradient(96deg, var(--far), var(--near) 52%, var(--nearest));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* --------------------------------------------------------------- the bands */
/* Full-bleed sections that break the cream and give the page its rhythm. */
.band { position: relative; isolation: isolate; }
.band > .wrap { position: relative; z-index: 3; }

.band-deep {
  background: var(--deep); color: var(--deep-ink);
  --ink: var(--deep-ink); --ink-soft: var(--deep-ink-soft); --ink-faint: #8d8172;
  --line: var(--deep-line); --line-strong: var(--deep-line-hi);
  --paper: var(--deep); --paper-raised: var(--deep-raised); --paper-sunk: #0a0908;
  --accent: #9ecad6; --accent-strong: #c1e2ea; --accent-ink: #0a0908;
  --copper-ink: #e39d88; --line-hair: rgba(244, 234, 216, .1);
}
.band-deep .label { color: var(--nearest); }
.band-sunk { background: var(--paper-sunk); }

/* Image-backed bands. The wash sits between the art and the type so the
   contrast never depends on what the art happened to render. */
.band-art::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background-size: cover; background-position: center;
  background-repeat: no-repeat;
}
.band-art::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: var(--band-wash, linear-gradient(180deg,
    color-mix(in srgb, var(--paper) 78%, transparent),
    color-mix(in srgb, var(--paper) 92%, transparent)));
}
.band-ramp::before  { background-image: url("../img/art/band-ramp.342b2e8734bc.jpg"); }
.band-cta::before   { background-image: url("../img/art/cta-reach.c33fef6f01b9.jpg"); opacity: .8; }
.band-lib::before   { background-image: url("../img/art/library-band.cdb071e9cb65.jpg"); }
.band-cta::after {
  background: radial-gradient(120% 90% at 50% 50%,
    rgba(18, 16, 15, .45), rgba(10, 9, 8, .93) 76%);
}
.band-lib::after {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--paper) 62%, transparent),
    color-mix(in srgb, var(--paper) 96%, transparent));
}

/* A hairline seam between adjacent sections, used instead of a border so it
   can carry the ramp. */
.seam { height: 1px; border: 0; margin: 0;
  background: linear-gradient(90deg, transparent, var(--line) 12%, var(--line) 88%, transparent); }
.seam-hot { background: linear-gradient(90deg,
  transparent, var(--far) 16%, var(--near) 50%, var(--nearest) 84%, transparent); opacity: .8; }

/* ------------------------------------------------------------------ header */
.site-header {
  position: sticky; top: 0; z-index: 60;
  background: var(--paper-veil);
  backdrop-filter: saturate(150%) blur(14px);
  -webkit-backdrop-filter: saturate(150%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .25s var(--ease), background .25s var(--ease);
}
.site-header.stuck { border-bottom-color: var(--line); }

/* The read-progress hairline rides the ramp along the bottom of the header. */
.head-progress {
  position: absolute; left: 0; bottom: -1px; height: 2px; width: 0;
  background: linear-gradient(90deg, var(--far), var(--near) 60%, var(--nearest));
  transition: width .12s linear; z-index: 2;
}

.header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; height: var(--header-h);
}

.brand { display: flex; align-items: center; gap: 11px; color: var(--ink); }
.brand:hover { text-decoration: none; }
.brand-mark { width: 34px; height: 34px; flex: none; display: block; }
.brand-mark .n-pt { transition: opacity .5s var(--ease); }
.brand:hover .n-pt { opacity: 1; }
.brand-text { display: flex; flex-direction: column; line-height: 1.14; }
.brand-text strong {
  font-family: var(--display); font-size: 1.02rem; font-weight: 600;
  letter-spacing: -.028em; white-space: nowrap;
}
.brand-text em {
  font-style: normal; font-size: .68rem; color: var(--ink-faint);
  letter-spacing: .07em; text-transform: uppercase; white-space: nowrap;
}

.site-nav { display: flex; align-items: center; gap: 6px; }
.site-nav > a:not(.btn) {
  position: relative; white-space: nowrap;
  color: var(--ink-soft); font-size: .93rem; font-weight: 500;
  padding: 8px 12px; border-radius: var(--radius-pill);
  transition: color .18s var(--ease), background .18s var(--ease);
}
.site-nav > a:not(.btn)::after {
  content: ""; position: absolute; left: 12px; right: 12px; bottom: 4px; height: 1px;
  background: var(--near); transform: scaleX(0); transform-origin: left;
  transition: transform .3s var(--ease-out);
}
/* The copper rule under the word is the affordance; the word itself holds. */
.site-nav > a:not(.btn):hover { text-decoration: none; }
.site-nav > a:not(.btn):hover::after { transform: scaleX(1); }
.site-nav .btn { margin-left: 8px; }

.theme-toggle {
  width: 36px; height: 36px; flex: none; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: transparent; color: var(--ink-soft); cursor: pointer; padding: 0;
  transition: border-color .18s var(--ease), color .18s var(--ease), transform .3s var(--ease);
}
.theme-toggle:hover { border-color: var(--ink); transform: rotate(-18deg); }
.theme-toggle svg { width: 17px; height: 17px; display: block; }
.theme-toggle .t-sun  { display: none; }
:root[data-theme="dark"] .theme-toggle .t-sun,
:root:not([data-theme="light"]) .theme-toggle .t-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .t-moon,
:root:not([data-theme="light"]) .theme-toggle .t-moon { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle .t-sun  { display: none; }
  :root:not([data-theme="dark"]) .theme-toggle .t-moon { display: block; }
}

.nav-toggle {
  display: none; background: none; border: 1px solid var(--line); cursor: pointer;
  width: 38px; height: 38px; border-radius: var(--radius-pill); padding: 0;
}
.nav-toggle span {
  display: block; width: 15px; height: 1.5px; background: var(--ink);
  margin: 3px auto; border-radius: 2px;
  transition: transform .28s var(--ease-out), opacity .18s var(--ease);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(4.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-4.5px) rotate(-45deg); }

@media (max-width: 900px) {
  .nav-toggle { display: block; }
  .site-nav {
    position: absolute; top: var(--header-h); left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--paper); border-bottom: 1px solid var(--line);
    padding: 10px var(--gutter) 22px;
    clip-path: inset(0 0 100% 0); pointer-events: none;
    transition: clip-path .4s var(--ease-out);
  }
  .site-nav.open { clip-path: inset(0 0 0 0); pointer-events: auto; }
  .site-nav > a:not(.btn), .site-nav > button:not(.btn) {
    padding: 14px 2px; border-bottom: 1px solid var(--line); text-align: left;
    border-radius: 0;
  }
  .site-nav > a, .site-nav > button {
    opacity: 0; transform: translateY(-8px);
    transition: opacity .3s var(--ease), transform .3s var(--ease-out);
  }
  .site-nav.open > a, .site-nav.open > button { opacity: 1; transform: none; }
  .site-nav.open > *:nth-child(1) { transition-delay: .05s; }
  .site-nav.open > *:nth-child(2) { transition-delay: .09s; }
  .site-nav.open > *:nth-child(3) { transition-delay: .13s; }
  .site-nav.open > *:nth-child(4) { transition-delay: .17s; }
  .site-nav.open > *:nth-child(5) { transition-delay: .21s; }
  .site-nav .btn { margin: 16px 0 0; justify-content: center; }
  .site-nav > a:not(.btn)::after { display: none; }
}

/* ----------------------------------------------------------------- buttons */
.btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  gap: 9px; padding: 12px 22px; border-radius: var(--radius-pill);
  font-family: var(--display); font-size: .95rem; font-weight: 600; letter-spacing: -.012em;
  border: 1px solid transparent; cursor: pointer; text-decoration: none;
  white-space: nowrap; overflow: hidden; isolation: isolate;
  transform: translate(var(--mgx, 0px), var(--mgy, 0px));
  transition: color .2s var(--ease), border-color .2s var(--ease),
              background .2s var(--ease), box-shadow .25s var(--ease);
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translate(var(--mgx, 0px), var(--mgy, 0px))
                        translateY(1px) scale(.99); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
.btn:disabled:hover, .btn[aria-disabled="true"]:hover { transform: none; }

/* The arrow leans out on hover: the button reaches, like the arms do. */
.btn .arw {
  display: inline-block; font-family: var(--display); font-size: 1.05em; line-height: 1;
  transition: transform .32s var(--ease-out);
}
.btn:hover .arw { transform: translate(3px, -3px); }

/* A wash that sweeps the ramp across the fill on hover. */
.btn-primary {
  background: var(--ink); color: var(--paper); border-color: var(--ink);
}
/* The wash rides at half strength over the ink fill rather than replacing it.
   At full strength the amber end of the ramp is light enough that the label
   drops to about 2:1; tinted this way the sweep still reads and the label
   holds better than 5:1 across the whole ramp. */
.btn-primary::before {
  content: ""; position: absolute; inset: 0; z-index: -1; opacity: .5;
  background: linear-gradient(100deg, var(--far), var(--near) 55%, var(--nearest));
  transform: translateX(-101%); transition: transform .45s var(--ease-out);
}
.btn-primary:hover:not(:disabled) { box-shadow: var(--shadow); }
.btn-primary:hover:not(:disabled)::before { transform: none; }

.btn-secondary {
  background: transparent; color: var(--ink); border-color: var(--line-strong);
}
.btn-secondary:hover:not(:disabled) {
  border-color: var(--ink); background: color-mix(in srgb, var(--ink) 5%, transparent);
}

.btn-ghost { background: transparent; color: var(--ink-soft); border-color: transparent; }
.btn-ghost:hover:not(:disabled) { background: color-mix(in srgb, var(--ink) 8%, transparent); }

.btn-sm { padding: 8px 16px; font-size: .87rem; }
.btn-lg { padding: 15px 30px; font-size: 1.02rem; }
.btn-block { display: flex; width: 100%; }

/* ------------------------------------------------------------------- cards */
.card {
  position: relative;
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(20px, 2.3vw, 28px);
  transition: border-color .3s var(--ease), transform .4s var(--ease-out),
              box-shadow .4s var(--ease);
}
.card + .card { margin-top: 16px; }
.card h3 { margin: 0 0 .5em; }
.card-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; margin-bottom: 16px;
}

/* Spotlight. A pointer-tracked wash, positioned from two custom properties
   that the motion script writes; no style attribute is involved. */
.card-lit { overflow: hidden; }
.card-lit::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 50%),
    color-mix(in srgb, var(--near) 15%, transparent), transparent 62%);
  transition: opacity .4s var(--ease);
}
.card-lit:hover::before { opacity: 1; }
.card-lit:hover { border-color: var(--line-strong); transform: translateY(-4px); box-shadow: var(--shadow); }

/* An art-topped card. The plate is the header, cropped and desaturated a
   touch so the three read as a set rather than three loud pictures. */
.card-art { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.card-art .plate {
  aspect-ratio: 4 / 3; overflow: hidden; border-bottom: 1px solid var(--line);
  background: var(--paper-sunk);
}
.card-art .plate img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 1.1s var(--ease-out), filter .5s var(--ease);
  filter: saturate(.92);
}
.card-art:hover .plate img { transform: scale(1.045); filter: saturate(1.05); }
.card-art .card-body { padding: clamp(20px, 2.3vw, 26px); flex: 1; }
.card-art .card-body > :last-child { margin-bottom: 0; }

/* ------------------------------------------------------------------ badges */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 12px; border-radius: var(--radius-pill);
  font-family: var(--display); font-size: .73rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  background: transparent; color: var(--ink-soft); border: 1px solid var(--line-strong);
}
.badge-good { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, transparent); background: var(--good-bg); }
.badge-warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); background: var(--warn-bg); }
.badge-bad  { color: var(--bad);  border-color: color-mix(in srgb, var(--bad) 45%, transparent);  background: var(--bad-bg); }
.badge-accent { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); }

/* The engine chips on the book page double as a legend for the ramp. */
.chip-row { display: flex; flex-wrap: wrap; gap: 9px; }
.chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 15px 7px 12px; border-radius: var(--radius-pill);
  border: 1px solid var(--line-strong); background: var(--paper-raised);
  font-family: var(--display); font-weight: 600; font-size: .88rem; letter-spacing: -.01em;
  transition: border-color .25s var(--ease), transform .25s var(--ease-out);
}
.chip::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--chip-hue, var(--far));
}
.chip:hover { border-color: var(--chip-hue, var(--far)); transform: translateY(-2px); }
.chip:nth-child(5n+1) { --chip-hue: #265360; }
.chip:nth-child(5n+2) { --chip-hue: #3f7a72; }
.chip:nth-child(5n+3) { --chip-hue: #8f7a55; }
.chip:nth-child(5n+4) { --chip-hue: #c16b5a; }
.chip:nth-child(5n+5) { --chip-hue: #e8a33d; }

/* -------------------------------------------------------------------- hero */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--deep); color: var(--deep-ink);
  --ink: var(--deep-ink); --ink-soft: var(--deep-ink-soft); --ink-faint: #8d8172;
  --line: var(--deep-line); --line-strong: var(--deep-line-hi);
  --paper: var(--deep); --paper-raised: var(--deep-raised);
  --accent: #9ecad6; --accent-strong: #c1e2ea; --accent-ink: #0a0908;
  padding: clamp(72px, 11vw, 148px) 0 clamp(56px, 7vw, 96px);
}
.hero .label { color: var(--nearest); }
.hero-art {
  position: absolute; inset: 0; z-index: 0;
  background: url("../img/art/hero-field.031e871c9561.jpg") center / cover no-repeat;
  opacity: .72;
}
/* The live field sits on top of the plate: the plate gives depth and the
   canvas gives it motion and answers the pointer.

   width/height are stated rather than left to inset:0, because a canvas is a
   REPLACED element: an absolutely positioned replaced box resolves auto
   dimensions to its intrinsic size and then ignores the over-constrained
   edge, so inset:0 alone leaves it at the default 300x150 in the corner. */
#hero-field {
  position: absolute; inset: 0; z-index: 1; display: block;
  width: 100%; height: 100%;
}
.hero::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    radial-gradient(105% 78% at 22% 44%, rgba(10, 9, 8, .93), rgba(10, 9, 8, .5) 46%, transparent 74%),
    linear-gradient(180deg, rgba(10, 9, 8, .82), transparent 26%, rgba(10, 9, 8, .74));
}
.hero > .wrap { position: relative; z-index: 3; }

.hero-grid {
  display: grid; grid-template-columns: minmax(0, 1.22fr) minmax(0, .78fr);
  gap: clamp(32px, 5vw, 68px); align-items: center;
}
@media (max-width: 940px) { .hero-grid { grid-template-columns: 1fr; gap: 40px; } }
.hero-grid > * { min-width: 0; }

.hero h1 { margin-bottom: .28em; }
.hero .lead { font-size: clamp(1.1rem, 1rem + .55vw, 1.38rem); max-width: 26ch; color: #cdbfa9; }
.hero-blurb { max-width: 48ch; color: var(--deep-ink-soft); font-size: 1.01rem; }
.hero-authors {
  color: var(--ink-faint); font-size: .93rem; margin: 0 0 22px;
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
}
.hero-authors .dot { width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: .6; }
.hero-cta { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; margin-top: 30px; }
.hero .btn-primary { background: var(--deep-ink); color: var(--deep); border-color: var(--deep-ink); }
.hero .btn-secondary { color: var(--deep-ink); border-color: var(--deep-line-hi); }
.hero .btn-secondary:hover { border-color: var(--deep-ink); background: rgba(244, 234, 216, .09); }

/* The cover is the one photograph on the site, so it is presented as an
   object: lifted, edge-lit, and tilted a degree off the grid. */
.cover-mount { perspective: 1400px; }
.book-cover {
  aspect-ratio: 30 / 37; border-radius: 4px 10px 10px 4px; overflow: hidden;
  background: var(--paper-sunk);
  box-shadow:
    -14px 0 22px -18px rgba(0, 0, 0, .9) inset,
    0 30px 60px -22px rgba(0, 0, 0, .75),
    0 2px 0 0 rgba(244, 234, 216, .12);
  transform: rotate(-1.4deg);
  transition: transform .7s var(--ease-out), box-shadow .7s var(--ease);
  will-change: transform;
}
.cover-mount:hover .book-cover {
  transform: rotate(0deg) translateY(-8px) scale(1.015);
  box-shadow:
    -14px 0 22px -18px rgba(0, 0, 0, .9) inset,
    0 46px 80px -24px rgba(0, 0, 0, .8),
    0 2px 0 0 rgba(244, 234, 216, .18);
}
.book-cover picture { display: block; height: 100%; }
.book-cover img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* ------------------------------------------------------------------- stats */
.stat-row {
  display: flex; gap: clamp(22px, 3.4vw, 46px); flex-wrap: wrap;
  margin-top: 38px; padding-top: 28px; border-top: 1px solid var(--line);
}
.stat b {
  display: block; font-family: var(--display); font-size: clamp(1.6rem, 1.2rem + 1.4vw, 2.3rem);
  font-weight: 600; letter-spacing: -.035em; line-height: 1; font-variant-numeric: tabular-nums;
}
.stat span {
  display: block; margin-top: 7px; font-size: .78rem; color: var(--ink-faint);
  letter-spacing: .1em; text-transform: uppercase;
}

/* Stepped stat blocks. Flat rectangles that climb like a bar chart, so a row
   of numbers reads as a measurement rather than as decoration. */
.blocks { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; align-items: end; }
@media (max-width: 860px) { .blocks { grid-template-columns: repeat(2, 1fr); } }
.block {
  display: flex; flex-direction: column; justify-content: space-between;
  background: var(--paper-sunk); border: 1px solid var(--line);
  padding: 22px 20px 20px; min-height: 168px;
  transition: background .35s var(--ease), border-color .35s var(--ease),
              transform .45s var(--ease-out);
}
.block:nth-child(1) { min-height: 250px; }
.block:nth-child(2) { min-height: 216px; }
.block:nth-child(3) { min-height: 190px; }
.block:hover { transform: translateY(-5px); border-color: var(--near); background: var(--paper-raised); }
.block b {
  font-family: var(--display); font-weight: 600; letter-spacing: -.04em;
  font-size: clamp(1.9rem, 1.3rem + 2vw, 3rem); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.block span { font-size: .9rem; color: var(--ink-soft); }

/* ----------------------------------------------------------------- pricing */
.price-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 18px; align-items: stretch;
}
.price-card { display: flex; flex-direction: column; height: 100%; }
.price-card.featured {
  border-color: var(--near);
  box-shadow: 0 0 0 1px var(--near), var(--shadow);
  background: var(--paper-raised);
}
.price-card.featured::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: linear-gradient(90deg, var(--far), var(--near) 55%, var(--nearest));
  border-radius: var(--radius) var(--radius) 0 0;
}
.price-tag { display: flex; align-items: baseline; gap: 9px; margin: 18px 0 4px; }
.price-tag b {
  font-family: var(--display); font-size: 2.6rem; font-weight: 600; letter-spacing: -.045em;
  line-height: 1;
}
.price-window { font-size: .82rem; color: var(--ink-faint); min-height: 1.3em; }
.feature-list { list-style: none; padding: 0; margin: 20px 0; flex: 1; }
.feature-list li {
  display: flex; gap: 10px; padding: 6px 0; font-size: .93rem; align-items: flex-start;
  border-bottom: 1px solid color-mix(in srgb, var(--line) 55%, transparent);
}
.feature-list li:last-child { border-bottom: 0; }
.feature-list li::before {
  content: "+"; color: var(--good); font-weight: 700; flex: none;
  font-family: var(--display); line-height: 1.5; width: 1ch;
}
.feature-list li.excluded { color: var(--ink-faint); }
.feature-list li.excluded::before { content: "\2013"; color: var(--ink-faint); }

/* ---------------------------------------------------------------- manifest */
/* The chapter list. Full-bleed hairline rules, a circled arrow that fills on
   hover, and a number set in tabular figures so the column stays true. */
.manifest { border-top: 1px solid var(--line); }
.manifest-row {
  display: grid; grid-template-columns: 3.4rem minmax(0, 1fr) auto auto;
  align-items: center; gap: clamp(12px, 2vw, 26px);
  padding: clamp(16px, 1.9vw, 24px) 4px;
  border-bottom: 1px solid var(--line);
  color: var(--ink); position: relative; overflow: hidden;
  transition: padding-left .35s var(--ease-out), color .25s var(--ease);
}
.manifest-row::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: linear-gradient(180deg, var(--far), var(--near), var(--nearest));
  transform: scaleY(0); transform-origin: top;
  transition: transform .4s var(--ease-out);
}
a.manifest-row:hover { text-decoration: none; padding-left: 18px; }
.manifest-row:hover::before { transform: scaleY(1); }
.manifest-num {
  font-family: var(--display); font-weight: 600; font-size: .95rem;
  color: var(--ink-faint); font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.manifest-title {
  font-family: var(--display); font-weight: 600; letter-spacing: -.022em;
  font-size: clamp(1.05rem, .96rem + .5vw, 1.42rem); line-height: 1.2;
}
.manifest-part { font-size: .82rem; color: var(--ink-faint); white-space: nowrap; }
.manifest-mins { font-size: .82rem; color: var(--ink-faint); white-space: nowrap;
  font-variant-numeric: tabular-nums; }
@media (max-width: 720px) {
  .manifest-row { grid-template-columns: 2.6rem minmax(0, 1fr) auto; }
  .manifest-part { display: none; }
}

/* The arrow list used for the feature manifest. */
.arrow-list { border-top: 1px solid var(--line); }
.arrow-row {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: clamp(18px, 2.2vw, 26px) 2px; border-bottom: 1px solid var(--line);
  color: var(--ink);
}
.arrow-row h3 { margin: 0; font-size: clamp(1.15rem, 1rem + .8vw, 1.7rem); }
.arrow-row p { margin: 6px 0 0; color: var(--ink-soft); font-size: .93rem; max-width: 58ch; }
.arrow-mark {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  border: 1.5px solid var(--ink); display: grid; place-items: center;
  font-family: var(--display); font-size: 1.05rem; line-height: 1;
  transition: background .3s var(--ease), color .3s var(--ease), transform .4s var(--ease-out);
}
a.arrow-row:hover { text-decoration: none; }
/* The one place a glyph changes colour, and it is not a word changing colour:
   the disc behind the arrow fills, and the arrow follows its ground to stay
   visible. The row's heading and copy hold their ink. */
a.arrow-row:hover .arrow-mark,
.arrow-row:hover .arrow-mark { background: var(--ink); color: var(--paper); transform: translateX(5px); }

/* ---------------------------------------------------------------- marquee */
/* An engine rail. Duplicated once in the markup so the translate can loop
   seamlessly at exactly -50%. */
.marquee {
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 15px 0; overflow: hidden; position: relative;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
}
.marquee-track {
  display: flex; width: max-content; gap: 0;
  animation: marquee 44s linear infinite;
}
.marquee:hover .marquee-track { animation-play-state: paused; }
.marquee-item {
  display: inline-flex; align-items: center; gap: 12px; padding: 0 26px;
  font-family: var(--display); font-weight: 600; font-size: .94rem;
  letter-spacing: -.012em; color: var(--ink-soft); white-space: nowrap;
}
.marquee-item::after {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--near); opacity: .55; margin-left: 14px;
}
@keyframes marquee { to { transform: translateX(-50%); } }

/* ------------------------------------------------- the neighbourhood probe */
/* A live nearest-neighbour search over the table of contents. It is a toy and
   the copy says so; the point is to let a visitor feel the operation the book
   spends 25 chapters making rigorous. */
.probe {
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--paper-raised); overflow: hidden; box-shadow: var(--shadow);
}
.probe-bar {
  display: flex; align-items: center; gap: 12px; padding: 14px 16px;
  border-bottom: 1px solid var(--line); background: var(--paper);
}
.probe-bar .q-mark {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  border: 1px solid var(--near); display: grid; place-items: center;
  color: var(--copper-ink); font-family: var(--display); font-weight: 600; font-size: .8rem;
}
.probe-bar input {
  flex: 1; border: 0; background: transparent; padding: 6px 0; min-width: 0;
  font-family: var(--display); font-size: clamp(1rem, .94rem + .4vw, 1.2rem);
  font-weight: 500; letter-spacing: -.02em; color: var(--ink);
}
.probe-bar input:focus { outline: none; }
.probe-bar input::placeholder { color: var(--ink-faint); }
.probe-hits { list-style: none; margin: 0; padding: 6px; min-height: 236px; }
.probe-hit {
  display: grid; grid-template-columns: minmax(0, 1fr) 92px 4.6rem;
  align-items: center; gap: 14px; padding: 11px 12px; border-radius: var(--radius-sm);
  animation: hit-in .38s var(--ease-out) both;
}
.probe-hit:nth-child(1) { animation-delay: .02s; }
.probe-hit:nth-child(2) { animation-delay: .06s; }
.probe-hit:nth-child(3) { animation-delay: .10s; }
.probe-hit:nth-child(4) { animation-delay: .14s; }
.probe-hit:nth-child(5) { animation-delay: .18s; }
.probe-hit:hover { background: var(--paper-sunk); }
@keyframes hit-in { from { opacity: 0; transform: translateY(7px); } }
.probe-hit .t {
  font-family: var(--display); font-weight: 600; font-size: .97rem; letter-spacing: -.018em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.probe-hit .t small { display: block; font-weight: 500; font-size: .76rem; color: var(--ink-faint);
  letter-spacing: .04em; text-transform: uppercase; margin-top: 2px; }
.probe-hit .meter { height: 5px; border-radius: 3px; background: var(--paper-sunk); overflow: hidden; }
.probe-hit .meter i {
  display: block; height: 100%; border-radius: 3px; width: 0;
  background: linear-gradient(90deg, var(--far), var(--near), var(--nearest));
  transition: width .55s var(--ease-out);
}
.probe-hit .score {
  font-family: var(--display); font-weight: 600; font-size: .86rem; text-align: right;
  font-variant-numeric: tabular-nums; color: var(--ink-soft);
}
.probe-foot {
  padding: 12px 16px; border-top: 1px solid var(--line);
  background: var(--paper); font-size: .82rem; color: var(--ink-faint);
}
.probe-seeds { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.probe-seed {
  border: 1px solid var(--line-strong); background: transparent; cursor: pointer;
  border-radius: var(--radius-pill); padding: 6px 14px; color: var(--ink-soft);
  font-family: var(--sans); font-size: .84rem;
  transition: border-color .2s var(--ease), color .2s var(--ease), transform .2s var(--ease-out);
}
.probe-seed:hover { border-color: var(--ink); transform: translateY(-1px); }

/* -------------------------------------------------------------------- FAQ */
.faq-item { border-bottom: 1px solid var(--line); }
.faq-q {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: 20px; padding: clamp(18px, 2.1vw, 24px) 2px; background: none; border: 0;
  cursor: pointer; text-align: left; color: var(--ink);
  font-family: var(--display); font-weight: 600; letter-spacing: -.022em;
  font-size: clamp(1.02rem, .96rem + .45vw, 1.24rem); line-height: 1.3;
}
.faq-q:hover { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.faq-sign { flex: none; width: 22px; height: 22px; position: relative; }
.faq-sign::before, .faq-sign::after {
  content: ""; position: absolute; inset: 50% 0 auto 0; height: 1.5px;
  background: currentColor; border-radius: 2px;
  transition: transform .35s var(--ease-out);
}
.faq-sign::after { transform: rotate(90deg); }
.faq-q[aria-expanded="true"] .faq-sign::after { transform: rotate(0deg); }
.faq-a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .42s var(--ease-out); }
.faq-q[aria-expanded="true"] + .faq-a { grid-template-rows: 1fr; }
.faq-a > div { overflow: hidden; }
.faq-a p { color: var(--ink-soft); max-width: 74ch; padding-bottom: 20px; margin: 0; }
.faq-a p + p { padding-top: 12px; }

/* ------------------------------------------------------------------- forms */
.form-field { margin-bottom: 18px; }
label { display: block; font-size: .87rem; font-weight: 600; margin-bottom: 7px; }
input[type=text], input[type=email], input[type=password], input[type=search],
select, textarea {
  width: 100%; padding: 12px 14px; font: inherit; font-size: .96rem;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), background .2s var(--ease);
}
input:hover:not(:disabled), select:hover, textarea:hover { border-color: var(--ink-faint); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}
input:disabled { background: var(--paper-sunk); color: var(--ink-faint); }
textarea { min-height: 140px; resize: vertical; }
.help { font-size: .82rem; color: var(--ink-faint); margin-top: 6px; }
.checkbox { display: flex; gap: 10px; align-items: flex-start; font-weight: 400; font-size: .9rem; }
.checkbox input { width: auto; margin-top: 4px; flex: none; accent-color: var(--accent); }
.card-spaced { margin-top: 28px; }
.form-footnote { margin-top: 18px; margin-bottom: 0; }

/* An auth page is a two-column plate: the form, and one quiet drawing. */
.auth-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .82fr);
  gap: clamp(28px, 4vw, 60px); align-items: center;
}
@media (max-width: 900px) { .auth-grid { grid-template-columns: 1fr; }
  .auth-plate { display: none; } }
.auth-plate {
  border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden;
  background: var(--paper-sunk); aspect-ratio: 3 / 4; max-height: 560px;
}
.auth-plate img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ----------------------------------------------------------------- captcha */
/* Honeypot. Off-canvas rather than display:none or hidden, because the
   crawlers worth catching skip fields the latter two hide. It must never be
   reachable by keyboard or announced by a screen reader. */
.hp { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }

.captcha[hidden] { display: none; }
.captcha-row { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; flex-wrap: wrap; }
.captcha-image {
  display: block; width: 220px; height: 72px;
  background: var(--paper-sunk); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
}
.captcha-image:not([src]) { visibility: hidden; }
.captcha input[type=text] {
  max-width: 220px; letter-spacing: .24em; text-transform: uppercase;
  font-family: var(--mono);
}

.alert {
  padding: 13px 16px; border-radius: var(--radius-sm); font-size: .92rem;
  margin-bottom: 18px; border: 1px solid transparent;
}
.alert-error { background: var(--bad-bg);  color: var(--bad);  border-color: color-mix(in srgb, var(--bad) 30%, transparent); }
.alert-ok    { background: var(--good-bg); color: var(--good); border-color: color-mix(in srgb, var(--good) 30%, transparent); }
.alert-info  { background: var(--accent-soft); color: var(--accent); border-color: var(--accent-line); }
.alert-warn  { background: var(--warn-bg); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.alert[hidden] { display: none; }

/* ------------------------------------------------------------------- table */
.table { width: 100%; border-collapse: collapse; font-size: .93rem; }
.table th, .table td { padding: 13px 14px; text-align: left; border-bottom: 1px solid var(--line); }
.table th {
  font-family: var(--display); font-weight: 600; font-size: .74rem;
  text-transform: uppercase; letter-spacing: .12em; color: var(--ink-faint);
}
.table tbody tr { transition: background .2s var(--ease); }
.table tbody tr:hover { background: var(--paper-sunk); }
.table tbody tr:last-child td { border-bottom: 0; }
.table-scroll { overflow-x: auto; }

/* ------------------------------------------------------------------- toast */
.toast {
  position: fixed; left: 50%; bottom: 30px; transform: translateX(-50%) translateY(90px);
  background: var(--ink); color: var(--paper); padding: 13px 22px;
  border-radius: var(--radius-pill); font-size: .91rem; font-weight: 500;
  box-shadow: var(--shadow-lift); opacity: 0; pointer-events: none;
  transition: opacity .25s var(--ease), transform .4s var(--ease-out); z-index: 200;
  max-width: min(520px, calc(100vw - 40px));
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ------------------------------------------------------------------ footer */
.site-footer {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--deep); color: var(--deep-ink);
  --ink: var(--deep-ink); --ink-soft: var(--deep-ink-soft); --ink-faint: #8d8172;
  --line: var(--deep-line);
  margin-top: 0; padding: clamp(56px, 6.5vw, 92px) 0 32px;
}
.site-footer::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: url("../img/art/footer-field.9321242d985a.jpg") center bottom / cover no-repeat;
  opacity: .5;
}
.site-footer::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(10, 9, 8, .9), rgba(10, 9, 8, .62));
}
.site-footer > .wrap { position: relative; z-index: 3; }
.footer-inner { display: grid; grid-template-columns: 2.2fr 1fr 1fr; gap: clamp(24px, 3vw, 40px); }
@media (max-width: 760px) { .footer-inner { grid-template-columns: 1fr 1fr; gap: 28px; }
  .footer-inner > :first-child { grid-column: 1 / -1; } }
.footer-title {
  font-family: var(--display); font-weight: 600; font-size: .78rem; margin: 0 0 14px;
  text-transform: uppercase; letter-spacing: .15em; color: var(--nearest);
}
.site-footer a {
  display: block; color: var(--deep-ink-soft); font-size: .89rem; padding: 4px 0;
  transition: color .2s var(--ease), transform .25s var(--ease-out);
}
.site-footer a:hover { text-decoration: none; transform: translateX(4px); }
.footer-brand-title {
  font-family: var(--display); font-weight: 600; letter-spacing: -.03em;
  font-size: clamp(1.4rem, 1.1rem + 1.1vw, 2rem); margin: 0 0 6px; color: var(--deep-ink);
}
.footer-legal { margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--deep-line); }

/* ------------------------------------------------------------- misc atoms */
.spinner {
  display: inline-block; width: 15px; height: 15px; vertical-align: -2px;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.empty { text-align: center; padding: 52px 20px; color: var(--ink-faint); }

/* Data pairs used across library, orders and account. */
.pairs { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 18px; }
.pair-k { margin: 0; font-size: .74rem; color: var(--ink-faint);
  text-transform: uppercase; letter-spacing: .12em; font-weight: 600; }
.pair-v { margin: 5px 0 0; font-family: var(--display); font-weight: 600;
  letter-spacing: -.02em; font-size: 1.02rem; }

/* --------------------------------------------------------------- reveals */
/* Scroll reveals are opt-in per element and are removed entirely under
   prefers-reduced-motion, where the class simply never applies. */
.reveal { opacity: 0; transform: translateY(22px); }
.motion-on .reveal {
  transition: opacity .8s var(--ease-out), transform .8s var(--ease-out);
}
.reveal.in { opacity: 1; transform: none; }
.motion-on .reveal-d1 { transition-delay: .08s; }
.motion-on .reveal-d2 { transition-delay: .16s; }
.motion-on .reveal-d3 { transition-delay: .24s; }
.motion-on .reveal-d4 { transition-delay: .32s; }

/* A headline that wipes up line by line. Lines are wrapped by the motion
   script; without JS the text is simply there, unwrapped and readable. */
.line-mask { display: block; overflow: hidden; }
.line-mask > i {
  display: block; font-style: inherit; transform: translateY(105%);
}
.motion-on .line-mask > i { transition: transform .95s var(--ease-out); }
.in .line-mask > i, .line-mask.in > i { transform: none; }
.line-mask:nth-child(2) > i { transition-delay: .09s; }
.line-mask:nth-child(3) > i { transition-delay: .18s; }
.line-mask:nth-child(4) > i { transition-delay: .27s; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
  .reveal { opacity: 1; transform: none; }
  .line-mask > i { transform: none; }
  .marquee-track { animation: none; }
}

/* -------------------------------------------------- page-specific layouts */
.header-tools { gap: 8px; flex-wrap: nowrap; }
@media (min-width: 901px) { .header-tools .nav-toggle { display: none; } }
.mb-l { margin-bottom: clamp(18px, 2vw, 26px); }
.toc-note { padding: 14px 18px; }
.cta-row { justify-content: center; }

/* The probe sits beside its explanation on a wide screen and under it on a
   narrow one, so the widget is never squeezed below a usable width. */
.probe-head {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(28px, 4vw, 56px); align-items: start;
}
@media (max-width: 960px) { .probe-head { grid-template-columns: 1fr; } }

.contents-grid {
  display: grid; grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
  gap: clamp(30px, 4.5vw, 66px); align-items: start;
}
@media (max-width: 900px) { .contents-grid { grid-template-columns: 1fr; } }
.contents-plate {
  margin: clamp(26px, 3vw, 40px) 0 0; border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; background: var(--paper-raised);
}
.contents-plate img { display: block; width: 100%; height: auto; }
.contents-plate figcaption { padding: 12px 14px; border-top: 1px solid var(--line); }
@media (max-width: 900px) { .contents-plate { display: none; } }

/* The book page: the chapter table becomes a plotted manifest. */
.book-hero {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .62fr);
  gap: clamp(28px, 4vw, 60px); align-items: center;
}
@media (max-width: 900px) { .book-hero { grid-template-columns: 1fr; }
  .book-hero-plate { display: none; } }
.book-hero-plate {
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  overflow: hidden; background: var(--paper-sunk); aspect-ratio: 16 / 10;
}
.book-hero-plate img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Account and library surfaces get one quiet band at the top rather than art
   on every card: these are pages people use, not pages that sell. */
.page-head { position: relative; isolation: isolate; padding: clamp(40px, 5vw, 72px) 0 clamp(28px, 3vw, 44px); }
.page-head-art::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: url("../img/art/library-band.cdb071e9cb65.jpg") center / cover no-repeat;
  opacity: .85;
}
.page-head-art::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--paper) 55%, transparent),
    color-mix(in srgb, var(--paper) 97%, transparent));
}
.page-head > .wrap { position: relative; z-index: 2; }

/* The access-denied and error pages carry one plate, sized so it never
   dominates the message it illustrates. */
.notice-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .72fr);
  gap: clamp(24px, 3.5vw, 48px); align-items: center;
}
@media (max-width: 800px) { .notice-grid { grid-template-columns: 1fr; }
  .notice-plate { display: none; } }
.notice-plate {
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  background: var(--paper-sunk); aspect-ratio: 4 / 3;
}
.notice-plate img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* On a phone the three-column hit row leaves the title about eleven
   characters, so the meter drops to its own line and the title is allowed to
   wrap. A truncated chapter name is worse than a taller row. */
@media (max-width: 560px) {
  .probe-hit { grid-template-columns: minmax(0, 1fr) 3.4rem; row-gap: 8px; }
  .probe-hit .t { white-space: normal; }
  .probe-hit .t small { white-space: normal; }
  .probe-hit .meter { grid-column: 1 / -1; }
  .probe-hits { min-height: 0; }
}

/* The stepped blocks are a bar chart at three columns or more. At two they
   read as a broken grid, so the steps flatten. */
@media (max-width: 860px) {
  .block, .block:nth-child(1), .block:nth-child(2), .block:nth-child(3) { min-height: 172px; }
}

/* ------------------------------------------------------------- the walk */
/* An index you can drop a query into. The canvas is the control, so it
   carries a tabindex and a focus ring like any other one. */
/* Explicit placement, because the reading order differs from the visual one.
   On a wide screen the copy and its controls stack in the left column beside
   the graph. On a narrow one the graph moves up between them, so nobody reads
   "click anywhere in the graph" before the graph is on screen. */
.walk-grid {
  display: grid; grid-template-columns: minmax(0, .78fr) minmax(0, 1.22fr);
  gap: clamp(26px, 3.4vw, 52px); align-items: start;
}
.walk-intro { grid-column: 1; grid-row: 1; }
.walk-side  { grid-column: 1; grid-row: 2; }
.walk-stage { grid-column: 2; grid-row: 1 / span 2; }
@media (max-width: 960px) {
  .walk-grid { grid-template-columns: 1fr; }
  .walk-intro, .walk-side, .walk-stage { grid-column: 1; }
  .walk-intro { grid-row: 1; }
  .walk-stage { grid-row: 2; }
  .walk-side  { grid-row: 3; }
}
.walk-side .walk-controls { margin-top: 0; }

.walk-stage {
  position: relative; border: 1px solid var(--line); border-radius: var(--radius-lg);
  overflow: hidden; background: color-mix(in srgb, var(--paper-sunk) 60%, transparent);
}
.walk-canvas {
  display: block; width: 100%; height: clamp(280px, 34vw, 420px);
  cursor: crosshair; touch-action: manipulation;
}
.walk-canvas:focus-visible { outline: 2px solid var(--nearest); outline-offset: -4px; }

.walk-readout {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: clamp(14px, 2.4vw, 30px);
  padding: 14px clamp(14px, 2vw, 20px);
  border-top: 1px solid var(--line);
  background: color-mix(in srgb, var(--paper-sunk) 55%, transparent);
}
.walk-figure { display: flex; align-items: baseline; gap: 8px; }
.walk-figure b {
  font-family: var(--display); font-weight: 600; letter-spacing: -.03em;
  font-size: clamp(1.3rem, 1.1rem + .8vw, 1.8rem); font-variant-numeric: tabular-nums;
  line-height: 1; min-width: 1.6ch;
}
.walk-figure span {
  font-size: .74rem; text-transform: uppercase; letter-spacing: .12em;
  color: var(--ink-faint);
}
/* The mode labels the picture, not the figures, so it sits on the picture.
   In the readout row it was wide enough to force its own line and leave the
   row two-thirds empty. */
.walk-mode {
  position: absolute; top: 12px; right: 12px; z-index: 2;
  padding: 5px 13px; border-radius: var(--radius-pill);
  border: 1px solid var(--line); background: color-mix(in srgb, var(--paper) 72%, transparent);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  font-family: var(--display); font-size: .69rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft);
  line-height: 1.5; pointer-events: none;
}

.walk-verdict {
  flex: 1 1 100%; font-size: .87rem; color: var(--ink-soft);
  padding-top: 4px; min-height: 1.4em;
}
.walk-verdict.is-ok   { color: var(--good); }
.walk-verdict.is-warn { color: var(--nearest); }

.walk-controls { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
.walk-legend { list-style: none; padding: 0; margin: 26px 0 0; }
.walk-legend li {
  display: flex; align-items: flex-start; gap: 11px; padding: 7px 0;
  font-size: .88rem; color: var(--ink-soft);
}
.walk-legend .k {
  flex: none; width: 11px; height: 11px; border-radius: 50%; margin-top: .42em;
  background: var(--key, var(--near));
}
.walk-legend .k-ring {
  background: transparent; border: 1.5px solid var(--key, var(--near));
}
.walk-legend .k-dash {
  background: transparent; border: 1.5px dashed var(--key, var(--far));
}

/* ------------------------------------------------------- the probe map */
/* The same five results, drawn as the space they live in. */
.probe-map-wrap { border-top: 1px solid var(--line); background: var(--paper); }
.probe-map { display: block; width: 100%; height: 148px; }
@media (max-width: 560px) { .probe-map { height: 128px; } }

/* --------------------------------------------- fields on other surfaces */
/* The CTA band and the footer get the same drifting graph as the hero, quieter
   and sparser, so the idea carries through the page rather than living only
   at the top. */
.band-cta .field-layer, .site-footer .field-layer {
  position: absolute; inset: 0; z-index: 2; display: block;
  width: 100%; height: 100%; pointer-events: none;
}
.band-cta .field-layer { opacity: .85; }
.site-footer .field-layer { opacity: .6; }
