/* ───────────────────────────────────────────────────────────────────────────
   DevHub — shared design system
   Link it from any visualizer:   <link rel="stylesheet" href="devhub.css">
   Pick the track accent on <body>: e.g. <body class="track-angular">
   Pages keep ONLY their page-specific styles inline (those override these).
   ─────────────────────────────────────────────────────────────────────────── */

/* ── design tokens ─────────────────────────────────────────────────────── */
:root{
  /* surfaces — three deliberate tiers so the eye can parse containers at a
     glance: page bg darkest → cards/panels one step lighter → code blocks a
     third, near-black "terminal" tone (see pre{}). Don't flatten these. */
  --bg:#0a0f1e; --panel:#161f38; --panel2:#111a2e; --border:#213150;
  --text:#e2e8f0; --muted:#94a3b8;
  /* semantic */
  --good:#4ade80; --warn:#facc15; --bad:#f87171; --info:#22d3ee;
  /* named palette used across pages */
  --blue:#22d3ee; --green:#4ade80; --purple:#a78bfa; --amber:#f59e0b;
  --red:#f87171; --orange:#fb923c; --ng:#fda4af; --sp:#a6e07f;
  /* accent — default is purple; track classes below override per section */
  --accent:#a78bfa; --accent2:#22d3ee;
}

/* ── light theme (opt-in via <html data-theme="light">) ──────────────────
   Dark is still the default look everywhere (no attribute = unchanged).
   Code/console-style panels (pre, .rt-headers, .cw-code/.cw-out/.cw-vis,
   .rt-result, .cw-callout) deliberately STAY dark in both themes — that's
   a normal "terminal" convention and keeps this override small & safe. */
:root[data-theme="light"]{
  --bg:#f3f5fb; --panel:#ffffff; --panel2:#eef1f8; --border:#d7deec;
  --text:#1b2536; --muted:#5b6b85;
  --blue:#0284c7; --info:#0e7490;
}

/* ── per-track accents (one body class re-themes the whole page) ────────── */
body.track-angular  { --accent:#dd0031; --accent2:#38bdf8; }
body.track-spring   { --accent:#6db33f; --accent2:#a78bfa; }
body.track-ts       { --accent:#3178c6; --accent2:#22d3ee; }
body.track-debug    { --accent:#fb923c; --accent2:#f87171; }
body.track-identity { --accent:#22d3ee; --accent2:#a78bfa; }
body.track-data     { --accent:#f59e0b; --accent2:#22d3ee; }
body.track-ping     { --accent:#be1522; --accent2:#ff6070; }
body.track-java     { --accent:#e76f00; --accent2:#f89820; }  /* coffee orange */
body.track-config   { --accent:#818cf8; --accent2:#c4b5fd; }  /* settings indigo */
body.track-interview{ --accent:#fb7185; --accent2:#fbbf24; }  /* target rose→gold */
body.track-tools    { --accent:#2dd4bf; --accent2:#22d3ee; }  /* tooling teal */
body.track-cloud    { --accent:#ff9900; --accent2:#fbbf24; }  /* AWS orange */
body.track-playground{ --accent:#10b981; --accent2:#22d3ee; }  /* live emerald → cyan */
body.track-python   { --accent:#3776ab; --accent2:#ffd43b; }  /* Python blue → yellow */
body.track-react    { --accent:#61dafb; --accent2:#0ea5e9; }  /* React cyan → sky blue */
body.track-go         { --accent:#00acd7; --accent2:#5dc9e2; }  /* Go gopher blue */
body.track-kubernetes { --accent:#326ce5; --accent2:#54c3f1; }  /* K8s blue */
body.track-sql        { --accent:#cc6600; --accent2:#f59e0b; }  /* SQL amber */
body.track-nodejs   { --accent:#83cd29; --accent2:#3c873a; }  /* Node green */
body.track-csharp   { --accent:#9b6dde; --accent2:#9078e3; }  /* .NET purple — accent2 lightened from the #512bd4 logo mark (1.95:1 as text on a dark panel) to the same hue at higher lightness */
body.track-php      { --accent:#8892bf; --accent2:#777bb4; }  /* PHP indigo */
body.track-ruby     { --accent:#e0544a; --accent2:#cc342d; }  /* Ruby red */
body.track-rust     { --accent:#dea584; --accent2:#f74c00; }  /* Rust rust */
body.track-stacks   { --accent:#a78bfa; --accent2:#22d3ee; }  /* full-stack violet */
body.track-mulesoft { --accent:#00a0df; --accent2:#5b9bd5; }  /* Anypoint blue */
body.track-devops   { --accent:#34d399; --accent2:#a78bfa; }  /* CI green → IaC violet */
body.track-azure    { --accent:#0078d4; --accent2:#50e6ff; }  /* Azure blue → light cyan */
body.track-gcp      { --accent:#4285f4; --accent2:#34a853; }  /* Google blue → green */
body.track-ai-dev   { --accent:#d97757; --accent2:#8b5cf6; }  /* Anthropic clay → violet */
body.track-data-science { --accent:#4d77cf; --accent2:#ff6f00; }  /* pandas blue → data-viz orange */
body.track-web-fundamentals { --accent:#e34c26; --accent2:#f06529; }  /* HTML5 orange → red-orange */
body.track-ai-engineering { --accent:#10a37f; --accent2:#ab68ff; }  /* GPT green → Claude/LLM violet */

/* ── difficulty-themed page background (the hub sets <body data-level> on load) ──
   ambient glow keyed to the page's level: beginner→green … expert→rose          */
body[data-level]::after{ content:""; position:fixed; inset:0; z-index:0; pointer-events:none;
  background:radial-gradient(ellipse 80% 55% at 50% -10%, var(--lvl-tint,transparent) 0%, transparent 62%); }
body[data-level="beginner"]    { --lvl-tint:rgba(74,222,128,.14);  }  /* green  */
body[data-level="intermediate"]{ --lvl-tint:rgba(34,211,238,.14);  }  /* cyan   */
body[data-level="advanced"]    { --lvl-tint:rgba(167,139,250,.16); }  /* violet */
body[data-level="expert"]      { --lvl-tint:rgba(251,113,133,.17); }  /* rose   */

/* ── base ──────────────────────────────────────────────────────────────── */
*{ box-sizing:border-box; }
/* Global text-size lever — one knob to bump every px font-size on every page
   at once (raise/lower this single value instead of editing per-page CSS). */
html{ zoom:1.12; }
body{
  margin:0;
  background:var(--bg);
  background-image:radial-gradient(ellipse at 20% 0%,rgba(10,28,60,.6) 0%,transparent 55%),
                   radial-gradient(ellipse at 80% 100%,rgba(4,12,28,.5) 0%,transparent 45%);
  color:var(--text); line-height:1.6;
  font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
}
.page,.container{ max-width:clamp(40rem,94vw,90rem); margin:0 auto; padding:1.75rem 1.5rem 3.5rem; }
a.back{ color:var(--blue); text-decoration:none; font-size:14px; }
a.back:hover{ text-decoration:underline; }

/* ── typography ────────────────────────────────────────────────────────── */
/* Editorial title: big, confident, near-white with only a hint of the track
   accent at the tail. The old full-rainbow gradient made the title compete
   with everything else on the page; reserving color is what makes the accent
   mean something when it does appear (labels, kickers, marks). */
h1{
  margin:10px 0 10px; font-size:34px; font-weight:900;
  background:linear-gradient(115deg,#f8fafc 0%,#e4ebf5 55%,var(--accent2) 160%);
  -webkit-background-clip:text; background-clip:text; color:transparent;
  letter-spacing:-.035em; line-height:1.15;
}
/* Vertical rhythm: markedly more space ABOVE a section header than below it,
   so headers visually attach to the content they introduce and the page
   chunks into scannable sections instead of a continuous column. Headings are
   clean bold near-white — no colored bars/borders; the accent lives in the
   small uppercase kicker labels (h3 / .hf-kicker) instead. */
h2{
  color:#e9eef7; font-size:20px; font-weight:800;
  margin:46px 0 12px; letter-spacing:-.015em; line-height:1.3;
}
h2:first-child{ margin-top:6px; }
h3{
  color:#445a76; font-size:13px; font-weight:700;
  text-transform:uppercase; letter-spacing:.06em; margin:14px 0 8px;
}
.subtitle, p.sub, .sub{ color:var(--muted); font-size:13px; line-height:1.6; margin:0 0 22px; }
.note{ color:var(--muted); font-size:12.5px; line-height:1.65; margin:10px 0 0; max-width:78ch; }
.note.top{ margin:0 0 12px; }

/* ── code & syntax highlighting ────────────────────────────────────────── */
pre{
  background:#040810; border:1px solid var(--border); border-radius:12px;
  padding:13px 16px; margin:10px 0 0; overflow-x:auto;
  font-family:'Cascadia Code',ui-monospace,Consolas,monospace; font-size:12.5px; line-height:1.7; color:#cbd5e1;
}
/* Editor-window chrome on real code blocks (anything the highlighter touched
   or that's hand-annotated): a slim title bar with traffic-light dots, like
   an IDE. Purely decorative — dynamic inspector panes and plain-text pres
   never match these selectors, so they stay chromeless. Add data-file="x.java"
   to show a filename in the bar. */
pre[data-hl-done], pre:has(.kw), pre[data-file]{ position:relative; padding-top:38px; }
pre[data-hl-done]::before, pre:has(.kw)::before, pre[data-file]::before{
  content:''; position:absolute; top:0; left:0; right:0; height:27px;
  background:rgba(255,255,255,.035); border-bottom:1px solid rgba(255,255,255,.06);
  border-radius:12px 12px 0 0;
}
pre[data-hl-done]::after, pre:has(.kw)::after, pre[data-file]::after{
  content:'\25CF\2002\25CF\2002\25CF' '\2003\2003' attr(data-file);
  position:absolute; top:7px; left:13px; font-size:8.5px; letter-spacing:.5px;
  color:#39496a; font-family:ui-monospace,monospace;
}
pre[data-file]::after{ font-size:10px; color:#5b6b85; }
/* Inline code stays quiet: colored mono text with a whisper of tint, no
   border. The old bordered "pill" made prose exhausting — the eye tripped on
   a chip every few words. Reserve full styling for real <pre> blocks. */
code{
  background:rgba(125,211,252,.07); border:none; border-radius:4px;
  padding:1px 4px; font-family:ui-monospace,monospace; font-size:12px; color:#7dd3fc;
}
pre .kw{ color:#c084fc; } pre .str{ color:#fbbf24; }
/* Comments are the widest-reaching offender in the audit (every <pre> on the
   site, not just CodeWalk): #64748b measured 4.23:1 on pre's #040810 ground.
   Same hue, stepped up to clear AA (4.89:1). */
pre .cm{ color:#6c7f97; font-style:italic; } pre .fn{ color:#22d3ee; }
pre .dec{ color:#f472b6; } pre .tag{ color:#4ade80; }
pre .attr{ color:#facc15; } pre .num{ color:#fb923c; } pre .type{ color:#5eead4; }
pre .hl{ background:rgba(221,0,49,.15); display:inline-block; padding:0 4px; border-radius:3px; }
pre .hlgood{ background:rgba(74,222,128,.15); display:inline-block; padding:0 4px; border-radius:3px; }

/* ── generic table ─────────────────────────────────────────────────────── */
table{ width:100%; border-collapse:collapse; font-size:12px; margin-top:8px; }
th{
  background:var(--panel2); color:var(--muted); font-size:10px; text-transform:uppercase;
  letter-spacing:.07em; padding:8px 10px; text-align:left; border-bottom:1px solid var(--border);
}
td{ padding:8px 10px; border-bottom:1px solid rgba(148,163,184,.1); vertical-align:top; }
tr:last-child td{ border-bottom:none; }

/* ── callout ───────────────────────────────────────────────────────────── */
.callout{
  background:rgba(250,204,21,.08); border:1px solid rgba(250,204,21,.35); border-radius:8px;
  padding:10px 14px; margin:12px 0 0; font-size:13px; line-height:1.65; color:#fde68a;
  max-width:88ch; /* edge-to-edge paragraphs are past comfortable reading length */
}
.callout code{ color:var(--warn); }

/* ── intro primer ("what is this / why it matters") ─────────────────────────
   Scannable replacement for the old wall-of-text intro paragraph. Accent-aware
   (uses --accent/--accent2), so one markup works on every track.
   Structure: .intro > .intro-head, .intro-tldr, .intro-gist, .intro-cards, .intro-ciam */
.intro{ background:linear-gradient(180deg,var(--panel) 0%,var(--panel2) 100%);
  border:1px solid var(--border);
  border-radius:16px; padding:22px 26px; margin:0 0 26px;
  box-shadow:0 10px 28px rgba(0,0,0,.28); /* lift the card off the page tier */ }
.intro-head{ display:flex; align-items:center; gap:9px; font-size:15px; font-weight:800;
  color:var(--accent2); margin:0 0 12px; letter-spacing:-.01em; }
.intro-head .ico{ font-size:18px; line-height:1; }
.intro-kicker{ font-size:10px; font-weight:800; text-transform:uppercase; letter-spacing:.08em;
  color:var(--accent); margin:0 0 4px; }
.intro-lead,.intro-tldr{ font-size:14px; color:#dbe6f3; line-height:1.62; margin:0 0 13px; max-width:72ch; }
.intro-lead b,.intro-tldr b{ color:#fff; }
.intro-lead code,.intro-tldr code{ font-size:12px; }
.intro-lead + .intro-lead{ margin-top:-4px; }
.intro-gist{ list-style:none; margin:0 0 15px; padding:0; display:grid; gap:7px; max-width:78ch; }
.intro-gist li{ position:relative; padding-left:21px; font-size:13px; color:#cbd5e1; line-height:1.6; }
.intro-gist li b{ color:#eef4fb; }
.intro-gist li::before{ content:"\25B8"; position:absolute; left:3px; top:2px; color:var(--accent2); font-size:11px; }
.intro-gist code{ font-size:11.5px; }
.intro-cards{ display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:10px; margin:0 0 12px; }
/* A card holding a real code block goes full-width: code crammed into a
   narrow column forces awkward wrapping and unreadably tiny lines. */
.intro-mini:has(pre){ grid-column:1 / -1; }
.intro-mini{ background:var(--panel2); border:1px solid var(--border); border-radius:14px; padding:11px 13px; }
.intro-mini h4{ margin:0 0 6px; font-size:10.5px; font-weight:800; text-transform:uppercase; letter-spacing:.05em; color:var(--accent2); }
.intro-mini ul,.intro-mini ol{ margin:0; padding-left:15px; font-size:12px; color:var(--muted); line-height:1.8; }
.intro-mini li b{ color:#cdd8e6; }
.intro-mini p{ margin:0; font-size:12px; color:var(--muted); line-height:1.68; }
.intro-mini code{ font-size:11px; }
.intro-ciam{ background:var(--panel2); border-radius:12px; border-left:3px solid var(--amber);
  padding:9px 13px; font-size:12.5px; color:#cbd5e1; line-height:1.6; max-width:92ch; }
.intro-ciam .tag{ font-size:10.5px; font-weight:800; text-transform:uppercase; letter-spacing:.05em; color:var(--amber); margin-right:6px; }
.intro-ciam code{ font-size:11px; }

/* ── layout: deep-dive cards & grids (pages may override) ──────────────── */
section{ margin-bottom:2.375rem; }
.grid{ display:grid; gap:.875rem; margin-bottom:2.125rem; }
.grid-2{ grid-template-columns:repeat(auto-fit,minmax(27.5rem,1fr)); }
.grid-3{ grid-template-columns:repeat(auto-fit,minmax(17.5rem,1fr)); }
.grid-4{ grid-template-columns:repeat(auto-fit,minmax(13rem,1fr)); }
/* book-style two/three-column layouts for page content */
.two-col{ display:grid; grid-template-columns:1fr 1fr; gap:1.75rem; align-items:start; }
.three-col{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:1.25rem; align-items:start; }
.two-col-wide{ display:grid; grid-template-columns:1.4fr 1fr; gap:1.75rem; align-items:start; }
/* code + explanation pairs that go side-by-side on wide screens */
.code-pair{ display:grid; grid-template-columns:1fr 1fr; gap:1.25rem; align-items:start; }
/* explanation / description text on left, code block on right */
.explain-pair{ display:grid; grid-template-columns:1fr 1.6fr; gap:1.5rem; align-items:start; }
@media(max-width:51.25rem){ .two-col,.three-col,.two-col-wide,.code-pair,.explain-pair{ grid-template-columns:1fr; } }
.card{ background:var(--panel); border:1px solid var(--border); border-radius:18px; padding:18px 20px; }
.card-accent{ border-left:3px solid var(--accent); }
.card-blue{ border-left:3px solid var(--blue); }
.card-green{ border-left:3px solid var(--green); }
.card-purple{ border-left:3px solid var(--purple); }
.card-amber{ border-left:3px solid var(--amber); }
.card-red{ border-left:3px solid var(--red); }

/* ── interactive controls (steppers / segmented toggles / run buttons) ──── */
.ctlbar{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-bottom:12px; }
.ctlbar .lbl{ font-size:11.5px; color:var(--muted); font-weight:700; }
.runbtn{ padding:9px 16px; border-radius:9px; border:1px solid var(--accent); background:var(--panel2); color:var(--accent2); font-size:12.5px; font-weight:800; cursor:pointer; }
.runbtn:hover:not(:disabled){ background:var(--accent); color:#fff; }
.runbtn:disabled{ opacity:.45; cursor:default; }
.seg{ display:flex; gap:6px; flex-wrap:wrap; }
.seg button{ padding:7px 11px; border-radius:12px; border:1px solid var(--border); background:var(--panel2); color:var(--muted); font-size:11.5px; font-weight:700; cursor:pointer; }
.seg button.on{ background:var(--accent); color:#fff; border-color:var(--accent); }
.seg button:disabled{ opacity:.5; cursor:default; }

/* ── in-hub cross-link (pairs with the page's dlh-navigate click script) ─── */
.dd-link{ color:#38bdf8; text-decoration:none; white-space:nowrap; border-bottom:1px dashed rgba(56,189,248,.5); transition:color .15s,border-color .15s; }
.dd-link:hover{ color:#7dd3fc; border-bottom-color:#7dd3fc; }

/* ── EXTREME-VIZ HERO KIT (the shared rt-* animated-walk + live-inspector) ──
   Pages built before 2026-06-10 ship an identical copy inline (which wins via
   the cascade); newer pages rely on this one. Colors follow the track accent,
   so a hero re-themes itself per <body class="track-*"> with zero page CSS. */
.rt-ctlbar{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:4px 0 12px; }
.rt-ctlbar .lbl{ font-size:11px; color:var(--muted); font-weight:700; text-transform:uppercase; letter-spacing:.05em; }
.userseg{ display:inline-flex; gap:6px; flex-wrap:wrap; }
.userseg button{ background:var(--panel2); border:1px solid var(--border); color:var(--muted); font-size:12px; padding:6px 12px; border-radius:999px; cursor:pointer; font-weight:600; }
.userseg button.on{ background:var(--accent); color:#fff; border-color:var(--accent); }
.userseg button:disabled{ opacity:.5; cursor:default; }

/* ── a phone gets ONE column ────────────────────────────────────────────────
   35 pages give their table of contents `columns: 3`, dropping to 2 under
   800px. Nobody carried that down to a phone, so at 390px each column is about
   107px and an entry like "Modern: ViewContainerRef.createComponent()" is
   clipped by 138px — the link text simply ends mid-word.

   `body .toc` rather than `.toc`: the page's own rule lives in a <style> that
   comes after this file's <link>, so at equal specificity the page wins. The
   extra element selector out-specifies it without needing !important. */
@media (max-width: 560px){
  body .toc{ columns:1; column-gap:0; }
  body .toc a{ overflow-wrap:anywhere; }   /* one identifier is still longer than the column */
}

/* ── inline code must wrap; block code must scroll ─────────────────────────
   A phone content column is ~220px and `ViewContainerRef.createComponent(...)`
   renders 434px wide. As one unbreakable token with overflow-wrap:normal it
   neither wrapped nor scrolled — it was CLIPPED, so the reader lost the end of
   the identifier with nothing on screen to say so. Measured on the intro card,
   which nearly every lesson page has.

   `:not(pre) > code` is the whole point of the selector: code INSIDE a <pre> is
   a block listing that scrolls itself, and forcing it to wrap would destroy the
   line structure the CodeWalk depends on. Only the inline chips wrap. */
:not(pre) > code{ overflow-wrap:anywhere; }

/* The prose that holds them breaks only when a word genuinely will not fit
   (break-word), rather than anywhere (which also changes min-content sizing). */
.intro-lead, .intro-tldr, .intro-gist li, .intro-ciam, .intro-mini p,
.intro-mini li, .callout, .panel p, .card p{ overflow-wrap:break-word; }

/* A bare <a> in lesson prose was never styled anywhere in this file, so it fell
   through to the browser default #0000EE — navy on a near-black page, which is
   how cross-links between lessons became invisible on ~50 pages of the SHIPPING
   theme. Specificity (0,0,1) is deliberately the lowest possible: every link
   that already has a class (.card, .back, .dd-link, .toc a, .topbar a) still
   wins, so this only dresses the ones nobody dressed. */
a{ color:var(--blue); text-decoration:underline; text-underline-offset:2px; text-decoration-thickness:1px; }
a:hover{ color:var(--accent2); }

/* The selected segment filled solid --accent and wrote #fff on top. That is
   fine for a dark track accent and unreadable for a light one — the default
   #a78bfa measures 1.62:1 — and the accent is per-track, so no single ink is
   right for all 33 of them. A tinted wash with the accent as INK inverts the
   problem: it reads on every accent, light or dark, because the text and the
   background now differ by the wash, not by luck. It also matches how the rest
   of the site already draws an active control (.cw-btn.play, .rt-run). */
.userseg button.on, .seg button.on{
  background:color-mix(in srgb, var(--accent) 16%, transparent);
  color:var(--accent); border-color:var(--accent); font-weight:800;
}
.rt-run{ padding:9px 16px; border-radius:9px; border:1px solid var(--good); background:rgba(74,222,128,.14); color:#86efac; font-size:12.5px; font-weight:800; cursor:pointer; }
.rt-run:disabled{ opacity:.45; cursor:default; }
.rt-pill{ font-family:ui-monospace,monospace; font-size:11px; background:var(--panel2); border:1px solid var(--border); border-radius:6px; padding:4px 9px; color:var(--info); }
.rt-stage{ display:grid; grid-template-columns:1.05fr .95fr; gap:14px; margin-top:6px; align-items:start; }
@media(max-width:42.5rem){ .rt-stage{ grid-template-columns:1fr; } }
.rt-diagram{ position:relative; padding:6px 0 6px 44px; }
.rt-rail{ position:absolute; left:22px; top:8px; bottom:8px; width:3px; transform:translateX(-50%); background:linear-gradient(var(--info),var(--good)); opacity:.4; border-radius:3px; }
.rt-chip{ position:absolute; left:22px; top:0; transform:translate(-50%,-50%); background:var(--info); color:#04222a; font-family:ui-monospace,monospace; font-size:10px; font-weight:800; padding:3px 8px; border-radius:12px; white-space:nowrap; opacity:0; z-index:4; box-shadow:0 2px 8px rgba(0,0,0,.45); transition:top .9s cubic-bezier(.5,0,.3,1),opacity .25s,background .2s; }
.rt-chip.ok{ background:var(--good); color:#052e16; }
.rt-chip.bad{ background:var(--bad); color:#2a0606; }
.rt-node{ display:flex; align-items:center; gap:10px; background:var(--panel2); border:1px solid var(--border); border-radius:14px; padding:9px 11px; margin:9px 0; transition:opacity .25s,border-color .25s,box-shadow .25s; opacity:.5; }
.rt-node.vchk{ opacity:1; border-color:rgba(74,222,128,.5); }
.rt-node.vskip{ opacity:1; border-color:rgba(248,113,113,.5); }
.rt-node.cur{ box-shadow:0 0 0 1px var(--info) inset, 0 0 16px rgba(34,211,238,.18); }
.rt-node.cur.curbad{ box-shadow:0 0 0 1px var(--bad) inset, 0 0 16px rgba(248,113,113,.2); }
.rt-node.rtpulse{ animation:rtpulse .5s; }
@keyframes rtpulse{ 0%{ box-shadow:0 0 0 0 rgba(34,211,238,.4); } 100%{ box-shadow:0 0 0 13px transparent; } }
.rt-node .ic{ font-size:16px; line-height:1; }
.rt-node .nl{ font-size:12.5px; font-weight:700; color:var(--text); font-family:ui-monospace,monospace; }
.rt-node .ns{ font-size:10px; color:var(--muted); font-family:ui-monospace,monospace; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.rt-node .who{ font-size:8px; font-weight:800; letter-spacing:.03em; padding:1px 6px; border-radius:7px; background:rgba(148,163,184,.18); color:#cbd5e1; }
.rt-inspect{ background:var(--panel2); border:1px solid var(--border); border-radius:16px; padding:12px 14px; position:sticky; top:12px; }
.rt-dir{ display:inline-block; font-size:10px; font-weight:800; letter-spacing:.05em; padding:2px 9px; border-radius:7px; margin-bottom:8px; background:rgba(34,211,238,.14); color:#7dd3fc; }
.rt-dir.bad{ background:rgba(248,113,113,.16); color:#fca5a5; }
.rt-cap{ font-size:12.5px; font-weight:700; color:var(--text); min-height:34px; }
.rt-payload{ font-size:11px; color:var(--muted); font-family:ui-monospace,monospace; margin:7px 0 9px; line-height:1.5; }
.rt-headers{ background:#040810; border:1px solid var(--border); border-radius:7px; padding:9px 11px; font-family:ui-monospace,monospace; font-size:11px; line-height:1.7; white-space:pre-wrap; color:#9fb0c9; min-height:120px; overflow-x:auto; }
.rt-headers .ht{ display:block; color:var(--accent2); font-weight:800; font-size:9.5px; text-transform:uppercase; letter-spacing:.06em; margin-bottom:5px; }
.rt-headers .hl-ok{ color:#86efac; font-weight:700; }
.rt-headers .hl-bad{ color:#fca5a5; font-weight:700; }
.rt-legend{ font-size:10.5px; color:var(--muted); margin-top:12px; display:flex; gap:16px; flex-wrap:wrap; }
.rt-result{ margin-top:12px; font-size:12.5px; font-weight:700; padding:11px 14px; border-radius:9px; display:none; }
.rt-result.show{ display:block; }
.rt-result.ok{ background:#052e16; color:#86efac; border:1px solid #134d28; }
.rt-result.warn{ background:#3a2a06; color:#fcd34d; border:1px solid #5b4410; }
.rt-result.bad{ background:#2a0606; color:#fca5a5; border:1px solid #5b1010; }
/* per-page node-role badges (.who-XXX) still live inline on each page */

/* ── playful engagement layer ── Instagram / YouTube / TikTok energy ─────── */

/* Entrance animation — every page fades up on load */
@keyframes dhFadeUp{ from{ opacity:0; transform:translateY(12px); } to{ opacity:1; transform:translateY(0); } }
.page,.container{ position:relative; z-index:1; animation:dhFadeUp .38s cubic-bezier(.22,.9,.36,1) both; }

/* Per-track accent halo behind the page content (fixed, so it stays in view) */
body::before{ content:''; position:fixed; inset:0; pointer-events:none; z-index:0; }
body.track-angular::before  { background:radial-gradient(ellipse at 92% 4%,rgba(221,0,49,.09)   0%,transparent 46%); }
body.track-spring::before   { background:radial-gradient(ellipse at 92% 4%,rgba(109,179,63,.08) 0%,transparent 46%); }
body.track-ts::before       { background:radial-gradient(ellipse at 92% 4%,rgba(49,120,198,.09) 0%,transparent 46%); }
body.track-debug::before    { background:radial-gradient(ellipse at 92% 4%,rgba(251,146,60,.08) 0%,transparent 46%); }
body.track-identity::before { background:radial-gradient(ellipse at 92% 4%,rgba(34,211,238,.08) 0%,transparent 46%); }
body.track-data::before     { background:radial-gradient(ellipse at 92% 4%,rgba(245,158,11,.08) 0%,transparent 46%); }
body.track-java::before     { background:radial-gradient(ellipse at 92% 4%,rgba(231,111,0,.09)  0%,transparent 46%); }
body.track-ping::before     { background:radial-gradient(ellipse at 92% 4%,rgba(190,21,34,.09)  0%,transparent 46%); }
body.track-config::before   { background:radial-gradient(ellipse at 92% 4%,rgba(129,140,248,.08)0%,transparent 46%); }
body.track-interview::before{ background:radial-gradient(ellipse at 92% 4%,rgba(251,113,133,.08)0%,transparent 46%); }
body.track-tools::before    { background:radial-gradient(ellipse at 92% 4%,rgba(45,212,191,.08) 0%,transparent 46%); }
body.track-cloud::before    { background:radial-gradient(ellipse at 92% 4%,rgba(255,153,0,.09)  0%,transparent 46%); }
body.track-playground::before{background:radial-gradient(ellipse at 92% 4%,rgba(16,185,129,.08) 0%,transparent 46%); }
body.track-python::before   { background:radial-gradient(ellipse at 92% 4%,rgba(55,118,171,.09) 0%,transparent 46%); }
body.track-react::before    { background:radial-gradient(ellipse at 92% 4%,rgba(97,218,251,.08) 0%,transparent 46%); }
body.track-go::before       { background:radial-gradient(ellipse at 92% 4%,rgba(0,172,215,.09)  0%,transparent 46%); }
body.track-kubernetes::before{background:radial-gradient(ellipse at 92% 4%,rgba(50,108,229,.09) 0%,transparent 46%); }
body.track-sql::before      { background:radial-gradient(ellipse at 92% 4%,rgba(204,102,0,.09)  0%,transparent 46%); }
body.track-nodejs::before   { background:radial-gradient(ellipse at 92% 4%,rgba(131,205,41,.09) 0%,transparent 46%); }
body.track-csharp::before   { background:radial-gradient(ellipse at 92% 4%,rgba(81,43,212,.10)  0%,transparent 46%); }
body.track-php::before      { background:radial-gradient(ellipse at 92% 4%,rgba(119,123,180,.10) 0%,transparent 46%); }
body.track-ruby::before     { background:radial-gradient(ellipse at 92% 4%,rgba(204,52,45,.09)  0%,transparent 46%); }
body.track-rust::before     { background:radial-gradient(ellipse at 92% 4%,rgba(222,165,132,.09) 0%,transparent 46%); }
body.track-stacks::before   { background:radial-gradient(ellipse at 92% 4%,rgba(167,139,250,.09) 0%,transparent 46%); }
body.track-azure::before    { background:radial-gradient(ellipse at 92% 4%,rgba(0,120,212,.11)  0%,transparent 46%); }
body.track-gcp::before      { background:radial-gradient(ellipse at 92% 4%,rgba(66,133,244,.11)  0%,transparent 46%); }
body.track-ai-dev::before   { background:radial-gradient(ellipse at 92% 4%,rgba(217,119,87,.10)  0%,transparent 46%); }
body.track-data-science::before { background:radial-gradient(ellipse at 92% 4%,rgba(77,119,207,.11)  0%,transparent 46%); }
body.track-web-fundamentals::before { background:radial-gradient(ellipse at 92% 4%,rgba(227,76,38,.11)  0%,transparent 46%); }
body.track-ai-engineering::before { background:radial-gradient(ellipse at 92% 4%,rgba(16,163,127,.11)  0%,transparent 46%); }

/* Card lift on hover */
.card{
  transition:transform .22s cubic-bezier(.22,.9,.36,1),
             box-shadow .22s,
             border-color .22s;
}
.card:hover{
  transform:translateY(-3px);
  box-shadow:0 12px 40px rgba(0,0,0,.5),0 0 0 1px rgba(148,163,184,.1);
  border-color:rgba(148,163,184,.16);
}

/* Button glow — makes every interactive button feel punchy */
.rt-run:hover:not(:disabled){
  background:rgba(74,222,128,.26);
  box-shadow:0 0 20px rgba(74,222,128,.25);
}
.userseg button.on{
  box-shadow:0 0 16px rgba(167,139,250,.32);
}
.seg button.on{
  box-shadow:0 0 14px rgba(167,139,250,.28);
}
.runbtn:hover:not(:disabled){
  box-shadow:0 0 20px rgba(167,139,250,.22);
}

/* Glowing inline badge — use anywhere you want text to pop */
.badge-glow{
  display:inline-block; padding:2px 10px; border-radius:20px;
  background:linear-gradient(90deg,var(--accent),var(--accent2));
  color:#fff; font-size:11px; font-weight:800; letter-spacing:.04em;
}
/* Static accent chip — for status labels */
.chip{
  display:inline-block; padding:1px 8px; border-radius:6px; font-size:11px; font-weight:700;
  background:rgba(167,139,250,.15); color:var(--accent); border:1px solid rgba(167,139,250,.25);
}

/* Table row hover */
tr:hover td{ background:rgba(148,163,184,.035); transition:background .12s; }

/* Scrollbar — subtle dark style */
::-webkit-scrollbar{ width:7px; height:7px; }
::-webkit-scrollbar-track{ background:var(--bg); }
::-webkit-scrollbar-thumb{ background:var(--border); border-radius:4px; }
::-webkit-scrollbar-thumb:hover{ background:#334155; }

/* Keyboard focus ring */
*:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; border-radius:4px; }

/* Striped table variant */
.table-stripe tr:nth-child(even) td{ background:rgba(148,163,184,.03); }

/* Subtle section-level separator with accent glow */
section+section{ border-top:1px solid rgba(148,163,184,.06); padding-top:8px; }

/* ── CODE WALKTHROUGH widget ── line-by-line stepper + live state panel.
   Markup is generated by devhub-codewalk.js: DevHubCodeWalk.mount(el, data).
   Code on the left highlights line-by-line; the right panel narrates each line
   and shows live variable boxes / output / gotchas. ───────────────────────── */
.cw{ border:1px solid var(--border); border-radius:12px; overflow:hidden; background:var(--panel); margin:14px 0; }
.cw-head{ display:flex; align-items:center; gap:10px; padding:9px 13px; background:var(--panel2); border-bottom:1px solid var(--border); flex-wrap:wrap; }
.cw-title{ font-family:ui-monospace,monospace; font-size:12px; font-weight:800; color:var(--accent2); }
.cw-ctl{ display:flex; align-items:center; gap:6px; margin-left:auto; }
.cw-btn{ background:var(--panel); border:1px solid var(--border); color:var(--text); border-radius:7px; font-size:12px; font-weight:700; padding:5px 11px; cursor:pointer; font-family:inherit; transition:border-color .15s,background .15s; }
.cw-btn:hover:not(:disabled){ border-color:var(--accent2); }
/* Restates background/color, not just opacity: a page-local `button:disabled`
   rule (background+color together) outranks the bare .cw-btn selector on
   specificity alone, so without this a codewalk step button borrows whatever
   a page says about disabled buttons in general — seen at 1.95-2.18:1 on
   several pages. */
.cw-btn:disabled{ opacity:.4; cursor:default; background:var(--panel); color:var(--text); }
.cw-btn.play{ border-color:var(--good); color:#86efac; background:rgba(74,222,128,.12); }
.cw-counter{ font-family:ui-monospace,monospace; font-size:11px; color:var(--muted); min-width:50px; text-align:center; }
.cw-grid{ display:grid; grid-template-columns:1.1fr .9fr; }
@media(max-width:46rem){ .cw-grid{ grid-template-columns:1fr; } }
.cw-code{ background:#04070f; overflow-x:auto; padding:8px 0; font-family:'Cascadia Code',ui-monospace,Consolas,monospace; font-size:12.5px; line-height:1.65; border-right:1px solid var(--border); }
@media(max-width:46rem){ .cw-code{ border-right:none; border-bottom:1px solid var(--border); } }
.cw-line{ display:flex; white-space:pre; cursor:pointer; border-left:3px solid transparent; transition:background .15s,opacity .2s; }
.cw-line:hover{ background:rgba(148,163,184,.07); }
.cw-line.cur{ background:rgba(34,211,238,.12); border-left-color:var(--accent2); }
.cw-line.done{ opacity:.95; }
.cw-line.dim{ opacity:.4; }
/* #3a4a63 measured 2.24:1 on the console's #04070f ground — just above the
   site's "invisible" floor but nowhere near readable. Line numbers stay
   furniture (never competing with the code, same target cream's own .cw-ln
   repair uses: findable, not prominent), so this only steps it up to 2.93:1
   rather than chasing full AA. */
.cw-ln{ flex:0 0 34px; width:34px; text-align:right; padding-right:12px; color:#4a5b78; user-select:none; }
.cw-line.cur .cw-ln{ color:var(--accent2); font-weight:700; }
.cw-src{ flex:1; padding-right:12px; }
.cw-panel{ padding:13px 15px; display:flex; flex-direction:column; gap:11px; min-height:200px; }
.cw-step{ font-size:10px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--accent); }
.cw-note{ font-size:13px; line-height:1.62; color:var(--text); }
.cw-note code{ font-size:11.5px; }
.cw-vars{ display:flex; flex-wrap:wrap; gap:8px; }
.cw-var{ background:var(--panel2); border:1px solid var(--border); border-radius:8px; padding:5px 10px; font-family:ui-monospace,monospace; font-size:12px; }
.cw-var .k{ display:block; color:var(--muted); font-size:9px; text-transform:uppercase; letter-spacing:.04em; margin-bottom:1px; }
.cw-var .v{ color:var(--accent2); font-weight:700; word-break:break-word; }
.cw-var.changed{ animation:cwPulse .65s; border-color:var(--accent2); }
@keyframes cwPulse{ 0%{ box-shadow:0 0 0 0 rgba(34,211,238,.5); } 100%{ box-shadow:0 0 0 12px transparent; } }
.cw-out{ background:#070b16; border:1px solid var(--border); border-radius:7px; padding:8px 11px; font-family:ui-monospace,monospace; font-size:11px; line-height:1.6; color:#9fb0c9; white-space:pre-wrap; word-break:break-word; }
.cw-out .lbl{ display:block; font-size:9px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin-bottom:3px; }
.cw-callout{ font-size:12px; line-height:1.55; padding:8px 11px; border-radius:8px; background:rgba(250,204,21,.08); border:1px solid rgba(250,204,21,.3); color:#fde68a; }
.cw-callout.bad{ background:rgba(248,113,113,.09); border-color:rgba(248,113,113,.32); color:#fca5a5; }
.cw-callout.ok{ background:rgba(74,222,128,.08); border-color:rgba(74,222,128,.32); color:#a7f3d0; }
.cw-vis{ background:#070b16; border:1px solid var(--border); border-radius:8px; padding:10px 12px; }
.cw .kw{ color:#c792ea; } .cw .str{ color:#c3e88d; } .cw .num{ color:#fb923c; }
/* Comments carry the teacher's own line-by-line words, unlike the gutter
   numbers above — #546e7a measured 3.73:1 on this console's #04070f ground.
   Same hue, stepped up to clear AA (4.78:1). */
.cw .cm{ color:#62808c; font-style:italic; } .cw .fn{ color:#82aaff; } .cw .type{ color:#f78c6c; }

/* ── light theme: hardcoded-literal overrides ─────────────────────────────
   Everything above already flips via the --bg/--panel/--panel2/--border/
   --text/--muted/--blue/--info tokens. These are the handful of rules that
   used a literal hex/rgba instead of a token and need a legible light-mode
   counterpart (mostly text sitting directly on the page, not inside a
   dark code/console panel — those intentionally stay dark, see above). */
:root[data-theme="light"] body{
  background-image:radial-gradient(ellipse at 20% 0%,rgba(59,130,246,.06) 0%,transparent 55%),
                    radial-gradient(ellipse at 80% 100%,rgba(99,102,241,.05) 0%,transparent 45%);
}
:root[data-theme="light"] h1{ background:linear-gradient(135deg,var(--accent) 10%,var(--accent2) 55%,#1e293b 100%); -webkit-background-clip:text; background-clip:text; }
:root[data-theme="light"] h2{ color:#0e7490; }
:root[data-theme="light"] code{ color:#0369a1; }
:root[data-theme="light"] .callout{ color:#92400e; }
:root[data-theme="light"] .intro-lead,:root[data-theme="light"] .intro-tldr{ color:#334155; }
:root[data-theme="light"] .intro-lead b,:root[data-theme="light"] .intro-tldr b{ color:#0f172a; }
:root[data-theme="light"] .intro-gist li{ color:#334155; }
:root[data-theme="light"] .intro-gist li b{ color:#0f172a; }
:root[data-theme="light"] .intro-mini li b{ color:#1e293b; }
:root[data-theme="light"] .intro-ciam{ color:#334155; }
:root[data-theme="light"] .dd-link{ color:#0284c7; }
:root[data-theme="light"] .dd-link:hover{ color:#0369a1; }
:root[data-theme="light"] .rt-dir{ color:#0e7490; }
:root[data-theme="light"] .rt-dir.bad{ color:#b91c1c; }
:root[data-theme="light"] .rt-run{ color:#15803d; }
:root[data-theme="light"] .rt-node .who{ background:rgba(15,23,42,.08); color:#334155; }
:root[data-theme="light"] .card:hover{ box-shadow:0 12px 32px rgba(15,23,42,.12),0 0 0 1px rgba(15,23,42,.08); border-color:rgba(15,23,42,.14); }

/* ── Personal Notebook: sitewide "+ Notebook" quick-add button ──────────────
   Injected by devhub-notebook.js onto every <h2> section heading. Deliberately
   small and inline so it reads as a corner affordance, not a competing CTA. */
h2{ position:relative; }
.dnb-btn{ display:inline-flex; align-items:center; gap:5px; vertical-align:middle;
  margin-left:12px; font-family:ui-sans-serif,system-ui,sans-serif; font-size:11px; font-weight:700;
  letter-spacing:.01em; padding:4px 10px; border-radius:20px; cursor:pointer;
  background:var(--panel2); border:1px solid var(--border); color:var(--muted);
  transition:all .15s; -webkit-user-select:none; user-select:none; }
.dnb-btn:hover{ border-color:var(--accent); color:var(--accent2); }
.dnb-btn.saved{ background:rgba(74,222,128,.14); border-color:#134d28; color:#86efac; }
.dnb-btn.saved:hover{ border-color:var(--bad); color:#fca5a5; }
:root[data-theme="light"] .dnb-btn{ background:#eef1f8; color:#64748b; }
:root[data-theme="light"] .dnb-btn.saved{ background:#dcfce7; border-color:#86efac; color:#15803d; }

/* ── Notebook review sessions (flashcards / quiz-me / interleaved) ──────────
   Shared "Learn more" chip rendered on a flashcard front/back, and the
   free-recall quiz-me scratchpad used by devhub-notebook-review.js. */
.dnb-link-chip{ position:absolute; bottom:12px; right:16px; font-size:10.5px; font-weight:700;
  color:var(--accent2, #22d3ee); background:rgba(34,211,238,.12); padding:3px 10px; border-radius:6px;
  text-decoration:none; cursor:pointer; }
.dnb-link-chip:hover{ text-decoration:underline; }
.dnb-scratch{ width:100%; min-height:70px; margin-top:14px; background:#0b1426; border:1px solid var(--border);
  border-radius:9px; color:var(--text, #e2e8f0); font:inherit; font-size:13px; padding:10px 12px; resize:vertical; }
.dnb-scratch::placeholder{ color:var(--muted); }
.dnb-meta-row{ display:flex; gap:8px; flex-wrap:wrap; font-size:11px; color:var(--muted); margin:0 0 10px; }
.dnb-meta-chip{ background:var(--panel2); border:1px solid var(--border); border-radius:6px; padding:3px 9px; }

/* ── Sitewide interaction & transition polish (devhub-transitions.js) ──────
   Applies with zero per-page markup: press feedback + ripple on every real
   button/tab/clickable-card, a fade-in on page load, and a fade-out before
   leaving to another page. Everything here is a no-op under
   prefers-reduced-motion, and the fade-out layer only ever engages for a
   real top-level document navigation (devhub-transitions.js additionally
   skips it inside the app.html hub's iframe, where existing sidebar/
   postMessage navigation already owns the transition). */
@media (prefers-reduced-motion: no-preference) {
  button, .tab, [role="button"], .page-link, .track-card, .tc-dot {
    transition: transform .14s cubic-bezier(.22,.61,.36,1), filter .14s ease, box-shadow .14s ease;
  }
  /* Hover: a whisper of lift + brightness, so every control feels alive
     before it's even pressed. Press: quick scale-down. */
  button:hover, .tab:hover, [role="button"]:hover, .page-link:hover, .track-card:hover {
    transform: translateY(-1px); filter: brightness(1.07);
  }
  button:active, .tab:active, [role="button"]:active, .page-link:active, .track-card:active, .tc-dot:active {
    transform: scale(.96);
  }
  /* Press pulse — devhub-transitions.js toggles .dh-press on pointerdown: an
     accent ring blooms outward from the control's own outline via animated
     box-shadow. No injected child, no positioning, no clipping — box-shadow
     follows the element's exact border-radius, so it cannot misalign the way
     the two retired fill-ripple implementations did. The script injects an
     identical copy of these rules (#dh-press-css) for pages without this
     stylesheet — keep the two in sync. */
  .dh-press { animation: dhPress .42s cubic-bezier(.22,.61,.36,1); }
  @keyframes dhPress {
    0%    { box-shadow: 0 0 0 0 rgba(167,139,250,.55); } /* fallback */
    0.1%  { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent, #a78bfa) 60%, transparent); }
    100%  { box-shadow: 0 0 0 12px transparent; }
  }

  /* Page-load fade-in — pure CSS, needs no JS, applies to every page that
     loads this stylesheet (all of them). */
  body { animation: dhPageIn .32s ease both; }
  @keyframes dhPageIn {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  /* Page-leave fade-out — devhub-transitions.js adds .dh-leaving to <html>
     just before navigating to another DevHub page, then delays the actual
     navigation to let this play. */
  html.dh-leaving body {
    animation: dhPageOut .22s ease both;
  }
  @keyframes dhPageOut {
    from { opacity: 1; transform: translateY(0); }
    to   { opacity: 0; transform: translateY(-6px); }
  }
}

/* ══ Head First kit ═══════════════════════════════════════════════════════
   The site's teaching standard is Head First Design Patterns: visually rich,
   brain-friendly, redundant-on-purpose. This section gives every page the
   book's visual vocabulary with zero JS: marker-pen highlights, handwritten
   sticky notes, annotation arrows pointing at code, "Brain Power" exercise
   boxes, "no dumb questions" Q&As, and exaggerated big-type contrast.
   Everything tints from var(--accent), so each track keeps its identity.

   SITEWIDE AUTO-EFFECT: <b>/<strong> inside the intro cards get a
   highlighter-marker sweep automatically — 8,000+ existing emphasis tags
   across ~500 pages light up without touching a single page.            */

/* marker sweep on existing emphasis in intro cards (auto, no markup) */
.intro-lead b, .intro-tldr b, .intro-gist b, .intro-ciam b,
.intro-lead strong, .intro-tldr strong, .intro-gist strong, .intro-ciam strong{
  color:#fff; border-radius:3px; padding:0 3px;
  background:linear-gradient(180deg, transparent 60%, rgba(250,204,21,.30) 60%);
  background:linear-gradient(180deg, transparent 60%, color-mix(in srgb, var(--accent) 38%, transparent) 60%);
}
/* Light theme: the dark-theme sweep (38% accent) reads as a washed-out
   lavender smear on pale backgrounds — drop to a thin 18% underline band and
   keep the text ink-dark. */
:root[data-theme="light"] .intro-lead b, :root[data-theme="light"] .intro-tldr b,
:root[data-theme="light"] .intro-gist b, :root[data-theme="light"] .intro-ciam b,
:root[data-theme="light"] .intro-lead strong, :root[data-theme="light"] .intro-tldr strong,
:root[data-theme="light"] .intro-gist strong, :root[data-theme="light"] .intro-ciam strong{
  color:var(--text);
  background:linear-gradient(180deg, transparent 72%, rgba(250,204,21,.35) 72%);
  background:linear-gradient(180deg, transparent 72%, color-mix(in srgb, var(--accent) 22%, transparent) 72%);
}
:root[data-theme="light"] code{ color:#0e7490; background:rgba(2,132,199,.08); }

/* marker-pen highlight — wrap any phrase: <span class="hf-mark">like this</span>
   variants: .g green (do this) / .r rose (danger) / .b blue (definition) */
.hf-mark{ border-radius:3px; padding:0 3px; color:inherit;
  background:linear-gradient(180deg, transparent 55%, rgba(250,204,21,.35) 55%); }
.hf-mark.g{ background:linear-gradient(180deg, transparent 55%, rgba(74,222,128,.32) 55%); }
.hf-mark.r{ background:linear-gradient(180deg, transparent 55%, rgba(248,113,113,.35) 55%); }
.hf-mark.b{ background:linear-gradient(180deg, transparent 55%, rgba(34,211,238,.32) 55%); }

/* colored-text utilities for prose ("manipulated text"): green=good,
   rose=bad, amber=careful, violet=concept, cyan=term */
.hf-g{ color:var(--good); font-weight:700; }
.hf-r{ color:var(--bad); font-weight:700; }
.hf-a{ color:var(--warn); font-weight:700; }
.hf-v{ color:var(--purple); font-weight:700; }
.hf-c{ color:var(--info); font-weight:700; }

/* handwritten voice — the book's margin-scribble look */
.hf-hand{ font-family:'Segoe Print','Bradley Hand','Comic Sans MS',cursive;
  font-size:13px; line-height:1.5; }

/* sticky note — a Post-it slapped on the page. Default paper-yellow;
   .pink / .blue / .green variants. Slight rotation sells the effect. */
.hf-note{
  background:#fef3c7; color:#4a3a12; max-width:340px;
  font-family:'Segoe Print','Bradley Hand','Comic Sans MS',cursive;
  font-size:13px; line-height:1.55; padding:14px 16px; margin:16px 8px;
  border-radius:2px; transform:rotate(-1.2deg);
  box-shadow:2px 4px 12px rgba(0,0,0,.35), 0 1px 0 rgba(255,255,255,.4) inset;
  position:relative;
}
.hf-note::before{ /* the bit of "tape" holding it up */
  content:''; position:absolute; top:-8px; left:50%; transform:translateX(-50%) rotate(2deg);
  width:74px; height:18px; background:rgba(255,255,255,.28);
  border-left:1px dashed rgba(0,0,0,.12); border-right:1px dashed rgba(0,0,0,.12);
}
.hf-note.pink { background:#fbcfe8; color:#581c3b; transform:rotate(1deg); }
.hf-note.blue { background:#bae6fd; color:#0c3550; transform:rotate(-.8deg); }
.hf-note.green{ background:#bbf7d0; color:#14442a; transform:rotate(1.4deg); }
.hf-note b{ font-weight:800; color:inherit; }
/* The note is paper-colored in BOTH themes (see comment above), but
   [data-hf] code:not(pre code) (devhub-hf.css) paints inline code
   var(--hf-orange2) for the dark kit unconditionally — an orange chip on this
   note's pale paper measured 1.6:1. Inherit the note's own ink instead, same
   fix as .hf-note b above. The [data-hf] copy exists only to out-specify that
   kit rule ((0,2,1) beats its (0,1,3)) — this plain one is the real fallback. */
.hf-note code{ color:inherit; background:rgba(0,0,0,.08); }
[data-hf] .hf-note code{ color:inherit; background:rgba(0,0,0,.08); }

/* annotation arrow — a handwritten label that points at the thing above or
   below it, like the book's scribbled arrows into code.
     <div class="hf-arrow up">this constructor is the whole trick</div>
   .up points at content above; .down (default) points at content below. */
.hf-arrow{
  font-family:'Segoe Print','Bradley Hand','Comic Sans MS',cursive;
  font-size:12.5px; color:var(--warn); max-width:46ch;
  margin:6px 0 6px 34px; transform:rotate(-1deg); position:relative; line-height:1.45;
}
.hf-arrow::before{
  content:'\2935'; /* curved down-right arrow */ position:absolute; left:-26px; top:-4px;
  font-size:22px; transform:rotate(12deg); font-family:inherit;
}
.hf-arrow.up::before{ content:'\2934'; top:auto; bottom:-2px; transform:rotate(-8deg); }
.hf-arrow.cyan{ color:var(--info); } .hf-arrow.green{ color:var(--good); } .hf-arrow.rose{ color:var(--bad); }

/* Brain Power — the book's "stop and actually think" exercise box */
.hf-brain{
  border:2px dashed var(--accent); border-radius:12px; background:var(--panel2);
  padding:14px 18px 15px; margin:18px 0; max-width:86ch;
}
.hf-brain .hf-brain-head{
  display:flex; align-items:center; gap:8px; font-weight:900; font-size:13px;
  text-transform:uppercase; letter-spacing:.08em; color:var(--accent); margin-bottom:7px;
}
.hf-brain .hf-brain-head::before{ content:'\26A1'; font-size:16px; }
.hf-brain p{ margin:0; font-size:13.5px; line-height:1.6; color:var(--text); }

/* "There are no Dumb Questions" — the book's Q&A sidebar
     <dl class="hf-qa"><dt>...</dt><dd>...</dd></dl>  (Q:/A: added by CSS) */
.hf-qa{ background:var(--panel2); border-radius:12px; padding:16px 18px; margin:18px 0;
  border-left:3px solid var(--accent); max-width:88ch; }
.hf-qa::before{ content:'there are no Dumb Questions'; display:block;
  font-family:'Segoe Print','Bradley Hand','Comic Sans MS',cursive;
  font-weight:700; font-size:14px; color:var(--accent); margin-bottom:10px; }
.hf-qa dt{ font-weight:800; color:var(--text); font-size:13.5px; margin:10px 0 4px; }
.hf-qa dt::before{ content:'Q: '; color:var(--accent); font-weight:900; }
.hf-qa dd{ margin:0 0 8px; color:var(--muted); font-size:13px; line-height:1.62; }
.hf-qa dd::before{ content:'A: '; color:var(--good); font-weight:900; }

/* big type — one exaggerated sentence that sticks
     <p class="hf-big">The decorator IS-A beverage AND HAS-A beverage.</p> */
.hf-big{
  font-size:clamp(19px, 2.6vw, 27px); font-weight:900; line-height:1.3;
  margin:20px 0; max-width:30ch;
  background:linear-gradient(90deg, var(--accent), var(--accent2));
  -webkit-background-clip:text; background-clip:text; color:transparent;
}

/* exaggerated before/after contrast
     <div class="hf-vs">
       <div class="bad"><h5>40,000 queries</h5>...</div>
       <div class="good"><h5>1 query</h5>...</div>
     </div>                                                              */
.hf-vs{ display:grid; grid-template-columns:1fr 1fr; gap:12px; margin:16px 0; }
@media (max-width:720px){ .hf-vs{ grid-template-columns:1fr; } }
.hf-vs > .bad, .hf-vs > .good{ border-radius:12px; padding:13px 15px; font-size:12.5px; line-height:1.6; }
.hf-vs > .bad { background:rgba(248,113,113,.08); border:1px solid rgba(248,113,113,.4); }
.hf-vs > .good{ background:rgba(74,222,128,.08);  border:1px solid rgba(74,222,128,.4); }
.hf-vs h5{ margin:0 0 6px; font-size:14px; font-weight:900; }
.hf-vs > .bad  h5{ color:var(--bad); }  .hf-vs > .bad  h5::before{ content:'\274C '; }
.hf-vs > .good h5{ color:var(--good); } .hf-vs > .good h5::before{ content:'\2705 '; }

/* ── Head First kit, wave 2 — components from Bobby's reference mockup ──── */

/* kicker badge — the pill above a page/section title:
     <span class="hf-kicker">Head First &middot; Design Patterns &middot; Ch. 3</span> */
.hf-kicker{
  display:inline-flex; align-items:center; gap:8px;
  background:var(--panel2); border:1px solid var(--border); border-radius:999px;
  padding:5px 13px; font-size:10.5px; font-weight:800;
  text-transform:uppercase; letter-spacing:.09em; color:var(--accent);
}
.hf-kicker::before{ content:''; width:6px; height:6px; border-radius:50%; background:var(--accent); }

/* receipt — a running total built one line at a time (decorator orders,
   query costs, latency budgets). Monospace, dashed like thermal paper:
     <div class="hf-receipt"><div class="hf-receipt-head">order total</div>
       <div class="row"><span>House Blend</span><span>$0.89</span></div>
       <div class="row total"><span>Total</span><span>$1.29</span></div></div> */
.hf-receipt{
  background:var(--panel2); border:1px dashed rgba(148,163,184,.35); border-radius:10px;
  padding:14px 16px; max-width:340px; margin:12px 0;
  font-family:'Cascadia Code',ui-monospace,Consolas,monospace; font-size:12.5px; color:#dbe6f3;
}
.hf-receipt-head{
  font-size:10px; font-weight:800; text-transform:uppercase; letter-spacing:.08em;
  color:var(--muted); margin-bottom:9px;
}
.hf-receipt .row{ display:flex; justify-content:space-between; gap:20px; padding:2.5px 0; }
.hf-receipt .row.total{
  border-top:1px solid rgba(148,163,184,.25); margin-top:8px; padding-top:8px;
  font-weight:800; color:var(--warn);
}

/* chain — a wraps-B-wraps-C flow of chips (decorator stacks, filter chains,
   middleware pipelines):
     <div class="hf-chain"><span class="n">RealClient</span><span class="a">&rarr;</span>
       <span class="n">CircuitBreaker</span><span class="a">&rarr;</span><span class="n">LoggingClient</span></div> */
.hf-chain{ display:flex; flex-wrap:wrap; align-items:center; gap:7px; margin:10px 0; }
.hf-chain .n{
  background:var(--panel2); border:1px solid var(--border); border-radius:8px;
  padding:4px 11px; font-family:ui-monospace,monospace; font-size:12px; color:#dbe6f3;
}
.hf-chain .a{ color:var(--accent2); font-weight:800; }

/* ── mobile safety net (added with the sitewide viewport-meta fix) ─────────
   410 pages had no <meta name="viewport">, so phones laid them out at 980px
   and scaled the result down. Adding the tag makes the phone layout REAL,
   which exposes overflow that the old scaling quietly hid — a fixed-px h1
   running off the edge, a wide <pre> pushing the whole document sideways.
   These rules are the floor that makes the tag safe on every page. Scoped to
   a phone media query, so desktop layout is untouched. */
@media (max-width:680px){
  body{ padding:16px 14px; }
  h1{ font-size:clamp(22px,6.4vw,30px); line-height:1.15; overflow-wrap:break-word; }
  h2{ font-size:clamp(18px,5vw,22px); overflow-wrap:break-word; flex-wrap:wrap; }
  h3{ font-size:clamp(16px,4.4vw,19px); overflow-wrap:break-word; flex-wrap:wrap; }
  .grid2,.grid3{ grid-template-columns:1fr; }
}
/* A wide code block or table must scroll ITSELF, never the page. */
pre,table,.rt-stage,.cw-code{ max-width:100%; }
pre,table{ overflow-x:auto; }
html,body{ overflow-x:hidden; }

/* ── reduced motion: honor the OS setting everywhere ───────────────────────
   Before this block, prefers-reduced-motion appeared on 3 of 528 pages while
   ~300 pages define unguarded @keyframes/transitions in their own <style> —
   per-page CSS this file cannot reach rule-by-rule, but CAN blanket-override.
   Every animation/transition collapses to .01ms: state changes still land
   (the chip still ARRIVES at its node, animationend still fires for any code
   waiting on it) — they just stop moving. Engines are timer-driven, so the
   step walk itself keeps its pacing; only the glide is stilled.
   Bonus for tooling: tmp_shot screenshots become deterministic under
   reducedMotion:'reduce' instead of catching mid-flight frames. */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    scroll-behavior:auto !important;
  }
}
