/* ═══════════════════════════════════════════════════════════
   THE FILTER AI · shared stylesheet for the reference pages
   (field guide, patterns, attacks, labs, research)

   The landing page keeps its own inline copy of the base so it
   stays a single self-contained file. Everything below the
   "REFERENCE PAGES" divider is new and lives only here.

   Palette logic is unchanged from the landing page: green means
   allowed, red means blocked, amber means you were wrong about a
   real user. Colour is data, not decoration.
   ═══════════════════════════════════════════════════════════ */

:root {
  /* ── Night sky palette ──────────────────────────────────
     The ground is sampled from a real night photograph: a near-black with a
     blue cast rather than a neutral one, which is what makes the star field
     above it read as sky instead of as dots on a page. No gradient and no
     ambient glow. Colour is reserved for meaning: green for content you
     wrote, red for content from outside, amber for a mix, violet for another
     company's service. Everything else is a cool neutral. */
  --bg:        #05060d;
  --bg-top:    #05060d;
  --bg-foot:   #05060d;
  --surface:   #0b0d16;
  --surface-2: #10131e;
  --line:      #1f2434;
  --line-soft: #161a27;
  --text:      #eef1f7;
  --read:      #aeb6c6;   /* body copy, 11.4:1 on the page ground */
  --muted:     #7f889b;
  --safe:      #4ade8a;
  --threat:    #ff5a72;
  --amber:     #e8b33c;
  --brand:     #3ddba3;
  --brand-hi:  #5fe5b5;
  --brand-lo:  #2cc08c;
  --violet:    #a98bff;
  --on-accent: #06130e;
  --shadow:      0 16px 40px -30px rgba(0,0,0,.8);
  --shadow-soft: 0 10px 28px -24px rgba(0,0,0,.7);
  --inner-top:   none;
  --mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; scroll-padding-top: 5.5rem; }
@media (max-width: 620px) { html { scroll-padding-top: 7.5rem; } }

body {
  margin: 0;
  background-color: var(--bg);
  background-image: linear-gradient(180deg, var(--bg-top) 0%, var(--bg) 46%, var(--bg-foot) 100%);
  background-attachment: fixed;
  color: var(--text);
  font-family: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: clamp(16.5px, 1.6vw, 18px);
  line-height: 1.72;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* The reading pages carry no background pattern at all. The graph paper that
   used to sit here put a repeating texture behind every line of text at
   roughly the spatial frequency of the letterforms, which is the worst place
   to put one. The node field runs on the landing page only. */

/* A scrim over the sky, densest where the reading column sits and clearing
   towards the edges. Stars stay fully visible in the margins, and text always
   has a calm ground under it however the field happens to fall. */
body::before {
  content: "";
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(125% 75% at 50% 38%,
    rgba(5,6,13,.86) 0%,
    rgba(5,6,13,.62) 42%,
    rgba(5,6,13,.15) 72%,
    transparent 88%);
}

/* The night sky. Fixed behind everything, drawn by stars.js. It holds
   perfectly still while you read and only travels when you scroll. */
#bgCanvas {
  position: fixed; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: -1;
}

.wrap   { width: min(1080px, 100% - 2.5rem); margin-inline: auto; position: relative; z-index: 1; }
.narrow { width: min(760px, 100% - 2.5rem); margin-inline: auto; }

/* ── Type ─────────────────────────────────────────────── */
h1, h2, h3, h4 { font-family: "IBM Plex Sans", "Segoe UI", system-ui, sans-serif; font-weight: 700; letter-spacing: -.028em; line-height: 1.06; margin: 0; }
h1 { font-size: clamp(2rem, 5.2vw, 3.2rem); letter-spacing: -.034em; }
h2 { font-size: clamp(1.5rem, 3.4vw, 2.15rem); }
h3 { font-size: clamp(1.02rem, 1.9vw, 1.16rem); letter-spacing: -.014em; line-height: 1.28; }
h4 { font-size: .97rem; letter-spacing: -.01em; }
p  { margin: 0 0 1.05rem; }
a  { color: inherit; }

.eyebrow {
  font-family: var(--mono);
  font-size: .69rem; font-weight: 700; letter-spacing: .21em;
  text-transform: uppercase; color: var(--muted);
  display: flex; align-items: center; gap: .75rem; margin-bottom: 1.3rem;
}
.eyebrow::after { content: ""; height: 1px; flex: 1; background: linear-gradient(90deg, var(--line), transparent); }

.lede { font-size: clamp(1.02rem, 2vw, 1.16rem); line-height: 1.62; color: var(--muted); max-width: 62ch; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--brand); color: var(--on-accent);
  padding: .8rem 1.2rem; border-radius: 0 0 10px 0; font-weight: 600;
}
.skip:focus { left: 0; }

/* ── Nav ──────────────────────────────────────────────── */
.nav {
  position: sticky; top: 0; z-index: 40;
  backdrop-filter: blur(16px) saturate(130%);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  border-bottom: 1px solid var(--line-soft);
  box-shadow: 0 1px 0 rgba(255,255,255,.02);
}
.nav-in { display: flex; align-items: center; gap: 1rem; padding: .82rem 0; }

.brand { text-decoration: none; display: flex; align-items: center; gap: .5rem; flex: none; }
/* The Hamcodes mark. It is a detailed figure, so it needs more room than the
   old single-line funnel did, and its own crimson (#d42c50) for the glow —
   a green halo around a red logo reads as a rendering fault. */
.brand .mark {
  width: 27px; height: 27px; flex: none;
  filter: drop-shadow(0 0 9px rgba(212,44,80,.30));
}
.brand .wordmark {
  font-family: var(--mono); font-weight: 700; font-size: .82rem;
  letter-spacing: .15em; text-transform: uppercase; line-height: 1;
  display: inline-flex; align-items: baseline; gap: .42em;
}
.brand .wordmark .ai { color: var(--brand); }
@media (hover: hover) { .brand:hover .mark { filter: drop-shadow(0 0 13px rgba(212,44,80,.5)); } }

/* Section links. They scroll sideways on a phone rather than wrapping the bar
   onto a second line, so the sticky header keeps one fixed height. */
.nav-links {
  display: flex; align-items: center; gap: .15rem;
  margin-right: auto; overflow-x: auto; scrollbar-width: none;
  -webkit-overflow-scrolling: touch; padding: .2rem 0;
}
.nav-links::-webkit-scrollbar { display: none; }
.nav-links a {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); text-decoration: none;
  padding: .45rem .62rem; border-radius: 7px; white-space: nowrap;
  transition: color .14s, background .14s;
}
.nav-links a:hover { color: var(--text); background: rgba(255,255,255,.035); }
.nav-links a[aria-current="page"] { color: var(--brand); background: color-mix(in srgb, var(--brand) 9%, transparent); }
/* Phone: the bar becomes two rows rather than hiding the sections.
   Brand and buy button hold row one, every section link sits on row two
   and scrolls sideways, with a fade on the right edge so it is visible
   that more exists past the cut. */
@media (max-width: 620px) {
  .nav-in { flex-wrap: wrap; gap: .6rem; padding: .7rem 0 .5rem; }
  .brand { margin-right: auto; }
  .nav-links {
    order: 3; width: 100%; margin-right: 0;
    padding-top: .45rem; border-top: 1px solid var(--line-soft);
    -webkit-mask-image: linear-gradient(90deg, #000 88%, transparent);
            mask-image: linear-gradient(90deg, #000 88%, transparent);
  }
  .nav-links a { font-size: .68rem; padding: .38rem .5rem; }
}

/* ── Buttons ──────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  font-family: "IBM Plex Sans", system-ui, sans-serif;
  font-weight: 600; font-size: .96rem; letter-spacing: -.004em; text-decoration: none;
  padding: .84rem 1.5rem; border-radius: 11px; border: 1px solid transparent; cursor: pointer;
  transition: transform .12s ease, filter .15s ease, border-color .15s, background .15s, box-shadow .18s ease;
  line-height: 1.2;
}
.btn-primary {
  background: linear-gradient(180deg, var(--brand-hi) 0%, var(--brand) 48%, var(--brand-lo) 100%);
  color: var(--on-accent);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.38), inset 0 -1px 0 rgba(0,0,0,.16), 0 12px 34px -18px rgba(46,230,168,.85);
}
.btn-primary:hover {
  filter: brightness(1.05); transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.5), inset 0 -1px 0 rgba(0,0,0,.16), 0 18px 46px -18px rgba(46,230,168,.95);
}
.btn-primary:active { transform: translateY(0); }
.btn-ghost {
  background: linear-gradient(180deg, rgba(255,255,255,.022), transparent);
  color: var(--text); border-color: var(--line);
}
.btn-ghost:hover {
  border-color: color-mix(in srgb, var(--brand) 34%, var(--line));
  background: linear-gradient(180deg, rgba(46,230,168,.06), transparent);
  transform: translateY(-1px);
}
.btn-sm { padding: .58rem 1.05rem; font-size: .87rem; border-radius: 9px; }
.btn-lg { padding: 1.02rem 2rem; font-size: 1.04rem; }
.btn .price { font-family: var(--mono); font-weight: 700; }
.nav .btn { flex: none; }

:where(a, button, [tabindex], summary):focus-visible {
  outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 6px;
}

section { padding: clamp(3.2rem, 6.4vw, 5rem) 0; position: relative; z-index: 1; }
.rule { border: 0; border-top: 1px solid var(--line-soft); margin: 0; }

/* ── Footer ───────────────────────────────────────────── */
footer {
  border-top: 1px solid var(--line-soft);
  padding: 2rem 0 2.6rem;
  font-family: var(--mono); font-size: .72rem; letter-spacing: .05em; color: var(--muted);
}
.foot-in { display: flex; flex-wrap: wrap; gap: .9rem 1.4rem; align-items: center; }
/* Attribution lives here and only here, so the mark comes with it. */
.foot-brand { display: inline-flex; align-items: center; gap: .5rem; }
.foot-mark { width: 21px; height: 21px; flex: none; opacity: .92; }
footer a { color: var(--muted); text-decoration: none; }
footer a:hover { color: var(--text); }
.foot-in .sp { margin-left: auto; }

/* ── Reveal ───────────────────────────────────────────── */
.rv { opacity: 0; transform: translateY(14px); transition: opacity .55s ease, transform .55s ease; }
.rv.in { opacity: 1; transform: none; }


/* ═══════════════════════════════════════════════════════
   REFERENCE PAGES
   Everything below is specific to the guide, the pattern
   library, the attack index, the labs and the source desk.
   ═══════════════════════════════════════════════════════ */

/* ── Page head ────────────────────────────────────────── */
.crumb {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
  display: flex; align-items: center; gap: .5rem; margin-bottom: 1.15rem;
}
.crumb a { text-decoration: none; color: var(--muted); }
.crumb a:hover { color: var(--brand); }
.crumb .sep { opacity: .45; }

.pagehead { padding: clamp(2.6rem, 5vw, 4rem) 0 clamp(1.6rem, 3vw, 2.4rem); }
.pagehead h1 + .lede { margin-top: 1.15rem; }

/* Stamp line: what this page is, how big it is, when it was last read over.
   Honest metadata beats a badge that just says "updated". */
.stamp {
  display: flex; flex-wrap: wrap; gap: .5rem .55rem; margin-top: 1.5rem;
}
.stamp span {
  font-family: var(--mono); font-size: .64rem; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: .26rem .68rem;
}
.stamp span b { color: var(--text); font-weight: 600; }

/* ── Contents rail ────────────────────────────────────── */
/* Grid and flex children default to min-width:auto, which means they
   refuse to shrink below their own content. A 640px-wide table sitting
   inside .body therefore forced the whole column wider than a phone
   screen — and because body carries overflow-x:hidden, the excess was
   clipped instead of scrolled. That is why every page with a table or a
   diagram looked cut off on mobile, and the two without one did not.
   These min-width:0 declarations let the column shrink so the scroll
   boxes inside it can do their job. */
.doc > *, .body, .body > section, .figscroll, .tablewrap { min-width: 0; }
.body { overflow-wrap: break-word; }
.atk .row > dt, .atk .row > dd { min-width: 0; }
.atk .rows { margin: .95rem 0 0; }

.doc { display: grid; gap: 2.4rem; align-items: start; }
@media (min-width: 1000px) { .doc { grid-template-columns: 208px 1fr; gap: 3.2rem; } }

.toc { display: none; }
@media (min-width: 1000px) {
  .toc {
    display: block; position: sticky; top: 5.2rem;
    max-height: calc(100vh - 7rem); overflow-y: auto;
    border-left: 1px solid var(--line-soft); padding-left: 1.1rem;
  }
}
.toc p {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 .85rem;
}
.toc ol { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; counter-reset: toc; }
.toc a {
  display: block; text-decoration: none; color: var(--muted);
  font-size: .82rem; line-height: 1.35; padding: .34rem 0;
  border-left: 2px solid transparent; margin-left: -1.1rem; padding-left: 1.1rem;
  transition: color .14s, border-color .14s;
}
.toc a:hover { color: var(--text); }
.toc a.on { color: var(--brand); border-left-color: var(--brand); }

/* ── Document body ────────────────────────────────────── */
.body > section { padding: 0 0 clamp(2.6rem, 5vw, 3.6rem); }
.body > section + section { border-top: 1px solid var(--line-soft); padding-top: clamp(2.6rem, 5vw, 3.4rem); }
.body h2 { margin-bottom: .95rem; scroll-margin-top: 5.5rem; }
.body h3 { margin: 2rem 0 .6rem; }
.body p { color: var(--read); max-width: 72ch; line-height: 1.75; }
.body p strong, .body li strong { color: var(--text); font-weight: 600; }
.body ul, .body ol { color: var(--read); max-width: 72ch; padding-left: 1.15rem; margin: 0 0 1.05rem; line-height: 1.72; }
.body li { margin-bottom: .5rem; }
.body a:not(.btn) { color: var(--brand); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--brand) 32%, transparent); }
.body a:not(.btn):hover { border-bottom-color: var(--brand); }
.sec-n {
  font-family: var(--mono); font-size: .66rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--brand);
  display: block; margin-bottom: .55rem;
}

/* ── Callouts ─────────────────────────────────────────── */
.note {
  border: 1px solid var(--line); border-left-width: 2px;
  border-radius: 0 12px 12px 0; padding: 1.05rem 1.15rem;
  background: var(--surface); margin: 1.6rem 0; max-width: 72ch;
}
.note p:last-child { margin-bottom: 0; }
.note .lbl {
  font-family: var(--mono); font-size: .64rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; margin-bottom: .45rem; color: var(--muted);
}
.note.key   { border-left-color: var(--brand);  } .note.key .lbl   { color: var(--brand); }
.note.warn  { border-left-color: var(--threat); } .note.warn .lbl  { color: var(--threat); }
.note.trap  { border-left-color: var(--amber);  } .note.trap .lbl  { color: var(--amber); }

/* ── Layer stack (01–07) ──────────────────────────────────
   Flex, not grid. The earlier grid gave the number a row span and
   auto-sized its column, so a fourth sibling dropped into column one
   and stretched the rail to the width of a paragraph. A fixed rail
   plus a body that owns every child cannot do that. */
.layers { display: grid; gap: .7rem; margin: 2rem 0; }
.layer {
  display: flex; align-items: stretch; gap: 1rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 13px; padding: 1.1rem 1.15rem;
}
.layer .n {
  /* Two mono digits at 1.15rem need ~1.4rem of content box. The rail is
     border-box, so the width has to carry the padding as well or "00"
     wraps to two lines. nowrap is the belt to that braces. */
  flex: none; width: 2.75rem; padding-right: 1rem; white-space: nowrap;
  border-right: 1px solid var(--line-soft);
  font-family: var(--mono); font-weight: 700; font-size: 1.15rem;
  letter-spacing: -.02em; color: var(--brand); line-height: 1.15;
}
.layer-body { flex: 1 1 auto; min-width: 0; }
.layer h3 { margin: 0 0 .3rem; }
.layer p { margin: 0; font-size: .95rem; color: var(--read); max-width: none; }
.layer .parts {
  font-family: var(--mono); font-size: .66rem; letter-spacing: .04em;
  color: var(--muted); margin-top: .7rem; display: flex; flex-wrap: wrap; gap: .35rem;
}
.layer .parts span { border: 1px solid var(--line-soft); border-radius: 5px; padding: .12rem .42rem; }
.layer .edge {
  margin-top: .75rem; padding-top: .6rem; border-top: 1px dashed var(--line);
  font-size: .89rem; color: var(--read); line-height: 1.62;
}
.layer .edge b { color: var(--text); font-weight: 600; }
@media (max-width: 560px) {
  .layer { gap: .8rem; }
  .layer .n { width: 2.05rem; padding-right: .7rem; font-size: 1rem; }
}

/* ── Figures ──────────────────────────────────────────────
   SVG diagrams. Colour carries meaning here exactly as it does
   everywhere else: green is content you authored, red arrived from
   outside, amber is mixed and needs a decision. */
.fig {
  margin: 1.8rem 0; padding: 1.25rem 1.1rem 1rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
}
/* A 760-unit diagram scaled into a 300px phone renders its labels at
   about 4px. So the svg keeps a floor width and the box around it
   scrolls, exactly as the wide tables do. On any screen wider than the
   floor, width:100% wins and nothing scrolls. */
.figscroll { overflow-x: auto; overflow-y: hidden; }
.fig svg { display: block; width: 100%; min-width: 700px; height: auto; }
/* The build-lab figures are drawn on a 690 grid so they fit inside a numbered
   step, whose padding takes about 65px off the column. */
.steps .fig svg { min-width: 640px; }
.steps .fig { margin: 1.1rem 0 0; }
@media (max-width: 760px) {
  .figscroll {
    -webkit-mask-image: linear-gradient(90deg, #000 92%, transparent);
            mask-image: linear-gradient(90deg, #000 92%, transparent);
  }
  .fig figcaption::before {
    content: "Scroll the diagram sideways to see all of it \2192";
    display: block; margin-bottom: .6rem;
    font-family: var(--mono); font-size: .64rem; letter-spacing: .1em;
    text-transform: uppercase; color: var(--brand);
  }
  .tablewrap { position: relative; }
  .tablewrap::after {
    content: "Scroll sideways \2192";
    position: sticky; left: 0; display: block;
    padding: .5rem .95rem 0; font-family: var(--mono); font-size: .62rem;
    letter-spacing: .1em; text-transform: uppercase; color: var(--brand);
  }
}
.fig figcaption {
  margin-top: 1rem; padding-top: .8rem; border-top: 1px dashed var(--line);
  font-size: .89rem; line-height: 1.62; color: var(--read);
}
.fig figcaption b { color: var(--text); font-weight: 600; }
.figkey {
  display: flex; flex-wrap: wrap; gap: .45rem .9rem; margin-bottom: 1rem;
  font-family: var(--mono); font-size: .62rem; letter-spacing: .08em; text-transform: uppercase;
}
.figkey span { display: inline-flex; align-items: center; gap: .4rem; color: var(--muted); }
.figkey i { width: 9px; height: 9px; border-radius: 2px; flex: none; }

/* ── Tables ───────────────────────────────────────────── */
.tablewrap { overflow-x: auto; margin: 1.7rem 0; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); }
table { border-collapse: collapse; width: 100%; min-width: 640px; font-size: .93rem; }
thead th {
  text-align: left; font-family: var(--mono); font-weight: 700;
  font-size: .63rem; letter-spacing: .11em; text-transform: uppercase; color: var(--muted);
  padding: .8rem .95rem; border-bottom: 1px solid var(--line); background: var(--surface-2);
  white-space: nowrap;
}
tbody td { padding: .88rem .95rem; border-bottom: 1px solid var(--line-soft); color: var(--read); vertical-align: top; line-height: 1.6; }
tbody tr:last-child td { border-bottom: 0; }
tbody td:first-child { color: var(--text); font-weight: 600; }
td .id, .idcode {
  font-family: var(--mono); font-size: .7rem; font-weight: 700;
  letter-spacing: .05em; color: var(--brand);
}

/* ── Code / payload boxes ─────────────────────────────── */
.codebox {
  background: var(--surface-2); border: 1px solid var(--line-soft);
  border-radius: 10px; padding: .85rem .95rem; margin: 1rem 0;
  font-family: var(--mono); font-size: .84rem; line-height: 1.7;
  color: var(--read); overflow-x: auto; white-space: pre-wrap; word-break: break-word;
}
.codebox .hl { background: color-mix(in srgb, var(--threat) 22%, transparent); color: var(--text); border-radius: 3px; padding: 0 2px; }
.codebox .ok { color: var(--safe); }
.codebox .cm { color: color-mix(in srgb, var(--muted) 62%, transparent); }


code {
  font-family: var(--mono); font-size: .85em;
  background: var(--surface-2); border: 1px solid var(--line-soft);
  border-radius: 5px; padding: .08em .38em; color: var(--text);
}
.body a code { color: var(--brand); }

/* ── Card grids (hub, patterns, tools) ────────────────── */
.grid { display: grid; gap: .9rem; }
@media (min-width: 700px)  { .grid.two   { grid-template-columns: 1fr 1fr; } }
@media (min-width: 700px)  { .grid.three { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .grid.three { grid-template-columns: repeat(3, 1fr); } }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 13px; padding: 1.25rem 1.2rem;
  display: flex; flex-direction: column; gap: .45rem;
  text-decoration: none; color: inherit;
  transition: border-color .16s, transform .16s, background .16s;
}
a.card:hover {
  border-color: color-mix(in srgb, var(--brand) 40%, var(--line));
  background: linear-gradient(180deg, color-mix(in srgb, var(--brand) 3%, var(--surface)), var(--surface) 60%);
  transform: translateY(-2px);
}
.card .kicker {
  font-family: var(--mono); font-size: .64rem; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--brand);
}
.card h3 { font-size: 1.05rem; }
.card p { margin: 0; font-size: .93rem; color: var(--read); max-width: none; }
.card .meta {
  margin-top: auto; padding-top: .8rem;
  font-family: var(--mono); font-size: .64rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
  display: flex; align-items: center; gap: .45rem;
}
.card .meta .arrow { margin-left: auto; color: var(--brand); font-size: .85rem; }

/* ── Pattern entries ──────────────────────────────────── */
.tier {
  font-family: var(--mono); font-size: .6rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  border: 1px solid var(--line); border-radius: 999px; padding: .2rem .55rem;
  color: var(--muted); white-space: nowrap;
}
.tier.t1 { color: var(--safe);   border-color: color-mix(in srgb, var(--safe) 42%, var(--line)); }
.tier.t2 { color: var(--brand);  border-color: color-mix(in srgb, var(--brand) 42%, var(--line)); }
.tier.t3 { color: var(--amber);  border-color: color-mix(in srgb, var(--amber) 42%, var(--line)); }
.tier.t4 { color: var(--violet); border-color: color-mix(in srgb, var(--violet) 42%, var(--line)); }

.pattern {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; padding: 1.45rem 1.35rem; margin-bottom: 1rem;
  scroll-margin-top: 5.5rem;
}
.pattern > header { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin-bottom: .7rem; }
.pattern > header .idcode { margin-right: .1rem; }
.pattern > header h3 { flex: 1 1 14ch; margin: 0; }
.pattern > p:first-of-type { color: var(--text); font-size: .95rem; }

/* Stops / does not stop / cost, as a three-way split so the tradeoff is
   never hidden behind the benefit. */
.tradeoff { display: grid; gap: .75rem; margin: 1.1rem 0 .3rem; }
@media (min-width: 760px) { .tradeoff { grid-template-columns: repeat(3, 1fr); } }
.tradeoff div {
  background: var(--surface-2); border: 1px solid var(--line-soft);
  border-radius: 10px; padding: .8rem .85rem;
}
.tradeoff .lbl {
  font-family: var(--mono); font-size: .6rem; font-weight: 700;
  letter-spacing: .11em; text-transform: uppercase; margin-bottom: .35rem; display: block;
}
.tradeoff .stops   .lbl { color: var(--safe); }
.tradeoff .misses  .lbl { color: var(--threat); }
.tradeoff .costs   .lbl { color: var(--amber); }
.tradeoff p { margin: 0; font-size: .9rem; line-height: 1.62; color: var(--read); max-width: none; }

/* ── Attack entries ───────────────────────────────────── */
.filters { display: flex; flex-wrap: wrap; gap: .4rem; margin: 1.8rem 0 1.6rem; }
.chip {
  font-family: var(--mono); font-size: .66rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  background: var(--surface); border: 1px solid var(--line); color: var(--muted);
  border-radius: 999px; padding: .4rem .82rem; cursor: pointer;
  transition: border-color .14s, color .14s, background .14s;
}
.chip:hover { color: var(--text); }
.chip[aria-pressed="true"] {
  color: var(--on-accent); background: var(--brand); border-color: var(--brand);
}
.atk {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 13px; padding: 1.3rem 1.25rem; margin-bottom: .85rem;
  scroll-margin-top: 5.5rem;
}
.atk[hidden] { display: none; }
.atk > header { display: flex; flex-wrap: wrap; align-items: baseline; gap: .55rem; margin-bottom: .55rem; }
.atk > header h3 { flex: 1 1 16ch; margin: 0; }
.atk .via {
  font-family: var(--mono); font-size: .6rem; letter-spacing: .09em; text-transform: uppercase;
  border: 1px solid var(--line); border-radius: 999px; padding: .18rem .55rem; color: var(--muted);
}
.atk > p { font-size: .95rem; color: var(--read); margin-bottom: .6rem; }
.atk .rows { display: grid; gap: .55rem; margin-top: .95rem; }
.atk .row { display: grid; gap: .12rem .8rem; grid-template-columns: 8.5rem 1fr; align-items: start; }
@media (max-width: 620px) { .atk .row { grid-template-columns: 1fr; } }
.atk .row dt {
  font-family: var(--mono); font-size: .61rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted); padding-top: .2rem;
}
.atk .row dd { margin: 0; font-size: .93rem; line-height: 1.62; color: var(--read); }
.atk .row.tell dt { color: var(--brand); }
.atk .row.trap dt { color: var(--amber); }

/* ── Source desk ──────────────────────────────────────────
   Flex for the same reason .layer is: the grade badge used to span
   three rows of a two-column grid while each entry has four siblings,
   so the fourth dropped into the auto-sized column and stretched it. */
.sources { display: grid; gap: .7rem; margin: 1.8rem 0; }
.src {
  display: flex; align-items: flex-start; gap: .95rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; padding: 1.05rem 1.15rem;
}
.grade {
  flex: none;
  font-family: var(--mono); font-weight: 700; font-size: .72rem;
  width: 1.9rem; height: 1.9rem; border-radius: 8px;
  display: grid; place-items: center;
  border: 1px solid var(--line); color: var(--muted);
}
.grade.a { color: var(--safe);   border-color: color-mix(in srgb, var(--safe) 45%, var(--line));   background: color-mix(in srgb, var(--safe) 8%, transparent); }
.grade.b { color: var(--brand);  border-color: color-mix(in srgb, var(--brand) 45%, var(--line));  background: color-mix(in srgb, var(--brand) 8%, transparent); }
.grade.c { color: var(--amber);  border-color: color-mix(in srgb, var(--amber) 45%, var(--line));  background: color-mix(in srgb, var(--amber) 8%, transparent); }
.src-body { flex: 1 1 auto; min-width: 0; }
.src h3 { font-size: .99rem; margin: 0 0 .1rem; }
.src h3 a { text-decoration: none; }
.src .who { font-family: var(--mono); font-size: .64rem; letter-spacing: .06em; color: var(--muted); text-transform: uppercase; margin-bottom: .45rem; }
.src p { margin: 0 0 .4rem; font-size: .93rem; color: var(--read); max-width: none; line-height: 1.68; }
.src .limit { margin-bottom: 0; margin-top: .55rem; padding-top: .5rem; font-size: .89rem; color: var(--read); border-top: 1px dashed var(--line); line-height: 1.62; }
.src .limit b { color: var(--amber); font-weight: 600; }

/* ── Flowing diagrams ─────────────────────────────────────
   A short bright pulse travelling each arrow, so direction of flow is
   something you watch rather than something you infer. pathLength="100"
   normalises every path so they all move at one speed regardless of
   length. Only runs while the figure is on screen. */
@keyframes figflow { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
.fig .flow {
  stroke-dasharray: 6 94; stroke-dashoffset: 100;
  stroke-linecap: round; opacity: 0;
}
.fig.live .flow { opacity: .95; animation: figflow 3s linear infinite; }
@media (prefers-reduced-motion: reduce) {
  .fig.live .flow { animation: none; opacity: 0; }
}

/* ── Attack diagrams ──────────────────────────────────────
   One per entry in the catalogue on the attacks page: where the text
   is written, which part of the system carries it into the prompt,
   and what it reaches. Typography lives here rather than on every
   text element, so all twenty-seven can be retuned in one place.

   Inside a card the figure needs a darker ground, because a surface
   panel sitting on a surface card is an invisible panel. */
.atk .fig {
  margin: 1.15rem 0 .3rem;
  padding: 1rem .85rem .85rem;
  background: color-mix(in srgb, var(--bg) 62%, transparent);
  border-color: var(--line-soft);
}
.atk .fig figcaption { margin-top: .85rem; font-size: .87rem; }
.sysfig svg text {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .03em;
  fill: var(--muted); text-anchor: middle; dominant-baseline: central;
}
.sysfig .t {
  font-family: "IBM Plex Sans", system-ui, sans-serif;
  font-size: 14px; font-weight: 600; letter-spacing: 0; fill: var(--text);
}
.sysfig .c { font-size: 9.5px; font-weight: 700; letter-spacing: .12em; }
.sysfig .k {
  font-size: 9.5px; font-weight: 700; letter-spacing: .14em;
  fill: var(--amber); text-anchor: start;
}
.sysfig .n { fill: var(--read); text-anchor: start; }
.sysfig .b { fill: var(--surface-2); }
.sysfig .p { fill: var(--surface); }
.sysfig .band { fill: none; stroke: var(--amber); stroke-dasharray: 5 5; opacity: .72; }

/* ── Labs ─────────────────────────────────────────────── */
.tool {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 15px; overflow: hidden; margin: 1.8rem 0;
  box-shadow: var(--shadow-soft); scroll-margin-top: 5.5rem;
}
.tool-bar {
  display: flex; align-items: center; gap: .6rem; padding: .78rem 1.1rem;
  background: linear-gradient(180deg, color-mix(in srgb, var(--surface-2) 90%, #ffffff 10%), var(--surface-2));
  box-shadow: var(--inner-top); border-bottom: 1px solid var(--line);
  font-family: var(--mono); font-size: .67rem; letter-spacing: .13em;
  text-transform: uppercase; color: var(--muted);
}
.pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--brand); flex: none; animation: pulse 2.4s ease-out infinite; }
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--brand) 55%, transparent); }
  70%  { box-shadow: 0 0 0 7px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
.tool-bar .right { margin-left: auto; }
.tool-body { padding: 1.2rem 1.15rem 1.3rem; }
.tool-body > p:first-child { margin-top: 0; }

textarea, input[type="number"], select {
  width: 100%; background: var(--surface-2); color: var(--text);
  border: 1px solid var(--line); border-radius: 10px;
  padding: .7rem .8rem; font-family: var(--mono); font-size: .82rem; line-height: 1.6;
}
textarea { min-height: 148px; resize: vertical; }
label {
  display: block; font-family: var(--mono); font-size: .62rem; font-weight: 700;
  letter-spacing: .11em; text-transform: uppercase; color: var(--muted); margin-bottom: .4rem;
}
.field { margin-bottom: .95rem; }
.fieldrow { display: grid; gap: .8rem; }
@media (min-width: 640px) { .fieldrow { grid-template-columns: repeat(4, 1fr); } }
.toolactions { display: flex; flex-wrap: wrap; gap: .55rem; margin-top: .3rem; }

.findings { margin-top: 1.2rem; display: grid; gap: .55rem; }
.finding {
  border: 1px solid var(--line); border-left-width: 2px; border-radius: 0 10px 10px 0;
  padding: .78rem .9rem; background: var(--surface-2);
}
.finding.hi { border-left-color: var(--threat); }
.finding.md { border-left-color: var(--amber); }
.finding.lo { border-left-color: var(--brand); }
.finding.none { border-left-color: var(--safe); }
.finding h4 { font-size: .89rem; margin-bottom: .25rem; display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; }
.finding .sev {
  font-family: var(--mono); font-size: .58rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
}
.finding.hi   .sev { color: var(--threat); }
.finding.md   .sev { color: var(--amber); }
.finding.lo   .sev { color: var(--brand); }
.finding.none .sev { color: var(--safe); }
.finding p { margin: 0; font-size: .9rem; color: var(--read); max-width: none; line-height: 1.62; }
.finding .ev {
  display: block; margin-top: .45rem; font-family: var(--mono); font-size: .74rem;
  color: var(--text); background: var(--bg); border: 1px solid var(--line-soft);
  border-radius: 6px; padding: .35rem .5rem; overflow-x: auto; white-space: pre-wrap; word-break: break-all;
}
.finding .ev mark { background: color-mix(in srgb, var(--threat) 30%, transparent); color: var(--text); border-radius: 2px; }

.readout { display: grid; grid-template-columns: repeat(2, 1fr); gap: .5rem; margin-top: 1.1rem; }
@media (min-width: 640px) { .readout { grid-template-columns: repeat(4, 1fr); } }
.readout div { background: var(--surface-2); border: 1px solid var(--line); border-radius: 10px; padding: .75rem .6rem; text-align: center; }
.readout .n { font-family: var(--mono); font-weight: 700; font-size: 1.3rem; display: block; line-height: 1.2; }
.readout .k { font-family: var(--mono); font-size: .56rem; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.readout .good .n { color: var(--safe); }
.readout .bad  .n { color: var(--threat); }
.readout .warn .n { color: var(--amber); }
.readout .neutral .n { color: var(--text); }
.verdictline { margin-top: 1rem; font-size: .95rem; color: var(--read); }
.verdictline b { color: var(--text); }

/* ── FAQ ──────────────────────────────────────────────── */
.faq { margin-top: 1.6rem; display: grid; gap: .6rem; max-width: 72ch; }
details { background: var(--surface); border: 1px solid var(--line); border-radius: 11px; padding: 0 1.1rem; }
details[open] { border-color: color-mix(in srgb, var(--brand) 32%, var(--line)); }
summary {
  cursor: pointer; list-style: none; padding: 1rem 0;
  font-family: "IBM Plex Sans", system-ui, sans-serif; font-weight: 600; font-size: .98rem;
  display: flex; align-items: center; gap: .75rem; color: var(--text);
}
summary::-webkit-details-marker { display: none; }
summary::after { content: "+"; margin-left: auto; color: var(--muted); font-family: var(--mono); font-size: 1.05rem; flex: none; }
details[open] summary::after { content: "\2212"; color: var(--brand); }
details p { font-size: .96rem; color: var(--read); margin: 0 0 1rem; line-height: 1.7; }

/* ── End-of-page next steps ───────────────────────────── */
.nextup {
  border-top: 1px solid var(--line-soft);
  padding: clamp(2.4rem, 5vw, 3.4rem) 0 clamp(3rem, 6vw, 4.4rem);
}
.cta-band {
  background: linear-gradient(180deg, color-mix(in srgb, var(--brand) 4%, var(--surface)), var(--surface) 55%);
  border: 1px solid color-mix(in srgb, var(--brand) 30%, var(--line));
  border-radius: 16px; padding: 1.8rem 1.6rem; margin-top: 1.4rem;
  box-shadow: var(--inner-top);
}
.cta-band h3 { font-size: 1.2rem; margin-bottom: .5rem; }
.cta-band p { color: var(--read); font-size: .97rem; max-width: 60ch; }
.cta-band .acts { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: 1.2rem; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  html { scroll-behavior: auto; }
  .rv { opacity: 1; transform: none; }
}

/* ═══════════════════════════════════════════════════════
   LEARNING PATH COMPONENTS
   The site is a course, read in order. These five pieces
   carry that: where you are, what a word means, what to do
   right now, and whether you actually got it.
   ═══════════════════════════════════════════════════════ */

/* ── Stage rail: where this page sits in the six steps ── */
.path {
  display: flex; flex-wrap: wrap; align-items: center; gap: .4rem;
  margin: 1.6rem 0 0; padding: 0; list-style: none;
}
.path li {
  font-family: var(--mono); font-size: .63rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--line-soft); border-radius: 999px;
  padding: .26rem .66rem; margin: 0;
}
.path li a { color: inherit; text-decoration: none; }
.path li a:hover { color: var(--brand); }
.path li b { font-weight: 600; color: color-mix(in srgb, var(--muted) 80%, var(--text)); }
.path li[aria-current="step"] {
  border-color: color-mix(in srgb, var(--brand) 52%, var(--line));
  background: color-mix(in srgb, var(--brand) 9%, transparent);
  color: var(--text);
}
.path li[aria-current="step"] b { color: var(--brand); }

/* ── Plain words: a definition, before the jargon lands ── */
.plain {
  border: 1px dashed color-mix(in srgb, var(--brand) 34%, var(--line));
  border-radius: 12px; padding: 1rem 1.15rem;
  background: color-mix(in srgb, var(--brand) 3.5%, var(--surface));
  margin: 1.5rem 0; max-width: 72ch;
}
.plain .lbl {
  font-family: var(--mono); font-size: .63rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--brand); margin-bottom: .5rem;
}
.plain dl { margin: 0; display: grid; gap: .7rem; }
.plain dt {
  font-weight: 600; color: var(--text); font-size: .95rem;
}
.plain dd {
  margin: .18rem 0 0; color: var(--read); font-size: .95rem; line-height: 1.65;
}
.plain p:last-child { margin-bottom: 0; }

/* ── Do this now: a small action, not a thought ───────── */
.try {
  border: 1px solid color-mix(in srgb, var(--violet) 34%, var(--line));
  border-left-width: 2px; border-left-color: var(--violet);
  border-radius: 0 12px 12px 0; padding: 1.05rem 1.15rem;
  background: color-mix(in srgb, var(--violet) 4%, var(--surface));
  margin: 1.6rem 0; max-width: 72ch;
}
.try .lbl {
  font-family: var(--mono); font-size: .63rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--violet); margin-bottom: .45rem;
}
.try p:last-child, .try ol:last-child, .try ul:last-child { margin-bottom: 0; }

/* ── Check yourself: the end of a section, answered ───── */
.check {
  border: 1px solid color-mix(in srgb, var(--safe) 28%, var(--line));
  border-radius: 12px; padding: 1.05rem 1.15rem;
  background: color-mix(in srgb, var(--safe) 3.5%, var(--surface));
  margin: 1.8rem 0 0; max-width: 72ch;
}
.check .lbl {
  font-family: var(--mono); font-size: .63rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--safe); margin-bottom: .5rem;
}
.check ul, .check ol { margin: 0; padding-left: 1.15rem; }
.check li { color: var(--read); font-size: .95rem; margin-bottom: .3rem; }
.check li:last-child { margin-bottom: 0; }
.check p:last-child { margin-bottom: 0; }

/* ── Numbered walkthrough: one action per row ─────────── */
.steps { counter-reset: stepn; list-style: none; padding: 0; margin: 1.7rem 0; }
.steps > li {
  counter-increment: stepn; position: relative;
  padding: 0 0 1.5rem 3.1rem; margin: 0;
  border-left: 1px solid var(--line-soft); margin-left: .95rem;
}
.steps > li:last-child { border-left-color: transparent; padding-bottom: 0; }
.steps > li::before {
  content: counter(stepn, decimal-leading-zero);
  position: absolute; left: -.95rem; top: -.15rem;
  width: 1.9rem; height: 1.9rem; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--line);
  font-family: var(--mono); font-size: .66rem; font-weight: 700;
  color: var(--brand); letter-spacing: .02em;
}
.steps > li > h4 {
  font-size: 1rem; margin: 0 0 .4rem; color: var(--text);
}
.steps > li > p { margin: 0 0 .6rem; color: var(--read); }
.steps > li > p:last-child, .steps > li > .codebox:last-child { margin-bottom: 0; }
.steps .codebox { margin: .7rem 0; }

/* ── Section opener: what you get out of the next screen ─ */
.aim {
  font-family: var(--mono); font-size: .72rem; letter-spacing: .04em;
  color: var(--muted); border-left: 2px solid var(--line);
  padding: .1rem 0 .1rem .9rem; margin: 0 0 1.3rem; max-width: 66ch;
  line-height: 1.75;
}
.aim b { color: var(--brand); font-weight: 600; }

/* ═══════════════════════════════════════════════════════
   HEADER ATTENTION
   Two slow pulses in the nav: the mark and the buy button.
   Both breathe rather than flash. The period is deliberately
   long (3-4s) and the opacity floor is high, so it reads as a
   heartbeat you notice at the edge of vision rather than a
   strobe you have to look away from. Well under the 3-per-
   second flash threshold.

   The resting state of each element is its own static rule,
   so when the animation is switched off the header looks
   exactly as it did before. The global reduced-motion block
   at the end of this file does that switching off.
   ═══════════════════════════════════════════════════════ */

/* The mark: a crimson glow that swells and settles. */
@keyframes markBreathe {
  0%, 100% { filter: drop-shadow(0 0 6px  rgba(212,44,80,.20)); opacity: .72; }
  50%      { filter: drop-shadow(0 0 18px rgba(212,44,80,.85)); opacity: 1;   }
}
.nav .brand .mark { animation: markBreathe 4s ease-in-out infinite; }

/* Hover takes the animation off so the pointer still gets a
   response. A paused animation keeps painting its own filter
   and would swallow the hover state. */
@media (hover: hover) {
  .nav .brand:hover .mark {
    animation: none;
    filter: drop-shadow(0 0 13px rgba(212,44,80,.5));
  }
}

/* The buy button: a ring that expands out of the edge and fades.
   Listed after the inset shadows so the button's own lit surface
   is untouched. */
@keyframes navCtaPulse {
  0% {
    box-shadow: inset 0 1px 0 rgba(255,255,255,.38), inset 0 -1px 0 rgba(0,0,0,.16),
                0 12px 34px -18px rgba(46,230,168,.85),
                0 0 0 0 rgba(46,230,168,.5);
  }
  70% {
    box-shadow: inset 0 1px 0 rgba(255,255,255,.44), inset 0 -1px 0 rgba(0,0,0,.16),
                0 14px 38px -17px rgba(46,230,168,.92),
                0 0 0 9px rgba(46,230,168,0);
  }
  100% {
    box-shadow: inset 0 1px 0 rgba(255,255,255,.38), inset 0 -1px 0 rgba(0,0,0,.16),
                0 12px 34px -18px rgba(46,230,168,.85),
                0 0 0 0 rgba(46,230,168,0);
  }
}
.nav .btn-primary { animation: navCtaPulse 3.2s ease-out infinite; }
@media (hover: hover) { .nav .btn-primary:hover { animation: none; } }

/* ── How to apply it ──────────────────────────────────────
   Sits inside a solution entry, between what the control is
   and what it costs. The steps inside reuse .steps, so the
   numbering and rail match the walkthroughs elsewhere. */
.apply {
  border: 1px solid color-mix(in srgb, var(--brand) 26%, var(--line));
  border-radius: 12px; padding: 1.1rem 1.2rem 1.15rem;
  background: color-mix(in srgb, var(--brand) 3%, var(--surface));
  margin: 1.5rem 0;
}
.apply > .lbl {
  font-family: var(--mono); font-size: .63rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--brand); margin-bottom: .2rem;
}
.apply > .where {
  font-size: .9rem; color: var(--read); margin: 0 0 .2rem; max-width: 68ch;
}
.apply > .where b { color: var(--text); font-weight: 600; }
.apply .steps { margin: 1rem 0 0; }
.apply .steps > li { padding-bottom: 1.15rem; }
.apply .steps > li:last-child { padding-bottom: 0; }
.apply .steps > li > h4 { font-size: .95rem; }
.apply .steps > li > p { font-size: .93rem; }
.apply .codebox { font-size: .8rem; }
