/* ============================================================
   NotedML — shared chrome
   ============================================================
   Everything that must look identical on every page: tokens, type,
   buttons, nav, footer, the ground wash, the texture primitives, the
   post list, and the v2 entry animation.

   Page-specific layout (the index hero copy block, the module rows, the
   founder split, article prose) stays inline in its own page.

   URLs here resolve against THIS file, i.e. against assets/. That is why
   they read url("tex/x.jpg") and not url("assets/tex/x.jpg") — it makes
   the sheet work unchanged from / and from /blog/.

   See DESIGN.md for the rules these styles encode. The short version:
   all colour comes from the texture images, type is black on white, one
   texture element per section.
   ============================================================ */

:root{
  --ink:#000000;
  --ink-64:rgba(0,0,0,.64);
  --ink-46:rgba(0,0,0,.46);
  --paper:#FFFFFF;
  --rule:rgba(0,0,0,.14);
  --rule-hard:#000000;

  --display:"Poppins","Century Gothic",Futura,system-ui,sans-serif;
  --body:"Inter Tight",system-ui,-apple-system,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  --gut:clamp(22px,5.5vw,96px);
  --max:1280px;
  --sec-pad:clamp(88px,13vh,168px);

  /* v2 mono chrome: lowercase, regular weight, tighter tracking than v1.
     Caps at .18em tracking read as shouting; lowercase needs less air. */
  --lab-size:12px;
  --lab-track:.10em;

  --ease:cubic-bezier(.22,1,.36,1);

  /* driven by the scroll handler; stays 0 with JS off or reduced motion */
  --ground-y:0px;

  /* Mask atlases. Solid black layers only, used to cut hard geometric
     windows out of the texture crops. Nothing here renders as a visible
     gradient. See DESIGN.md §3 for the position maths. */
  --wave-img: linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000);
  --wave-size: 3.2% 30%, 3.2% 54%, 3.2% 76%, 3.2% 44%, 3.2% 90%, 3.2% 62%, 3.2% 96%, 3.2% 38%, 3.2% 70%, 3.2% 56%, 3.2% 86%, 3.2% 34%, 3.2% 66%, 3.2% 94%, 3.2% 48%, 3.2% 78%, 3.2% 52%, 3.2% 30%;
  --wave-pos: 0% 50%, 5.88% 50%, 11.76% 50%, 17.65% 50%, 23.53% 50%, 29.41% 50%, 35.29% 50%, 41.18% 50%, 47.06% 50%, 52.94% 50%, 58.82% 50%, 64.71% 50%, 70.59% 50%, 76.47% 50%, 82.35% 50%, 88.24% 50%, 94.12% 50%, 100% 50%;

  --grid-img: linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000);
  --grid-size: 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%, 22.5% 30.6%;
  --grid-pos: 0% 0%, 33.33% 0%, 66.67% 0%, 100% 0%, 0% 50%, 33.33% 50%, 66.67% 50%, 100% 50%, 0% 100%, 33.33% 100%, 66.67% 100%, 100% 100%;

  --bars-img: linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000);
  --bars-size: 100% 19%, 76% 19%, 55% 19%, 36% 19%;
  --bars-pos: 0% 0%, 0% 33.33%, 0% 66.67%, 0% 100%;

  /* MCPeer — three concentric frames around a solid core. Nine layers:
     four bars per ring, plus the core. The element is 5/4, so the two
     thicknesses differ on purpose: 8% of the width and 10% of the height
     are the same number of pixels, which is what makes the stroke read as
     even all the way round. Ring 2 is inset one stroke-gap (20% of width,
     25% of height) so the gaps match too. */
  --rings-img: linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000), linear-gradient(#000,#000);
  --rings-size: 100% 10%, 100% 10%, 8% 100%, 8% 100%, 60% 10%, 60% 10%, 8% 50%, 8% 50%, 20% 25%;
  --rings-pos: 0% 0%, 0% 100%, 0% 0%, 100% 0%, 50% 27.78%, 50% 72.22%, 21.74% 50%, 78.26% 50%, 50% 50%;

  /* ---------- icon atlas ----------
     Masks for .ticon — the oversized texture icon that hangs out of the top
     right corner of a card. Every glyph is a BOLD FILLED SILHOUETTE built
     from hard-edged geometry: no strokes, no rounded joins, no curves. That
     is not a style preference, it is the same rule the crops obey, and it is
     what keeps an icon reading as a cut piece of paint rather than as UI.

     Each is a 64x64 viewBox, black fill, used only as a mask — nothing here
     contributes colour. Where a glyph needs holes it declares fill-rule
     evenodd and the sub-paths must not overlap; where sub-paths are meant to
     merge it declares nonzero. Getting that backwards punches a hole through
     the middle of the icon, which is the one failure mode to watch for.

     Adding a glyph: keep it inside the 64 box, keep every edge axis-aligned
     or a clean diagonal, and check it still reads at 60px. */

  /* figure 1 — the six components of an agent */
  --ico-model: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M12 12h40v40H12zM24 24h16v16H24zM18 2h6v10h-6zM29 2h6v10h-6zM40 2h6v10h-6zM18 52h6v10h-6zM29 52h6v10h-6zM40 52h6v10h-6zM2 18h10v6H2zM2 29h10v6H2zM2 40h10v6H2zM52 18h10v6H52zM52 29h10v6H52zM52 40h10v6H52z'/%3E%3C/svg%3E");
  --ico-instructions: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M10 2h30l14 14v46H10zM40 2v14h14zM18 26h28v5H18zM18 37h28v5H18zM18 48h18v5H18z'/%3E%3C/svg%3E");
  --ico-tools: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M16 16h32v32H16zM27 27h10v10H27zM25 4h14v12H25zM25 48h14v12H25zM4 25h12v14H4zM48 25h12v14H48zM4 4h12v12H4zM48 4h12v12H48zM4 48h12v12H4zM48 48h12v12H48z'/%3E%3C/svg%3E");
  --ico-knowledge: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' d='M20 10h26v11H20zM13 25h40v11H13zM6 40h50v11H6zM6 55h50v5H6z'/%3E%3C/svg%3E");
  --ico-memory: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M4 14h56v28H4zM12 20h5v16h-5zM23 20h5v16h-5zM34 20h5v16h-5zM45 20h5v16h-5zM10 42h8v8h-8zM28 42h8v8h-8zM46 42h8v8h-8z'/%3E%3C/svg%3E");
  --ico-orchestration: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' d='M24 2h16v16H24zM30 18h4v12h-4zM8 30h48v4H8zM10 34h4v6h-4zM30 34h4v6h-4zM50 34h4v6h-4zM4 40h16v16H4zM24 40h16v16H24zM44 40h16v16H44z'/%3E%3C/svg%3E");

  /* figure 3 — the eight assurance domains */
  --ico-quality: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M4 4h56v56H4zM15 32l5-5 7 7 17-17 5 5-22 22z'/%3E%3C/svg%3E");
  /* scales, not a gavel: a gavel's whole identity is one long diagonal into
     the bottom right, and the corner crop ate it. Scales keep their mass low
     and central, which is the part that survives. */
  --ico-policy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' d='M6 14h52v6H6zM30 20h4v30h-4zM16 50h32v7H16zM11 20h2v6h-2zM51 20h2v6h-2zM2 26h22l-5 12H7zM40 26h22l-5 12H45z'/%3E%3C/svg%3E");
  --ico-brand: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M4 8h56v34H26L12 58V42H4zM14 18h36v5H14zM14 28h24v5H14z'/%3E%3C/svg%3E");
  --ico-safety: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M32 2l26 10v20L32 62 6 32V12zM28 16h8v10h9v8h-9v10h-8V34h-9v-8h9z'/%3E%3C/svg%3E");
  --ico-security: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M16 6h32v20h-9V15H25v11h-9zM8 26h48v34H8zM28 36h8v14h-8z'/%3E%3C/svg%3E");
  /* key: head enlarged and shaft shortened so the identifying end is the one
     that stays inside the card rather than the teeth */
  --ico-privacy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M4 14h28v34H4zM14 24h9v14h-9zM32 26h22v10H32zM40 36h7v12h-7zM49 36h7v10h-7z'/%3E%3C/svg%3E");
  --ico-trace: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' d='M4 4h14v14H4zM9 18h4v14H9zM9 28h16v4H9zM25 25h14v14H25zM30 39h4v14h-4zM30 49h16v4H30zM46 46h14v14H46z'/%3E%3C/svg%3E");
  --ico-ops: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' d='M26 8h30v11H26zM45 19h11v26H45zM8 45h48v11H8zM8 28h11v17H8zM1 28h25L13.5 8z'/%3E%3C/svg%3E");

  /* ---------- sigil atlas ----------
     Masks for .sigil — the article mark. Same 64x64 viewBox, same bold
     filled hard-edged discipline as --ico-* above, but a different job and
     therefore a different constraint.

     A .ticon is CUT OUT OF the paint: the glyph is the window. A .sigil is
     the KNOCKOUT: the box is paint and the glyph is the hole punched
     through it, so what you read as white is the paper underneath, not a
     colour anyone authored. Same rule, opposite polarity.

     The other difference is the crop. .ticon loses its top 14% and right
     16%, so trap 11 says put the identifying mass low and central. A .sigil
     is scaled past the box on ALL FOUR sides — that collision is the whole
     point of the motif — so the rule tightens: the identifying mass must be
     CENTRAL IN BOTH AXES, and every glyph here is drawn to run off all four
     edges at --sigil-scale so nothing sits neatly inside its box.

     fill-rule is load-bearing and fails silently (trap 10). Declared per
     glyph: evenodd where a glyph needs holes, and then no two subpaths may
     overlap — they may only TOUCH; nonzero where subpaths are meant to
     merge. --sig-check is the one that relies on nonzero. */

  /* THE WEIGHT BUDGET IS NOT THE ONE --ico-* USES, and this is the mistake
     to avoid. A .ticon glyph is the window onto the paint, so mass IS the
     image and ~50% coverage reads as bold. A .sigil glyph is the hole, so
     mass is what DESTROYS the image: the first cut of these five was drawn
     at ticon weight and every box came back as a white field with four
     corners of paint left in it — the exact opposite of a texture box with
     a white mark on it. The paint must stay the majority.

     So: keep each glyph near 30-35% of the 64 box, and get boldness from
     stroke thickness rather than from coverage. Rings and frames are cheap
     here in a way they never are for a ticon, because a ring's interior
     stays paint. Judge every glyph on a render at both sizes — the index
     row is ~90px and the article head is ~236px. */

  /* primer 1 — the distribution over the next token. Three heavy columns,
     wide gaps. The tall middle one runs the full height of the box and the
     outer two are cut to slivers, which reads correctly as a distribution
     continuing past the frame. */
  --sig-bars: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='nonzero' d='M0 34h14v30H0zM25 14h11v50H25zM50 42h14v22H50z'/%3E%3C/svg%3E");

  /* primer 2 — a confident statement decaying into fragments. Horizontal on
     purpose: primer 1 is vertical columns and the two must not read as the
     same mark at a glance. The bottom row's three fragments are spaced so
     they stay spread once the box cuts ~10 units off each side. */
  --sig-decay: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='nonzero' d='M2 0h14l18 26H20zM30 38h14l18 26H48z'/%3E%3C/svg%3E");

  /* primer 3 — a chain of reasoning with the middle link broken open. The
     broken link is deliberately the CENTRE one: the outer two are cut away
     by the box and the one carrying the meaning is the one that survives.
     The two whole links are ring outlines (outer + hole, evenodd); the
     broken one is five separate bars with a gap in its right wall, which is
     also why it needs no hole subpath of its own. */
  --sig-chain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='nonzero' d='M4 12h56v10H4zM4 42h20v10H4zM40 42h20v10H40zM4 22h10v20H4zM50 22h10v20H50z'/%3E%3C/svg%3E");

  /* primer 4 — a target: square ring, small core, arms crossing the gap and
     running off the box. Every subpath here only ever TOUCHES its neighbour
     (each arm starts exactly where the ring or the core ends — the cog-teeth
     pattern) because evenodd would turn any actual overlap into a hole. The
     ring's interior stays paint, which is what keeps the coverage down. */
  --sig-target: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M12 12h40v40H12zM19 19h26v26H19zM28 28h8v8H28zM0 29h12v6H0zM52 29h12v6H52z'/%3E%3C/svg%3E");

  /* the framework — sign-off. One subpath, no frame: at this scale a frame
     survives only as slivers at the four edges and reads as noise, so the
     check carries the whole mark alone. Both arms are true 45 degrees and
     the perpendicular thickness is ~10 of 64. */
  --sig-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23000' fill-rule='nonzero' d='M4 24L20 40L58 2L58 16L20 54L4 38Z'/%3E%3C/svg%3E");
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
/* The nav is sticky, so every anchor target has to clear it or the heading
   lands underneath the bar. One declaration on the scroll container covers
   fragment links, the writing rail, and the landing page's scroll snap. */
html{scroll-padding-top:96px}
@media (max-width:760px){html{scroll-padding-top:86px}}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

/* The paper is the canvas; body must stay transparent so the ground wash
   below shows through every white area of the page. */
html{background:var(--paper)}

body{
  margin:0;
  background:transparent;
  color:var(--ink);
  font-family:var(--body);
  font-size:17px;
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
::selection{background:#000;color:#fff}

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

/* ---------- ground ----------
   S1 turned 90 deg clockwise and washed to within 3% of white in the build,
   so the tint is baked into the asset rather than authored in CSS, and the
   JPEG is opaque — it *is* the paper. Raise or lower the 0.03 to make the
   wash more or less present; nothing in CSS needs to change:

     K=0.03
     ffmpeg -y -i assets/xai-tmp-imgen-1fa912f6-f5b9-9946-b983-94cf602be368-d0437d24.png \
       -vf "transpose=1,lutrgb=r='255-(255-val)*$K':g='255-(255-val)*$K':b='255-(255-val)*$K'" \
       -q:v 3 assets/tex/ground.jpg

   The layer is deliberately taller than the viewport; the script drifts it
   upward at a fraction of scroll speed and never past that slack. */
.ground{
  position:fixed;
  top:0;left:0;
  width:100%;
  height:200vh;
  z-index:-1;
  pointer-events:none;
  background-image:url("tex/ground.jpg");
  background-repeat:no-repeat;
  background-position:center top;
  background-size:cover;
  transform:translate3d(0,var(--ground-y),0);
}

/* screen-reader copy of any text that gets scrambled */
.vh{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* skip link — visible only once focused, and only on white */
.skip{
  position:absolute;left:-9999px;top:0;z-index:100;
  background:#000;color:#fff;padding:12px 18px;
  font-family:var(--mono);font-size:var(--lab-size);letter-spacing:var(--lab-track);
  text-transform:lowercase;
}
.skip:focus{left:0}

/* ---------- type ---------- */
h1,h2,h3{font-family:var(--display);font-weight:400;margin:0}
h1{font-size:clamp(34px,6.4vw,86px);line-height:1;letter-spacing:-.035em}
h2{font-size:clamp(29px,4.2vw,54px);line-height:1.08;letter-spacing:-.028em}
h3{font-size:clamp(25px,3.2vw,42px);line-height:1.12;letter-spacing:-.026em}
p{margin:0}

.lab{
  font-family:var(--mono);
  font-size:var(--lab-size);
  font-weight:400;
  letter-spacing:var(--lab-track);
  text-transform:lowercase;
  line-height:1.5;
}
.lab-dim{color:var(--ink-46)}
.prose{max-width:60ch;font-size:clamp(17px,1.35vw,19px)}
.prose + .prose{margin-top:1.1em}

/* ---------- buttons ---------- */
.btn{
  display:inline-block;
  font-family:var(--mono);
  font-size:var(--lab-size);
  font-weight:400;
  letter-spacing:.08em;
  text-transform:lowercase;
  padding:15px 26px;
  background:#000;
  color:#fff;
  border:1px solid #000;
  transition:background .18s linear,color .18s linear;
}
.btn:hover{background:#fff;color:#000}
.btn-ghost{background:transparent;color:#000}
.btn-ghost:hover{background:#000;color:#fff}

/* ---------- nav ---------- */
/* Flat opaque white, deliberately — the bar reads as chrome sitting on top of
   the page, not as part of it. An earlier version carried its own slice of the
   ground wash so the tint ran unbroken behind it; that has been dropped in
   favour of a clean white bar. Do not reintroduce a texture layer here.
   Hairline is an outer shadow rather than a border so appearing on scroll
   costs no layout shift. */
.nav{
  position:sticky;top:0;z-index:50;
  background:var(--paper);
  box-shadow:0 1px 0 transparent;
  transition:box-shadow .2s linear;
}
.nav.is-stuck{box-shadow:0 1px 0 var(--rule)}
.nav-in{position:relative;z-index:1;display:flex;align-items:center;justify-content:space-between;gap:24px;height:76px}
.mark{display:block;flex:none}
.mark img{width:clamp(104px,11vw,132px);height:auto}
.nav-links{display:flex;align-items:center;gap:clamp(18px,2.6vw,38px)}
.nav-links a{
  font-family:var(--mono);font-size:var(--lab-size);font-weight:400;
  letter-spacing:var(--lab-track);text-transform:lowercase;
}
.nav-links a:not(.btn):hover{box-shadow:inset 0 -1px 0 #000}
/* the page you are on, marked without colour */
.nav-links a[aria-current="page"]{box-shadow:inset 0 -1px 0 #000}
.nav-links .btn{padding:12px 20px}
@media (max-width:760px){
  .nav-links a:not(.btn){display:none}
  .nav-in{height:66px}
}

/* ---------- texture primitives ----------
   One texture element per section. Every crop is cut to a hard geometric
   shape: no soft masks, no rounded corners, no shadows. */
.tex{
  background-repeat:no-repeat;
  background-position:center;
  background-size:cover;
}
/* Planner — stepped / staircase */
.tex-stair{
  aspect-ratio:1/1;
  background-image:url("tex/planner-indigo.jpg");
  -webkit-clip-path:polygon(0 0,38% 0,38% 30%,68% 30%,68% 62%,100% 62%,100% 100%,62% 100%,62% 74%,32% 74%,32% 42%,0 42%);
  clip-path:polygon(0 0,38% 0,38% 30%,68% 30%,68% 62%,100% 62%,100% 100%,62% 100%,62% 74%,32% 74%,32% 42%,0 42%);
}
/* Transcriptor — horizontal waveform band */
.tex-wave{
  aspect-ratio:16/9;
  background-image:url("tex/transcriptor-orange.jpg");
  -webkit-mask-image:var(--wave-img);mask-image:var(--wave-img);
  -webkit-mask-size:var(--wave-size);mask-size:var(--wave-size);
  -webkit-mask-position:var(--wave-pos);mask-position:var(--wave-pos);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
}
/* Dash — grid of tiles */
.tex-grid{
  aspect-ratio:4/3;
  background-image:url("tex/dash-teal.jpg");
  -webkit-mask-image:var(--grid-img);mask-image:var(--grid-img);
  -webkit-mask-size:var(--grid-size);mask-size:var(--grid-size);
  -webkit-mask-position:var(--grid-pos);mask-position:var(--grid-pos);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
}
/* Ranker — stacked bars of decreasing width */
.tex-bars{
  aspect-ratio:3/2;
  background-image:url("tex/ranker-magenta.jpg");
  -webkit-mask-image:var(--bars-img);mask-image:var(--bars-img);
  -webkit-mask-size:var(--bars-size);mask-size:var(--bars-size);
  -webkit-mask-position:var(--bars-pos);mask-position:var(--bars-pos);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
}
/* MCPeer — concentric frames around a solid core */
.tex-rings{
  aspect-ratio:5/4;
  background-image:url("tex/mcpeer-coral.jpg");
  -webkit-mask-image:var(--rings-img);mask-image:var(--rings-img);
  -webkit-mask-size:var(--rings-size);mask-size:var(--rings-size);
  -webkit-mask-position:var(--rings-pos);mask-position:var(--rings-pos);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
}
.tex-square{aspect-ratio:1/1}
.tex-column{aspect-ratio:1/2.5;background-image:url("tex/founder.jpg")}

/* ---------- .ticon — the oversized texture icon ----------
   The motif: a card that carries a heading gets one bold filled icon hung
   out of its top right corner, cut from paint rather than drawn in ink. The
   glyph is only a mask; every pixel of colour is the crop underneath, which
   is what keeps it inside the "no CSS-authored colour" rule.

   Oversized and deliberately overflowing: the icon is bigger than the corner
   it sits in and the host card clips it dead straight on its own border. The
   crop is what you see, the geometry is what cuts it — the same relationship
   the module shapes have, one order of magnitude smaller.

   §1 says one texture element per section. A grid of these is the one
   sanctioned relaxation, and it only holds while every icon in a figure is
   cut from the SAME crop, each sampling a different passage of it via
   --ticon-x. One material, one accent, many windows. Mix crops inside one
   figure and the grid immediately reads as confetti — see DESIGN.md §3.

   Host requirements: position:relative and overflow:hidden (that is what
   .has-ticon is for), plus padding-right on whatever text runs under the
   corner. The icon is decoration, so it is aria-hidden in the markup and
   never carries meaning the heading doesn't already carry. */
.has-ticon{position:relative;overflow:hidden}
.ticon{
  position:absolute;
  top:0;right:0;
  width:var(--ticon-size,96px);
  height:var(--ticon-size,96px);
  /* Pushed out past the corner so the card's own edge does the cropping.
     Tuned, not guessed: at 22%/26% the cut took the top off every glyph and
     a document, a gavel and a shield all collapsed into the same anonymous
     block of paint. Enough overhang that the card is visibly slicing the
     icon, not so much that it stops being a picture of anything. */
  transform:translate(16%,-14%);
  background-image:url("tex/writing.jpg");
  background-repeat:no-repeat;
  background-size:cover;
  background-position:var(--ticon-x,50%) center;
  -webkit-mask-image:var(--ticon);mask-image:var(--ticon);
  -webkit-mask-size:contain;mask-size:contain;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  pointer-events:none;
}

/* ---------- .sigil — the article mark ----------
   Each piece of writing owns one: a hard square of paint with a bold white
   glyph knocked straight through it, scaled past the box so it collides with
   all four edges. It is the inverse of .ticon directly above — there the
   glyph is the window onto the paint, here the glyph is the hole punched out
   of it — and the two never appear in the same section.

   NOTHING HERE IS WHITE. The glyph is a hole: two mask layers, a full-box
   rectangle and the oversized glyph, XORed so the glyph subtracts. What
   shows through is the paper and the ground wash beneath the element, which
   is what keeps the motif inside "all colour comes from texture" (DESIGN.md
   §1) — painting the glyph var(--paper) would look almost identical on white
   and wrong over the wash, and would be an authored colour besides.

   Layer order matters: the glyph is listed FIRST (top layer) and
   mask-composite describes how it combines with the rectangle below it.
   -webkit-mask-composite uses the older WebKit keyword (xor) for the same
   operation; both are emitted. Where neither is supported the layers simply
   add and the element degrades to a plain square of paint — no glyph, but
   nothing broken.

   §1 says one texture element per section. A row of these is the same
   sanctioned relaxation .ticon gets and holds on the same condition: every
   sigil is cut from the SAME crop and only --sigil-x moves, walking each
   piece to a different passage of sigil.jpg. So five marks on the writing
   index are still one material and one accent. The passages are evenly
   spaced by the usual formula, P_i = i x 100 / (n - 1). */
.sigil{
  display:block;
  width:var(--sigil-size,120px);
  aspect-ratio:1/1;
  flex:none;
  background-image:url("tex/sigil.jpg");
  background-repeat:no-repeat;
  background-size:cover;
  background-position:var(--sigil-x,50%) center;
  -webkit-mask-image:var(--sigil),linear-gradient(#000,#000);
          mask-image:var(--sigil),linear-gradient(#000,#000);
  -webkit-mask-size:var(--sigil-scale,130%) var(--sigil-scale,130%),100% 100%;
          mask-size:var(--sigil-scale,130%) var(--sigil-scale,130%),100% 100%;
  -webkit-mask-position:center,center;
          mask-position:center,center;
  -webkit-mask-repeat:no-repeat,no-repeat;
          mask-repeat:no-repeat,no-repeat;
  -webkit-mask-composite:xor;
          mask-composite:exclude;
  pointer-events:none;
}

/* Which glyph and which passage of the crop belongs to which piece. This is
   the one thing about a sigil that must be identical wherever it appears —
   the mark on a post's own header and the mark on its row in the writing
   index have to be the same mark — so it is shared chrome and lives here,
   not inline on either page. A post carries .sigil plus its own class and
   sets nothing else. */
.sig--predicts { --sigil:var(--sig-bars);   --sigil-x:0%   }
.sig--halluc   { --sigil:var(--sig-decay);  --sigil-x:25%  }
.sig--reasoning{ --sigil:var(--sig-chain);  --sigil-x:50%  }
.sig--mindset  { --sigil:var(--sig-target); --sigil-x:75%  }
.sig--framework{ --sigil:var(--sig-check);  --sigil-x:100% }

/* ---------- full-bleed texture panel ----------
   Used by the index hero and by the two writing pages. The wordmark stays
   on white in the nav above it. Text is only ever placed on this panel
   after its contrast has been measured — DESIGN.md §5. */
.hero-panel{
  position:relative;
  min-height:clamp(460px,74vh,760px);
  display:flex;
  align-items:center;
  background-image:url("tex/hero.jpg");
  background-repeat:no-repeat;
  background-position:left center;
  background-size:cover;
}
.hero-panel-in{
  /* left edge tracks the .wrap content edge so anything in the panel hangs
     off the same margin as the wordmark in the nav above */
  width:100%;
  padding-left:max(var(--gut),calc((100vw - var(--max))/2 + var(--gut)));
  padding-right:var(--gut);
  transform:translateY(-2%);
}
@media (max-width:900px){
  .hero-panel{min-height:clamp(420px,62vh,560px)}
  .hero-panel-in{width:100%;transform:none}
}
/* There used to be a .hero-panel--band here: a short textless strip of
   writing.jpg that punctuated the writing index and the head of every post.
   The sigil does that job now, per piece rather than identically on all six
   pages, so the modifier had no callers left and is gone. .hero-panel itself
   is still the landing page's hero. */

/* ---------- generic section ---------- */
.sec{padding:var(--sec-pad) 0}
.sec-head .lab{margin-bottom:clamp(24px,3.5vw,44px)}
.sec-head h2{margin-bottom:clamp(22px,2.6vw,32px);max-width:22ch}
.sec-head .prose{max-width:56ch;color:var(--ink-64)}

/* ---------- post list ----------
   Shared by the writing index and the "more writing" block at the foot of a
   post. Rows, not cards: hairlines only, no boxes, no shadows. */
.posts{border-top:1px solid var(--rule-hard)}
.post-row{
  display:grid;
  grid-template-columns:clamp(96px,12vw,150px) minmax(0,1fr) auto;
  gap:clamp(20px,4vw,56px);
  align-items:baseline;
  padding:clamp(26px,3.2vw,38px) 0;
  border-bottom:1px solid var(--rule);
}
.post-row .post-meta{color:var(--ink-46)}
.post-row h3{
  font-size:clamp(21px,2.3vw,32px);
  letter-spacing:-.022em;
  margin-bottom:10px;
}
.post-row .post-dek{color:var(--ink-64);font-size:16px;max-width:56ch;line-height:1.55}
.post-row .post-go{
  font-family:var(--mono);font-size:var(--lab-size);letter-spacing:var(--lab-track);
  text-transform:lowercase;white-space:nowrap;
}
/* A row carrying the piece's sigil takes one extra column in front of the
   date. Opt-in via the modifier rather than changing the base template,
   because .posts is shared with the "more writing" block at the foot of a
   post and a row there has three children, not four — widening the base
   grid would shift every one of them into the wrong column.

   Deliberately declared ABOVE the 760 block below: it carries no more
   specificity than .post-row, so source order is the only thing that lets
   the single-column stack win at that width. */
.post-row--mark{grid-template-columns:auto clamp(96px,12vw,150px) minmax(0,1fr) auto}
.post-row .sigil{--sigil-size:clamp(60px,7.4vw,104px);align-self:start}
a.post-row:hover h3{box-shadow:inset 0 -1px 0 #000}
/* a post that isn't written yet: no link, dimmed, and it says so */
.post-row--soon{color:var(--ink-46)}
.post-row--soon h3{color:var(--ink-46)}
.post-row--soon .post-dek{color:var(--ink-46)}
@media (max-width:760px){
  .post-row{grid-template-columns:1fr;gap:12px}
  .post-row .post-go{order:3}
}

/* ---------- the article shell ----------
   Everything a post in blog/ is built from. This lived in each post's own
   <style> while there was one post; there are five now, and the shell has to
   stay identical across all of them, so it belongs here under the same rule
   as the nav and the footer. What stays inline in a post is only its own
   figures: the pseudocode block and the token distribution (primer 1), the
   faithfulness bars (primer 3), the habit list and the arithmetic tables
   (primer 4), the axis / split / tiers / gates / RACI blocks (the framework),
   and the .ticon glyph mapping for whichever grid that post carries.

   An article is TWO MEASURES ON ONE LEFT MARGIN: prose capped at 64ch,
   figures and tables running the full .wrap width. Nothing is centred — the
   whole piece hangs off the gutter the wordmark hangs off.

   Every diagram is pure black hairline art (DESIGN.md §1). The only colour on
   these pages is the texture band and the .ticon corner icons, which are
   paint showing through a mask rather than anything authored in CSS. */

/* the head: kicker, title, dek, byline row, buttons */
/* Two columns: everything the reader reads on the left, the piece's sigil on
   the right. The sigil is this section's ONE texture element — it replaced
   the strip of writing.jpg that used to sit under every article head, which
   is why there is no .hero-panel--band in a post any more. Two texture
   elements in one header is the thing §1 does not allow.

   .art-head also carries .wrap, so its vertical padding stays LONGHAND. The
   shorthand would take the page gutter with it — trap 1. */
.art-head{
  padding-top:clamp(48px,6vw,88px);padding-bottom:clamp(44px,5.5vw,76px);
  display:flex;align-items:flex-start;justify-content:space-between;
  gap:clamp(30px,5vw,76px);
}
.art-head-txt{flex:1 1 auto;min-width:0}
.art-head .sigil{--sigil-size:clamp(124px,17vw,236px);margin-top:clamp(2px,.8vw,10px)}
@media (max-width:900px){
  /* Stacked, mark first: at this width it is the piece's opening image,
     which is the job the band used to do. */
  .art-head{flex-direction:column-reverse;align-items:flex-start;gap:clamp(26px,5vw,40px)}
  .art-head .sigil{--sigil-size:clamp(104px,26vw,156px);margin-top:0}
}
.art-head .lab{margin-bottom:clamp(24px,3vw,40px)}
.art-head h1{max-width:14ch;margin-bottom:clamp(24px,2.8vw,34px)}
.art-dek{
  font-family:var(--display);font-weight:400;
  font-size:clamp(19px,2vw,28px);line-height:1.34;letter-spacing:-.018em;
  max-width:34ch;
  margin-bottom:clamp(34px,4vw,52px);
}
.art-meta{
  display:flex;flex-wrap:wrap;gap:clamp(16px,3vw,44px);
  align-items:baseline;
  padding-top:22px;border-top:1px solid var(--rule);
  margin-bottom:clamp(30px,3.4vw,42px);
}
.art-cta{display:flex;flex-wrap:wrap;gap:12px}

/* the running text */
.art{padding-bottom:var(--sec-pad)}
.art-body{max-width:64ch}
.art-body p{font-size:clamp(17px,1.32vw,18.5px);line-height:1.62}
.art-body p + p{margin-top:1.15em}
.art-body strong{font-weight:500}
.art-body a:not(.btn){box-shadow:inset 0 -1px 0 var(--rule)}
.art-body a:not(.btn):hover{box-shadow:inset 0 -1px 0 #000}

/* a Part opens on a hard rule; sections within it on a hairline. Same rhythm
   as the landing page's sections. */
.part{border-top:1px solid var(--rule-hard);padding-top:clamp(44px,5vw,72px);margin-top:clamp(64px,8vw,112px)}
.part > .lab{margin-bottom:clamp(22px,2.6vw,34px)}
.part > h2{max-width:20ch;margin-bottom:clamp(26px,3vw,38px)}
.sub{margin-top:clamp(46px,5.5vw,80px)}
.sub > h3{
  font-size:clamp(21px,2.1vw,29px);letter-spacing:-.02em;
  margin-bottom:clamp(18px,2vw,24px);max-width:26ch;
}
.sub > .lab{margin-bottom:12px}

/* hairline bullet rows, same idiom as the module capability lists */
.bullets{list-style:none;margin:clamp(24px,2.8vw,34px) 0 0;padding:0;border-top:1px solid var(--rule);max-width:64ch}
.bullets li{
  padding:15px 0 15px 26px;border-bottom:1px solid var(--rule);
  position:relative;font-size:16px;line-height:1.55;
}
.bullets li::before{content:"";position:absolute;left:0;top:24px;width:9px;height:1px;background:#000}
.bullets li b{font-weight:500}

/* a boxed aside — one hard rectangle, no fill */
.callout{
  border:1px solid var(--rule-hard);
  padding:clamp(24px,3vw,34px);
  margin:clamp(38px,4.5vw,60px) 0;
  max-width:64ch;
}
.callout .lab{margin-bottom:14px}
.callout p{font-size:16.5px;line-height:1.58}
.callout p + p{margin-top:1.1em}

/* figures: a block, then a hairline caption in dimmed mono */
.fig{margin:clamp(46px,5.5vw,80px) 0 0}
.fig figcaption{
  margin-top:18px;padding-top:14px;border-top:1px solid var(--rule);
  font-family:var(--mono);font-size:var(--lab-size);letter-spacing:var(--lab-track);
  text-transform:lowercase;color:var(--ink-46);line-height:1.6;max-width:70ch;
}
.fig figcaption .kase{text-transform:none}
/* copy that runs straight on from a figure needs the same air a .sub gets —
   the caption's own rule is not enough separation on its own */
.fig + .art-body,.fig + .bullets{margin-top:clamp(38px,4.5vw,60px)}

/* Card grid used by the figures — hairline boxes, no fills. Cells draw their
   own right and bottom edge; the grid supplies the two outer edges those
   leave open, so every join stays a single hairline. */
.figgrid{display:grid;gap:0;border-top:1px solid var(--rule-hard);border-left:1px solid var(--rule-hard)}
.figgrid-2{grid-template-columns:repeat(2,1fr)}
.figgrid-3{grid-template-columns:repeat(3,1fr)}
.figgrid-4{grid-template-columns:repeat(4,1fr)}
.figcell{
  border-right:1px solid var(--rule-hard);
  border-bottom:1px solid var(--rule-hard);
  padding:clamp(18px,2vw,26px);
  min-width:0;
  /* a trailing block — the failure surface, the consequence link — sits on
     the floor of every cell, so the cards in a row finish level however long
     the copy runs */
  display:flex;
  flex-direction:column;
}
.figcell .lab{margin-bottom:12px}
.figcell h4{
  font-family:var(--display);font-weight:400;margin:0 0 12px;
  font-size:clamp(16px,1.5vw,19px);line-height:1.2;letter-spacing:-.015em;
}
.figcell p{font-size:14.5px;line-height:1.5;color:var(--ink-64)}
/* margin-bottom on the body copy, not margin-top on .fail — margin-top:auto
   is doing the pushing and would swallow a second margin. :not(:last-child)
   keeps it off the cards that carry no trailing block at all. */
.figcell > p:not(:last-child){margin-bottom:18px}
.figcell .fail{
  margin-top:auto;padding-top:14px;border-top:1px solid var(--rule);
}
.figcell .fail p:last-child{margin-bottom:0}
/* Bottom-aligning is only half of it: where two cells in a row have a
   different number of lines, their internal rules still land at different
   heights. Subgrid hands every cell in the row the same tracks, so the label,
   the heading, the body and the rule all line up straight across. Guarded,
   because without subgrid the flex fallback above is the better of the two.
   The span has to match the number of children a cell actually has, and that
   differs by figure — hence the three variants. Per DESIGN.md trap 9 this
   block sits AFTER every rule it is meant to beat. */
@supports (grid-template-rows: subgrid){
  .figcell{display:grid;grid-template-rows:subgrid;row-gap:0}
  .figgrid--r4 .figcell{grid-row:span 4}
  .figgrid--r3 .figcell{grid-row:span 3}
  .figgrid--r2 .figcell{grid-row:span 2}
  .figcell > p:not(:last-child){margin-bottom:0}
  .figcell .fail{margin-top:0}
}
.figcell .fail .lab{margin-bottom:8px}
.figcell .fail a{
  font-family:var(--mono);font-size:var(--lab-size);letter-spacing:var(--lab-track);
  text-transform:lowercase;box-shadow:inset 0 -1px 0 var(--rule);
}
.figcell .fail a:hover{box-shadow:inset 0 -1px 0 #000}
/* A card carrying a corner icon has to keep its own text clear of it. The
   clear is derived from the size, not guessed: .ticon hangs 16% of its width
   past the corner, so 90% of the width is a little more than what actually
   lands inside the card. Change the overhang above and this factor moves with
   it. The size itself stays per figure — a 4-up cell is much narrower than a
   2-up one and the same icon would swallow the heading. */
.figcell.has-ticon > .lab,
.figcell.has-ticon h4{padding-right:calc(var(--ticon-size) * .9)}
@media (max-width:900px){
  .figgrid-4{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:720px){
  .figgrid-2,.figgrid-3,.figgrid-4{grid-template-columns:1fr}
}

/* Stage boxes joined by a hairline with a chevron tip — the same idiom as the
   gate chevrons in the framework post. Two boxes by default, .stages--3 for
   three. Below 820 they stack and the chevron turns to point down. */
.stages{display:grid;grid-template-columns:1fr auto 1fr;align-items:stretch;margin-top:clamp(24px,3vw,36px)}
.stages--3{grid-template-columns:1fr auto 1fr auto 1fr}
.stage{border:1px solid var(--rule-hard);padding:clamp(18px,2.2vw,28px);min-width:0}
.stage .lab{margin-bottom:12px;display:block}
.stage h4{font-family:var(--display);font-weight:400;margin:0 0 12px;font-size:clamp(16px,1.6vw,21px);line-height:1.16;letter-spacing:-.018em}
.stage p{font-size:14.5px;line-height:1.55;color:var(--ink-64)}
.stage p + p{margin-top:.9em}
.stage-join{display:flex;align-items:center;padding:0 clamp(10px,1.8vw,24px)}
.stage-join span{display:block;width:clamp(20px,3vw,44px);height:1px;background:#000;position:relative}
.stage-join span::after{
  content:"";position:absolute;right:-1px;top:-5.5px;width:11px;height:12px;background:#000;
  -webkit-clip-path:polygon(0 0,100% 50%,0 100%);clip-path:polygon(0 0,100% 50%,0 100%);
}
@media (max-width:820px){
  .stages,.stages--3{grid-template-columns:1fr}
  .stage-join{padding:18px 0;justify-content:center}
  .stage-join span{width:1px;height:clamp(22px,5vw,40px)}
  .stage-join span::after{right:-5.5px;top:auto;bottom:-1px;
    -webkit-clip-path:polygon(0 0,100% 0,50% 100%);clip-path:polygon(0 0,100% 0,50% 100%)}
}

/* Tables. A wide one sits in .tbl-scroll and carries a min-width; the page
   body itself must never scroll horizontally. */
.tbl-scroll{overflow-x:auto;margin-top:clamp(24px,3vw,36px);-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;font-size:14.5px;line-height:1.5}
table caption{
  text-align:left;padding-bottom:14px;
  font-family:var(--mono);font-size:var(--lab-size);letter-spacing:var(--lab-track);
  text-transform:lowercase;color:var(--ink-46);
}
th,td{text-align:left;vertical-align:top;padding:13px 18px 13px 0;border-bottom:1px solid var(--rule)}
th{
  font-family:var(--mono);font-weight:400;font-size:var(--lab-size);
  letter-spacing:var(--lab-track);text-transform:lowercase;
  border-bottom:1px solid var(--rule-hard);
  white-space:nowrap;
}
tbody th{white-space:normal;border-bottom:1px solid var(--rule);color:var(--ink)}
td:last-child,th:last-child{padding-right:0}
.tbl-wide{min-width:860px}
.tbl-mid{min-width:660px}
/* a figure inside a table cell: mono, so the column reads as a comparison */
.num{font-family:var(--mono);font-size:13.5px;letter-spacing:.04em;white-space:nowrap}

/* references, the closing block, and the "more writing" rows under it */
.refs{list-style:none;margin:clamp(24px,2.8vw,34px) 0 0;padding:0;border-top:1px solid var(--rule)}
.refs li{padding:13px 0;border-bottom:1px solid var(--rule);font-size:15.5px;line-height:1.5;color:var(--ink-64)}
.refs li b{font-weight:500;color:var(--ink)}
.art-end{border-top:1px solid var(--rule-hard);padding:var(--sec-pad) 0}
.art-end h2{max-width:18ch;margin-bottom:clamp(30px,3.6vw,46px)}
.art-end .lab{margin-bottom:clamp(24px,3vw,40px)}
.art-end .prose{color:var(--ink-64);margin-bottom:clamp(32px,4vw,48px)}
.more{border-top:1px solid var(--rule);padding:var(--sec-pad) 0}
.more > .wrap > .lab{margin-bottom:clamp(26px,3.2vw,40px)}

/* ---------- reading rail ----------
   A long piece needs to say where you are in it. This is that, in the same
   register as everything else: one hairline, black ticks, lowercase mono.
   No colour, no scrollbar chrome, no shadow. The filled part of the line is
   how far through the article you are; the filled square is the part you
   are in now.

   It lives in the left margin OUTSIDE .wrap, so how much room it has depends
   entirely on the viewport. That margin is max(gut, (100vw - max)/2 + gut) —
   the same expression .hero-panel-in uses — so the width is derived from it
   rather than guessed, and the rail can never grow into the text:

     1400px and up   line + ticks + labels   (>=90px of margin to play with)
     1180 - 1399     line + ticks only, labels dropped
     below 1180      hidden; the article is already single-column there

   Fixed and vertically centred, under the nav's z-index. Nothing in here
   carries data-rv or data-scr — it is chrome, not content, and an orphan
   reveal attribute would hide it forever (DESIGN.md trap 8). */
.rail{
  --rail-left:clamp(16px,2vw,40px);
  position:fixed;
  left:var(--rail-left);
  top:50%;
  transform:translateY(-50%);
  z-index:40;
  width:min(150px,calc(max(var(--gut),(100vw - var(--max))/2 + var(--gut)) - var(--rail-left) - 18px));
  display:none;
  opacity:0;
  transition:opacity .28s linear;
  pointer-events:none;
}
/* the script adds this once the article body is actually on screen */
.rail.is-on{opacity:1;pointer-events:auto}
.rail-track{position:absolute;left:0;top:0;bottom:0;width:1px;background:var(--rule)}
/* progress: a solid black line, height driven by the scroll handler */
.rail-fill{position:absolute;left:0;top:0;width:1px;height:var(--rail-p,0%);background:var(--ink)}
.rail ol{list-style:none;margin:0;padding:0}
.rail li{position:relative}
.rail a{
  display:block;
  /* min-height keeps the tick spacing and the hit area intact in the
     labels-dropped mode below, where the anchor has no visible text */
  min-height:17px;
  padding:8px 0 8px 17px;
  font-family:var(--mono);font-size:11px;
  letter-spacing:var(--lab-track);text-transform:lowercase;
  line-height:1.3;
  color:var(--ink-46);
  transition:color .18s linear;
}
/* the tick: a hairline stub that becomes a filled square on the current part */
.rail a::before{
  content:"";position:absolute;
  left:-3px;top:14px;width:8px;height:1px;
  background:var(--rule);
}
.rail a:hover,.rail a:focus-visible{color:var(--ink)}
.rail a:hover::before,.rail a:focus-visible::before{background:#000}
.rail .is-here > a{color:var(--ink)}
.rail .is-here > a::before{left:-3.5px;top:10px;width:9px;height:9px;background:#000}
@media (min-width:1180px){
  .rail{display:block}
}
@media (max-width:1399px){
  /* Not enough margin for words; the spine and the ticks still carry it.
     Hidden the .vh way, not display:none — the labels are the only
     accessible name these links have, and dropping them from the tree
     would leave seven unnamed anchors. */
  .rail{width:auto}
  .rail-txt{
    position:absolute;width:1px;height:1px;margin:-1px;padding:0;
    overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
  }
  .rail a{padding-left:12px}
}

/* ---------- footer ----------
   Opaque white, to match the nav. body is transparent so the fixed ground
   wash shows through every other white area of the page; the footer opts out
   of that explicitly. */
footer{
  position:relative;
  background:var(--paper);
  border-top:1px solid var(--rule);
  padding:36px 0 48px;
}
.foot-in{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:20px}
.foot-in .mark img{width:96px}

/* Opts a label out of the lowercase transform so the brand name keeps its own
   casing inside otherwise-lowercase chrome. Author these in final case. Must
   stay after every rule that sets text-transform -- same specificity, so
   source order decides. */
.kase{text-transform:none}

/* ================= v2 entry animation =================
   Three moves, all monochrome and hard-edged, in the same register as the
   rest of the page:
     1. mono labels resolve out of random glyphs  (.scr / [data-scr])
     2. the hero headline types with a block cursor (.ch / .cursor)
     3. the block underneath rises in, texture wipes in from the left
   Everything below is scoped to html.js, so with JS off — or with
   prefers-reduced-motion — the page renders finished and static. */

html.js [data-scr]{opacity:0}
html.js [data-scr].is-in{opacity:1}

html.js [data-rv]{
  opacity:0;
  transform:translateY(12px);
  transition:opacity .62s var(--ease),transform .62s var(--ease);
  will-change:opacity,transform;
}
html.js [data-rv].is-in{opacity:1;transform:none}

/* hard left-to-right wipe over a texture element (or its wrapper, where the
   element itself already spends its clip-path on a shape) */
html.js .wipe{
  -webkit-clip-path:inset(0 100% 0 0);
  clip-path:inset(0 100% 0 0);
  transition:clip-path .8s var(--ease),-webkit-clip-path .8s var(--ease);
}
html.js .wipe.is-in{
  -webkit-clip-path:inset(0 0 0 0);
  clip-path:inset(0 0 0 0);
}
html.js .hero-panel{
  -webkit-clip-path:inset(0 100% 0 0);
  clip-path:inset(0 100% 0 0);
  transition:clip-path .9s var(--ease),-webkit-clip-path .9s var(--ease);
}
html.js .hero-panel.is-in{
  -webkit-clip-path:inset(0 0 0 0);
  clip-path:inset(0 0 0 0);
}

/* typed headline: every character is laid out from the start and only its
   visibility flips, so the line breaks never move while it types */
html.js .ch{visibility:hidden}
html.js .ch.on{visibility:visible}

/* zero-width so moving it through the line costs no reflow */
.cursor{
  position:relative;
  display:inline-block;
  width:0;height:1em;
  vertical-align:baseline;
  transition:opacity .3s linear;
}
.cursor::after{
  content:"";
  position:absolute;
  left:.05em;bottom:.03em;
  width:.44em;height:.72em;
  background:var(--ink);
  animation:blink 1.05s steps(1,end) infinite;
}
.cursor.out{opacity:0}
@keyframes blink{0%,50%{opacity:1}50.01%,100%{opacity:0}}
