/* ==========================================================================
   NOVATECHRAY — BRAND DESIGN SYSTEM v2.0  "Editorial"
   Rebuilt 2026-09-11 after studying how iPullRank, Siege Media and Minuttia
   actually present. All three run light, editorial, serif-led layouts. The
   dark-ground / electric-accent look this site used is the crowded default in
   the AI-search category, so it has been inverted: light paper is now the
   ground, Signal Blue is retained as the brand mark, and the dark theme is
   kept as a real alternate rather than the primary.

   Ground: Paper #FCFBF9   Ink: #15171B   Accent: Signal Blue #4C6FFF
   Type:   Source Serif 4 (display) / Public Sans (body) / JetBrains Mono (data)

   NOTE ON CONTRAST: #4C6FFF on white is 4.0:1 and fails AA for text, so it is
   the MARK colour only (borders, rules, icons, the logo ray). Text and fills
   use --accent-strong #2C46CC, which measures 7.2:1 on paper.
   ========================================================================== */

:root {
  /* ---- light is the default ground ---- */
  --bg-void: #FCFBF9;
  --bg-void-rgb: 252, 251, 249;
  --bg-panel: #FFFFFF;
  --bg-panel-hover: #F3F3F4;
  --border-line: #E1E1E2;
  --border-line-light: #C6C9CC;

  --text-signal: #15171B;
  --text-muted: #4F545B;
  /* 4.5:1 on both --bg-void and --bg-panel; #767C85 measured 4.07 and failed. */
  /* Solved against the worst composite (rules + grain): 4.57:1. #686E77 was
     4.97 clean but 3.88 through the texture. */
  --text-dim: #646568;

  --accent-blue: #4C6FFF;
  --accent-strong: #2C46CC;
  --accent-strong-hover: #23389F;
  --accent-light: #2C46CC;
  --accent-dim: #DEE4FF;
  --accent-glow: rgba(76, 111, 255, 0.10);
  --accent-on-fill: #FFFFFF;

  --state-pass: #0C6E49;
  --state-pass-bg: rgba(12, 110, 73, 0.07);
  --state-block: #A8322A;
  --state-block-bg: rgba(168, 50, 42, 0.07);
  --state-part: #8A6210;
  --state-part-bg: rgba(138, 98, 16, 0.07);

  /* Editorial pages carry weight with rules, not drop shadows. */
  --shadow-panel: 0 1px 2px rgba(20, 22, 26, 0.04), 0 10px 28px rgba(20, 22, 26, 0.05);
  --shadow-card: 0 1px 2px rgba(20, 22, 26, 0.05);
  --sheen: rgba(20, 22, 26, 0.02);

  --font-display: 'Source Serif 4', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
  --font-body: 'Public Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --container-width: 1180px;
  --measure: 68ch;
  --radius-sm: 3px;
  --radius-md: 4px;
  --radius-lg: 8px;
  --radius-full: 9999px;

  --transition-fast: 0.15s cubic-bezier(0.19, 0.94, 0.25, 1);
  --transition-normal: 0.25s cubic-bezier(0.19, 0.94, 0.25, 1);
  --transition-slow: 0.4s cubic-bezier(0.19, 0.94, 0.25, 1);
}

/* --------------------------------------------------------------------------
   DARK THEME — kept as a real alternate, not an afterthought. Warm-neutral
   dark rather than the old pure void, so the serif does not glare.
   -------------------------------------------------------------------------- */
:root[data-theme="dark"] {
  --bg-void: #14151A;
  --bg-void-rgb: 20, 21, 26;
  --bg-panel: #1C1E24;
  --bg-panel-hover: #23262D;
  --border-line: #2C2F37;
  --border-line-light: #3D424C;

  --text-signal: #F0F0F1;
  --text-muted: #A2A8B2;
  /* 4.5:1 on --bg-panel-hover, the brightest dark surface. #7B828C was 3.90. */
  /* Worst composite through the texture: 4.52:1. */
  --text-dim: #909399;

  --accent-blue: #4C6FFF;
  /* Text and fills need more than the mark colour gives on a dark ground. */
  --accent-strong: #7B95FF;
  --accent-strong-hover: #98ACFF;
  --accent-light: #A8B8FF;
  --accent-dim: #25356E;
  --accent-glow: rgba(76, 111, 255, 0.16);
  --accent-on-fill: #0B1020;

  --state-pass: #52D8A0;
  --state-pass-bg: rgba(82, 216, 160, 0.10);
  --state-block: #FF8674;
  --state-block-bg: rgba(255, 134, 116, 0.10);
  --state-part: #E8B65C;
  --state-part-bg: rgba(232, 182, 92, 0.10);

  --shadow-panel: 0 1px 2px rgba(0, 0, 0, 0.5), 0 14px 34px rgba(0, 0, 0, 0.35);
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.4);
  --sheen: rgba(255, 255, 255, 0.025);
}

::selection {
  background-color: var(--accent-dim);
  color: var(--text-signal);
}

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

html {
  scroll-behavior: smooth;
  font-size: 16px;
  background-color: var(--bg-void);
  color: var(--text-signal);
}

body {
  font-family: var(--font-body);
  background-color: var(--bg-void);
  color: var(--text-signal);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  position: relative;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  color: var(--text-signal);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.011em;
  text-wrap: balance;
}

/* Editorial scale: the old display face ran to 4.25rem, which a serif cannot
   carry without looking like a magazine cover. Topped out lower and tighter. */
h1 { font-size: clamp(2.25rem, 4.6vw, 3.4rem); }
h2 { font-size: clamp(1.7rem, 2.9vw, 2.35rem); }
h3 { font-size: clamp(1.2rem, 1.7vw, 1.5rem); }

p {
  color: var(--text-muted);
  font-size: 1.03rem;
  line-height: 1.7;
}

/* Running text should not sprawl. Opt out with .measure-none where a grid cell
   is already narrow. */
.section-desc, .hero-subhead, .post-body p { max-width: var(--measure); }

a {
  color: inherit;
  text-decoration: none;
  transition: color var(--transition-fast);
}

ul { list-style: none; }
img, svg, canvas { display: block; max-width: 100%; }

::selection {
  background-color: var(--accent-dim);
  color: var(--text-signal);
}

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

.container {
  width: 100%;
  max-width: var(--container-width);
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* --------------------------------------------------------------------------
   HEADER & NAVIGATION
   -------------------------------------------------------------------------- */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 1000;
  background-color: rgba(var(--bg-void-rgb), 0.88);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--border-line);
  transition: border-color var(--transition-normal);
}

/* Set by app.js on scroll. Was an inline style written on every scroll event. */
.site-header.is-scrolled { border-bottom-color: var(--border-line-light); }

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 72px;
}

.brand-logo {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.25rem;
}

.brand-logo-mark {
  width: 32px;
  height: 32px;
}

.brand-logo-text { color: var(--text-signal); }
.brand-logo-text .accent-ray { color: var(--accent-blue); }

.nav-links {
  display: flex;
  align-items: center;
  gap: 2rem;
}

.nav-link {
  font-size: 0.925rem;
  font-weight: 500;
  color: var(--text-muted);
  transition: color var(--transition-fast);
  /* Target size: the text box alone measured 24px, exactly on the WCAG 2.2
     minimum. Padding gives a comfortable hit area inside the 72px header. */
  display: inline-flex;
  align-items: center;
  min-height: 40px;
}

.nav-link:hover, .nav-link.active {
  color: var(--text-signal);
}

.header-cta {
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* The drawer CTA is a mobile-only duplicate of .header-cta's button. It is
   revealed in the max-width:768px block, where the header button is hidden. */
.nav-links .btn { display: none; }

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--border-line);
  color: var(--text-signal);
  width: 42px; height: 42px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  align-items: center;
  justify-content: center;
}

/* --------------------------------------------------------------------------
   BUTTONS & COMPONENTS
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 600;
  padding: 0.75rem 1.5rem;
  border-radius: var(--radius-sm);
  transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
  cursor: pointer;
  border: 1px solid transparent;
  white-space: nowrap;
}

.btn-primary {
  background-color: var(--accent-strong);
  color: var(--accent-on-fill);
  border-color: var(--accent-strong);
}

.btn-primary:hover {
  background-color: var(--accent-strong-hover);
  border-color: var(--accent-strong-hover);
}

/* Outline, not a filled second button. A page gets one solid CTA. */
.btn-secondary {
  background-color: transparent;
  color: var(--text-signal);
  border-color: var(--border-line-light);
}

.btn-secondary:hover {
  background-color: var(--bg-panel-hover);
  border-color: var(--text-signal);
  color: var(--text-signal);
}

.btn-sm { padding: 0.5rem 1rem; font-size: 0.875rem; }
.btn-lg { padding: 1rem 2rem; font-size: 1.05rem; }

.eyebrow-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent-strong);
  padding: 0;
  margin-bottom: 1.1rem;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
}

.eyebrow-tag::before {
  content: '';
  width: 22px;
  height: 1px;
  background-color: var(--accent-blue);
}

/* --------------------------------------------------------------------------
   3D SPATIAL PANEL CARDS
   -------------------------------------------------------------------------- */
.panel-card {
  background-color: var(--bg-panel);
  border: 1px solid var(--border-line);
  border-radius: var(--radius-md);
  padding: 2rem;
  position: relative;
  transition: border-color 0.25s ease;
}

.panel-card:hover { border-color: var(--border-line-light); }

.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2rem; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; }

.section {
  padding: 7.5rem 0;
  position: relative;
  border-bottom: 1px solid var(--border-line);
}

.section-header {
  max-width: 720px;
  margin-bottom: 3.5rem;
}

.section-header.center {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

.section-title { margin-bottom: 1rem; }
.section-desc { font-size: 1.15rem; color: var(--text-muted); }

/* --------------------------------------------------------------------------
   HERO SECTION & 3D CANVAS
   -------------------------------------------------------------------------- */
.hero-section {
  padding-top: 9rem;
  padding-bottom: 5rem;
  display: flex;
  align-items: center;
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid var(--border-line);
}

.hero-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3.5rem;
  align-items: center;
}

.hero-headline {
  font-size: clamp(2.4rem, 4.8vw, 3.65rem);
  line-height: 1.08;
  margin-bottom: 1.4rem;
  letter-spacing: -0.014em;
}

.hero-subhead {
  font-size: 1.12rem;
  line-height: 1.66;
  color: var(--text-muted);
  margin-bottom: 2rem;
  max-width: 56ch;
}

.hero-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}

/* A bare wrapper. It used to be a 500px panel framing the node canvas; the
   fetch card is now the object itself and draws its own chrome. */
.hero-visual {
  position: relative;
  width: 100%;
}

/* --------------------------------------------------------------------------
   INTERACTIVE 3D GEO CALCULATOR
   -------------------------------------------------------------------------- */
.geo-simulator-card {
  background-color: var(--bg-panel);
  border: 1px solid var(--border-line);
  border-radius: var(--radius-lg);
  padding: 2.5rem;
  box-shadow: var(--shadow-card);
}

.sim-query-bar {
  display: flex;
  gap: 0.75rem;
  margin-bottom: 2rem;
}

.sim-input-wrapper {
  flex: 1;
  position: relative;
  display: flex;
  align-items: center;
}

.sim-query-input {
  width: 100%;
  background-color: var(--bg-void);
  border: 1px solid var(--border-line);
  color: var(--text-signal);
  font-family: var(--font-mono);
  font-size: 0.95rem;
  padding: 0.85rem 1rem 0.85rem 2.75rem;
  border-radius: var(--radius-sm);
  transition: border-color var(--transition-fast);
}

.sim-query-input:focus-visible {
  border-color: var(--accent-blue);
  outline: 2px solid var(--accent-blue);
  outline-offset: 2px;   /* 2 not the global 4, so the ring stays inside the card */
}
.sim-query-input:focus { border-color: var(--accent-blue); }

.sim-search-icon {
  position: absolute;
  left: 1rem;
  color: var(--text-dim);
  font-size: 0.9rem;
}

.preset-queries {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 2.5rem;
  flex-wrap: wrap;
}

.preset-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-right: 0.5rem;
}

.preset-btn {
  background-color: var(--bg-void);
  border: 1px solid var(--border-line);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  padding: 0.35rem 0.75rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
}

.preset-btn:hover, .preset-btn.active {
  border-color: var(--accent-blue);
  color: var(--text-signal);
  background-color: rgba(76, 111, 255, 0.08);
}

.sim-results-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
}

.check-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.85rem; }

.check-item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.75rem;
  align-items: start;
  padding-bottom: 0.85rem;
  border-bottom: 1px solid var(--border-line);
}

.check-item:last-child { border-bottom: 0; padding-bottom: 0; }

.check-state {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.25rem 0.5rem;
  border: 1px solid currentColor;
  border-radius: 3px;
  white-space: nowrap;
  min-width: 5.4rem;
  text-align: center;
  transition: color 0.35s ease, background-color 0.35s ease;
}

.check-state[data-state="pass"]    { color: var(--state-pass);  background-color: var(--state-pass-bg); }
.check-state[data-state="blocked"] { color: var(--state-block); background-color: var(--state-block-bg); }
.check-state[data-state="partial"] { color: var(--state-part);  background-color: var(--state-part-bg); }

.check-body { display: grid; gap: 0.2rem; min-width: 0; }

.check-name {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--text-signal);
  line-height: 1.4;
}

.check-note { font-size: 0.82rem; color: var(--text-muted); line-height: 1.5; }

@media (max-width: 460px) {
  .check-item { grid-template-columns: 1fr; gap: 0.4rem; }
  .check-state { justify-self: start; }
}

.llm-output-box {
  background-color: var(--bg-void);
  border: 1px solid var(--border-line);
  border-radius: var(--radius-md);
  padding: 1.5rem;
  height: 100%;
}

.llm-box-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--border-line);
  padding-bottom: 0.75rem;
  margin-bottom: 1rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--text-muted);
}

.llm-response-text {
  font-size: 0.95rem;
  line-height: 1.7;
  color: var(--text-signal);
}

.llm-highlight {
  color: var(--accent-light);
  font-weight: 600;
  background-color: rgba(76, 111, 255, 0.12);
  padding: 0.1em 0.3em;
  border-radius: 2px;
}

/* --------------------------------------------------------------------------
   PROCESS & METHODOLOGY
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   COMPARE — the SEO / GEO contrast, as a spec sheet
   Was two boxed cards of ticks and crosses with an accent ring on the winning
   column, which is the canonical landing-page ad block. Recut so each row is
   one axis answered twice: the reader compares like with like, and the page
   stops telling them which column to prefer.
   -------------------------------------------------------------------------- */
.compare { border-top: 1px solid var(--border-line); margin: 0; }

.compare-row {
  display: grid;
  grid-template-columns: 9.5rem 1fr 1fr;
  gap: 0 2.5rem;
  align-items: start;
  padding: 1.45rem 0;
  border-bottom: 1px solid var(--border-line);
}

.compare-row dt, .compare-row dd { margin: 0; }
.compare-row dd { margin: 0; font-size: 0.95rem; line-height: 1.55; color: var(--text-muted); }

.compare-axis {
  font-family: var(--font-mono);
  font-size: 0.71rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding-top: 0.22rem;
}

.compare-head {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 600;
  color: var(--text-signal);
  line-height: 1.3;
}

/* The column heads sit outside the <dl>: they are labels for the layout, not
   terms with descriptions, and each dd already names its own column for screen
   readers and for the stacked layout below. */
.compare-heads {
  display: grid;
  grid-template-columns: 9.5rem 1fr 1fr;
  gap: 0 2.5rem;
  padding-bottom: 0.85rem;
}

@media (max-width: 860px) {
  .compare-row { grid-template-columns: 1fr; gap: 0.5rem; }
  .compare-row dd::before {
    content: attr(data-col);
    display: block;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 0.2rem;
  }
  .compare-heads { display: none; }
  .compare-axis { padding-top: 0; }
}

/* --------------------------------------------------------------------------
   STAGE INDEX — the four stages of the engagement
   Same decision as .cat-index: this was a 4-up grid of boxed cards opening with
   a 36px accent numeral, which is the most recognisable AI-template pattern
   there is, and it sat two screens below a section that had already rejected it.
   The numerals stay because the stages are a real sequence; they drop to the
   mono scale the rest of the site numbers things at.
   -------------------------------------------------------------------------- */
.stage-index { border-top: 1px solid var(--border-line); }

.stage-row {
  display: grid;
  grid-template-columns: 3rem 1fr;
  gap: 0 1.5rem;
  padding: 1.7rem 0;
  border-bottom: 1px solid var(--border-line);
}

.stage-num {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  color: var(--accent-strong);
  padding-top: 0.42rem;
  font-variant-numeric: tabular-nums;
}

.stage-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--text-signal);
  line-height: 1.25;
}

.stage-desc {
  grid-column: 2;
  margin: 0.4rem 0 0;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 68ch;
}

@media (max-width: 640px) {
  .stage-row { grid-template-columns: 1fr; gap: 0.3rem; }
  .stage-desc { grid-column: 1; }
  .stage-num { padding-top: 0; }
}

/* --------------------------------------------------------------------------
   FAQ ACCORDION
   -------------------------------------------------------------------------- */
.faq-list { display: flex; flex-direction: column; gap: 1rem; }

.faq-item {
  background-color: var(--bg-panel);
  border: 1px solid var(--border-line);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: border-color var(--transition-fast);
}

.faq-item.active { border-color: var(--border-line-light); }

.faq-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.5rem 1.75rem;
  background: none;
  border: none;
  color: var(--text-signal);
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.faq-icon {
  font-family: var(--font-mono);
  font-size: 1.25rem;
  color: var(--accent-blue);
  transition: transform var(--transition-fast);
}

/* The +/- affordance is a lie without JS: nothing would happen if it were
   pressed, and every answer is already open. */
.faq-icon { display: none; }
html.js-faq .faq-icon { display: inline; }
html.js-faq .faq-item.active .faq-icon { transform: rotate(45deg); }

/* Open at rest. Everything that collapses an answer is gated on `js-faq`, a
   class js/app.js adds only when it has wired the triggers up. With JS off the
   answers render in full and the page is complete -- which is the one claim
   this site cannot afford to get wrong on its own FAQ page. Gated on js-faq
   rather than js-motion because motion.js is deferred and app.js is not, so
   js-motion can still be absent while the accordion is already live. */
.faq-content { padding: 0 1.75rem 1.75rem 1.75rem; }

html.js-faq .faq-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.35s var(--ease-out), padding 0.35s var(--ease-out);
  padding: 0 1.75rem;
}

html.js-faq .faq-item:not(.active) .faq-content { visibility: hidden; }

html.js-faq .faq-item.active .faq-content {
  /* Ceiling, not a height. 400px clipped the longest answer (483 chars) at
     360px wide; nothing on the site comes close to 60rem. */
  max-height: 60rem;
  padding: 0 1.75rem 1.75rem 1.75rem;
}

/* --------------------------------------------------------------------------
   FOOTER
   -------------------------------------------------------------------------- */
.site-footer {
  background-color: var(--bg-void);
  border-top: 1px solid var(--border-line);
  padding: 5rem 0 3rem 0;
}

.footer-grid {
  display: grid;
  grid-template-columns: 2fr repeat(3, 1fr);
  gap: 3rem;
  margin-bottom: 4rem;
}

.footer-brand p { margin-top: 1rem; max-width: 320px; font-size: 0.95rem; }

.footer-title {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-signal);
  margin-bottom: 1.25rem;
}

.footer-links { display: flex; flex-direction: column; gap: 0.35rem; }
.footer-link {
  font-size: 0.9rem;
  color: var(--text-muted);
  /* Was 23px tall, under the 24px minimum. Gap reduced so the column keeps
     the same overall rhythm. */
  display: inline-flex;
  align-items: center;
  min-height: 28px;
}
.footer-link:hover { color: var(--text-signal); }

.footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-top: 1px solid var(--border-line);
  padding-top: 2rem;
  font-size: 0.85rem;
  color: var(--text-dim);
}

/* --------------------------------------------------------------------------
   RESPONSIVE BREAKPOINTS
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  .hero-grid { grid-template-columns: 1fr; gap: 3rem; }
  .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .footer-grid { grid-template-columns: 1fr 1fr; gap: 2.5rem; }
}

@media (max-width: 768px) {
  .section { padding: 5rem 0; }
  .nav-links {
    position: fixed;
    top: 72px; left: 0; right: 0;
    background-color: var(--bg-void);
    border-bottom: 1px solid var(--border-line);
    flex-direction: column;
    padding: 2rem; gap: 1.5rem;
    transform: translateY(-120%);
    transition: transform var(--transition-normal);
    z-index: 999;
  }
  .nav-links.open { transform: translateY(0); }
  .nav-toggle { display: flex; }

  /* The header CTA does not fit beside the wordmark and the burger at 375px.
     It moves into the drawer instead of being dropped, so mobile keeps the
     primary action. */
  .header-cta .btn { display: none; }
  .nav-links .btn {
    display: inline-flex;
    width: 100%;
    justify-content: center;
    margin-top: 0.5rem;
  }

  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .sim-results-grid { grid-template-columns: 1fr; }
  .form-row { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; }
  .footer-bottom { flex-direction: column; gap: 1rem; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* --------------------------------------------------------------------------
   SKIP LINK
   -------------------------------------------------------------------------- */
.skip-link {
  position: absolute;
  left: 1rem;
  top: -100px;
  z-index: 2000;
  background-color: var(--bg-panel);
  color: var(--text-signal);
  border: 1px solid var(--accent-blue);
  border-radius: var(--radius-sm);
  padding: 0.75rem 1.25rem;
  font-size: 0.9rem;
  font-weight: 600;
  transition: top var(--transition-fast);
}

.skip-link:focus,
.skip-link:focus-visible { top: 1rem; }

/* --------------------------------------------------------------------------
   THEME TOGGLE (fixed, bottom right)
   -------------------------------------------------------------------------- */
.theme-toggle {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 1200;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-line-light);
  background-color: rgba(var(--bg-void-rgb), 0.82);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--text-signal);
  cursor: pointer;
  box-shadow: var(--shadow-card);
  transition: border-color var(--transition-fast), transform var(--transition-fast);
}

.theme-toggle:hover {
  border-color: var(--accent-blue);
  transform: translateY(-2px);
}

.theme-toggle svg { width: 19px; height: 19px; }

/* Show exactly one glyph: the moon offers dark, the sun offers light. */
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: block; }
:root[data-theme="light"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="light"] .theme-toggle .icon-moon { display: block; }
/* Light is the default ground, so an unstamped root shows the moon (offering dark). */
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }

/* --------------------------------------------------------------------------
   BOOKING (Calendly) FRAME
   The embed is wrapped so it can never set the page width or corner radius.
   -------------------------------------------------------------------------- */
.booking-frame {
  /* Calendly renders the iframe on its own light ground and reads its colours
     once at init, so it cannot follow the theme toggle. Rather than leak white
     onto a dark page, the frame keeps a paper background in both themes and the
     embed reads as a printed booking card sitting on the page. */
  background-color: #FCFBF9;
  border: 1px solid var(--border-line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  min-height: 700px;
}

.booking-frame .calendly-inline-widget {
  min-width: 280px;
  width: 100%;
  height: 700px;
}

.booking-fallback {
  padding: 2rem;
  color: var(--text-muted);
  font-size: 0.95rem;
}

.booking-fallback a { color: var(--accent-light); text-decoration: underline; }

.booking-aside li {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  color: var(--text-muted);
  font-size: 0.95rem;
}

.booking-aside .step-dot {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  margin-top: 0.1rem;
  border-radius: var(--radius-full);
  background-color: rgba(76, 111, 255, 0.12);
  border: 1px solid var(--accent-blue);
  color: var(--accent-light);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

@media (max-width: 768px) {
  .booking-frame, .booking-frame .calendly-inline-widget { min-height: 640px; height: 640px; }
  .theme-toggle { right: 14px; bottom: 14px; }
}


/* --------------------------------------------------------------------------
   INLINE ICONS
   Replaced Font Awesome. Nine glyphs did not justify ~100KB of CDN stylesheet
   plus a webfont, a third-party CSP origin, and boxes until the font loaded.
   -------------------------------------------------------------------------- */
.ic {
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
  display: inline-block;
  vertical-align: -0.125em;
  color: inherit;
}

.nav-toggle .ic { width: 20px; height: 20px; }
.panel-card .ic, .service-card .ic { width: 1.5rem; height: 1.5rem; }


/* --------------------------------------------------------------------------
   SUNK SECTION
   Was an inline background-color: #0D0D10 on three sections. A literal cannot
   follow the theme, so in light mode those bands stayed near-black while their
   text turned dark. This is the same one-step-recessed surface, per theme.
   -------------------------------------------------------------------------- */
.section--sunk { background-color: rgba(var(--sunk-rgb), 0.62); }
:root[data-theme="dark"] .section--sunk { background-color: rgba(var(--sunk-rgb), 0.55); }


.sim-disclaimer {
  margin-top: 1.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border-line);
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--text-dim);
  max-width: 60rem;
}

/* ---------------------------------------------------------------
   Evidence page
   --------------------------------------------------------------- */
.ev-tier { margin-bottom: 4rem; }
.ev-tier:last-child { margin-bottom: 0; }

.ev-tier-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.85rem;
  padding-bottom: 0.9rem;
  margin-bottom: 1.75rem;
  border-bottom: 1px solid var(--border-line-light);
}

.ev-tier-head h2 { font-size: 1.5rem; margin: 0; }
.ev-tier-head p { margin: 0; font-size: 0.92rem; color: var(--text-muted); flex-basis: 100%; }

.ev-chip {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.25rem 0.55rem;
  border: 1px solid currentColor;
  border-radius: 3px;
  white-space: nowrap;
}

.ev-chip[data-tier="measured"]      { color: var(--state-pass);  background-color: var(--state-pass-bg); }
.ev-chip[data-tier="correlational"] { color: var(--state-part);  background-color: var(--state-part-bg); }
.ev-chip[data-tier="debunked"]      { color: var(--state-block); background-color: var(--state-block-bg); }

.ev-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.5rem; }

.ev-item {
  display: grid;
  grid-template-columns: 2.25rem 1fr;
  gap: 1.25rem;
  align-items: start;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--border-line);
}

.ev-item:last-child { border-bottom: 0; padding-bottom: 0; }

.ev-num {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--accent-strong);
  padding-top: 0.15rem;
  font-variant-numeric: tabular-nums;
}

.ev-body { min-width: 0; }
.ev-claim { font-size: 1.05rem; font-weight: 600; color: var(--text-signal); margin: 0 0 0.5rem; line-height: 1.4; }
.ev-claim code { font-family: var(--font-mono); font-size: 0.9em; }
.ev-body p { margin: 0 0 0.65rem; font-size: 0.95rem; line-height: 1.65; }
.ev-body p:last-child { margin-bottom: 0; }

.ev-src {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-dim);
  display: block;
}

.ev-src a { color: var(--accent-light); text-underline-offset: 2px; }

.ev-note {
  border-left: 2px solid var(--accent-blue);
  padding: 0.85rem 0 0.85rem 1.1rem;
  margin: 1.5rem 0 0;
  font-size: 0.95rem;
  color: var(--text-muted);
  line-height: 1.65;
}

@media (max-width: 560px) {
  .ev-item { grid-template-columns: 1fr; gap: 0.5rem; }
  .ev-num { padding-top: 0; }
}

/* ==========================================================================
   v2 — DROPDOWN NAVIGATION
   The IA now has a services hub with children and four industry pages, which
   does not fit a flat nav. Hover opens on pointer devices; click/Enter opens
   on touch and keyboard, so the parent is still reachable without a pointer.
   ========================================================================== */
.nav-item { position: relative; display: flex; align-items: center; }

.nav-parent {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 0.93rem;
  font-weight: 500;
  color: var(--text-muted);
  padding: 0;
  transition: color var(--transition-fast);
}

.nav-parent:hover, .nav-parent[aria-expanded="true"], .nav-item.active .nav-parent {
  color: var(--text-signal);
}

.nav-parent .caret {
  width: 9px; height: 9px;
  transition: transform var(--transition-fast);
  flex-shrink: 0;
}

.nav-parent[aria-expanded="true"] .caret { transform: rotate(180deg); }

.nav-menu {
  position: absolute;
  top: calc(100% + 0.85rem);
  left: -1rem;
  min-width: 310px;
  background-color: var(--bg-panel);
  border: 1px solid var(--border-line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-panel);
  padding: 0.5rem;
  display: grid;
  gap: 1px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--transition-fast), transform var(--transition-fast), visibility var(--transition-fast);
  z-index: 1100;
}

.nav-item:hover .nav-menu,
.nav-item:focus-within .nav-menu,
.nav-menu.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* A hover bridge, so the pointer can cross the gap without the menu closing. */
.nav-item::after {
  content: '';
  position: absolute;
  top: 100%; left: 0; right: 0;
  height: 0.9rem;
}

.nav-menu a {
  display: grid;
  gap: 0.15rem;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radius-sm);
  transition: background-color var(--transition-fast);
}

.nav-menu a:hover, .nav-menu a:focus-visible { background-color: var(--bg-panel-hover); }

.nav-menu .nm-title {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-signal);
  line-height: 1.3;
}

.nav-menu .nm-desc {
  font-size: 0.78rem;
  color: var(--text-dim);
  line-height: 1.45;
}

.nav-menu .nm-sep {
  height: 1px;
  background-color: var(--border-line);
  margin: 0.35rem 0.5rem;
}

@media (max-width: 1024px) {
  .nav-menu {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    box-shadow: none;
    border: 0;
    border-left: 1px solid var(--border-line);
    border-radius: 0;
    min-width: 0;
    margin: 0.35rem 0 0.6rem 0.4rem;
    padding: 0 0 0 0.75rem;
    display: none;
  }
  .nav-menu.is-open { display: grid; }
  .nav-item { display: block; width: 100%; }
  .nav-item::after { content: none; }
  .nav-parent { width: 100%; justify-content: space-between; font-size: 1.05rem; padding: 0.35rem 0; }
  .nav-menu a { padding: 0.45rem 0; }
}

/* ==========================================================================
   v2 — EDITORIAL PAGE COMPONENTS (service, industry and teardown pages)
   ========================================================================== */
.prose-col { max-width: 720px; }
.prose-col > * + * { margin-top: 1.35rem; }
.prose-col p { max-width: var(--measure); }
.prose-col strong { color: var(--text-signal); font-weight: 600; }

.prose-col h2 {
  margin-top: 3rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--border-line);
}

.prose-col > h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.prose-col h3 { margin-top: 2rem; }

.prose-col a {
  color: var(--accent-strong);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.prose-col a:hover { color: var(--accent-strong-hover); }
.prose-col code { font-family: var(--font-mono); font-size: 0.88em; background-color: var(--bg-panel-hover); padding: 0.1em 0.35em; border-radius: var(--radius-sm); }

/* The paragraph a retrieval step stores. Sits high on the page deliberately. */
.answer-lead {
  max-width: 760px;
  border-left: 2px solid var(--accent-blue);
  padding: 0.25rem 0 0.25rem 1.5rem;
}

.answer-lead-label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-strong);
  margin-bottom: 0.6rem;
}

.answer-lead-body {
  font-size: 1.14rem;
  line-height: 1.62;
  color: var(--text-signal);
  max-width: none;
}

/* Attribute / spec lists — the filterable facts a query constrains on. */
.spec-list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; max-width: var(--measure); }

.spec-list li {
  /* Deliberately NOT display:grid. These items are `<strong>Lead.</strong> then
     a run of bare text`, and an anonymous text node cannot be assigned a grid
     column — it wraps into the 1.5rem counter track and renders as an 85px
     ribbon. Absolute-positioning the counter keeps the numbering and leaves
     the item as ordinary flowing text. */
  position: relative;
  padding: 0.7rem 0 0.7rem 2.25rem;
  border-bottom: 1px solid var(--border-line);
  font-size: 0.97rem;
  color: var(--text-muted);
  line-height: 1.55;
}

.spec-list li:last-child { border-bottom: 0; }
.spec-list li::before {
  content: counter(spec, decimal-leading-zero);
  counter-increment: spec;
  position: absolute;
  left: 0;
  top: 0.85rem;          /* 0.7rem padding + the optical nudge the mono face needs */
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent-strong);
}
.spec-list { counter-reset: spec; }
.spec-list strong { color: var(--text-signal); }

/* Cross-links to the four service pages. */
.svc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(248px, 1fr)); gap: 1px; background-color: var(--border-line); border: 1px solid var(--border-line); border-radius: var(--radius-md); overflow: hidden; }

.svc-grid a {
  display: grid;
  gap: 0.35rem;
  align-content: start;
  padding: 1.4rem;
  background-color: var(--bg-panel);
  transition: background-color var(--transition-fast);
  text-decoration: none;
}

.svc-grid a:hover { background-color: var(--bg-panel-hover); }
.svc-grid .sg-num { font-family: var(--font-mono); font-size: 0.72rem; color: var(--accent-strong); letter-spacing: 0.1em; }
.svc-grid .sg-title { font-family: var(--font-display); font-size: 1.08rem; font-weight: 600; color: var(--text-signal); line-height: 1.25; }
.svc-grid .sg-desc { font-size: 0.86rem; color: var(--text-muted); line-height: 1.5; }

/* Related reading strip */
.read-next { display: grid; gap: 0.6rem; max-width: var(--measure); }
.read-next a { display: flex; gap: 0.75rem; align-items: baseline; padding: 0.7rem 0; border-bottom: 1px solid var(--border-line); text-decoration: none; }
.read-next a:last-child { border-bottom: 0; }
.read-next .rn-kicker { font-family: var(--font-mono); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-dim); white-space: nowrap; }
.read-next .rn-title { color: var(--accent-strong); text-decoration: underline; text-underline-offset: 2px; font-size: 0.97rem; }
.read-next a:hover .rn-title { color: var(--accent-strong-hover); }


/* ---- Teardown evidence table ---- */
.table-scroll { overflow-x: auto; border: 1px solid var(--border-line); border-radius: var(--radius-md); margin: 1.5rem 0; }
.td-table { border-collapse: collapse; width: 100%; font-size: 0.88rem; min-width: 680px; }
.td-table th {
  text-align: left; padding: 0.8rem 1rem; background-color: var(--bg-panel-hover);
  border-bottom: 1px solid var(--border-line); font-family: var(--font-mono);
  font-size: 0.68rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-muted); font-weight: 500; white-space: nowrap;
}
.td-table td { padding: 0.8rem 1rem; border-bottom: 1px solid var(--border-line); vertical-align: top; color: var(--text-muted); }
.td-table tr:last-child td { border-bottom: 0; }
.td-table strong { color: var(--text-signal); }
.td-dom { font-family: var(--font-mono); font-size: 0.72rem; color: var(--text-dim); }
.td-yes  { color: var(--state-pass); font-weight: 600; }
.td-no   { color: var(--state-block); font-weight: 600; }
.td-part { color: var(--state-part); font-weight: 600; }
.td-note { font-size: 0.85rem; color: var(--text-dim); line-height: 1.6; }

/* Clickable category cards on the homepage */
a.panel-card { display: block; text-decoration: none; }
a.panel-card:hover { border-color: var(--accent-blue); }
.card-more {
  display: inline-block;
  margin-top: 0.9rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-strong);
}
a.panel-card:hover .card-more { text-decoration: underline; text-underline-offset: 3px; }

/* Visible breadcrumbs on child pages. Two of the five agencies studied render
   these; all five emit BreadcrumbList. They also give the parent hubs the
   in-body inbound links the old site never had. */
.crumbs-bar { font-family: var(--font-mono); font-size: 0.74rem; letter-spacing: 0.05em; color: var(--text-dim); margin-bottom: 1.4rem; }
.crumbs-bar a { color: var(--text-muted); text-decoration: none; }
.crumbs-bar a:hover { color: var(--accent-strong); text-decoration: underline; text-underline-offset: 3px; }
.crumbs-bar span[aria-hidden] { opacity: 0.45; padding: 0 0.4rem; }

/* ==========================================================================
   v3 — TEXTURE & MOTION
   --------------------------------------------------------------------------
   MOTION CONTRACT, and it is not negotiable on this site of all sites:
   this agency sells "your page must be readable without JavaScript". So every
   reveal here is ADDITIVE. Content is visible at rest; the `.js-motion` class
   that JS puts on <html> is what opts a page into animating. No JS, no class,
   no hidden content. Nothing is ever parked at opacity:0 waiting for a script.
   ========================================================================== */
:root {
  /* Durations. Stripe Press — the best-executed editorial site checked during
     research — animates only opacity, between 0.2s and 0.5s. That is the range. */
  --dur-fast: 160ms;
  --dur-base: 260ms;
  --dur-slow: 480ms;
  --dur-reveal: 620ms;

  /* Easing. A long, slow tail is what reads as expensive; a symmetric ease
     reads as a default. --ease-out is used for anything entering. */
  --ease-out: cubic-bezier(0.17, 0.89, 0.32, 1);
  --ease-in-out: cubic-bezier(0.62, 0.03, 0.31, 1);

  --reveal-shift: 14px;   /* travel distance. More than ~18px reads as a slide */
  --reveal-stagger: 70ms; /* between siblings */

  /* The sunk band's ground. Grain opacity lives in the SVG filter's feFlood
     (js/motion.js), not in a CSS token -- a filter cannot read one. */
  --sunk-rgb: 232, 232, 233;
}

:root[data-theme="dark"] {
  --sunk-rgb: 12, 13, 17;
}

/* --------------------------------------------------------------------------
   GRAIN
   Synthesised, not loaded. One SVG filter, injected by motion.js, applied to an
   empty fixed layer. Static by design: grain is a material property, not an
   event, and animating a full-viewport layer costs a repaint per frame for no
   legibility gain.
   -------------------------------------------------------------------------- */
.grain {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  filter: url('#ntr-grain');
}

:root[data-theme="dark"] .grain { filter: url('#ntr-grain-dark'); }


/* Content sits above the grain (z 1) and above the ruled backdrop (z 0).
   `position` is deliberately NOT set here: .site-header is fixed at :193 and
   .theme-toggle is fixed at :899, and an earlier version of this line flattened
   both to `relative`, which unstuck the nav and parked the theme toggle below
   the footer. Only the two in-flow landmarks need a position to take a z-index. */
main, .site-footer { position: relative; z-index: 2; }
.site-header { z-index: 1000; }
.theme-toggle { z-index: 1200; }

/* --------------------------------------------------------------------------
   SCROLL REVEAL — additive only
   -------------------------------------------------------------------------- */
.js-motion [data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--reveal-shift), 0);
  transition: opacity var(--dur-reveal) var(--ease-out),
              transform var(--dur-reveal) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}

.js-motion [data-reveal].is-in {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  will-change: auto;
}

/* --------------------------------------------------------------------------
   MICRO-INTERACTIONS
   -------------------------------------------------------------------------- */
.prose-col a, .ev-src a, .footer-link, .nav-link {
  transition: color var(--dur-fast) var(--ease-out);
}

.btn { transition: background-color var(--dur-fast) var(--ease-out),
                   border-color var(--dur-fast) var(--ease-out),
                   color var(--dur-fast) var(--ease-out); }
.btn:active { transform: translateY(1px); }

.svc-grid a, .panel-card, .check-item, .ev-item {
  transition: background-color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}

/* --------------------------------------------------------------------------
   REDUCED MOTION
   Everything that moves stops. The grain and the ruling stay: they are texture,
   not motion. Nothing becomes invisible.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .js-motion [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .btn:active { transform: none; }
}

/* ==========================================================================
   v3 — THE ANIMATED BACKGROUND
   --------------------------------------------------------------------------
   The concept, and the reason it is not a particle field: this business exists
   because a machine reads a page line by line and keeps about 200 characters of
   it. So the ground is a sheet of ruled paper, and something is slowly reading
   down it. Every element here is literal to the subject rather than decorative.

   Three stacked layers, all fixed, all pointer-events:none, all cheap:
     .grain       feTurbulence filter  -> paper tooth            (~600 B, static)
     .rules       repeating-gradient   -> the ruled lines        (0 bytes)
     .scan        one grid-locked band -> the reading head       (0 bytes)

   Cost: two gradients and one transform. No canvas, no JS loop, no particles.
   ========================================================================== */
:root {
  --rule-gap: 30px;
  --rule-ink: 21, 23, 27;
  --rule-alpha: 0.052;
  --scan-alpha: 0.075;
  --head-steps: 56;
  --head-step: 0.9s;
}

:root[data-theme="dark"] {
  --rule-ink: 226, 228, 232;
  --rule-alpha: 0.05;
  --scan-alpha: 0.11;
}

/* Both layers are fixed viewport decoration. `position` and `pointer-events`
   are load-bearing: without them these are two trailing divs in normal flow
   below the footer, and .scan's transform then extends scrollable overflow by
   30px every 0.9s forever. */
.rules,
.scan {
  position: fixed;
  z-index: 0;
  pointer-events: none;
}

/* The ruled sheet. A hairline every --rule-gap. Sized in px, not em, so the
   rhythm stays constant while type scales. */
.rules {
  inset: 0;
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(var(--rule-ink), var(--rule-alpha)) 0,
    rgba(var(--rule-ink), var(--rule-alpha)) 0.5px,
    transparent 0.5px,
    transparent var(--rule-gap)
  );
  /* Fade at the top and bottom of the viewport. A hard-stopped rule field reads
     as a background image; a faded one reads as stock the page is printed on. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 7vh, #000 92%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 7vh, #000 92%, transparent 100%);
  /* Hairlines alias to uneven grey at fractional DPR; promoting the layer pins
     them to the pixel grid. */
  transform: translateZ(0);
}

/* The read head: one wide, very soft band travelling down the page. 28s is slow
   enough to read as ambient rather than as a loading indicator. */
.scan {
  left: 0;
  right: 0;
  top: 0;
  height: 42vh;
  background-image: linear-gradient(
    to bottom,
    transparent 0%,
    rgba(var(--rule-ink), var(--scan-alpha)) 45%,
    rgba(var(--rule-ink), var(--scan-alpha)) 55%,
    transparent 100%
  );
  /* Horizontal falloff at both margins so it reads as a beam rather than a
     full-bleed bar. The middle 48% is clear, which is where body copy sits. */
  -webkit-mask-image: linear-gradient(to right, #000 0, transparent 26%, transparent 74%, #000 100%);
          mask-image: linear-gradient(to right, #000 0, transparent 26%, transparent 74%, #000 100%);
  opacity: 0;
  will-change: transform;
}

/* Travel is an exact integer multiple of --rule-gap, so every step lands ON a
   rule. If the two ever drift out of integer alignment the head lands between
   rules and instantly looks broken. 56 steps x 30px = 1680px, 56 x 0.9s = 50.4s. */
@keyframes scan-read {
  from { transform: translate3d(0, -42vh, 0); }
  to   { transform: translate3d(0, calc(-42vh + var(--rule-gap) * var(--head-steps)), 0); }
}

.js-motion .scan {
  animation: scan-read calc(var(--head-step) * var(--head-steps)) steps(var(--head-steps), end) infinite;
  will-change: transform;
}

/* Without JS the band would sit frozen across the page, which reads as a
   rendering fault rather than as texture. It only appears once JS confirms it
   can move, and only on a pointer device with a real viewport. */
@media (min-width: 1024px) and (pointer: fine) {
  .js-motion .scan { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .js-motion .scan { animation: none; opacity: 0; }
}

/* The header's backdrop-filter is declared once, at :197, with its -webkit-
   prefix. It used to be restated here, which did nothing except make the
   header a containing block twice over. */

/* --------------------------------------------------------------------------
   HERO: the fetch card
   What an answering crawler actually receives, with the extractable facts
   marked. Replaces a decorative particle canvas that illustrated nothing.
   -------------------------------------------------------------------------- */
.fetch-card {
  margin: 0;
  background-color: var(--bg-panel);
  border: 1px solid var(--border-line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.fetch-head,
.fetch-foot {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.75rem 1.1rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  background-color: var(--bg-panel-hover);
}

.fetch-head { border-bottom: 1px solid var(--border-line); }
.fetch-foot { border-top: 1px solid var(--border-line); justify-content: space-between; }
.fetch-count strong { color: var(--accent-strong); font-weight: 600; }

.fetch-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background-color: var(--state-pass);
  flex-shrink: 0;
}

.js-motion .fetch-dot { animation: fetch-pulse 2.4s var(--ease-in-out) infinite; }

@keyframes fetch-pulse {
  0%, 70%, 100% { opacity: 1; }
  85%           { opacity: 0.25; }
}

.fetch-body {
  margin: 0;
  padding: 1.25rem 1.1rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  line-height: 1.85;
  color: var(--text-muted);
  overflow-x: auto;
  white-space: pre;
  tab-size: 2;
}

/* The extractable facts. At rest they are simply marked — no JS, no animation,
   still legible and still making the point. */
.fetch-body mark.fx {
  background-color: transparent;
  color: var(--text-signal);
  font-weight: 600;
  box-shadow: inset 0 -0.42em 0 rgba(76, 111, 255, 0.16);
  border-radius: 2px;
  padding: 0 0.1em;
}

/* With JS, they light up in reading order once, then hold. A crawler reads the
   page top to bottom exactly once; looping this would misrepresent that and get
   annoying on a page people sit on. */
.js-motion .fetch-body mark.fx {
  box-shadow: inset 0 -0.42em 0 rgba(76, 111, 255, 0);
  color: var(--text-muted);
  animation: fx-extract 620ms var(--ease-out) forwards;
  animation-delay: calc(900ms + var(--fx) * 380ms);
}

@keyframes fx-extract {
  to {
    box-shadow: inset 0 -0.42em 0 rgba(76, 111, 255, 0.16);
    color: var(--text-signal);
  }
}

@media (prefers-reduced-motion: reduce) {
  .js-motion .fetch-dot { animation: none; }
  .js-motion .fetch-body mark.fx {
    animation: none;
    box-shadow: inset 0 -0.42em 0 rgba(76, 111, 255, 0.16);
    color: var(--text-signal);
  }
}

@media (max-width: 1024px) {
  .fetch-body { font-size: 0.72rem; }
}

/* A published correction. The evidence page's credibility depends on showing
   these rather than silently editing, so they are marked, not hidden. */
.ev-correction {
  font-size: 0.86rem;
  color: var(--text-dim);
  border-left: 2px solid var(--state-part);
  padding-left: 0.85rem;
  margin-top: 0.9rem;
}

/* --------------------------------------------------------------------------
   CATEGORY INDEX (home)
   Was a 4-up grid of boxed cards with generic line icons, which is one of the
   most recognisable AI-template patterns. Same content, set as an index: rules
   instead of boxes, the category in the display serif, the deciding constraints
   in mono underneath.
   -------------------------------------------------------------------------- */
.cat-index {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0 3rem;
  border-top: 1px solid var(--border-line);
}

.cat-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 0 1rem;
  padding: 1.6rem 0;
  border-bottom: 1px solid var(--border-line);
  text-decoration: none;
  transition: padding-left var(--dur-base) var(--ease-out);
}

.cat-row:hover { padding-left: 0.6rem; }

.cat-name {
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-weight: 600;
  color: var(--text-signal);
  margin: 0;
  line-height: 1.2;
}

.cat-desc {
  grid-column: 1 / -1;
  grid-row: 2;
  font-size: 0.92rem;
  color: var(--text-muted);
  margin: 0.45rem 0 0;
  max-width: 46ch;
  line-height: 1.55;
}

.cat-more {
  font-family: var(--font-mono);
  color: var(--accent-strong);
  font-size: 1rem;
  grid-column: 2;
  grid-row: 1;
  transition: transform var(--dur-base) var(--ease-out);
  display: inline-block;
}

.cat-row:hover .cat-more { transform: translateX(4px); }

@media (max-width: 768px) {
  .cat-index { grid-template-columns: 1fr; gap: 0; }
}

/* --------------------------------------------------------------------------
   POST SOURCES
   The eight posts that went live unreviewed had no citations at all. Each now
   ends with the primary sources its claims were verified against. On a site
   whose position is "we publish the source", this is the minimum.
   -------------------------------------------------------------------------- */
.post-sources {
  margin-top: 3.5rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--border-line);
}

.post-sources h2 {
  font-size: 1.15rem;
  margin: 0 0 0.6rem;
}

.post-sources > p {
  font-size: 0.9rem;
  color: var(--text-dim);
  margin: 0 0 1.1rem;
  max-width: var(--measure);
}

.post-sources ol {
  display: grid;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: src;
  max-width: var(--measure);
}

.post-sources li {
  counter-increment: src;
  display: grid;
  grid-template-columns: 2rem 1fr;
  gap: 0.6rem;
  font-size: 0.92rem;
  line-height: 1.5;
}

.post-sources li::before {
  content: counter(src, decimal-leading-zero);
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--text-dim);
  padding-top: 0.2em;
}

.post-sources a {
  color: var(--accent-strong);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  overflow-wrap: anywhere;
}

.post-sources a:hover { color: var(--accent-strong-hover); }
