@import url('/fonts/fonts.css');

/* ===========================================================================
   subnethistory.com

   Light theme. Every value below is taken from the design; nothing is invented.
   The report is built by app.js out of the primitives in the COMPONENTS block,
   so app.js emits class names and this file owns every pixel.
   =========================================================================== */

:root {
  /* ------------------------------------------------- brand and density --- */
  --acc: #4f46e5;
  --pp: 26px;            /* panel padding */
  --fs: 15px;            /* report base size */

  /* -------------------------------------------------------- surfaces ----- */
  --bg-page: #faf9f7;
  --bg-panel: #ffffff;
  --bg-dark: #1c1840;
  --bg-terminal: #221e3f;
  --bg-neutral: #f4f2f9;
  --bg-skel: #f0eef6;
  --bg-skel-hi: #f8f7fc;

  /* ------------------------------------------------------------- ink ----- */
  --tx: #191631;
  --tx-2: #5c5875;
  --tx-3: #6e6a84;
  --tx-muted: #8a86a0;
  --tx-on-dark: #b7b2d4;

  /* ------------------------------------------------------- hairlines ----- */
  --bd: #eceaf4;
  --bd-strong: #e7e4ef;
  --bd-soft: #e0dcec;
  --bd-row: #f4f2f9;
  --dot-off: #d5d1e2;

  /* --------------------------- marker hues (timeline dots, rules) -------- */
  --mk-green: #279966;
  --mk-red: #d6455f;
  --mk-amber: #d9821f;
  --mk-purple: #7c5cd6;

  /* ------------------------------- semantic fg / bd / bg triples --------- */
  --red-fg: #c73558;      --red-bd: #f2c4d0;      --red-bg: #fdf1f4;
  --amber-fg: #b06508;    --amber-bd: #f0d3a4;    --amber-bg: #fdf4e7;
  --purple-fg: #7c5cd6;   --purple-bd: #ddd2f5;   --purple-bg: #f6f2fd;
  --green-fg: #1e8a58;    --green-bd: #bfe6d1;    --green-bg: #ecf8f1;
  --indigo-fg: #4f46e5;   --indigo-bd: #cfcaf7;   --indigo-bg: #f0eefd;
  --banner-bd: #f0c58a;
  --changes-bg: #fdf9f2;

  /* Accent derivations. The plain value lands first so an engine without
     color-mix keeps a usable colour; the color-mix line then wins where
     it is supported. */
  --acc-hover: #423bb8;
  --acc-hover: color-mix(in oklab, var(--acc) 75%, #191631);
  --acc-ring: rgba(79, 70, 229, 0.14);
  --acc-ring: color-mix(in oklab, var(--acc) 14%, transparent);
  --acc-orbit: rgba(79, 70, 229, 0.2);
  --acc-orbit: color-mix(in oklab, var(--acc) 20%, transparent);
  --acc-input-bd: rgba(79, 70, 229, 0.18);
  --acc-input-bd: color-mix(in oklab, var(--acc) 18%, #ffffff);
  --acc-icon-bd: rgba(79, 70, 229, 0.25);
  --acc-icon-bd: color-mix(in oklab, var(--acc) 25%, #ffffff);
  --acc-tint-4: #f4f3f8;   --acc-tint-4: color-mix(in oklab, var(--acc) 4%, #faf9f7);
  --acc-tint-5: #f3f2f8;   --acc-tint-5: color-mix(in oklab, var(--acc) 5%, #faf9f7);
  --acc-tint-9: #eeedfb;   --acc-tint-9: color-mix(in oklab, var(--acc) 9%, #ffffff);
  --acc-tint-10: #ecebfb;  --acc-tint-10: color-mix(in oklab, var(--acc) 10%, #ffffff);
  --acc-tint-11: #eae9fa;  --acc-tint-11: color-mix(in oklab, var(--acc) 11%, #ffffff);
  --acc-tint-12: #e8e7fa;  --acc-tint-12: color-mix(in oklab, var(--acc) 12%, #ffffff);
  --acc-cta: #2e2871;      --acc-cta: color-mix(in oklab, var(--acc) 36%, #1c1840);

  /* ------------------------------------------------------------ type ----- */
  --ff-display: 'Bricolage Grotesque', system-ui, sans-serif;
  --ff-body: 'Instrument Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --ff-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', Consolas, monospace;

  /* ----------------------------------------------------------- radii ----- */
  --r-pill: 999px;
  --r-badge: 4px;
  --r-state: 5px;
  --r-tile-sm: 9px;
  --r-tile: 10px;
  --r-chip: 12px;
  --r-sub: 14px;
  --r-field: 16px;
  --r-panel: 18px;
  --r-card: 20px;
  --r-band: 24px;
  --r-section: 28px;
  --r-skel: 6px;

  /* --------------------------------------------------------- shadows ----- */
  /* Values taken from the design's inline box-shadows, not eyeballed. */
  --sh-xs: 0 2px 8px rgba(25, 22, 49, 0.04);      /* .uc-card */
  --sh-sm: 0 4px 12px rgba(25, 22, 49, 0.06);     /* .src-pill */
  --sh-md: 0 8px 20px rgba(25, 22, 49, 0.1);      /* .mock-note */
  --sh-lg: 0 18px 44px rgba(25, 22, 49, 0.12);    /* .mock--light */
  --sh-lg-d: 0 18px 44px rgba(25, 22, 49, 0.18);  /* .mock--dark */
  --sh-xl: 0 24px 60px rgba(25, 22, 49, 0.12);    /* .hero-card */
  --sh-xl-s: 0 24px 60px rgba(25, 22, 49, 0.08);  /* .history-card */
  --sh-rail: 0 2px 8px rgba(25, 22, 49, 0.08);
  --sh-input: 0 2px 8px rgba(25, 22, 49, 0.05);
  --sh-focus: 0 0 0 4px var(--acc-ring);

  /* --------------------------------------------------------- metrics ----- */
  /* The hero is the widest thing on the page and everything below it steps in.
     A report is read line by line, so it wants a measure narrower than a banner
     that is only ever glanced at; matching them exactly made the document feel
     like it was spilling to the edges. */
  --w-shell: 1280px;
  --w-content: 1120px;
  --w-cta: 1120px;
  --w-search: 560px;
  --gutter: 8px;
  --sp-stack: 18px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* An author-origin `display` beats the UA's `[hidden] { display: none }`, so
   any component that sets display would ignore its own hidden attribute.
   #report and .rail both do. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; }

/* Decoration only. Honour a request for less motion. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .spinner, .skel, .hero-ring { animation: none; }
  .skel { background: var(--bg-skel); }
}

body {
  background: var(--bg-page);
  color: var(--tx);
  font-family: var(--ff-body);
  font-size: var(--fs);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--acc); text-decoration: none; }
a:hover { color: var(--acc-hover); text-decoration: underline; }

.accent { color: var(--acc); }
.mono { font-family: var(--ff-mono); }
.sans { font-family: var(--ff-body); }
.dim { color: var(--tx-3); }
.measure-42 { max-width: 42ch; }
.measure-44 { max-width: 44ch; }
.tight { margin: 0 0 6px; }
.tight-lead { line-height: 1.1; }

@keyframes spin { to { transform: rotate(360deg); } }
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* ======================================================= SHELL / HEADER === */

.shell {
  max-width: var(--w-shell); margin: 0 auto; padding: 12px var(--gutter) 0;
  min-height: 100vh;
  display: flex; flex-direction: column;
}
/* Takes the slack, so a short page pushes the footer down rather than leaving
   it stranded in the middle of the screen. */
.shell > main { flex: 1 0 auto; }
.shell > .site-footer { flex: none; }

.site-header {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; padding: 14px 12px; gap: 12px 16px;
}

.brand { display: flex; align-items: center; gap: 10px; color: var(--tx); }
.brand:hover { text-decoration: none; color: var(--tx); }
/* The mark: three strokes of the CIDR slash, stepping back in time. The solid
   stroke is now; each echo behind it is an older state of the same block.
   Every number is a fraction of the tile, matching scripts/brand.mjs, so the
   header and the generated favicon cannot drift apart. Book: 20° from vertical,
   58% tall, opacity 100 / 45 / 18, past fades left. */
.brand-mark {
  --tile: 30px;
  width: var(--tile); height: var(--tile);
  border-radius: calc(var(--tile) * 0.28);
  background: var(--acc);
  position: relative; overflow: hidden; flex: none;
}
.brand-mark i {
  position: absolute;
  top: calc(var(--tile) * 0.20833);
  width: calc(var(--tile) * 0.125);
  height: calc(var(--tile) * 0.58333);
  border-radius: calc(var(--tile) * 0.0625);
  background: #fff;
  transform: rotate(20deg);
}
.brand-mark i:nth-child(1) { left: calc(var(--tile) * 0.21875); opacity: 0.18; }
.brand-mark i:nth-child(2) { left: calc(var(--tile) * 0.38542); opacity: 0.45; }
.brand-mark i:nth-child(3) { left: calc(var(--tile) * 0.55208); }
.brand-text {
  font-family: var(--ff-display); font-size: 19px; font-weight: 700; letter-spacing: -0.01em;
}

.site-nav {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: clamp(14px, 2.5vw, 26px); font-size: 14px; font-weight: 500;
}
.site-nav a { color: var(--tx-3); }
.site-nav a:hover { color: var(--tx); text-decoration: none; }
/* Qualified with .site-nav: a bare `.nav-cta` loses to `.site-nav a`, which
   would paint the label muted grey on the indigo pill. */
.site-nav a.nav-cta {
  background: var(--acc); color: #fff; padding: 9px 18px;
  border-radius: var(--r-pill); font-weight: 600;
}
.site-nav a.nav-cta:hover { filter: brightness(1.08); color: #fff; text-decoration: none; }

/* -- burger -------------------------------------------------------------- *
 * Hidden entirely on desktop, where the nav fits. The bars are three strokes
 * that fold into a cross, echoing the mark's own geometry rather than
 * borrowing an icon set the rest of the site does not use. */
.nav-burger {
  display: none;
  width: 42px; height: 42px; flex: none; cursor: pointer;
  align-items: center; justify-content: center;
  background: var(--bg-panel); border: 1px solid var(--bd-soft);
  border-radius: var(--r-chip); color: var(--tx);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.nav-burger:hover { border-color: var(--acc); }
.nav-burger:focus-visible { outline: none; border-color: var(--acc); box-shadow: var(--sh-focus); }
.nav-burger-bars { position: relative; width: 18px; height: 12px; display: block; }
.nav-burger-bars i {
  position: absolute; left: 0; width: 100%; height: 2px; border-radius: 2px;
  background: currentColor;
  transition: transform 0.22s ease, opacity 0.15s ease, top 0.22s ease;
}
.nav-burger-bars i:nth-child(1) { top: 0; }
.nav-burger-bars i:nth-child(2) { top: 5px; }
.nav-burger-bars i:nth-child(3) { top: 10px; }
/* Open: the outer strokes meet in the middle and cross, the middle one goes. */
.nav-burger[aria-expanded='true'] { background: var(--acc); border-color: var(--acc); color: #fff; }
.nav-burger[aria-expanded='true'] .nav-burger-bars i:nth-child(1) { top: 5px; transform: rotate(45deg); }
.nav-burger[aria-expanded='true'] .nav-burger-bars i:nth-child(2) { opacity: 0; }
.nav-burger[aria-expanded='true'] .nav-burger-bars i:nth-child(3) { top: 5px; transform: rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
  .nav-burger-bars i { transition: none; }
}

/* ================================================================ HERO === */

.hero {
  border-radius: var(--r-section);
  padding: clamp(32px, 5vw, 64px) clamp(20px, 4.5vw, 56px) 36px;
  position: relative; overflow: hidden;
  background: linear-gradient(180deg, var(--acc-tint-11) 0%, var(--acc-tint-4) 70%, var(--bg-page) 100%);
}
.hero-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 48px; align-items: center; position: relative;
}
/* The floor was 34px, which "Read it in one search." cannot fit inside a 320px
   phone once the hero's own padding is taken out, so the headline pushed the
   page wider than the screen. It now scales with the viewport all the way down
   and is allowed to break a word rather than overflow. */
.hero-h1 {
  font-family: var(--ff-display); font-size: clamp(27px, 7.4vw, 54px);
  line-height: 1.06; letter-spacing: -0.025em; font-weight: 800; margin: 0 0 20px;
  overflow-wrap: break-word;
  /* Each sentence is one line wherever it fits. On a phone the first one has to
     break, and left alone it breaks after the last word that fits, stranding
     "past." on a line of its own. Balancing splits it near the middle instead.
     Ignored by browsers that do not know it, which simply wrap as before. */
  text-wrap: balance;
}
.hero-h1 span { display: block; }
.hero-lede { color: var(--tx-2); font-size: 16.5px; max-width: 46ch; margin: 0 0 28px; }

/* nowrap, and the input is allowed to shrink to nothing. A 220px floor plus a
   100px button plus the gap needs 330px of form, which a 360px phone does not
   have once the page padding is taken out, so the button dropped to its own
   line. The field shrinks instead; it is the element that can afford to. */
.searchbar { display: flex; flex-wrap: nowrap; gap: 10px; max-width: var(--w-search); }
#q {
  flex: 1 1 auto; min-width: 0; padding: 15px 18px;
  font-family: var(--ff-mono); font-size: 14.5px; color: var(--tx);
  background: var(--bg-panel); border: 1px solid var(--acc-input-bd);
  border-radius: var(--r-field); outline: none; box-shadow: var(--sh-input);
}
#q:focus { border-color: var(--acc); box-shadow: var(--sh-focus); }

button { font: inherit; }
.btn-accent {
  font-family: var(--ff-body); font-size: 15px; font-weight: 600; cursor: pointer;
  border: none; border-radius: var(--r-field); background: var(--acc); color: #fff;
  padding: 15px 26px;
  flex: 0 0 auto; white-space: nowrap;
}
.btn-accent:hover { filter: brightness(1.08); }

.examples {
  display: flex; align-items: center; gap: 8px; margin-top: 16px;
  flex-wrap: wrap; color: var(--tx-muted); font-size: 13px;
}
.chip {
  cursor: pointer; padding: 5px 12px; font-family: var(--ff-mono); font-size: 12.5px;
  color: var(--tx-2); background: rgba(255, 255, 255, 0.7);
  border: 1px solid var(--bd-soft); border-radius: var(--r-pill);
}
.chip:hover { color: var(--acc); border-color: var(--acc); }
.hero-micro { margin: 22px 0 0; color: var(--tx-muted); font-size: 12.5px; }

.hero-art {
  position: relative; min-height: 420px;
  display: flex; align-items: center; justify-content: center;
}
/* Concentric on the card, not on the column: both rings are centred on the
   same point the card is centred on, so they read as orbits around it. */
.hero-ring {
  position: absolute; top: 50%; left: 50%; border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.hero-ring--outer {
  width: 560px; height: 560px; border: 1px solid var(--acc-ring);
  animation: orbit-pulse 5.5s ease-in-out infinite;
}
.hero-ring--inner {
  width: 400px; height: 400px; border: 1px solid var(--acc-orbit);
  animation: orbit-pulse 5.5s ease-in-out infinite 1.4s;
}

/* A slow breath, not a throb: the rings sit behind live content. */
@keyframes orbit-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.75; }
  50% { transform: translate(-50%, -50%) scale(1.045); opacity: 1; }
}
.hero-card {
  position: relative; background: var(--bg-panel); border: 1px solid var(--bd);
  border-radius: var(--r-card); box-shadow: var(--sh-xl); padding: 22px 24px;
  max-width: 400px; margin: 0 auto;
}
/* The hero card is a miniature of a report, so it runs a notch smaller than
   the real panels rather than inheriting their scale. */
.hero-card .value { font-size: 19px; }
.hero-card .kind { font-size: 10px; padding: 2px 8px; }
.hero-card .tag { gap: 6px; padding: 4px 10px; font-size: 12px; }
.hero-card .tag .conf { font-size: 10.5px; }
.hero-card .timeline { padding-left: 18px; }
.hero-card .timeline::before { left: 3.5px; }
.hero-card .timeline li { padding-bottom: 14px; }
.hero-card .timeline li:last-child { padding-bottom: 0; }
.hero-card .timeline li::before { left: -18px; width: 8px; height: 8px; }
.hero-card .timeline .when { font-size: 11px; }
.hero-card .timeline .what { font-size: 13px; }
.hero-card .ongoing { font-size: 10px; margin-left: 6px; }

.hero-badge {
  position: absolute; background: var(--bg-panel); border: 1px solid var(--bd);
  border-radius: var(--r-chip); box-shadow: var(--sh-md); padding: 8px 13px;
  font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 7px;
}
.hero-badge i { width: 7px; height: 7px; border-radius: 50%; }
/* Clear of the card body: anchored to its edges so they never sit over the
   subject line or the timeline. */
.hero-badge--tl { top: -16px; left: -18px; }
.hero-badge--br { bottom: -16px; right: -18px; }

/* ==================================================== LANDING SECTIONS === */

.section { max-width: var(--w-content); margin: clamp(64px, 9vw, 110px) auto 0; padding: 0 var(--gutter); }
.section--split {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: clamp(32px, 5vw, 56px); align-items: center;
}
.section--wide { max-width: var(--w-cta); }
.section-h2 {
  font-family: var(--ff-display); font-size: clamp(28px, 4vw, 36px);
  font-weight: 800; letter-spacing: -0.02em; margin: 0 0 22px;
}
.section-h2--sm { font-size: clamp(26px, 3.5vw, 32px); }

/* -- #how terminals ------------------------------------------------------ */
.mockstage { position: relative; min-height: 340px; }
.mock--dark {
  position: absolute; top: 0; left: 0; right: 60px; background: var(--bg-terminal);
  border-radius: var(--r-field); padding: 16px 18px; box-shadow: var(--sh-lg-d);
}
.mock--light {
  position: absolute; bottom: 20px; right: 0; left: 18%; background: var(--bg-panel);
  border: 1px solid var(--bd); border-radius: var(--r-field); padding: 16px 18px;
  box-shadow: var(--sh-lg);
}
.lights { display: flex; gap: 6px; margin-bottom: 12px; }
.lights i { width: 10px; height: 10px; border-radius: 50%; }
.mock pre {
  font-family: var(--ff-mono); font-size: 12px; line-height: 1.7;
  white-space: pre-wrap; margin: 0;
}
.mock--dark pre { color: var(--tx-on-dark); }
.mock--light pre { color: var(--tx-2); }
.mock-cap { font-size: 12px; color: var(--tx-muted); margin-bottom: 8px; }
.mock-note {
  position: absolute; top: -14px; right: 14px; background: var(--bg-panel);
  border: 1px solid var(--bd); border-radius: var(--r-chip); box-shadow: var(--sh-md);
  padding: 7px 13px; font-size: 12px; font-weight: 600; color: var(--mk-red);
}

/* -- #history card ------------------------------------------------------- */
.history-card {
  background: var(--bg-panel); border: 1px solid var(--bd); border-radius: var(--r-card);
  padding: 26px 28px; box-shadow: var(--sh-xl-s);
}
.eyebrow {
  font-size: 11.5px; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--tx-muted); font-weight: 700; margin-bottom: 16px;
}
.tl--md { position: relative; padding-left: 24px; }
.tl--md > i {
  position: absolute; left: 5px; top: 6px; bottom: 6px; width: 1px; background: var(--bd-strong);
}
.tl--md > div { position: relative; padding-bottom: 16px; }
.tl--md > div > i {
  position: absolute; left: -22px; top: 5px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--bg-panel); border: 2px solid var(--tx-muted);
}
.tl--md .when { font-family: var(--ff-mono); font-size: 12px; color: var(--tx-muted); }
.tl--md .what { font-size: 13.5px; font-weight: 500; }

.dotchips { display: flex; flex-wrap: wrap; gap: 8px; }
.dotchip {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 13px;
  border-radius: var(--r-pill); font-size: 12.5px; font-weight: 500;
  border: 1px solid var(--bd-soft); background: var(--bg-panel);
}
.dotchip i { width: 7px; height: 7px; border-radius: 50%; }

/* -- #cases -------------------------------------------------------------- */
.uc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 16px; }
.uc-card {
  background: var(--bg-panel); border: 1px solid var(--bd); border-radius: var(--r-panel);
  padding: 20px; display: flex; flex-direction: column; gap: 10px; box-shadow: var(--sh-xs);
}
.uc-icon {
  width: 34px; height: 34px; border-radius: var(--r-tile); background: var(--acc-tint-10);
  display: inline-flex; align-items: center; justify-content: center;
}
.uc-title { font-weight: 700; font-size: 15px; }
.uc-body { color: var(--tx-3); font-size: 13px; flex: 1; }
.uc-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.uc-foot .mono { font-size: 12px; color: var(--tx-2); }
.tagpill {
  font-size: 11px; font-weight: 600; border-radius: var(--r-pill); padding: 2px 9px;
  color: var(--fg); background: var(--bg);
}

/* -- #sources ------------------------------------------------------------ */
.sources-band {
  border-radius: var(--r-band);
  background: linear-gradient(135deg, var(--acc-tint-12), var(--acc-tint-5));
  padding: clamp(28px, 5vw, 52px) clamp(24px, 5vw, 56px);
  display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 36px; align-items: center;
}
.src-pills { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.src-pill {
  background: var(--bg-panel); border: 1px solid var(--bd); border-radius: var(--r-pill);
  padding: 9px 18px; font-size: 13.5px; font-weight: 600; box-shadow: var(--sh-sm);
}

/* -- #features ----------------------------------------------------------- */
.feat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 36px 40px; }
.feat-icon {
  width: 30px; height: 30px; border-radius: var(--r-tile-sm);
  border: 1.5px solid var(--acc-icon-bd); display: inline-flex;
  align-items: center; justify-content: center; margin-bottom: 10px;
}
.feat-title { font-weight: 700; margin-bottom: 5px; }
.feat-body { color: var(--tx-3); font-size: 13.5px; }

/* -- #api CTA ------------------------------------------------------------ */
.cta {
  background: radial-gradient(900px 400px at 50% -100px, var(--acc-cta), var(--bg-dark));
  border-radius: var(--r-section); padding: clamp(44px, 7vw, 84px) clamp(24px, 5vw, 56px);
  text-align: center; position: relative; overflow: hidden;
}
.cta-h2 {
  font-family: var(--ff-display); font-size: clamp(30px, 4.5vw, 44px); font-weight: 800;
  letter-spacing: -0.02em; line-height: 1.12; color: #fff; margin: 0 0 18px;
}
.cta-h2 span { display: block; }
.cta-accent { color: #b9b2ff; }
.cta-lede { color: var(--tx-on-dark); margin: 0 0 28px; }
.cta-row { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
.btn-light {
  cursor: pointer; border: none; border-radius: var(--r-pill); background: #fff;
  color: var(--bg-dark); padding: 14px 26px; font-weight: 600; font-size: 15px;
}
.btn-light:hover { filter: brightness(0.94); text-decoration: none; color: var(--bg-dark); }
.btn-ghost {
  border-radius: var(--r-pill); border: 1px solid rgba(255, 255, 255, 0.28);
  color: #fff; padding: 14px 26px; font-weight: 600; font-size: 15px;
  font-family: var(--ff-mono);
}
.btn-ghost:hover { border-color: #fff; color: #fff; text-decoration: none; }
.cta-foot { color: #8f89bd; font-size: 12.5px; margin: 22px 0 0; }

/* -- footer -------------------------------------------------------------- */
.site-footer {
  max-width: var(--w-content); margin: clamp(56px, 7vw, 88px) auto 0;
  padding: 26px var(--gutter) 40px; border-top: 1px solid var(--bd);
  display: flex; flex-wrap: wrap; gap: 12px 26px;
  align-items: center; justify-content: space-between;
  color: var(--tx-muted); font-size: 12.5px;
}
.site-footer nav { display: flex; flex-wrap: wrap; gap: 18px; }
.site-footer a { color: var(--tx-3); }

/* ============================================================ COMPONENTS === */

.status {
  max-width: var(--w-content); margin: 28px auto 0; padding: 0 var(--gutter);
  font-size: 14px; color: var(--tx-2);
}
.status.error { color: var(--red-fg); }

.report {
  max-width: var(--w-content); margin: 24px auto 0; padding: 0 var(--gutter);
  display: flex; flex-direction: column; gap: var(--sp-stack); font-size: var(--fs);
}

.panel {
  background: var(--bg-panel); border: 1px solid var(--bd);
  border-radius: var(--r-panel); padding: var(--pp);
}
.panel h2 {
  margin: 0 0 14px; font-size: 11.5px; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--tx-muted); font-weight: 700;
}
.panel h2.tight { margin-bottom: 8px; }
.panel-note { margin: 0 0 14px; color: var(--tx-3); font-size: 0.93em; }
.panel-note .mono { color: var(--amber-fg); }
.panel-foot { margin: 14px 0 0; color: var(--tx-muted); font-size: 0.88em; }

/* -- palette utilities: every chip variant reads --fg / --bd / --bg ------ */
.pal-red { --fg: var(--red-fg); --bd: var(--red-bd); --bg: var(--red-bg); }
.pal-amber { --fg: var(--amber-fg); --bd: var(--amber-bd); --bg: var(--amber-bg); }
.pal-purple { --fg: var(--purple-fg); --bd: var(--purple-bd); --bg: var(--purple-bg); }
.pal-green { --fg: var(--green-fg); --bd: var(--green-bd); --bg: var(--green-bg); }
.pal-indigo { --fg: var(--indigo-fg); --bd: var(--indigo-bd); --bg: var(--indigo-bg); }
.pal-neutral { --fg: var(--tx-2); --bd: var(--bd-row); --bg: var(--bg-neutral); }

/* -- subject line -------------------------------------------------------- */
.subject { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; min-width: 0; }
img.flag {
  align-self: center; width: 26px; height: 19px; border-radius: 3px;
  border: 1px solid var(--bd-strong); object-fit: cover; display: block;
}
/* A country whose sources disagree gets a warmer edge, so the flag does not
   silently assert more certainty than we have. */
img.flag--disputed { border-color: var(--amber-bd); box-shadow: 0 0 0 2px var(--amber-bg); }
/* Shown when no flag exists for the code, or the image cannot be fetched. */
.flag--code {
  align-self: center; font-family: var(--ff-mono); font-size: 11px; font-weight: 700;
  letter-spacing: 0.06em; color: var(--tx-3); background: var(--bg-neutral);
  border: 1px solid var(--bd-strong); border-radius: var(--r-badge); padding: 3px 7px;
}
/* The small flag beside the holder name: the ASN's own registered country. It
   sits inline with the holder text (not a flex item like the big badge), so it
   overrides img.flag's display:block and rides the text baseline. */
img.flag--sm { width: 18px; height: 13px; border-radius: 2px; display: inline-block; vertical-align: -2px; margin-right: 6px; }
.flag--code.flag--sm { font-size: 9px; padding: 2px 5px; letter-spacing: 0.04em; vertical-align: -1px; margin-right: 6px; }
/* An IPv6 address is 39 characters of monospace and will not fit a phone at a
   fixed 26px. It scales with the viewport and is allowed to break mid-token,
   because a subject that overflows its own card is worse than one that wraps. */
.subject .value {
  font-family: var(--ff-mono); font-size: clamp(17px, 5.4vw, 26px);
  font-weight: 600; letter-spacing: -0.01em;
  overflow-wrap: anywhere; min-width: 0; max-width: 100%;
}
.subject .kind {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--tx-muted); border: 1px solid var(--bd-strong);
  border-radius: var(--r-pill); padding: 2px 9px;
}
.subject .holder { color: var(--tx-3); }
.subject .holder .holder-link { color: inherit; text-decoration: none; }
.subject .holder .holder-link:hover { text-decoration: underline; }
.special { color: var(--mk-amber); margin: 10px 0 0; font-size: 0.93em; }
.meta { margin-top: 14px; color: var(--tx-muted); font-size: 12.5px; font-family: var(--ff-mono); }

/* -- banner -------------------------------------------------------------- */
.banner {
  display: flex; gap: 12px; align-items: center; padding: 14px 16px;
  border-radius: var(--r-chip); font-size: 0.93em; margin-bottom: 16px; border: 1px solid;
}
/* Centred on the text block, so it reads as a mark for the whole banner rather
   than a bullet for its first line. Holds up when the tag list wraps. */
.alert-icon { flex: none; color: var(--dot); display: block; }
.banner .sub { color: var(--tx-3); margin-top: 3px; font-size: 0.93em; max-width: 72ch; }
.banner.sev1 { border-color: var(--purple-bd); background: var(--purple-bg); --dot: var(--mk-purple); }
.banner.sev2 { border-color: var(--banner-bd); background: var(--amber-bg); --dot: var(--mk-amber); }
.banner.sev3 { border-color: var(--red-bd); background: var(--red-bg); --dot: var(--mk-red); }

/* -- tag chips ----------------------------------------------------------- */
.tag-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 9px; }
.tag {
  display: inline-flex; align-items: center; gap: 7px; padding: 5px 12px;
  border-radius: var(--r-pill); font-size: 0.86em; font-weight: 500;
  color: var(--fg); border: 1px solid var(--bd); background: var(--bg);
}
.tag .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.tag .conf { font-family: var(--ff-mono); font-size: 0.85em; opacity: 0.7; }
.tag .src { font-family: var(--ff-mono); font-size: 0.78em; color: var(--tx-muted); }
/* "+N", the count of more-specific confirmations folded into this chip. */
.tag .tag-more { font-family: var(--ff-mono); font-size: 0.78em; font-weight: 700; color: var(--tx-muted); }
.tag.suggested { border: 1.5px dashed var(--bd); background: var(--bg-panel); }
.tag.suggested .dot { opacity: 0.55; }
.tag-group-label {
  margin-top: 18px; font-size: 10.5px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--tx-muted); font-weight: 700;
}

/* -- key/value ----------------------------------------------------------- */
.kv { display: grid; grid-template-columns: minmax(130px, 190px) 1fr; gap: 2px 20px; }
.kv dt { color: var(--tx-3); padding: 3.5px 0; }
.kv dd { margin: 0; padding: 3.5px 0; font-family: var(--ff-mono); font-size: 0.92em; word-break: break-word; }
.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; }
.cols .kv { grid-template-columns: minmax(115px, 150px) 1fr; gap: 2px 16px; }

/* -- tables -------------------------------------------------------------- */
.scroll { overflow-x: auto; }
/* A dense table must overflow into this scroller rather than squeeze itself to
   fit: `width: 100%` alone crushes columns until every cell wraps twice. Wide
   content scrolls in its own box, never the page. */
.scroll--wide table { min-width: 720px; }
.tbl-note { margin: 0 0 10px; }
/* Long operator names get one line and a tooltip. Letting them wrap made every
   row in the table two lines tall for the sake of one cell. */
td.clip { max-width: 26ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
table { width: 100%; border-collapse: collapse; font-size: 0.9em; }
th {
  text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--bd);
  color: var(--tx-muted); font-weight: 500; font-size: 11.5px;
  text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap;
}
td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--bd-row); vertical-align: top; }
tr:last-child td { border-bottom: none; }
td.mono, th.mono { font-family: var(--ff-mono); }
td.asn { font-family: var(--ff-mono); color: var(--acc); font-weight: 600; }
td.asn .sub { font-family: var(--ff-body); color: var(--tx-3); font-weight: 400; font-size: 0.92em; }
td.nowrap { white-space: nowrap; }
td.valid { color: var(--green-fg); font-weight: 600; }

/* -- inline markers, deliberately not scoped to .timeline ---------------- */
.ongoing {
  color: var(--mk-green); font-size: 10.5px; text-transform: uppercase;
  letter-spacing: 0.08em; margin-left: 8px; font-weight: 700;
}
.scope {
  color: var(--tx-muted); font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.08em; margin-left: 8px; border: 1px solid var(--bd-strong);
  border-radius: var(--r-badge); padding: 0 5px;
}
td .ongoing { margin-left: 0; font-size: 0.85em; letter-spacing: 0.05em; color: var(--green-fg); }

/* -- timeline ------------------------------------------------------------ */
.timeline { position: relative; margin: 0; padding: 0 0 0 26px; list-style: none; }
.timeline::before {
  content: ""; position: absolute; left: 6px; top: 6px; bottom: 6px;
  width: 1px; background: var(--bd-strong);
}
.timeline li { position: relative; padding: 0 0 20px; }
.timeline li::before {
  content: ""; position: absolute; left: -24px; top: 5px; width: 9px; height: 9px;
  border-radius: 50%; background: var(--bg-panel); border: 2px solid var(--tx-muted);
}
.timeline li.routing::before { border-color: var(--acc); }
.timeline li.registration::before { border-color: var(--mk-green); }
.timeline li.allocation::before { border-color: var(--mk-green); }
.timeline li.transfer::before { border-color: var(--mk-red); }
.timeline li.rir::before { border-color: var(--mk-purple); }
.timeline li.observed::before { border-color: var(--mk-amber); }
.timeline li.peeringdb::before { border-color: var(--tx-muted); }
.timeline .when { font-family: var(--ff-mono); font-size: 12.5px; color: var(--tx-muted); }
.timeline .what { font-size: 0.97em; margin-top: 1px; font-weight: 500; }
.timeline .detail { color: var(--tx-3); font-size: 0.88em; margin-top: 2px; word-break: break-word; }

.timeline .spells { margin-top: 5px; }
.timeline .spells summary { font-size: 0.82em; color: var(--tx-muted); }
.timeline .spells ul {
  list-style: none; margin: 7px 0 0; padding: 0 0 0 12px; border-left: 1px solid var(--bd-strong);
}
.timeline .spells li { padding: 1px 0; font-family: var(--ff-mono); font-size: 12px; color: var(--tx-muted); }
.timeline .spells li::before { display: none; }
.timeline .peers { margin-left: 10px; opacity: 0.65; }

/* -- filter pills -------------------------------------------------------- */
.filters { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 18px; }
.filters button {
  cursor: pointer; padding: 5px 13px; font-size: 12.5px; border-radius: var(--r-pill);
  font-family: inherit; color: var(--tx-3); border: 1px solid var(--bd-strong);
  background: var(--bg-panel);
}
.filters button:hover { border-color: var(--acc); }
.filters button.on { color: var(--acc); border-color: var(--acc); background: var(--acc-tint-9); font-weight: 600; }

/* -- load more ----------------------------------------------------------- */
.paged { display: contents; }
.more { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
.more button {
  cursor: pointer; padding: 7px 15px; font-size: 12.5px; font-weight: 600;
  border-radius: var(--r-pill); font-family: inherit; color: var(--tx-3);
  border: 1px solid var(--bd-strong); background: var(--bg-panel);
}
.more button:hover:not([disabled]) { border-color: var(--acc); color: var(--acc); }
.more button[disabled] { opacity: 0.55; cursor: default; }
.more .count { color: var(--tx-muted); font-size: 12.5px; font-family: var(--ff-mono); }

/* -- sampling ------------------------------------------------------------ */
.flagrow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.flagchip {
  display: inline-flex; align-items: center; padding: 4px 11px; border-radius: var(--r-pill);
  font-size: 0.82em; font-weight: 600; font-family: var(--ff-mono);
  color: var(--fg); background: var(--bg); border: 1px solid var(--bd);
}
/* ============================================================== LEGAL PAGES */
/* Narrower than the report on purpose: this is prose to be read line by line,
   not a document to be scanned, and 66 characters is where that stops being
   tiring. */
/* The reading column, nothing more. The page frame, the header, the footer and
   the sticky-footer behaviour all come from .shell now, which is why these
   pages line up with the landing page and the blog instead of running their own
   narrower masthead. */
.legal {
  max-width: var(--w-post); margin: 0 auto; padding-bottom: 64px;
}
.legal main { display: block; }
.legal-back { font-size: 14px; font-weight: 500; color: var(--tx-3); }
.legal-back:hover { color: var(--tx); text-decoration: none; }

.legal-title { margin-top: 36px; }
.legal-title h1 {
  font-family: var(--ff-display); font-size: clamp(32px, 4.5vw, 42px);
  font-weight: 800; letter-spacing: -0.02em; line-height: 1.1; margin: 0;
}
.legal-title .lede { color: var(--tx-2); margin: 12px 0 0; }
.legal-title .stamp { font-family: var(--ff-mono); font-size: 12.5px; color: var(--tx-muted); margin: 14px 0 0; }

.legal-short {
  margin-top: 32px; border-radius: var(--r-card); padding: 26px 28px;
  background: linear-gradient(180deg, color-mix(in oklab, var(--acc) 10%, #fff), color-mix(in oklab, var(--acc) 4%, var(--bg-page)));
}
.legal-short .eyebrow { color: var(--acc); margin-bottom: 14px; }
.legal-short ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 11px; }
.legal-short li { position: relative; padding-left: 19px; color: var(--tx-2); }
.legal-short li strong { color: var(--tx); }
.legal-short li::before {
  content: ""; position: absolute; left: 0; top: 8px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--acc);
}

.legal-body { margin-top: 12px; line-height: 1.62; }
.legal-body h2 {
  font-family: var(--ff-display); font-size: 22px; font-weight: 700;
  letter-spacing: -0.01em; margin: 38px 0 10px;
}
.legal-body p { margin: 0 0 10px; color: var(--tx-2); }
.legal-body strong { color: var(--tx); }
.legal-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.legal-list li { position: relative; padding-left: 18px; color: var(--tx-2); }
.legal-list li strong { color: var(--tx); }
.legal-list li::before {
  content: ""; position: absolute; left: 0; top: 9px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--bd-strong);
}
.legal .site-footer { max-width: none; padding-left: 0; padding-right: 0; }

/* -- block map ----------------------------------------------------------- */
.sub-h {
  font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--tx-muted); font-weight: 700; margin: 24px 0 0;
}
.bm-head { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: baseline; margin-top: 20px; color: var(--tx-3); }
.bm-big { font-family: var(--ff-mono); font-size: 1.75em; font-weight: 700; letter-spacing: -0.01em; color: var(--tx); }
.bm-bar { display: flex; height: 10px; border-radius: 999px; overflow: hidden; margin-top: 12px; background: var(--bd); }
.bm-bar span { display: block; }
.bm-legend { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 10px; font-size: 0.85em; color: var(--tx-3); }
.bm-legend span { display: inline-flex; align-items: center; gap: 6px; }
.bm-legend i { width: 9px; height: 9px; border-radius: 3px; }
.bm-legend b { font-family: var(--ff-mono); font-weight: 400; color: var(--tx-muted); }

.blockmap { display: grid; grid-template-columns: repeat(auto-fill, minmax(14px, 1fr)); gap: 3px; margin-top: 10px; }
.blockmap .cell {
  aspect-ratio: 1; border: none; border-radius: 3px; cursor: pointer; padding: 0;
  transition: transform 0.08s ease, outline-color 0.08s ease;
  outline: 2px solid transparent; outline-offset: 1px;
}
.blockmap .cell:hover, .blockmap .cell:focus-visible { transform: scale(1.35); outline-color: var(--tx); z-index: 1; }
.cell-detail { margin-top: 14px; }
.x-btn {
  cursor: pointer; border: 1px solid var(--bd); background: var(--bg-panel); color: var(--tx-muted);
  border-radius: 999px; width: 24px; height: 24px; font-size: 13px; line-height: 1; flex: none;
}
.x-btn:hover { color: var(--tx); border-color: var(--bd-strong); }

/* Solid fills for the map, bars and sparklines.
   Deliberately scoped: `pal-red` already means "red text on a tinted pill" on
   chips and tags across the report, so an unscoped background rule here would
   repaint every chip on the page solid. */
.bm-bar > .pal-red,    .bm-legend i.pal-red,    .blockmap .cell.pal-red,
.group-name i.pal-red, .spark i.pal-red,        .fs-slot i.pal-red    { background: var(--mk-red); }
.bm-bar > .pal-amber,  .bm-legend i.pal-amber,  .blockmap .cell.pal-amber,
.group-name i.pal-amber, .spark i.pal-amber,    .fs-slot i.pal-amber  { background: var(--mk-amber); }
.bm-bar > .pal-green,  .bm-legend i.pal-green,  .blockmap .cell.pal-green,
.group-name i.pal-green, .spark i.pal-green,    .fs-slot i.pal-green  { background: var(--mk-green); }
.bm-bar > .pal-idle,   .bm-legend i.pal-idle    { background: var(--bd-strong); }

.group-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.group-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  border: 1px solid var(--bd); border-radius: var(--r-sub); padding: 10px 14px;
}
.group-n { font-family: var(--ff-mono); font-weight: 700; min-width: 34px; text-align: right; background: none; }
.group-n.pal-red { color: var(--red-fg); }
.group-n.pal-amber { color: var(--amber-fg); }
.group-n.pal-green { color: var(--green-fg); }
.group-name { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: 0.92em; }
.group-name i { width: 9px; height: 9px; border-radius: 3px; }
.group-note { color: var(--tx-3); font-size: 0.88em; flex: 1; min-width: 180px; }

/* -- sparkline ----------------------------------------------------------- */
.spark { display: flex; align-items: flex-end; gap: 2px; height: 30px; margin-top: 12px; }
.spark i { flex: 1; min-width: 2px; border-radius: 2px 2px 0 0; }
.spark-cap { color: var(--tx-muted); font-size: 0.78em; margin-top: 4px; }

/* -- flagged share over time --------------------------------------------- */
.fshare { margin-top: 4px; }
.fs-bars { display: flex; align-items: flex-end; gap: 6px; height: 96px; margin-top: 12px; }
.fs-slot {
  flex: 1; height: 100%; display: flex; flex-direction: column; justify-content: flex-end;
  border-bottom: 1px solid var(--bd);
}
.fs-slot i { display: block; border-radius: 4px 4px 0 0; }
/* A month nobody checked reads as absence, not as a clean month. */
.fs-none { background: repeating-linear-gradient(45deg, transparent, transparent 4px, var(--bd-row) 4px, var(--bd-row) 5px); }
.fs-axis { display: flex; gap: 6px; margin-top: 6px; }
.fs-axis span { flex: 1; text-align: center; font-family: var(--ff-mono); font-size: 9.5px; color: var(--tx-muted); }

.addr-list { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }
.addr-card { border: 1px solid var(--bd); border-radius: var(--r-sub); padding: 14px 16px; }
.addr-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.addr-head .addr { font-family: var(--ff-mono); font-size: 1.02em; font-weight: 600; }
.addr-head .spacer { flex: 1; }
.addr-head .checked { color: var(--tx-muted); font-size: 0.82em; font-family: var(--ff-mono); }
.risk {
  display: inline-flex; align-items: center; padding: 3px 10px; border-radius: var(--r-pill);
  font-size: 0.8em; font-weight: 700; font-family: var(--ff-mono);
  color: var(--fg); background: var(--bg);
}
.meter {
  height: 5px; border-radius: var(--r-pill); background: var(--bg-skel);
  margin-top: 11px; overflow: hidden;
}
.meter > i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--fg); }
.chiprow { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
.chip2 {
  padding: 3px 10px; border-radius: var(--r-pill); font-size: 0.8em; font-weight: 600;
  color: var(--fg); background: var(--bg); border: 1px solid var(--bd);
}
.addr-note { color: var(--tx-3); font-size: 0.88em; margin-top: 10px; }

/* -- score trend chart --------------------------------------------------- */
.trend { margin-top: 18px; }
.ct-wrap { overflow-x: auto; }
.ct { width: 100%; min-width: 420px; height: auto; display: block; }
/* Chrome recedes: the marks carry the meaning. */
.ct-grid { stroke: var(--bd); stroke-width: 1; }
.ct-axis-line { stroke: var(--bd-strong); stroke-width: 1; }
.ct-axis { fill: var(--tx-muted); font-family: var(--ff-mono); font-size: 10px; }
.ct circle { cursor: default; }
.ct-legend { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 10px; }
.ct-key {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12px; color: var(--tx-3); font-family: var(--ff-mono);
}
.ct-key i { width: 10px; height: 3px; border-radius: 2px; flex: none; }

/* -- changes callout ----------------------------------------------------- */
.changes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.changes li {
  border-left: 3px solid var(--mk-amber); padding: 5px 12px; font-family: var(--ff-mono);
  font-size: 12px; color: var(--tx-2); background: var(--changes-bg); border-radius: 0 8px 8px 0;
}
.changes .prov { color: var(--tx-muted); }

/* -- insights ------------------------------------------------------------ */
.insights { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.insights li { display: flex; gap: 12px; align-items: flex-start; padding: 8px 0; }
.insights .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; margin-top: 8px; background: var(--fg); }
.insights .txt { font-size: 0.97em; line-height: 1.6; }
.insights .lead { font-weight: 700; }
.insights .rest { color: var(--tx-3); }
/* A finding that points at a resource: an underlined, accent-coloured, clickable
   CIDR/ASN, so it reads as a link rather than blending into the muted text. */
.insights .ins-link { color: var(--acc); text-decoration: underline; text-underline-offset: 2px; font-family: var(--ff-mono); white-space: nowrap; }
.insights .ins-link:hover { color: var(--acc-hover); }
.insights li.info { --fg: var(--indigo-fg); }
.insights li.notice { --fg: var(--amber-fg); }
.insights li.warn { --fg: var(--red-fg); }
.link-btn {
  cursor: pointer; border: none; background: none; padding: 10px 0 0 19px;
  color: var(--acc); font-size: 0.9em; font-weight: 600; font-family: inherit; text-align: left;
}
.link-btn:hover { text-decoration: underline; }

/* -- sources ------------------------------------------------------------- */
.sources { display: flex; flex-direction: column; gap: 7px; }
/* A grid, not a flex row: the state chips are different widths ("done" against
   "querying"), and on a narrow screen the dataset wraps to two lines while the
   chip stays vertically centred against it. Fixed first column and top
   alignment keep the chips in a straight edge however the text reflows. */
/* A fixed first column, not `auto`: sized to content, every row set its own
   chip width, so "FRESH" and "REFRESHED" pushed their provider names to
   different left edges and the list read as ragged rather than as a table. */
.source-row {
  display: grid; grid-template-columns: 84px minmax(0, 1fr);
  align-items: start; gap: 3px 10px;
  font-family: var(--ff-mono); font-size: 12.5px;
  padding-block: 3px;
}
.source-row .state { justify-self: start; }
/* Both continuation lines sit under the provider name. The note used to fall
   into column 1, under the chip, which is what broke the vertical edge. */
.source-row .ds { color: var(--tx-muted); grid-column: 2; overflow-wrap: anywhere; }
.source-row .note { color: var(--tx-muted); grid-column: 2; }
.state {
  font-size: 10.5px; padding: 1px 8px; border-radius: var(--r-state);
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--fg); background: var(--bg);
}
.state.fresh, .state.refreshed { --fg: var(--green-fg); --bg: var(--green-bg); }
.state.stale, .state.revalidating { --fg: var(--amber-fg); --bg: var(--amber-bg); }
.state.error { --fg: var(--red-fg); --bg: var(--red-bg); }
.state.pending, .state.queued { --fg: var(--tx-muted); --bg: var(--bg-neutral); }

/* -- disclosures and raw JSON -------------------------------------------- */
details summary { cursor: pointer; color: var(--tx-3); font-size: 0.9em; }
details[open] summary { margin-bottom: 12px; }
pre.raw {
  background: var(--bg-page); border: 1px solid var(--bd); border-radius: var(--r-chip);
  padding: 14px; overflow: auto; max-height: 380px;
  font-family: var(--ff-mono); font-size: 12px; margin: 12px 0 0;
}

/* -- section rail -------------------------------------------------------- */
.rail {
  position: fixed; right: 20px; top: 50%; transform: translateY(-50%); z-index: 50;
  display: flex; flex-direction: column; gap: 3px; align-items: flex-end;
}
.rail a {
  display: flex; align-items: center; gap: 9px; padding: 5px 12px;
  border-radius: var(--r-pill); font-size: 11.5px; letter-spacing: 0.02em;
  color: var(--tx-muted); font-weight: 500; border: 1px solid transparent;
}
.rail a:hover { color: var(--tx); text-decoration: none; }
.rail a.on {
  color: var(--acc); font-weight: 700; background: var(--bg-panel);
  border-color: var(--bd); box-shadow: var(--sh-rail);
}
.rail .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--dot-off); }
.rail a.on .dot { background: var(--acc); }

/* -- loading ------------------------------------------------------------- */
.loading { display: flex; flex-direction: column; gap: var(--sp-stack); }
.loading-head { display: flex; align-items: center; gap: 10px; }
.spinner {
  width: 16px; height: 16px; border-radius: 50%; flex: none;
  border: 2.5px solid var(--bd-strong); border-top-color: var(--acc);
  animation: spin 0.7s linear infinite;
}
.skel {
  background: linear-gradient(90deg, var(--bg-skel) 25%, var(--bg-skel-hi) 50%, var(--bg-skel) 75%);
  background-size: 200% 100%; animation: shimmer 1.4s linear infinite; border-radius: var(--r-skel);
}
.skel--h { height: 24px; width: min(300px, 70%); border-radius: 8px; }
.skel--p { height: 13px; margin-top: 8px; }
.skel--t { height: 12px; }
.loading .state { min-width: 70px; text-align: center; }

.skel-timeline { margin-top: 22px; position: relative; padding-left: 26px; }
.skel-timeline .rail-line {
  position: absolute; left: 6px; top: 6px; bottom: 6px; width: 1px; background: var(--bd-strong);
}
.skel-row { position: relative; padding-bottom: 18px; }
.skel-row:last-child { padding-bottom: 0; }
.skel-dot {
  position: absolute; left: -24px; top: 3px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--bg-panel); border: 2px solid var(--bd-soft);
}

/* ============================================================ RESPONSIVE === */

/* The key/value grid cannot collapse intrinsically: minmax(130px,190px) has no
   auto-fit escape. This is the only breakpoint the report needs. */
@media (max-width: 620px) {
  .kv, .cols .kv { grid-template-columns: 1fr; gap: 0; }
  .kv dt { padding-bottom: 0; }
  .kv dd { padding-top: 0; padding-bottom: 8px; }

  /* Provider name on its own line above the dataset, so neither is squeezed
     into a column too narrow to hold it. */
  .source-row { grid-template-columns: auto minmax(0, 1fr); }
  .source-row > span:not(.ds):not(.state) { grid-column: 2; font-weight: 600; }
  .loading .state { min-width: 0; }

  /* Long monospace values (v6 addresses, ranges, handles) must wrap rather
     than push the panel wider than the screen. */
  .kv dd, .addr-head .addr, .tm-kv dd { overflow-wrap: anywhere; }
}

/* The hero art and the terminal collage are absolutely positioned decoration
   with no intrinsic height; below ~720px they overflow their columns. */
@media (max-width: 720px) {
  .hero-art, .mockstage { display: none; }
  .rail { display: none; }

  /* The rhythm below the hero was 36px of its own padding plus a section margin
     whose clamp floors at 64px. That is breathing room beside a tall hero card,
     but the card is hidden here, so on a 375px phone it was 100px of blank
     screen between the search box and the next heading. The floor is the right
     place to fix it rather than the hero boundary alone: `.status` and `.report`
     sit between them in the DOM (hidden until a search runs), so an adjacency
     selector never matches, and 64px between every section is generous on a
     phone anyway. */
  .hero { padding-bottom: 18px; }
  .section { margin-top: 40px; }
}

/* A backstop, not a fix.
 *
 * The real causes of sideways scroll are fixed above (the 34px headline floor
 * and the search button's 220px field). This stops any future one reaching a
 * phone as a page that slides under the thumb. `clip` rather than `hidden`
 * because `hidden` makes the body a scroll container and breaks position:
 * sticky; and it is scoped to small screens so the same mistake stays visible
 * on a desktop where it will actually get noticed and fixed. */
@media (max-width: 760px) {
  body { overflow-x: clip; }
}

/* Below this the button's own padding starts to squeeze the field. */
@media (max-width: 400px) {
  .btn-accent { padding: 15px 18px; }
  #q { padding: 15px 14px; font-size: 14px; }
}

/* -- mobile navigation --------------------------------------------------- */
@media (max-width: 760px) {
  .site-header { padding: 12px 4px; position: relative; }
  .nav-burger { display: inline-flex; }

  /* A panel under the header rather than a full-screen takeover: the search
     box is the reason people are here, and burying it behind a menu that
     covers the page makes the site feel like it went somewhere. */
  .site-nav {
    position: absolute; top: calc(100% - 4px); right: 4px; left: 4px; z-index: 60;
    flex-direction: column; align-items: stretch; gap: 2px;
    background: var(--bg-panel); border: 1px solid var(--bd-strong);
    border-radius: var(--r-sub); box-shadow: var(--sh-lg);
    padding: 8px; font-size: 15px;

    /* Animated but not display:none, so the links stay out of the tab order
       only while genuinely hidden. `visibility` does that; opacity alone would
       leave a focusable invisible menu. */
    opacity: 0; visibility: hidden; transform: translateY(-6px);
    transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s linear 0.16s;
  }
  .site-nav.open {
    opacity: 1; visibility: visible; transform: translateY(0);
    transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s;
  }
  .site-nav a {
    padding: 11px 12px; border-radius: var(--r-chip); color: var(--tx);
  }
  .site-nav a:hover, .site-nav a:focus-visible { background: var(--bg-neutral); text-decoration: none; }
  /* The call to action keeps its pill, centred, with a little air above it. */
  .site-nav a.nav-cta { margin-top: 6px; text-align: center; }

  @media (prefers-reduced-motion: reduce) {
    .site-nav { transition: none; }
  }
}

/* An announced block is a wider target than a single address, and there are far
   fewer of them, so the space map gets bigger cells than the block map. */
.blockmap--wide { grid-template-columns: repeat(auto-fill, minmax(26px, 1fr)); gap: 4px; margin-top: 14px; }
.blockmap--wide .cell { border-radius: 5px; }

/* -- time machine -------------------------------------------------------- */
.tm-picker { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; align-items: center; }
.tm-chip {
  cursor: pointer; padding: 6px 14px; border-radius: var(--r-pill);
  font-family: var(--ff-mono); font-size: 12.5px; font-weight: 600;
  color: var(--tx-2); background: var(--bg-panel); border: 1px solid var(--bd-soft);
}
.tm-chip:hover { border-color: var(--acc); color: var(--acc); }
.tm-chip.on { background: var(--acc); border-color: var(--acc); color: #fff; }

.tm-body {
  border: 1px solid var(--indigo-bd); background: var(--acc-tint-4);
  border-radius: var(--r-sub); padding: 16px 18px; margin-top: 14px;
}
.tm-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tm-date { font-size: 0.95em; font-weight: 700; }
.tm-kv { margin-top: 12px; }
/* A field we never recorded reads as absent, not as a value. */
.tm-unknown { color: var(--tx-muted); font-style: italic; }
.tm-diff {
  margin-top: 12px; border-left: 3px solid var(--mk-amber); background: var(--changes-bg);
  border-radius: 0 8px 8px 0; padding: 6px 12px; font-size: 0.88em; color: var(--tx-2);
}
.tm-gap { margin-top: 10px; color: var(--tx-muted); font-size: 0.86em; max-width: 70ch; }

/* -- #what-is-a-subnet --------------------------------------------------- */
/* Prose gets a measure, the reference column does not: a definition list read
   by scanning down the terms wants to stay narrow whatever the viewport does. */
.explain-grid {
  display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: 40px; align-items: start;
}
.explain-main p { margin: 0 0 18px; max-width: 62ch; line-height: 1.65; }
.explain-main p:last-child { margin-bottom: 0; }
.explain-side {
  background: var(--bg-panel); border: 1px solid var(--bd);
  border-radius: var(--r-band); padding: 26px 28px;
}
.explain-h3 {
  margin: 0 0 16px; font-size: 12px; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--tx-muted); font-weight: 700;
}
.explain-dl { margin: 0; display: flex; flex-direction: column; gap: 10px; }
.explain-dl > div {
  display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: 14px;
  align-items: baseline;
}
.explain-dl > div + div { border-top: 1px solid var(--bd); padding-top: 10px; }
.explain-dl dt { font-size: 13px; color: var(--tx-2); }
.explain-dl dd { margin: 0; font-size: 13.5px; color: var(--tx-muted); }

.explain-terms {
  margin-top: 40px;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 28px 32px;
}

@media (max-width: 860px) {
  .explain-grid { grid-template-columns: minmax(0, 1fr); gap: 32px; }
}

/* ================================================================== BLOG === */
/*
 * /blog and the articles under it. Nothing above this line changes: the blog
 * pages reuse .shell, .site-header, .site-nav, .site-footer, .scroll, .mono and
 * .accent exactly as the landing page does, and only the reading column below
 * is new.
 *
 * The measure is the whole design decision here. A report is scanned, so it can
 * afford the 1120px content width; an article is read line after line, and past
 * about seventy characters the eye starts losing the beginning of the next one.
 *
 * The width is in pixels and was measured, not assumed. `68ch` is the obvious
 * way to write it and is wrong here: `ch` is the advance of the zero glyph, and
 * in Instrument Sans that is half again the width of an average letter, so
 * `max-width: 68ch` sets a 770px column carrying about a hundred characters.
 * 560px less the gutter renders at roughly 68.
 *
 * Declared in a second :root rule rather than in the metrics block at the top of
 * the file so that this whole section stays an append.
 */
:root { --w-post: 1120px; }

.post,
.blog {
  max-width: var(--w-content);
  margin: clamp(26px, 5vw, 52px) auto 0;
  padding: 0 var(--gutter);
}

/* -- article head -------------------------------------------------------- */
.post-head { margin-bottom: 34px; }
.post-h1,
.blog-h1 {
  font-family: var(--ff-display);
  font-size: clamp(29px, 5.6vw, 42px); font-weight: 800;
  letter-spacing: -0.022em; line-height: 1.12;
  margin: 0 0 14px; overflow-wrap: break-word;
}
/* Two markup forms are in use: three inline spans, and plain text with a middot
   sitting in it. Inline flow renders both; a flex container would only render
   the first. */
.post-meta { margin: 0 0 20px; color: var(--tx-muted); font-size: 13px; }
.post-lede { margin: 0; color: var(--tx-2); font-size: 18.5px; line-height: 1.6; }

/* -- article body -------------------------------------------------------- */
.post-body { font-size: 17px; line-height: 1.75; color: var(--tx); }
.post-body p { margin: 0 0 22px; }
.post-body p:last-child { margin-bottom: 0; }

.post-body h2 {
  font-family: var(--ff-display);
  font-size: clamp(22px, 3.4vw, 26px); font-weight: 700;
  letter-spacing: -0.015em; line-height: 1.25;
  margin: 46px 0 16px;
  /* The contents list scrolls to these. Without the offset a heading lands hard
     against the top edge with nothing above it to read as a start. */
  scroll-margin-top: 20px;
}
.post-body > h2:first-child { margin-top: 0; }
.post-body h3 {
  font-size: 17.5px; font-weight: 700; line-height: 1.35;
  margin: 32px 0 10px; scroll-margin-top: 20px;
}

.post-body ul, .post-body ol { margin: 0 0 22px; padding-left: 24px; }
.post-body li { margin-bottom: 10px; }
.post-body li:last-child { margin-bottom: 0; }
.post-body li::marker { color: var(--tx-muted); }

/* Prose links are underlined. A report can lean on colour alone because every
   coloured thing in it is a link; a paragraph of running text cannot. */
.post-body a { text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }

/* A prefix or an AS number set inside a sentence. JetBrains Mono runs large
   beside Instrument Sans, so it steps down; and a 39 character IPv6 literal is
   allowed to break mid-token rather than push the column past the screen. */
.post .mono { font-size: 0.92em; overflow-wrap: anywhere; }

/* -- contents ------------------------------------------------------------- */
/* Sits between the lede and the first heading in two of the articles and inside
   .post-body in the third, so it carries spacing that works either way. */
.post-toc {
  margin: 0 0 40px; padding: 20px 24px;
  background: var(--bg-panel); border: 1px solid var(--bd);
  border-radius: var(--r-sub);
}
.post-body > .post-toc { margin: 30px 0 40px; }
/* A visible label. <nav aria-label="Contents"> is announced only to somebody
   moving by landmark, and says nothing at all to everybody else. */
.post-toc::before {
  content: 'Contents';
  display: block; margin-bottom: 12px;
  font-size: 11.5px; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--tx-muted); font-weight: 700;
}
.post-toc ol { margin: 0; padding-left: 20px; }
.post-toc li { margin: 0; padding: 3px 0; font-size: 14.5px; line-height: 1.5; }
.post-toc li::marker { color: var(--tx-muted); font-size: 12px; }
.post-toc a { color: var(--tx-2); }
.post-toc a:hover { color: var(--acc); text-decoration: underline; }

/* -- aside ---------------------------------------------------------------- */
/* Written as a <div> around paragraphs in two articles and as a bare <p> in the
   third. Both are two classes deep from .post, which outranks `.post-body p`,
   so the type below wins wherever the note lands. */
.post .post-note {
  margin: 28px 0; padding: 16px 20px;
  border-left: 3px solid var(--acc); background: var(--acc-tint-4);
  border-radius: 0 var(--r-chip) var(--r-chip) 0;
  color: var(--tx-2); font-size: 15.5px; line-height: 1.65;
}
.post .post-note p { margin: 0; font-size: inherit; line-height: inherit; }
.post .post-note p + p { margin-top: 10px; }

/* -- tables --------------------------------------------------------------- */
/* A table is scanned, not read, so it does not want the prose measure. It
   reaches out of the column by an equal amount on each side wherever the
   viewport can hold it, which keeps it centred on the text and keeps the
   paragraphs at the width they were set for. Negative margins rather than a
   grid so that the vertical margins either side of it still collapse the way
   the rest of the flow does. */
.post-body .scroll { margin: 26px 0 30px; }
@media (min-width: 860px) {
  .post-body .scroll { margin-left: -100px; margin-right: -100px; }
}
/* Below this width the four column tables stop being tables and become four
   columns of one word each. They scroll inside .scroll instead; the page never
   does. Colour is --tx-3 so a caption written with .dim and one written without
   it come out identical. */
.post-table { min-width: 460px; font-size: 14.5px; }
.post-table caption {
  caption-side: top; text-align: left; padding: 0 10px 10px;
  color: var(--tx-3); font-size: 13px; line-height: 1.5;
}
.post-table th { padding: 9px 10px; }
.post-table td { padding: 10px; line-height: 1.6; }

/* -- article foot --------------------------------------------------------- */
.post-foot {
  margin-top: 46px; padding-top: 24px; border-top: 1px solid var(--bd);
  color: var(--tx-3); font-size: 15.5px; line-height: 1.7;
}
.post-foot p { margin: 0; }
.post-foot p + p { margin-top: 12px; }

/* -- index ---------------------------------------------------------------- */
.blog-head { margin-bottom: 40px; }
.blog-intro { color: var(--tx-2); font-size: 17px; line-height: 1.7; }
.blog-intro p { margin: 0; }
.blog-intro p + p { margin-top: 14px; }

.blog-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 16px; }
.blog-card {
  position: relative;
  background: var(--bg-panel); border: 1px solid var(--bd);
  border-radius: var(--r-panel); padding: 24px 26px; box-shadow: var(--sh-xs);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.blog-card:hover { border-color: var(--bd-soft); box-shadow: var(--sh-sm); }
/* The link is on the title and the title's ::after covers the card, so there is
   exactly one link in the accessibility tree, named by the headline, and the
   whole card is the target. The cost is that the standfirst cannot be selected
   with a mouse, which for a list of three posts is a fair trade. The ring is on
   the card because a keyboard lands on the title and the card is what moves. */
.blog-card-title {
  font-family: var(--ff-display); font-size: 21px; font-weight: 700;
  letter-spacing: -0.015em; line-height: 1.3; margin: 0 0 10px;
}
.blog-card-title a { color: var(--tx); }
.blog-card-title a:hover { color: var(--acc); text-decoration: none; }
.blog-card-title a::after { content: ''; position: absolute; inset: 0; }
.blog-card:focus-within { border-color: var(--acc); box-shadow: var(--sh-focus); }
.blog-card-standfirst { margin: 0 0 14px; color: var(--tx-2); font-size: 15.5px; line-height: 1.65; }
.blog-card-meta { margin: 0; color: var(--tx-muted); font-size: 12.5px; }

@media (prefers-reduced-motion: reduce) {
  .blog-card { transition: none; }
}

/* A phone holds fewer characters per line at any size, so the measure is
   already met and the type can come down to where it is comfortable in the
   hand. */
@media (max-width: 620px) {
  .post-body { font-size: 16.5px; line-height: 1.72; }
  .post-lede { font-size: 17px; }
  .post-toc { padding: 16px 18px; }
  .post .post-note { padding: 14px 16px; }
  .blog-intro { font-size: 16.5px; }
  .blog-card { padding: 20px; }
  .blog-card-title { font-size: 19px; }
}

/* -- blog index: cards and their artwork -------------------------------- */
/* The intro keeps a reading measure even though its container no longer does:
   prose wants roughly 68 characters whatever the card grid is doing. */
.blog-intro { max-width: none; }
.blog-intro p {
  margin: 0 0 12px;
  font-size: clamp(16px, 2.1vw, 18px);
  line-height: 1.6;
  color: var(--tx-3);
}
.blog-intro p:last-child { margin-bottom: 0; }

.blog-list {
  list-style: none; margin: clamp(28px, 4vw, 44px) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 26px;
}

.blog-card {
  display: flex; flex-direction: column;
  background: var(--bg-panel); border: 1px solid var(--bd);
  border-radius: var(--r-card); overflow: hidden;
  box-shadow: var(--sh-xs);
  transition: box-shadow 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
}
.blog-card:hover { box-shadow: var(--sh-md); border-color: var(--bd-soft); transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) { .blog-card, .blog-card:hover { transition: none; transform: none; } }

.blog-card-art { background: var(--acc-tint-4); border-bottom: 1px solid var(--bd); }
.blog-card-art .art { display: block; width: 100%; height: auto; }

.blog-card-title { font-family: var(--ff-display); font-size: 19px; font-weight: 700; line-height: 1.3; letter-spacing: -0.012em; margin: 22px 24px 0; }
.blog-card-title a { color: var(--tx); }
.blog-card-title a:hover { color: var(--acc); text-decoration: none; }
.blog-card-standfirst { color: var(--tx-3); font-size: 14.5px; line-height: 1.6; margin: 10px 24px 0; }
.blog-card-meta { display: flex; gap: 8px; align-items: baseline; color: var(--tx-muted); font-size: 12.5px; font-family: var(--ff-mono); margin: auto 24px 22px; padding-top: 18px; }

/* One cell per announced block. Green means we looked and found nothing; the
   faint cells mean nobody has looked, which is not the same thing. */
.art--cells .c-clean { fill: var(--green-bg); stroke: var(--green-bd); stroke-width: 1; }
.art--cells .c-mild  { fill: var(--amber-bg); stroke: var(--amber-bd); stroke-width: 1; }
.art--cells .c-flag  { fill: var(--red-bg);   stroke: var(--red-bd);   stroke-width: 1; }
.art--cells .c-none  { fill: var(--bg-page);  stroke: var(--bd-strong); stroke-width: 1; }

/* The brand mark's echo: what the block was, still behind what it is. */
.art--echo .e-1 { fill: var(--bg-panel); stroke: var(--acc); stroke-width: 2; }
.art--echo .e-2 { fill: none; stroke: var(--acc); stroke-width: 2; opacity: 0.45; }
.art--echo .e-3 { fill: none; stroke: var(--acc); stroke-width: 2; opacity: 0.18; }
.art--echo .e-mark { fill: var(--red-fg); opacity: 0.85; }

/* Above: the old announcement stops before the new one starts. Below: they
   overlap, which is the shape a hijack makes. */
.art--paths line { stroke-linecap: round; }
.art--paths .p-old { stroke: var(--tx-muted); stroke-width: 3; opacity: 0.5; }
.art--paths .p-new { stroke: var(--acc); stroke-width: 3; }
.art--paths .p-overlap { fill: var(--red-bg); stroke: var(--red-bd); stroke-width: 1; }
.art--paths .p-label { fill: var(--tx-muted); font-family: var(--ff-mono); font-size: 10px; text-anchor: middle; }

/* One row per feed, one column per address: where the feeds agree, and where
   one says yes while another says no. Contested is a finding, not an error. */
.art--signals .s-track { fill: var(--bg-page); stroke: var(--bd-strong); stroke-width: 1; }
.art--signals .s-yes { fill: var(--red-bg); stroke: var(--red-bd); stroke-width: 1; }
.art--signals .s-dot-yes { fill: var(--red-fg); }
.art--signals .s-dot-no { fill: var(--green-fg); }
.art--signals .s-dot-none { fill: var(--tx-muted); opacity: 0.4; }
.art--signals .s-label { fill: var(--tx-muted); font-family: var(--ff-mono); font-size: 10px; }

/* One network in the middle, its transit above and its customers below: the
   shape of an AS read from the routing table rather than the paperwork. */
.art--graph .g-edge { stroke: var(--tx-muted); stroke-width: 1.5; opacity: 0.45; }
.art--graph .g-peer { fill: var(--bg-page); stroke: var(--bd-strong); stroke-width: 1.5; }
.art--graph .g-core { fill: var(--acc); stroke: none; }
.art--graph .g-label { fill: var(--tx-muted); font-family: var(--ff-mono); font-size: 10px; text-anchor: middle; }
.art--graph .g-core-label { fill: #ffffff; font-family: var(--ff-mono); font-size: 10px; text-anchor: middle; }

/* A dated trail: registration, routing, a flag, a change we recorded. The
   colours are the report's own event categories. */
.art--trail .t-axis { stroke: var(--bd-strong); stroke-width: 2; }
.art--trail .t-stem { stroke: var(--bd-strong); stroke-width: 1.5; }
.art--trail .t-reg { fill: var(--acc); }
.art--trail .t-route { fill: var(--amber-fg); }
.art--trail .t-flag { fill: var(--red-fg); }
.art--trail .t-obs { fill: var(--green-fg); }
.art--trail .t-label { fill: var(--tx-muted); font-family: var(--ff-mono); font-size: 10px; text-anchor: middle; }

/* -- article hero artwork ------------------------------------------------ */
/* Full bleed of the reading column, not of the page: the drawing belongs to
   the article, and stretching it wider would break the column the eye is
   already tracking down. */
.post-art {
  margin: clamp(26px, 4vw, 38px) 0 clamp(30px, 4.5vw, 44px);
  padding: 0;
  background: var(--acc-tint-4);
  border: 1px solid var(--bd);
  border-radius: var(--r-card);
  overflow: hidden;
}
.post-art .art { display: block; width: 100%; height: auto; }
/* The caption carries the same information as the drawing, for anyone who is
   not reading it visually. It is not decoration and it is not hidden. */
.post-art figcaption {
  margin: 0; padding: 0 22px 18px;
  color: var(--tx-muted); font-size: 13px; line-height: 1.55;
}
/* -- article layout ------------------------------------------------------ */
/* `--w-post` was declared and never consumed: no rule matched `.post`, so every
   article ran the full width of the shell. A line of 17px type across 1,200px is
   about 150 characters, roughly twice what anyone can track back to the start of
   the next line. */
.post {
  max-width: var(--w-post);
  margin: clamp(26px, 5vw, 52px) auto 0;
  padding: 0 var(--gutter);
}

/* Wider than the prose, and only where the content needs it. A table squeezed
   into a reading measure wraps every cell onto three lines and stops being a
   table; the same one at full page width detaches from the argument around it. */
/* Flush with the prose. An earlier version bled this wider than the column on
   the theory that a table wants room; it just made the table look detached from
   the paragraph explaining it, with its caption hanging off the left edge. */
.post .scroll {
  margin: clamp(24px, 3.5vw, 32px) 0 clamp(26px, 4vw, 36px);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.post-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14.5px;
  background: var(--bg-panel);
  border: 1px solid var(--bd);
  border-radius: var(--r-chip);
}
.post-table thead th:first-child { border-top-left-radius: var(--r-chip); }
.post-table thead th:last-child { border-top-right-radius: var(--r-chip); }
.post-table caption {
  caption-side: top; text-align: left;
  padding: 0 16px 10px;
  color: var(--tx-muted); font-size: 13px;
}
.post-table th {
  text-align: left; vertical-align: bottom;
  padding: 12px 16px;
  font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--tx-muted); font-weight: 700;
  background: var(--bg-neutral);
  border-bottom: 1px solid var(--bd-strong);
  white-space: nowrap;
}
.post-table td {
  padding: 12px 16px; vertical-align: top;
  line-height: 1.55; color: var(--tx-2);
}
.post-table tbody tr + tr td { border-top: 1px solid var(--bd-row); }
/* The first column is the row's subject, so it reads as a label rather than as
   another value competing with the cells beside it. */
.post-table tbody td:first-child { color: var(--tx); font-weight: 600; white-space: nowrap; }

@media (max-width: 620px) {
  /* Below this the three columns genuinely cannot fit, so it scrolls. */
  .post-table { min-width: 460px; }
}
/* -- breadcrumbs --------------------------------------------------------- */
/* The separator is drawn in CSS rather than typed into the markup: a literal
   "/" between the items would be read out by a screen reader as part of the
   link text on every step. */
.crumbs { margin: 0 0 18px; font-size: 13px; }
.crumbs ol {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 8px;
}
.crumbs li { display: flex; align-items: baseline; gap: 8px; color: var(--tx-muted); }
.crumbs li + li::before {
  content: "/";
  color: var(--dot-off);
  font-family: var(--ff-mono);
}
.crumbs a { color: var(--tx-3); }
.crumbs a:hover { color: var(--acc); text-decoration: none; }
/* The page you are on is not a link, and it is the one item allowed to be
   long: it wraps rather than pushing the trail off the side. */
.crumbs [aria-current="page"] { color: var(--tx-muted); }

@media (max-width: 560px) {
  /* On a narrow screen the article title doubles the height of the trail for
     no benefit: the headline is directly beneath it. */
  .crumbs li:last-child { display: none; }
  .crumbs li:nth-last-child(2)::after { content: ""; }
}
