/* ═══════════════════════════════════════════════════════════
   SIGNAL DESK, SHARED STYLESHEET FOR THE CONTENT PAGES.

   Used by everything scripts/build-learn.py generates: /learn, the
   /learn/* articles, /methodology, /about and /changelog. It is NOT
   used by index.html or dashboard.html, which keep their own inline
   CSS and are not touched by the generator.

   That split is deliberate. Those two files have been tuned across
   eight documented passes and moving their styles into a shared file
   is a large, risky refactor with no reader benefit. The cost of the
   split is that this file can drift from them. That is a visual nit
   on new pages rather than a broken product, and it is the right
   trade. The tokens below were lifted from index.html verbatim, so
   the starting point is identical; when you change a token there,
   change it here.

   Two hard rules this file inherits from the rest of the repo:

   1. NO DASH-FAMILY CHARACTERS in anything a reader sees. Not in
      content, not in generated markup. U+2212 is the minus sign in
      negative percentages and is the only exception.
   2. Watch the equal-specificity trap. This repo has been bitten
      four times (.recipe/.recipe-modal v6.7, .dw-sig v7.0,
      .gate-col and .t-short v4): a later rule at equal specificity
      wins, so a media query declared ABOVE the rule it means to
      override silently loses. Every override here is either more
      specific or declared after its base.
   ═══════════════════════════════════════════════════════════ */

/* ── the four faces ──────────────────────────────────────────
   Self hosted. The old setup was a render blocking stylesheet on
   fonts.googleapis.com naming files on fonts.gstatic.com, two
   origins and two TLS handshakes, measured cold at ~330ms before
   any real glyph could paint; display=swap then made a flash of
   unstyled text certain. font-display: block holds text invisible
   until the face is ready and paints it once, so a wrong font is
   never shown, and the <link rel=preload> tags the generator
   writes into each page's head are what keep that window to the
   font's own download.

   THESE URLS ARE RESOLVED AGAINST THIS STYLESHEET, not against the
   page, which is the reason the content pages need no depth aware
   prefix here the way their links do. /site.css and /learn/x both
   reach /fonts/..., and under izaias.xyz/signaldesk the stylesheet
   is /signaldesk/site.css so they reach /signaldesk/fonts/...
   correctly too. The preload tags in the HTML use the generator's
   {pfx} to arrive at the same absolute URL; if they ever disagree,
   the file is fetched twice.

   All three families are variable, so one file covers every weight
   and font-weight below is the axis range rather than an instance.
   Four files, because Newsreader italic is its own outline set.

   Files come from scripts/fetch-fonts.py. Do not hand edit them,
   and keep these declarations in step with the copies inlined in
   index.html and dashboard.html. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: block;
  src: url('fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: block;
  src: url('fonts/space-grotesk-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 200 800;
  font-display: block;
  src: url('fonts/newsreader-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Newsreader';
  font-style: italic;
  font-weight: 200 800;
  font-display: block;
  src: url('fonts/newsreader-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

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

:root {
  --canvas:   #ffffff;
  --surface:  #faf9f5;
  --page:     #f4f1ea;
  --sunken:   #ebe8de;

  --line:     #e9e6e0;
  --line-mid: #dedad2;
  --line-str: #c8c3b9;

  --ink:      #1a1917;
  --ink-2:    #423f3a;
  --ink-3:    #55524b;
  --ink-4:    #6e6a62;

  --indigo:      #5c50e5;
  --indigo-deep: #4a3fd0;
  --indigo-soft: #efedfd;
  --indigo-line: #cdc7f8;

  --pos: #15803d;
  --neg: #c62828;

  --void:        #0c0c14;
  --void-border: #24243a;
  --void-line:   #8b7dff;
  --void-label:  #8c86c4;
  --void-body:   #9d9ac0;
  --void-note:   #6f6c92;

  --sans:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --display: 'Space Grotesk', 'Inter', sans-serif;
  --serif:   'Newsreader', Georgia, serif;
  --mono:    'SF Mono', 'Cascadia Code', ui-monospace, monospace;

  --fs-0: 10px;
  --fs-1: 11px;
  --fs-2: 12px;
  --fs-3: 13px;
  --fs-4: 15px;
  --fs-5: 19px;
  --fs-6: clamp(26px, 3.4vw, 40px);
  --track: .13em;

  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 72px; --sp-9: 108px;

  --r-sm: 4px;
  --r-md: 6px;
  --r-lg: 14px;

  --ease:    cubic-bezier(.22, 1, .36, 1);
  --ease-in: cubic-bezier(.4, 0, 1, 1);
  --t-1: .12s; --t-2: .22s; --t-3: .38s;

  --lift-line: #cfcabf;
  --lift-shadow: 0 1px 2px rgba(40, 36, 28, .05),
                 0 4px 10px -4px rgba(40, 36, 28, .12);

  /* The article measure. Long form prose wants a narrower column than
     the 1180px marketing wrap, so it gets its own token rather than
     overriding .wrap and having the header inherit the change. */
  --measure: 68ch;
}

html { scroll-behavior: smooth; }

body {
  background: var(--page);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-4);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: 'cv05' 1;
}

a { color: inherit; text-decoration: none; }
h1, h2, h3 { text-wrap: balance; }
p  { text-wrap: pretty; }
img, svg { max-width: 100%; }

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

.wrap { max-width: 1180px; margin-inline: auto; padding-inline: var(--sp-5); }
@media (max-width: 640px) { .wrap { padding-inline: var(--sp-4); } }

.skip-link {
  position: fixed; z-index: 999; top: -60px; left: var(--sp-4);
  background: var(--ink); color: #fff; padding: 10px 16px;
  border-radius: var(--r-md); font-size: var(--fs-3); font-weight: 600;
  transition: top var(--t-2) var(--ease);
}
.skip-link:focus { top: var(--sp-4); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── NAV ─────────────────────────────────────────────── */

.nav {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--page) 88%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--t-2) var(--ease);
}
body.scrolled .nav { border-bottom-color: var(--line); }

.nav-inner {
  display: flex; align-items: center; gap: var(--sp-5);
  height: 62px;
}
.brand { display: inline-flex; align-items: center; gap: 10px; font-weight: 600; }
.brand-mark { width: 24px; height: 24px; display: block; flex: none; }
.brand-mark svg { display: block; width: 100%; height: 100%; border-radius: 6px; }
.brand-word { font-family: var(--display); font-size: var(--fs-4); letter-spacing: -.01em; }

.nav-links {
  display: flex; gap: var(--sp-5); list-style: none;
  margin-left: auto; font-size: var(--fs-3); color: var(--ink-3);
}
.nav-links a { transition: color var(--t-1) var(--ease); }
.nav-links a:hover { color: var(--ink); }

.nav-cta {
  font-size: var(--fs-3); font-weight: 600;
  padding: 7px 14px; border-radius: 999px;
  border: 1px solid var(--line-str);
  transition: background var(--t-2) var(--ease), border-color var(--t-2) var(--ease);
}
.nav-cta:hover { background: var(--canvas); border-color: var(--lift-line); }

/* The nav is the first thing to run out of room. Links go before the
   CTA, because the CTA is the page's job and the links are navigation
   the footer repeats. */
@media (max-width: 720px) { .nav-links { display: none; } }
@media (max-width: 420px) { .nav-cta  { display: none; } }

/* ── ARTICLE ─────────────────────────────────────────── */

.doc { padding-block: var(--sp-7) var(--sp-9); }
.doc-inner { max-width: var(--measure); }

.crumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  font-family: var(--mono); font-size: var(--fs-1);
  letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-4); margin-bottom: var(--sp-5);
}
.crumbs a { transition: color var(--t-1) var(--ease); }
.crumbs a:hover { color: var(--indigo); }
.crumbs span[aria-hidden] { color: var(--line-str); }

.doc h1 {
  font-family: var(--display);
  font-size: var(--fs-6);
  line-height: 1.1;
  letter-spacing: -.02em;
  margin-bottom: var(--sp-4);
}

/* The direct answer. The single most-cited unit on the page for answer
   engines, so it is deliberately the first thing after the h1, is one
   self-contained paragraph, and is styled to look like the summary it
   is rather than like a pull quote. Roughly 40 to 60 words. */
.answer {
  font-size: var(--fs-5);
  line-height: 1.5;
  color: var(--ink-2);
  border-left: 2px solid var(--indigo);
  padding-left: var(--sp-4);
  margin-block: var(--sp-5) var(--sp-6);
}

.byline {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4);
  font-size: var(--fs-2); color: var(--ink-4);
  padding-bottom: var(--sp-5);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--sp-6);
}
.byline a { color: var(--ink-3); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--line-str); }
.byline a:hover { color: var(--indigo); text-decoration-color: currentColor; }

.prose > * + * { margin-top: var(--sp-4); }

.prose h2 {
  font-family: var(--display);
  font-size: var(--fs-5);
  line-height: 1.25;
  letter-spacing: -.01em;
  margin-top: var(--sp-7);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
}
.prose h3 {
  font-family: var(--display);
  font-size: var(--fs-4);
  margin-top: var(--sp-6);
}
.prose p { color: var(--ink-2); }
.prose strong { color: var(--ink); font-weight: 600; }
.prose em { font-style: italic; }

.prose a:not(.btn) {
  color: var(--indigo);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--indigo-line);
  transition: text-decoration-color var(--t-1) var(--ease);
}
.prose a:not(.btn):hover { text-decoration-color: currentColor; }

.prose ul, .prose ol { padding-left: var(--sp-5); color: var(--ink-2); }
.prose li + li { margin-top: var(--sp-2); }
.prose li::marker { color: var(--ink-4); }

.prose code {
  font-family: var(--mono); font-size: .88em;
  background: var(--sunken); padding: 2px 5px; border-radius: var(--r-sm);
}

.prose blockquote {
  font-family: var(--serif);
  font-size: var(--fs-5);
  line-height: 1.45;
  color: var(--ink);
  border-left: 2px solid var(--indigo);
  padding-left: var(--sp-4);
}

/* A fact taken straight from the running system. This is the thing no
   competing explainer page can copy, so it gets its own register. */
.live-note {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--sp-4) var(--sp-5);
  font-size: var(--fs-3);
  color: var(--ink-3);
}
.live-note strong { color: var(--ink); }
.live-note .live-label {
  display: block;
  font-family: var(--mono); font-size: var(--fs-0);
  letter-spacing: var(--track); text-transform: uppercase;
  color: var(--indigo); margin-bottom: var(--sp-2);
  margin-right: calc(var(--track) * -1em);
}

.prose table {
  width: 100%; border-collapse: collapse; font-size: var(--fs-3);
}
.prose table th, .prose table td {
  text-align: left; padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--line);
}
.prose table th {
  font-family: var(--mono); font-size: var(--fs-0);
  letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-4); margin-right: calc(var(--track) * -1em);
}
.prose table td { color: var(--ink-2); }
/* Wide content scrolls inside its own container so the page body never
   scrolls horizontally. */
.table-scroll { overflow-x: auto; }

/* ── STEPS (HowTo) ───────────────────────────────────── */

.steps { list-style: none; padding-left: 0; counter-reset: step; }
.steps li {
  counter-increment: step;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
}
.steps li + li { margin-top: var(--sp-5); }
.steps li::before {
  content: counter(step, decimal-leading-zero);
  display: block;
  font-family: var(--display); font-size: var(--fs-5); font-weight: 700;
  color: var(--indigo); line-height: 1; margin-bottom: var(--sp-2);
}
.steps h3 { margin-top: 0; }

/* ── SOURCES / FOOTER OF AN ARTICLE ──────────────────── */

.sec-label {
  font-family: var(--mono); font-size: var(--fs-0);
  letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-4); margin-right: calc(var(--track) * -1em);
  display: flex; align-items: center; gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
.sec-label::after {
  content: ''; flex: 1; height: 1px; background: var(--line);
}

.sources { margin-top: var(--sp-8); }
.sources ul { list-style: none; padding-left: 0; font-size: var(--fs-3); }
.sources li { color: var(--ink-3); }
.sources li + li { margin-top: var(--sp-2); }

.disclosure {
  margin-top: var(--sp-6);
  font-size: var(--fs-2);
  color: var(--ink-4);
  border-top: 1px solid var(--line);
  padding-top: var(--sp-4);
}

/* ── RELATED ─────────────────────────────────────────── */

.related { margin-top: var(--sp-8); }
.related-grid {
  display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.related-card {
  display: block;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  transition: border-color var(--t-2) var(--ease), box-shadow var(--t-2) var(--ease), background var(--t-2) var(--ease);
}
.related-card:hover {
  background: var(--canvas);
  border-color: var(--lift-line);
  box-shadow: var(--lift-shadow);
}
.related-card b { display: block; font-family: var(--display); font-size: var(--fs-4); margin-bottom: var(--sp-1); }
.related-card span { font-size: var(--fs-2); color: var(--ink-4); }

/* ── HUB INDEX ───────────────────────────────────────── */

.hub-list { list-style: none; padding-left: 0; margin-top: var(--sp-6); }
.hub-list li { border-top: 1px solid var(--line); }
.hub-list a {
  display: block; padding: var(--sp-5) 0;
  transition: color var(--t-1) var(--ease);
}
.hub-list a:hover { color: var(--indigo); }
.hub-list b {
  display: block; font-family: var(--display); font-size: var(--fs-5);
  letter-spacing: -.01em; margin-bottom: var(--sp-1); font-weight: 600;
}
.hub-list span { font-size: var(--fs-3); color: var(--ink-4); }

/* ── CAPTURE ─────────────────────────────────────────── */

.capture {
  margin-top: var(--sp-8);
  background: var(--void);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
  color: #fff;
}
.capture h2 {
  font-family: var(--serif); font-size: var(--fs-5); font-weight: 600;
  line-height: 1.25; margin-bottom: var(--sp-2);
}
.capture p { color: var(--void-body); font-size: var(--fs-3); margin-bottom: var(--sp-4); }

.capture-row {
  display: flex; gap: var(--sp-2);
  background: rgba(255,255,255,.05);
  border: 1px solid var(--void-border);
  border-radius: 999px;
  padding: 5px 5px 5px var(--sp-4);
  transition: border-color var(--t-2) var(--ease);
}
.capture-row:focus-within { border-color: var(--void-line); }

.capture-input {
  flex: 1; min-width: 0;
  background: none; border: none; color: #fff;
  font-family: var(--sans); font-size: var(--fs-3);
}
.capture-input::placeholder { color: var(--void-note); }
.capture-input:focus, .capture-input:focus-visible { outline: none; }

.btn {
  font-family: var(--sans); font-weight: 600; cursor: pointer;
  border: none; border-radius: 999px;
  transition: transform var(--t-1) var(--ease-in), background var(--t-2) var(--ease);
}
.btn:active { transform: scale(.97); }
.btn-on-void {
  background: #fff; color: var(--void);
  padding: 9px 18px; font-size: var(--fs-3);
}
.btn-on-void:hover { background: #ece9ff; }
.btn-on-void:disabled { opacity: .55; cursor: default; }

.wl-msg { display: block; font-size: var(--fs-2); margin-top: var(--sp-2); min-height: 1em; color: var(--void-body); }
.wl-msg.err { color: #ff9d9d; }
.wl-msg.ok  { color: #9dffc4; }
.capture-note { font-size: var(--fs-2); color: var(--void-note); margin-top: var(--sp-3); margin-bottom: 0; }

/* Spam honeypot. Off screen rather than display:none, because some bots
   skip hidden fields but fill positioned ones. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

/* Stacked, the welded row stops working: a transparent field inside a
   bordered box reads as a caption, not an input. Declared AFTER the base
   so the equal-specificity override actually lands. */
@media (max-width: 520px) {
  .capture-row { display: block; background: none; border: none; padding: 0; }
  .capture-row:focus-within { border: none; background: none; }
  .capture-input {
    width: 100%;
    background: rgba(255,255,255,.05);
    border: 1px solid var(--void-border);
    border-radius: 999px;
    padding: 11px var(--sp-4);
    font-size: var(--fs-4);
  }
  .capture-input:focus-visible { outline: 2px solid var(--void-line); outline-offset: 2px; }
  .capture-row .btn { width: 100%; margin-top: var(--sp-2); font-size: var(--fs-4); padding: 11px 18px; }
}

/* ── FOOT ────────────────────────────────────────────── */

.foot { border-top: 1px solid var(--line); padding-block: var(--sp-6) var(--sp-7); }
.foot-top {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-4) var(--sp-6); margin-bottom: var(--sp-5);
}
.foot-brand { display: inline-flex; align-items: center; gap: 10px; font-family: var(--display); font-weight: 600; }
.foot-links {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
  list-style: none; margin-left: auto; font-size: var(--fs-3); color: var(--ink-3);
}
.foot-links a:hover { color: var(--ink); }
.foot-legal { font-size: var(--fs-2); color: var(--ink-4); display: grid; gap: var(--sp-2); }
.foot-legal p { max-width: 76ch; }

@media (max-width: 640px) {
  .foot-links { margin-left: 0; }
}

/* ── MOTION ──────────────────────────────────────────── */

/* A keyframe carries the change, CSS carries the resting state. This
   repo learned that the hard way: a blanket reduced-motion rule kills
   the animation and LEAVES whatever the first keyframe declared, so
   tag-breathe froze on a solid amber ring and signal-flash left a
   permanent green wash. Nothing here animates a resting appearance. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

@media (pointer: coarse) {
  .nav-links a, .foot-links a, .crumbs a { padding-block: 4px; }
}
