/* =========================================================
   Design tokens — Vibe Coder Playground design system.
   One theme: the near-black ground the home page carries,
   her green as the accent, mono microcopy.
   (The admin CSS editor writes to :root variables here.)
   ========================================================= */
:root {
  /* ═══ ONE THEME, AND IT IS THE DARK ONE ════════════════════════════════════
   *
   * The site used to ship two designs: a pale-blue page (--paper #eff5fb) for
   * anyone whose OS said "light", and a near-black one behind
   * `prefers-color-scheme: dark`. The owner's decision, 2026-09-03: everyone
   * gets the black one, which is the design the home page and the "where are
   * you stuck" page already carry.
   *
   * So the dark values moved UP here and the media query is gone. That is
   * deliberately not the same as "dark mode is now the default" — there is no
   * light theme left to fall back to, and a token declared only inside a media
   * query would now apply to nobody.
   *
   * THE GROUND, THE SURFACES AND THE TEXT ARE THE SAME VALUES assets/experience.css
   * uses, name for name, so the two stylesheets describe ONE design rather than
   * two that happen to look similar:
   *
   *     --paper #0f1023 = experience --ink        --ink   #f1f2fa = --text
   *     --surface #161833 = --ink-2               --muted #a9abc6 = --muted
   *     --surface-2 #1c1f40 = --ink-3             --faint #7d80a3 = --faint
   *
   * EVERY VALUE BELOW WAS MEASURED against this ground, not carried over —
   * there is no automated contrast gate for the website (scripts/a11y-check.mjs
   * covers ui/src, the desktop app), so the ratios are written down here where
   * the next person changing a hex can see what it has to clear.
   *
   * (The admin CSS editor writes to :root variables here.)
   */
  --paper: #0f1023;
  /* The header ground, unchanged and now equal to --paper. The bar keeps its
     own token because .nav paints it at 94% opacity over the page, which is
     what gives the same translucent band the home page's .hud has. */
  --bar: #0f1023;
  --surface: #161833;
  --surface-2: #1c1f40;
  --ink: #f1f2fa;       /* 16.82:1 on the ground */
  --muted: #a9abc6;     /*  8.34:1 */
  /* LIFTED from experience.css's #7d80a3, and this is the one token where the two
     files differ on purpose. #7d80a3 is 4.91:1 on the page ground, which is fine
     there — but this stylesheet puts faint text on CARDS, and on --surface-2 the
     same value is 4.16:1. Measured on classic.html: seven small labels failed,
     every one of them at 4.16. #8588ab is the smallest lift that clears 4.5:1 on
     all three grounds (4.63 on the deepest). experience.css has no raised
     surfaces, so its value is right where it is. */
  --faint: #8588ab;     /*  5.46:1 ground · 5.04:1 surface · 4.63:1 surface-2 */
  --line: rgba(255, 255, 255, 0.10);
  --line-strong: rgba(255, 255, 255, 0.18);

  /* ─── PURPLE COMES IN TWO VALUES, AND WHICH ONE YOU WANT DEPENDS ON THE JOB ──
   *
   * Her brand purple is #6633cc. On this ground it measures 2.60:1, which is
   * below the 3:1 a filled control needs to be distinguishable from the page
   * and far below the 4.5:1 text needs. It is not usable as either here. So:
   *
   *   --brand    #7b95ff  TEXT. Links, accents, hover states.       6.78:1
   *   --brand-2  #7141d0  FILL. The button.        3.01:1 boundary, 6.23:1 white label
   *   --brand-2-lift #8654e6  the button's hover.  3.95:1 boundary, 4.75:1 white label
   *
   * Both fills are within a few percent of #6633cc and read as the same purple;
   * what they buy is a boundary a person can actually see. The home page's own
   * button is #6633cc at 2.60:1 — that is a finding about that page, not a
   * value to copy across ninety more.
   */
  --brand: #7b95ff;
  --brand-2: #7141d0;
  --brand-2-lift: #8654e6;
  --brand-soft: rgba(123, 149, 255, 0.14);

  /* ─── THE ACCENT IS HER GREEN, AND ON THIS GROUND IT NEEDS NO DARKENING ─────
   *
   * #66ff66 measures 14.36:1 here — the highest-contrast color in the palette.
   * The light theme had to darken it to #265f26 to use it at all, and that twin
   * is now the same value as the bright one because there is nothing left to
   * darken it for. It is what the home page sets "The Audit Layer" in, and it
   * is now what every accent word on the site is set in.
   */
  --accent-green: #66ff66;
  --accent-green-text: #66ff66;

  --info: #33ccff;          /* 10.03:1 — no darkened twin needed on this ground */
  --info-text: #33ccff;

  /* ─── SPARK: THE SECOND ACCENT, AND THE ONE THAT MEANS SOMETHING WENT WRONG ─
   *
   * It carries both jobs — the coral rails and small labels that emphasize, and
   * `.verdict.fail` / `.cd-*-bad` / `.cd-revoked-tag`. That is fine on a dark
   * ground, where coral reads as emphasis in one context and as an alert in the
   * other; what was NOT fine is what the light theme did with the same token.
   *
   * IT SET --spark TO A DARKENED GREEN (#3f9e3f), because the light ground
   * needed the hero accent darkened. Every failure marker on the site inherited
   * that: on a light-mode machine `.verdict.fail` and `.cd-pill.cd-bad` rendered
   * GREEN, on the site of a product that sells audit verdicts. Deleting the
   * light theme deletes that, which is worth writing down — it was never
   * reported, and nothing here was rewritten to fix it.
   *
   * What DID move is the primary accent: the eyebrow, the hero's emphasized
   * word, the stat number and the wordmark are --accent-green now, so they
   * match the home page instead of turning coral. See the Hero note below.
   */
  --spark: #ff6b4a;         /* 6.66:1 */
  --spark-text: #ff8a6d;    /* 8.14:1 */
  --spark-soft: rgba(255, 107, 74, 0.14);
  --good: #2fd196;          /* 9.55:1 */
  --good-text: #2fd196;
  --warn: #f0b44e;          /* 10.14:1 */
  --warn-text: #f0b44e;
  /* "on-tint": text sitting on its OWN tinted background. The tint DARKENS this
     ground rather than lifting it, so these go brighter, not darker. */
  --good-on-tint: #4ee0ad;
  --spark-on-tint: #ff9b80;
  --warn-on-tint: #ffca70;
  --star: #f5b73f;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 18px 44px rgba(0, 0, 0, 0.4);
  --radius: 10px;
  /* Motion curves, shared with assets/experience.css BY VALUE so the two files
     animate identically. Added when the "where are you stuck" page was ported
     off experience.css onto this token set (2026-09-03). */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --glide: cubic-bezier(.16, 1, .3, 1);
  --maxw: 1060px;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Form controls, scrollbars and the canvas behind the page render dark. The
     site has no light theme, so declaring one value is the whole answer — a
     `light dark` pair would put the search input back on white for half the
     visitors this change exists to move. */
  color-scheme: dark;

  /* ---- legacy aliases (admin panel + older inline styles) ---- */
  --ink-2: var(--surface);
  --ink-3: var(--line-strong);
  --fog: var(--faint);
  --paper-dim: var(--muted);
  --accent: var(--brand);
  --accent-2: var(--spark);
}

* { box-sizing: border-box; }

/* HIDDEN HAS TO MEAN HIDDEN, and without this rule it did not.
   The [hidden] display:none that browsers apply comes from the user-agent
   stylesheet, and a UA declaration loses to ANY author declaration regardless
   of specificity. `.signup-form { display: flex }` below is an author
   declaration, so every `<form class="signup-form" ... hidden>` on this site
   rendered fully visible and submittable. vibe-coding-course/submit-video.html is built on
   exactly that: it reveals a "submissions are not open yet" notice and then
   leaves a working form under it. Nothing looked broken, which is why it lasted.
   !important rather than ordering, so a class or id rule added later cannot
   quietly re-open the same hole. Safe to apply globally here: no script in this
   repo reveals an element by writing style.display while leaving the attribute
   on — they all clear `hidden` itself. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16.5px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
  html { scroll-behavior: auto; }
}

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 3px; }

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
.mono { font-family: var(--mono); }

/* ---------- Top nav ---------- */
/* The header bar is DARK on both themes — near-black with a blue cast, the
   same #0f1023 the dark theme already uses as its ground. It is deliberately
   not theme-reactive: the logo is a fixed multi-hue artwork, and a bar that
   flipped to light under it would put its dark violet frame (2.59:1 on light)
   at the wrong end of the contrast range half the time.

   Kept slightly translucent with a blur because it is sticky and content
   scrolls beneath it; the tint is opaque enough (94%) that text over a busy
   section never loses its ground. */
.nav {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bar) 94%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.09);
}
/* Links measured on the bar, not assumed: #c9cbe0 is 11.71:1 there, and the
   hover state lifts to near-white rather than changing hue. */
.nav .nav-links a { color: #c9cbe0; }

/* Course badges, carried in the bar so progress is visible while you are
   reading a stage and not only on the overview. Measured on the bar (#0f1023):
   the unearned outline is rgba(255,255,255,0.38), and --brand-2 is the same
   3.48:1 purple the nav button already uses there — --brand itself is 2.82:1
   on this ground and would read as a smudge. */
/* The Free Course nav item on course pages: the reader's five phase shields
   stacked ABOVE the label, the whole thing one link to the course. The shields
   show progress; the click resumes. On non-course pages this class is absent and
   the plain link is used. */
/* The label holds the SAME baseline as every other nav link; the shields FLOAT
   above it (absolutely positioned) so they never push "Free Course" down out of
   the row (owner 2026-09-03). The link itself is a normal inline nav item. */
.nav-course { position: relative; display: inline-flex; align-items: center; }
.nav-course-label { display: block; }
.nav-shields {
  position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
  margin-bottom: 3px; display: inline-flex; align-items: center; gap: 5px;
  pointer-events: none;   /* the whole item is one link; the shields are decoration on it */
}
.nav-shields:empty { display: none; }   /* no progress yet → nothing floats */
.nav-shields .vc101-shield { width: 13px; height: 15px; }
.nav-shields .vc101-shield path { stroke: rgba(255, 255, 255, 0.38); }
.nav-shields .vc101-shield.is-earned path { fill: var(--accent-green); stroke: var(--accent-green); }
@media (max-width: 640px) { .nav-shields { display: none; } }
.nav .nav-links a:hover { color: #eceef7; }
/* Same fill as .btn, stated so the nav cannot drift from it. */
.nav .btn { background: var(--brand-2); border-color: var(--brand-2); }
.nav .btn:hover, .btn:hover { background: var(--brand-2-lift); border-color: var(--brand-2-lift); }
/* The skip link lands on the dark bar, so it cannot keep the light-theme fill. */
.nav .skip-link:focus, .skip-link:focus { background: var(--bar); color: #eceef7; }
/* min-height tracks the logo (108 + 12 above and below) rather than a round
   number, so the two cannot drift apart when one is resized. */
.nav .wrap { display: flex; align-items: center; justify-content: space-between;
  min-height: 132px; gap: 16px; }
@media (max-width: 640px) { .nav .wrap { min-height: 100px; } }
@media (max-width: 400px) { .nav .wrap { min-height: 86px; } }
.brand {
  display: inline-flex; align-items: center; gap: 11px;
  font-weight: 680; font-size: 16px; color: var(--ink); letter-spacing: -0.01em;
  white-space: nowrap;
  /* A flex item defaults to min-width:auto, which refuses to shrink below its
     content — that is what let the nav push the whole page sideways. Allowing
     the brand to shrink means the bar absorbs a tight viewport instead of the
     document scrolling horizontally. */
  min-width: 0;
}
/* No overflow:hidden here: it existed to truncate the old TEXT wordmark, and
   there is no text now — it would clip the logo's deliberate overhang. */
.brand span { color: var(--accent-green); }
/* The real mark. No gradient plate behind it — the artwork carries its own
   color, and a tinted box under a multi-hue logo muddies both. */
/* Compact header, after ~64px of scroll. The bar is 133px tall and sticky, so
   at rest it holds roughly a sixth of a laptop viewport for as long as someone
   reads. Shrinking gives that back without losing the mark.

   Only the logo height and the bar's min-height transition: both are cheap, and
   animating either one alone would leave the logo and its container out of step
   mid-flight. Under prefers-reduced-motion the sizes still change — the change
   is the feature — but instantly, with no tween. */
.nav .wrap, .brand-logo { transition: min-height .22s ease, height .22s ease; }
.nav.is-compact .wrap { min-height: 72px; }
.nav.is-compact .brand-logo--full { height: 52px; }
.nav.is-compact .brand-logo--icon { height: 44px; }
@media (max-width: 640px) {
  .nav.is-compact .wrap { min-height: 64px; }
  .nav.is-compact .brand-logo--icon { height: 40px; }
}
@media (prefers-reduced-motion: reduce) {
  .nav .wrap, .brand-logo { transition: none; }
}

/* The bar CONTAINS the logo rather than the logo overhanging it. The overhang
   was dropped because the mark's bright colors (cyan 1.04:1, green 1.16:1,
   yellow 1.17:1 on this ground) are built for the near-black bar and faded out
   on the light half they spilled onto. The artwork works on one ground, so it
   stays on that one.

   Which image shows is decided HERE, not by <picture>: see partials.mjs — a
   resolved <picture> source does not revert when the viewport grows. */
.brand-logo { width: auto; flex: none; }
.brand-logo--full { display: block; height: 108px; }
.brand-logo--icon { display: none;  height: 76px; }

/* Owner 2026-09-16: "keep the full logo and not the icon when we go to other
   pages." The wordmark shows at every width; phones get it smaller instead of
   swapping to the icon. The icon <img> stays in the partial, never displayed. */
@media (max-width: 640px) {
  .brand-logo--full { height: 64px; }
  .nav.is-compact .brand-logo--full { height: 44px; }
}
@media (max-width: 400px) { .brand-logo--full { height: 56px; } }
.brand-sub { font-size: 11.5px; color: var(--faint); font-weight: 400; letter-spacing: 0; }
.brand-sub em { font-style: normal; color: var(--accent-green); font-weight: 600; }
.nav-links { display: flex; gap: 22px; align-items: center; }
.nav-links a { color: var(--muted); font-size: 14px; white-space: nowrap; }
.nav-links a:hover { color: var(--ink); text-decoration: none; }
.nav-links a.btn, .nav-links a.btn:hover { color: #fff; }
/* Tablets and small laptops (761–900px). The links are still shown here, and on
   the home variant — five items plus the CTA — they needed 836px against 720px
   of room, so the nav overflowed the page at 768px (iPad portrait) even though
   the phone rule below looked like it had covered mobile. It hadn't: the two
   rules left an untested band between them, which is where the bug lived.
   Tightening type and gaps keeps the links usable rather than hiding
   navigation from every tablet. */
@media (max-width: 900px) {
  .nav .wrap { gap: 12px; }
  .brand { font-size: 15px; }
  .nav-links { gap: 12px; }
  .nav-links a { font-size: 13px; }
  .nav-links a.btn { padding: 9px 12px; }
}
/* Small screens: keep the bar to brand + Download; sections are one scroll away
   and the KB is linked from the hero and footer. */
@media (max-width: 760px) {
  .nav-links { gap: 14px; }
  .nav-links a:not(.btn) { display: none; }
}
/* Phones: hiding the secondary links is NOT enough on its own. The brand
   (glyph + nowrap wordmark) and the CTA are both fixed-width, so at 375px they
   needed 387px against 327px of room and pushed the whole PAGE sideways — on
   every page, since the nav is templated.

   Two parts, and the second is the one that matters:
     1. Scale the pieces down so the real labels fit with room to spare.
     2. `min-width: 0` on the brand so it is ALLOWED to shrink, and the CTA is
        marked `flex: none` so it never gives up space instead. Without (2) the
        fix is tuned to the exact strings "Vibe Coder Playground" and "Get early
        access"; with it, a longer label ellipsizes rather than breaking the
        page layout. The bug was a nav that could not shrink, so the fix is to
        make it shrinkable — not to make today's text 8px narrower. */
@media (max-width: 560px) {
  .nav .wrap { gap: 10px; padding-left: 16px; padding-right: 16px; }
  /* The ellipsis machinery here was for the TEXT wordmark, and .glyph for a CSS
     mark — both gone. `overflow: hidden` in particular now CLIPS the logo's
     deliberate overhang, so it is removed rather than left as harmless.
     min-width: 0 stays: that is the part that keeps a tight viewport from
     pushing the page sideways, and it has nothing to do with clipping. */
  .brand { min-width: 0; }
  .nav-links { flex: none; gap: 10px; }
  .nav-links a.btn { font-size: 13px; padding: 9px 13px; }
}
/* Very small phones (iPhone SE is 320pt). At 560px the wordmark fits only by
   ellipsizing — "Vibe Coder P…" — which reads as a broken layout even though
   the page no longer scrolls sideways. The glyph is decorative and appears on
   the home page alone, so dropping it here buys back the width to show the
   whole name. Losing a decorative mark is a better trade than truncating the
   company's name. */
@media (max-width: 400px) {
  .nav .wrap { gap: 8px; padding-left: 14px; padding-right: 14px; }
  .nav .glyph { display: none; }
  .brand { font-size: 14px; }
  .nav-links a.btn { font-size: 12.5px; padding: 8px 10px; }
}
.btn {
  display: inline-block; font-family: var(--sans); font-size: 14px; font-weight: 620;
  padding: 10px 18px; border-radius: var(--radius);
  /* --brand-2, not --brand: the link purple takes a white label at 2.77:1.
     See the two-purples note in the token block. */
  background: var(--brand-2); color: #fff; border: 1px solid var(--brand-2);
  cursor: pointer; transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.btn:hover { transform: translateY(-1px); text-decoration: none; box-shadow: var(--shadow); }
.btn.ghost { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn.ghost:hover { border-color: var(--brand); color: var(--brand); box-shadow: none; }

/* ---------- Hero ----------
   THE ACCENT WORD IS --accent-green, NOT --spark. Eight rules on this site put a
   word or a rule in the brand accent — the nav and footer wordmark, the eyebrow
   and its dash, the hero's emphasized word, the big stat number, and an
   article's category tag. All of them read as identity, none of them as a
   warning, so they take her green (14.36:1 here) and --spark is left to mean
   bad/failed/revoked and nothing else. The home page sets "The Audit Layer" in
   the same value, which is the point. */
.hero { padding: clamp(56px, 8vw, 96px) 0 clamp(28px, 4vw, 48px); position: relative; }
.eyebrow, .kicker {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--accent-green);
  display: inline-flex; align-items: center; gap: 8px;
}
.eyebrow::before, .kicker::before { content: ""; width: 22px; height: 1px; background: var(--accent-green); }
.hero h1 {
  font-weight: 720; letter-spacing: -0.032em;
  font-size: clamp(2.4rem, 6vw, 4.1rem); line-height: 1.03; margin: 18px 0 0;
  max-width: 20ch; text-wrap: balance;
}
.hero h1 em { font-style: normal; color: var(--accent-green); }
.hero p.lede {
  font-size: clamp(1.05rem, 2vw, 1.28rem); color: var(--muted);
  max-width: 58ch; margin: 22px 0 0; line-height: 1.55;
}
.hero .who {
  margin-top: 24px; font-family: var(--mono); font-size: 12.5px; color: var(--faint);
  display: flex; gap: 10px; flex-wrap: wrap; align-items: center;
}
.hero .who b { color: var(--ink); font-weight: 600; }
.who .dot { width: 4px; height: 4px; border-radius: 50%; background: var(--line-strong); }
.hero-cta { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 30px; }

/* ---------- "Jump straight to an answer" — the course hero's search ----------
   Same tokens as .kb-search on purpose: it submits to that page, so looking
   like a different control would be a small lie about where it goes. */

/* ---------- Premise band (shell vs apps) ---------- */
.premise {
  margin: clamp(30px, 5vw, 44px) 0 8px;
  display: grid; grid-template-columns: 1fr auto 1fr; gap: 18px; align-items: stretch;
}
@media (max-width: 640px) { .premise { grid-template-columns: 1fr; } }
.premise .pm {
  background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
  padding: 18px 20px; display: flex; flex-direction: column; gap: 5px; box-shadow: var(--shadow);
}
.premise .pl { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--brand); }
.premise .pm:last-of-type .pl { color: var(--spark-text); }
.premise .pm strong { font-size: 1.05rem; letter-spacing: -0.01em; font-weight: 640; }
.premise .pm .pd { font-size: 13px; color: var(--muted); line-height: 1.5; }
.premise .arrow { align-self: center; font-family: var(--mono); font-size: 12px; color: var(--faint); white-space: nowrap; }
@media (max-width: 640px) { .premise .arrow { transform: rotate(90deg); justify-self: center; } }

/* ---------- Stat band ---------- */
.statband { border-top: 1px solid var(--line); background: linear-gradient(120deg, var(--brand-soft), transparent 62%); }
.statband .wrap { display: grid; grid-template-columns: auto 1fr; gap: clamp(24px, 5vw, 60px); align-items: center; padding: clamp(36px, 6vw, 60px) 24px; }
@media (max-width: 720px) { .statband .wrap { grid-template-columns: 1fr; gap: 18px; } }
.stat-figure { display: flex; flex-direction: column; gap: 8px; max-width: 320px; }
.stat-num { font-family: var(--mono); font-weight: 800; font-size: clamp(3.2rem, 10vw, 5.2rem); line-height: 0.95; letter-spacing: -0.035em; color: var(--accent-green); }
.stat-cap { font-size: 13.5px; color: var(--muted); line-height: 1.5; }
.stat-point p { margin: 0; font-size: clamp(1.08rem, 2vw, 1.3rem); color: var(--ink); line-height: 1.5; text-wrap: pretty; }
.stat-point strong { color: var(--spark-text); }
.stat-cite { margin-top: 14px !important; font-family: var(--mono); font-size: 12px !important; color: var(--faint) !important; }
.stat-cite a { color: var(--faint); text-decoration: underline; }
.stat-cite a:hover { color: var(--brand); }

/* ---------- Sections ---------- */
section.block { padding: clamp(46px, 7vw, 72px) 0; border-top: 1px solid var(--line); }
.section-head { margin-bottom: 30px; }
.section-head h2 {
  font-size: clamp(1.4rem, 3vw, 1.95rem); letter-spacing: -0.02em;
  font-weight: 660; margin: 10px 0 10px; text-wrap: balance;
}
.section-head p { color: var(--muted); max-width: 60ch; margin: 0; font-size: 15px; }

.grid { display: grid; gap: 16px; }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 820px) { .grid.cols-3, .grid.cols-2 { grid-template-columns: 1fr; } }

.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
  padding: 24px; box-shadow: var(--shadow);
  transition: transform 0.18s ease, border-color 0.18s ease;
  display: flex; flex-direction: column; gap: 8px;
}
.card:hover { transform: translateY(-3px); border-color: var(--line-strong); }
.card.spotlight { border-left: 3px solid var(--spark); }
.card.brandline { border-left: 3px solid var(--brand); }
.card .num, .card .tag {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em; color: var(--faint);
  display: flex; align-items: center; gap: 8px;
}
.card.spotlight .num, .card.spotlight .tag { color: var(--spark-text); }
.card.brandline .num, .card.brandline .tag { color: var(--brand); }
.card h3 { margin: 2px 0 0; font-size: 1.16rem; letter-spacing: -0.015em; font-weight: 640; }
.card p { margin: 0; color: var(--muted); font-size: 14.5px; line-height: 1.56; }
.card .feat-note {
  margin-top: 6px; font-family: var(--mono); font-size: 12px; color: var(--faint);
  padding-top: 12px; border-top: 1px dashed var(--line);
}
.card.wide { grid-column: 1 / -1; }
.card.wide .body { display: grid; grid-template-columns: 1.1fr 1fr; gap: 26px; align-items: start; }
@media (max-width: 720px) { .card.wide .body { grid-template-columns: 1fr; gap: 14px; } }
.steplist { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.steplist li { display: flex; gap: 11px; font-size: 14px; color: var(--muted); align-items: baseline; }
.steplist li b { color: var(--ink); font-weight: 600; }
.steplist .snum { font-family: var(--mono); font-size: 11px; color: var(--spark-text); flex: none; width: 18px; }

/* ---------- Quality gate ---------- */
.gate { background: var(--surface); border: 1px solid var(--line); border-radius: 18px; overflow: hidden; box-shadow: var(--shadow); }
.gate-top { display: flex; gap: 10px; padding: 20px 24px; border-bottom: 1px solid var(--line); flex-wrap: wrap; align-items: center; }
.verdict { font-family: var(--mono); font-size: 12px; font-weight: 600; padding: 5px 11px; border-radius: 999px; display: inline-flex; align-items: center; gap: 7px; }
.verdict::before { content: ""; width: 7px; height: 7px; border-radius: 50%; }
.verdict.pass { background: color-mix(in srgb, var(--good) 14%, transparent); color: var(--good-on-tint); }
.verdict.pass::before { background: var(--good); }
.verdict.caution { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn-on-tint); }
.verdict.caution::before { background: var(--warn); }
.verdict.fail { background: var(--spark-soft); color: var(--spark-on-tint); }
.verdict.fail::before { background: var(--spark); }
.gate-top .gt-label { font-size: 14px; color: var(--muted); }

/* Pillars — the 5 headline groups. The full per-check rubric is kept private (not on the site). */
.pillars { display: grid; grid-template-columns: repeat(5, 1fr); }
.pillar { padding: 20px 22px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.pillar .pn { font-weight: 660; font-size: 15.5px; letter-spacing: -0.01em; }
.pillar p { margin: 8px 0 12px; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.pillar .pc { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.pillar .pc b { color: var(--spark-text); font-weight: 600; }
@media (max-width: 900px) { .pillars { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .pillars { grid-template-columns: 1fr; } }

/* ---------- Measured stats ("by the numbers") ----------
   Rendered from the workbench's published feed by assets/stats.js. Tiles only
   appear for real, positive, measured values, so the grid is intentionally
   variable-length — hence auto-fit rather than a fixed column count. */
.stats-lead { font-size: 19px; line-height: 1.5; letter-spacing: -0.01em; max-width: 60ch; margin: 0 0 22px; }
.stats-lead strong { color: var(--spark-text); font-weight: 660; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); gap: 12px; }
.stat { background: var(--surface-2); border: 1px solid var(--line); border-radius: 13px; padding: 18px 16px; }
.stat .sv { font-size: 32px; font-weight: 680; letter-spacing: -0.03em; color: var(--brand); line-height: 1; }
.stat .sl { margin-top: 7px; font-size: 13px; color: var(--muted); line-height: 1.4; }
.stats-note { margin: 16px 0 0; font-family: var(--mono); font-size: 11.5px; color: var(--faint); }

/* ---------- Workflow strip ---------- */
.flow { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; }
@media (max-width: 860px) { .flow { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 480px) { .flow { grid-template-columns: repeat(2, 1fr); } }
.step { background: var(--surface-2); border: 1px solid var(--line); border-radius: 13px; padding: 16px 15px; }
.step .n { font-family: var(--mono); font-size: 11px; color: var(--brand); font-weight: 600; }
.step h4 { margin: 8px 0 4px; font-size: 14.5px; letter-spacing: -0.01em; }
.step p { margin: 0; font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.step.audit { border-color: color-mix(in srgb, var(--spark) 40%, var(--line)); }
.step.audit .n { color: var(--spark-text); }

/* ---------- Closer band ---------- */
.closer {
  margin: clamp(46px, 7vw, 72px) 0 0; padding: 30px; border-radius: 18px;
  border: 1px solid var(--line); background: linear-gradient(120deg, var(--brand-soft), transparent 70%);
  display: flex; gap: 22px; justify-content: space-between; align-items: center; flex-wrap: wrap;
}
.closer h3 { margin: 0 0 6px; font-size: 1.3rem; letter-spacing: -0.02em; }
.closer p { margin: 0; color: var(--muted); font-size: 14.5px; max-width: 52ch; }
.chips { display: flex; gap: 8px; flex-wrap: wrap; }
.chip { font-family: var(--mono); font-size: 11.5px; padding: 7px 12px; border-radius: 999px; border: 1px solid var(--line-strong); color: var(--muted); }

/* ---------- KB list ---------- */
.kb-item {
  display: block; background: var(--surface); border: 1px solid var(--line);
  border-radius: 16px; padding: 22px 24px; margin-bottom: 14px; box-shadow: var(--shadow);
  transition: transform 0.15s ease, border-color 0.15s ease;
  /* As a grid item this defaults to min-width:auto and refuses to shrink below
     its longest unbreakable word, bursting its track and scrolling the page
     sideways at 320px. Shrink instead, and break the offending word rather than
     the layout. */
  min-width: 0;
  overflow-wrap: anywhere;
}
.kb-item:hover { transform: translateY(-2px); border-color: var(--line-strong); text-decoration: none; }
.kb-item .tag { font-family: var(--mono); font-size: 11px; color: var(--accent-green); text-transform: uppercase; letter-spacing: 0.1em; }
.kb-item h3 { color: var(--ink); font-size: 19px; margin: 8px 0 6px; letter-spacing: -0.015em; font-weight: 640; }
.kb-item p { color: var(--muted); margin: 0; font-size: 14.5px; }
.kb-meta { font-family: var(--mono); font-size: 12px; color: var(--faint); margin-top: 12px; }

/* ---------- Article ---------- */
.article { padding: 56px 0 88px; }
.article-head { max-width: 760px; margin: 0 auto 36px; }
.article-head .tag { font-family: var(--mono); font-size: 11.5px; color: var(--accent-green); text-transform: uppercase; letter-spacing: 0.12em; }
.article-head h1 { font-size: clamp(1.9rem, 4.4vw, 2.8rem); line-height: 1.08; letter-spacing: -0.028em; font-weight: 700; margin: 14px 0 14px; text-wrap: balance; }
.article-head .byline { font-family: var(--mono); font-size: 12.5px; color: var(--faint); }
.prose { max-width: 720px; margin: 0 auto; }
/* Email addresses and URLs in prose are single unbreakable tokens; on a very
   narrow screen one of them is wider than the column and takes the page with
   it. Break the address, not the layout. */
.prose a { overflow-wrap: anywhere; }
.prose h2 { font-size: 1.5rem; letter-spacing: -0.02em; font-weight: 660; margin: 42px 0 12px; }
.prose h3 { font-size: 1.15rem; font-weight: 640; margin: 30px 0 10px; }
.prose p { color: var(--muted); margin: 0 0 17px; }
.prose ul, .prose ol { color: var(--muted); padding-left: 22px; margin: 0 0 17px; }
.prose li { margin: 8px 0; }
.prose strong, .prose b { color: var(--ink); }
.prose code {
  font-family: var(--mono); background: var(--brand-soft); border: 1px solid var(--line);
  padding: 2px 6px; border-radius: 5px; font-size: 0.88em; color: var(--brand);
}
.prose pre { background: var(--surface); border: 1px solid var(--line); border-radius: 10px; padding: 18px; overflow-x: auto; margin: 0 0 20px; box-shadow: var(--shadow); }
.prose pre code { background: none; border: none; padding: 0; color: var(--muted); }
.prose blockquote { border-left: 3px solid var(--spark); margin: 0 0 18px; padding: 4px 0 4px 18px; color: var(--muted); font-style: italic; }
.callout {
  background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--spark);
  border-radius: 12px; padding: 16px 18px; margin: 0 0 20px; box-shadow: var(--shadow);
  color: var(--muted); font-size: 15px;
}
.callout strong { color: var(--ink); }
.prose .table-wrap { overflow-x: auto; margin: 0 0 20px; }
.prose table { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 460px; }
.prose th, .prose td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
.prose th { font-family: var(--mono); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--faint); font-weight: 600; }
.prose td { color: var(--muted); }
.prose tbody tr:hover { background: var(--surface-2); }
.prose td strong { color: var(--ink); }
.placeholder { background: color-mix(in srgb, var(--warn) 10%, transparent); border: 1px dashed var(--warn); color: var(--warn-text); padding: 2px 6px; border-radius: 4px; font-family: var(--mono); font-size: 0.85em; }

/* ---------- Signup / survey ---------- */
.signup { max-width: 640px; margin: 44px auto 0; background: var(--surface); border: 1px solid var(--line); border-radius: 18px; padding: clamp(24px, 4vw, 36px); box-shadow: var(--shadow); }
.signup-head { text-align: center; margin-bottom: 24px; }
.signup-head h2 { margin: 0 0 8px; }
.signup-head p { margin: 0 auto; color: var(--muted); font-size: 15px; max-width: 48ch; }
.signup-form { display: flex; flex-direction: column; gap: 16px; }
.fld { display: flex; flex-direction: column; gap: 6px; margin: 0; border: 0; padding: 0; min-width: 0; }
.fld label, .fld legend { font-family: var(--mono); font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); padding: 0; }
.fld .req { color: var(--spark-text); }
.fld .opt { font-weight: 400; color: var(--faint); }
/* Shared field styling — the signup form and the marketplace review form use the
   same rules so they can never drift apart. */
/* Matched by EXCLUSION rather than by listing types. The old selector named
   input[type="email"] only, so the video-submission form — same .signup-form
   class, but type="url" and type="text" fields — rendered as unstyled browser
   defaults with 1px of padding inside an otherwise styled form. Enumerating
   types means every new field type is a silent regression; excluding the few
   that must keep native chrome means new types are styled by default. */
.signup-form input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="submit"]):not([type="button"]),
.signup-form select, .signup-form textarea,
.rform input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]),
.rform select, .rform textarea {
  width: 100%; background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: var(--radius);
  color: var(--ink); font-family: var(--sans); font-size: 15px; padding: 11px 13px;
}
.signup-form textarea, .rform textarea { resize: vertical; }
.signup-form input:focus, .signup-form select:focus, .signup-form textarea:focus,
.rform input:focus, .rform select:focus, .rform textarea:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.checks { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
@media (max-width: 560px) { .checks { grid-template-columns: repeat(2, 1fr); } }
.checks label { display: flex; align-items: center; gap: 7px; font-family: var(--sans); font-size: 13.5px; text-transform: none; letter-spacing: 0; color: var(--ink); background: var(--surface-2); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; cursor: pointer; }
.checks input { accent-color: var(--brand); flex: none; }
/* Interest checkboxes carry a second line of hint text under the label. */
.checks-interest { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 720px) { .checks-interest { grid-template-columns: 1fr; } }
.checks label.interest { flex-direction: column; align-items: flex-start; gap: 3px; }
.checks label.interest .ck { display: flex; align-items: center; gap: 7px; }
.checks label.interest .hint { font-size: 11px; color: var(--faint); font-weight: 400; padding-left: 21px; line-height: 1.35; }
.signup-form input[type="range"] { width: 100%; accent-color: var(--brand); margin: 8px 0 4px; cursor: pointer; }
.skill-scale { display: flex; justify-content: space-between; align-items: center; font-family: var(--mono); font-size: 12px; color: var(--faint); }
.skill-scale .skill-val { font-size: 15px; font-weight: 700; color: var(--brand); }
.chk-inline { display: flex; align-items: center; gap: 8px; font-family: var(--sans); font-size: 14px; color: var(--ink); cursor: pointer; }
.chk-inline input { accent-color: var(--brand); }
.fld-err { color: var(--warn-text); font-family: var(--mono); font-size: 12px; min-height: 14px; }
.signup-form .btn { margin-top: 4px; width: 100%; text-align: center; font-size: 15px; padding: 13px; }
.signup-fine { text-align: center; font-size: 12.5px; color: var(--faint); margin: 0; }
.signup-status { text-align: center; font-size: 14px; margin: 0; min-height: 20px; }
.signup-status.ok { color: var(--good-text); }
.signup-status.bad { color: var(--warn-text); }
.signup.done .signup-form { display: none; }
.signup-success { display: none; text-align: center; padding: 10px 0; }
.signup.done .signup-success { display: block; }
.signup-success .big { font-size: 44px; line-height: 1; }
.signup-success h3 { margin: 10px 0 6px; }
.signup-success p { color: var(--muted); margin: 0 auto; max-width: 42ch; }

/* ---------- Marketplace (app detail) ---------- */
/* Centralized here rather than in per-page <style> blocks so the marketplace
   inherits the same tokens/treatments as the rest of the site. */
.app-head { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 4px; }
.app-tagline { font-size: clamp(1rem, 2vw, 1.15rem); color: var(--muted); margin: 12px 0 0; max-width: 62ch; line-height: 1.55; }
.dd-badge {
  display: inline-flex; align-items: center; gap: 6px; font-family: var(--mono);
  font-size: 11.5px; font-weight: 600; border-radius: 999px; padding: 4px 12px;
}
.dd-badge.dd-ok { color: var(--good-text); background: color-mix(in srgb, var(--good) 12%, transparent); border: 1px solid color-mix(in srgb, var(--good) 42%, transparent); }
.dd-badge.dd-ok::before { content: "✓"; }
.dd-badge.dd-pending { color: var(--warn-on-tint); background: color-mix(in srgb, var(--warn) 14%, transparent); border: 1px solid color-mix(in srgb, var(--warn) 42%, transparent); }
.dd-badge.dd-pending::before { content: "…"; }
.app-shot { width: 100%; max-width: 640px; border-radius: 14px; border: 1px solid var(--line); box-shadow: var(--shadow); margin: 22px 0; display: block; }
.app-actions { display: flex; flex-wrap: wrap; gap: 14px; margin: 24px 0; align-items: center; }
.app-price { font-family: var(--mono); font-size: 14px; color: var(--muted); }
.app-meta {
  display: flex; flex-wrap: wrap; gap: 18px; margin: 18px 0 4px; padding-top: 16px;
  border-top: 1px dashed var(--line); font-family: var(--mono); font-size: 12.5px; color: var(--faint);
}

/* ---------- Reviews ---------- */
.reviews { margin-top: 44px; border-top: 1px solid var(--line); padding-top: 30px; }
.reviews-title { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin: 0 0 4px; }
.reviews-title h2 { margin: 0; }
.reviews-avg { font-family: var(--mono); font-size: 13px; color: var(--faint); }
.stars { color: var(--star); letter-spacing: 2px; }
.review-item {
  background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
  padding: 18px 20px; margin: 14px 0; box-shadow: var(--shadow);
}
.review-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.review-name { font-weight: 640; font-size: 14.5px; color: var(--ink); }
.review-meta { font-family: var(--mono); font-size: 12px; color: var(--faint); display: inline-flex; gap: 10px; align-items: baseline; }
.review-item p { margin: 0; color: var(--muted); font-size: 14.5px; line-height: 1.56; }
.review-empty { color: var(--faint); font-size: 14.5px; }
.rform { margin-top: 30px; background: var(--surface-2); border: 1px solid var(--line); border-radius: 16px; padding: 22px 24px; }
.rform h3 { margin: 0; font-size: 1.08rem; letter-spacing: -0.015em; }
.rform .rform-note { margin: 6px 0 2px; font-size: 13px; color: var(--faint); }
.rform label { display: block; font-family: var(--mono); font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin: 16px 0 6px; }
.rform .btn { margin-top: 18px; }

/* ---------- Footer ---------- */
footer { border-top: 1px solid var(--line); padding: 44px 0; margin-top: 48px; }
footer .wrap { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 20px; }
footer .brand { font-size: 15px; }
footer .cols { display: flex; gap: 48px; flex-wrap: wrap; }
/* Each column is a flex column of full (stretched) height, so a link marked
   margin-top:auto drops to the foot of the column and lines up with the bottom
   of the taller ones. */
footer .cols > div { display: flex; flex-direction: column; }
/* Free Code Blocks under "The standard": pushed to the bottom, separated from
   the three standard links by a rule and space, and given the site accent so it
   reads as the selling point it is rather than a fourth standard link. */
.fcol-feature {
  margin-top: auto; padding-top: 14px; margin-bottom: 0;
  border-top: 1px solid var(--line);
  color: var(--accent-green); font-weight: 600;
}
.fcol-feature:hover { color: var(--accent-green); text-decoration: underline; }
footer .fcol { font-family: var(--mono); font-size: 11.5px; color: var(--faint); text-transform: uppercase; letter-spacing: 0.1em; margin: 0 0 12px; font-weight: 600; }
footer a { display: block; color: var(--muted); font-size: 14px; margin-bottom: 8px; }
/* The links in the copyright line sit INSIDE a sentence, so the column rule
   above turns each of them into its own line — "…all rights reserved. ·" then
   "Home" then "Privacy", stacked. They are the only inline links in the footer;
   everything else is a column. */
footer .copy a { display: inline; margin: 0; color: var(--muted); }
footer .copy a:hover { color: var(--brand); }
footer a:hover { color: var(--brand); text-decoration: none; }
.copy { color: var(--faint); font-size: 12.5px; margin-top: 20px; font-family: var(--mono); }

/* =========================================================
   Vibe Coder 101 — the learning path.
   Tokens only; no new colors. Phase 1 ("weekend track") is
   given a warmer treatment so it reads as a sprint, while
   phases 2-4 read as a library you dip into.
   ========================================================= */
/* Five phases as five shut doors, not 29 open ones. The listing used to run to
   about ten thousand pixels on first sight; each phase is now a <details> whose
   summary carries the picture, the length and the progress, and the stages come
   out on click. Tokens only; no new colors. */
/* Vertical padding only. The shorthand used to set `0` on the sides, which
   canceled .wrap's own `0 24px` and let this column run 24px wider than the
   hero above it — invisible while the phases were bare sections, and plainly
   wrong the moment they became bordered cards. */
.vc101-main { padding-top: 8px; padding-bottom: 72px; display: grid; gap: 16px; }

.vc101-phase {
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); box-shadow: var(--shadow);
  overflow: hidden;
}
.vc101-phase[open] { border-color: var(--line-strong); }
/* Nothing opens itself any more, so the phase you are in has to say so while
   it is still shut. A rail, not a highlight: it survives being next to four
   other cards without shouting. */
.vc101-phase.is-current { border-color: var(--brand); box-shadow: inset 3px 0 0 var(--brand), var(--shadow); }
.vc101-weekend.is-current { border-color: var(--spark); box-shadow: inset 3px 0 0 var(--spark), var(--shadow); }

.vc101-phase-head {
  display: grid; grid-template-columns: auto 1fr auto;
  gap: 18px; align-items: center;
  padding: 18px 22px; cursor: pointer;
  list-style: none;
}
.vc101-phase-head::-webkit-details-marker { display: none; }
.vc101-phase-head:hover { background: var(--surface-2); }
.vc101-phase-head:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

.vc101-phase-art { display: none; }
.vc101-phase-art.has-art { display: block; width: 84px; flex: 0 0 auto; }
.vc101-phase-art img { display: block; width: 84px; height: auto; }

.vc101-phase-text { display: grid; gap: 4px; min-width: 0; }
.vc101-phase-head h2 { font-size: clamp(18px, 2.2vw, 23px); letter-spacing: -0.02em; margin: 2px 0 0; }
.vc101-phase-head h2 em { color: var(--brand); font-style: normal; }
.vc101-blurb { color: var(--muted); font-size: 14px; line-height: 1.55; margin: 2px 0 0; display: block; }

.vc101-phase-side {
  display: grid; gap: 6px; justify-items: end; text-align: right; flex: 0 0 auto;
}
/* The chevron is the only thing here that says "this opens". */
.vc101-phase-side::after {
  content: ""; width: 9px; height: 9px; margin-top: 2px;
  border-right: 2px solid var(--faint); border-bottom: 2px solid var(--faint);
  transform: rotate(45deg); transition: transform 0.2s ease;
}
.vc101-phase[open] .vc101-phase-side::after { transform: rotate(225deg); }
@media (prefers-reduced-motion: reduce) { .vc101-phase-side::after { transition: none; } }

.vc101-dots { display: flex; gap: 4px; }
.vc101-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--line-strong);
}
.vc101-dot.is-done { background: var(--brand); }
.vc101-dot.is-next { box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px var(--brand); }
.vc101-phase-count { font-family: var(--mono); font-size: 11.5px; color: var(--faint); white-space: nowrap; }
.vc101-phase-badge {
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--paper); background: var(--brand);
  padding: 3px 8px; border-radius: 999px; white-space: nowrap;
}

.vc101-phase-body { padding: 0 22px 22px; border-top: 1px solid var(--line); padding-top: 18px; }
.vc101-art-full { margin: 0 0 18px; }
.vc101-art-full img {
  display: block; width: 100%; max-width: 620px; height: auto; margin: 0 auto;
  /* The same edge fade the home page uses: a scene whose scenery reaches the
     frame otherwise ends in a hard line where the render stopped. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent),
                      linear-gradient(180deg, transparent, #000 9%, #000 91%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent),
                      linear-gradient(180deg, transparent, #000 9%, #000 91%, transparent);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

.vc101-weekend { border-color: var(--spark); }
.vc101-weekend .vc101-phase-head h2 em { color: var(--spark); }
.vc101-weekend .vc101-dot.is-done { background: var(--spark); }
.vc101-weekend .vc101-phase-badge { background: var(--spark); color: #17182b; }

@media (max-width: 760px) {
  /* A picture that small says nothing on a phone and costs a row of height. */
  .vc101-phase-art.has-art { display: none; }
  .vc101-phase-head { grid-template-columns: 1fr auto; gap: 12px; padding: 16px; }
  .vc101-phase-body { padding: 16px; }
  .vc101-blurb { display: none; }
}
.vc101-weekend-note { margin: 12px 0 0; font-family: var(--mono); font-size: 12.5px; color: var(--muted); }
.vc101-weekend-note.is-complete { color: var(--good-text); font-weight: 600; }

/* The variable picker on the project phases (ph32tsk79). Shipped [hidden] and
   revealed by assets/vibecoder101.js, so a reader with no script never sees a
   control that does nothing. Tokens only; no new colors. */
.vc101-picker {
  margin: 0 0 24px; padding: 14px 18px 12px;
  border: 1px solid var(--line); border-left: 3px solid var(--brand); border-radius: 12px;
  background: var(--surface);
}
.vc101-picker legend {
  padding: 0 6px; font-family: var(--mono); font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink);
}
.vc101-picker-opts { display: flex; flex-wrap: wrap; gap: 8px 18px; }
.vc101-picker-opt { display: inline-flex; gap: 6px; align-items: center; font-size: 15px; color: var(--ink); cursor: pointer; }
.vc101-picker-note { margin: 10px 0 0; font-size: 13px; color: var(--muted); }

.vc101-stage h3 { display: flex; align-items: center; gap: 8px; }
.vc101-check { width: 16px; height: 16px; flex: 0 0 auto; }
.vc101-stage.is-done .vc101-check::before {
  content: "✓"; color: var(--good-text); font-size: 13px; font-weight: 700;
}
.vc101-stage.is-done { border-color: var(--line-strong); }

/* Stage pager */
.vc101-pager {
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--line);
}
.vc101-pager a { display: grid; gap: 2px; max-width: 46%; }
.vc101-pager .vc101-next { text-align: right; margin-left: auto; }
.vc101-pager span { font-family: var(--mono); font-size: 11.5px; color: var(--faint); text-transform: uppercase; letter-spacing: 0.08em; }
.vc101-pager strong { font-size: 14.5px; color: var(--ink); font-weight: 600; }
.vc101-pager a:hover strong { color: var(--brand); }

/* Community video library */
.vc101-videos { margin-top: 56px; padding-top: 28px; border-top: 1px solid var(--line); }
.vc101-videos h2 { font-size: 20px; margin: 0 0 18px; }
.vc101-rowlabel {
  font-family: var(--mono); font-size: 11.5px; color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.08em; margin: 0 0 10px;
}
.vc101-vidgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr)); gap: 16px; }
.vc101-community { margin-top: 26px; }
.vc101-vid { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface); }
.vc101-vid img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; background: var(--surface-2); }
.vc101-vid-body { padding: 12px 14px 14px; }
.vc101-vid h3 { font-size: 14.5px; margin: 0 0 4px; line-height: 1.35; }
.vc101-vid .vc101-chan { font-size: 12.5px; color: var(--faint); margin: 0 0 10px; }
.vc101-badge {
  display: inline-block; font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  color: var(--brand); border: 1px solid var(--brand); border-radius: 20px;
  padding: 1px 8px; margin-bottom: 8px;
}
.vc101-vote { display: flex; align-items: center; gap: 8px; }
.vc101-vote button {
  font-family: var(--mono); font-size: 12px; padding: 4px 10px; border-radius: 20px;
  border: 1px solid var(--line); background: var(--surface); color: var(--muted); cursor: pointer;
}
.vc101-vote button:hover { border-color: var(--line-strong); color: var(--ink); }
.vc101-vote button[aria-pressed="true"] { background: var(--brand); border-color: var(--brand); color: var(--paper); }
.vc101-vote .vc101-count { font-family: var(--mono); font-size: 11.5px; color: var(--faint); }
.vc101-policy { font-size: 11.5px; color: var(--warn-text); margin: 8px 0 0; line-height: 1.45; }
.vc101-empty { color: var(--faint); font-size: 14px; }
.vc101-submit { margin-top: 20px; }

/* The public showcase gallery (/showcase/). It reuses the video card wholesale —
   one card design, so a change on either page cannot leave the other looking
   like a different site. Only the rank line is new. */
.sc-gallery { margin-top: 26px; }
.sc-rankline { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; }
.sc-rank { font-family: var(--mono); font-size: 12px; font-weight: 700; color: var(--brand); }
/* Deliberately NOT the filled brand pill .vc101-badge uses for a confirmed
   thing: this chip means "proposed by the votes, not yet checked by a person",
   and it should not look like a rosette. */
.sc-chip {
  font-family: var(--mono); font-size: 10.5px; color: var(--muted);
  border: 1px dashed var(--line-strong); border-radius: 20px; padding: 1px 8px;
}

@media (max-width: 640px) {
  .vc101-weekend { margin-left: 0; padding-left: 16px; }
  .vc101-pager a { max-width: 100%; }
  .vc101-pager .vc101-next { text-align: left; margin-left: 0; }
}

/* =========================================================
   Pricing plans — free column + paid/coming-soon tiers.
   Four across on wide screens, two, then one. Tokens only.
   ========================================================= */
.plans { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; align-items: start; }
@media (max-width: 1000px) { .plans { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .plans { grid-template-columns: 1fr; } }

.plan {
  background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
  padding: 22px 20px; box-shadow: var(--shadow); display: flex; flex-direction: column; height: 100%;
}
.plan-free { border-left: 3px solid var(--good); }
.plan-paid { border-left: 3px solid var(--spark); }
.plan-soon { border-left: 3px solid var(--line-strong); background: var(--surface-2); }

.plan-head { border-bottom: 1px solid var(--line); padding-bottom: 14px; margin-bottom: 14px; }
.plan .num {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--faint); display: block; margin-bottom: 8px;
}
.plan-free .num { color: var(--good-text); }
.plan-paid .num { color: var(--spark-text); }
.plan-head h3 {
  font-size: 30px; font-weight: 700; letter-spacing: -0.03em; margin: 0; line-height: 1.05;
}
.plan-soon .plan-head h3 { font-size: 21px; color: var(--muted); letter-spacing: -0.02em; }
.plan-per { font-size: 13px; font-weight: 500; color: var(--faint); letter-spacing: 0; }
.plan-sub { font-size: 12.5px; color: var(--muted); margin: 8px 0 0; line-height: 1.5; }
.plan-beta {
  display: inline-block; font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  color: var(--good-text); border: 1px solid var(--good); border-radius: 20px; padding: 1px 8px;
}
.plan-inc {
  font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--faint); margin: 0 0 10px;
}

.plan-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; flex: 1; }
.plan-list li {
  font-size: 13px; color: var(--muted); line-height: 1.45; padding-left: 18px; position: relative;
}
.plan-list li::before {
  content: "✓"; position: absolute; left: 0; top: 0;
  font-size: 11px; font-weight: 700; color: var(--good-text);
}
.plan-soon .plan-list li::before { content: "◇"; color: var(--faint); font-weight: 400; }
.plan-list li b { color: var(--ink); font-weight: 600; }
.plan-foot {
  font-size: 11.5px; color: var(--faint); line-height: 1.5;
  margin: 16px 0 0; padding-top: 12px; border-top: 1px solid var(--line);
}

/* The "needs your own Anthropic API key" marker. Coral so it reads as a caveat
   rather than decoration, and it carries a title + aria-label for anyone who
   can't see a superscript asterisk. */
.key-req {
  color: var(--spark-text); font-weight: 700; font-size: 0.72em;
  margin-left: 1px; cursor: help; text-decoration: none;
}
.key-legend {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 18px 0 0; padding: 12px 14px;
  border: 1px solid var(--line); border-left: 3px solid var(--spark);
  border-radius: var(--radius); background: var(--surface-2);
  font-size: 13px; color: var(--muted); line-height: 1.55; max-width: 74ch;
}
.key-legend .key-star { color: var(--spark-text); font-weight: 700; flex: none; }

/* ---------- Vibe Coder 101 · progress ---------- */
.vc101-bar {
  height: 6px; border-radius: 4px; background: var(--line);
  overflow: hidden; flex: 1; min-width: 60px;
}
.vc101-bar > span {
  display: block; height: 100%; width: 0;
  background: var(--brand); border-radius: 4px;
  transition: width 0.35s ease;
}
.vc101-bar-lg { height: 9px; }
@media (prefers-reduced-motion: reduce) { .vc101-bar > span { transition: none; } }

.vc101-progress {
  margin-top: 30px; padding: 18px 22px;
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--surface); box-shadow: var(--shadow);
}
.vc101-progress-top {
  display: flex; align-items: center; gap: 12px; margin-bottom: 10px;
}
/* The label first, the shields beside the figure they belong to, and the
   destructive action pushed to the far end where nothing is aimed at it. */
.vc101-progress-top .vc101-reset { margin-left: auto; }

/* The five badges as tiny icons, on the light panel this time: --brand is
   6.44:1 on the paper ground and legal as a fill, and an unearned shield uses
   the same line color every other outline on this page uses. */
.vc101-shields { display: inline-flex; align-items: center; gap: 5px; }
/* Shared shape and weight; each host sets its own stroke below. */
.vc101-shield { width: 14px; height: 16px; flex: 0 0 auto; overflow: visible; }
.vc101-shield path { fill: none; stroke-width: 2; stroke-linejoin: round; }
.vc101-shields .vc101-shield path { stroke: var(--line-strong); }
.vc101-shields .vc101-shield.is-earned path { fill: var(--brand); stroke: var(--brand); }

.vc101-bar-row { display: flex; align-items: center; gap: 12px; }
.vc101-more {
  font-family: var(--mono); font-size: 11px; color: var(--faint);
  background: none; border: 0; padding: 2px 0; cursor: pointer;
  text-decoration: underline; white-space: nowrap; flex: 0 0 auto;
}
.vc101-more:hover { color: var(--brand); }
.vc101-more:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.vc101-progress-body { margin-top: 14px; }
.vc101-progress-label { font-family: var(--mono); font-size: 12.5px; color: var(--ink); font-weight: 600; }
.vc101-reset {
  font-family: var(--mono); font-size: 11px; color: var(--faint);
  background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline;
}
.vc101-reset:hover { color: var(--spark); }
.vc101-progress-note { margin: 10px 0 0; font-size: 13px; color: var(--muted); }

/* The one thing the panel asks you to do, so it is the one thing that looks
   clickable. Everything under it reports; only this acts. */
.vc101-next {
  display: grid; gap: 2px; margin-top: 14px;
  padding: 12px 14px; border-radius: 12px;
  background: var(--surface-2); border: 1px solid var(--line);
  text-decoration: none;
}
.vc101-next:hover { border-color: var(--brand); background: var(--surface); text-decoration: none; }
.vc101-next:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.vc101-next-eyebrow {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint);
}
.vc101-next-title { font-size: 15.5px; font-weight: 600; color: var(--ink); line-height: 1.35; }
.vc101-next:hover .vc101-next-title { color: var(--brand); }
.vc101-next-note { font-size: 12.5px; color: var(--muted); }

.vc101-streak {
  margin: 12px 0 0; font-family: var(--mono); font-size: 12px; color: var(--muted);
}

.vc101-badges { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); }
.vc101-badges .vc101-rowlabel { margin: 0 0 8px; }
.vc101-badgerow {
  display: flex; flex-wrap: wrap; gap: 8px;
  list-style: none; margin: 0; padding: 0;
}
/* An unearned badge is drawn as what is ahead, not as an empty slot: it keeps
   its name and shows the count, so it reads as a target rather than a gap. */
.vc101-badgechip {
  display: grid; gap: 1px; padding: 6px 11px;
  border: 1px dashed var(--line-strong); border-radius: 999px;
  background: transparent;
}
.vc101-badgechip b { font-size: 12px; font-weight: 600; color: var(--muted); line-height: 1.2; }
.vc101-badgechip span { font-family: var(--mono); font-size: 10px; color: var(--faint); }
.vc101-badgechip.is-earned {
  border-style: solid; border-color: var(--brand); background: var(--brand); color: var(--paper);
}
.vc101-badgechip.is-earned b { color: inherit; }
.vc101-badgechip.is-earned span { color: inherit; }

/* Per-phase */
.vc101-phase-progress { display: flex; align-items: center; gap: 10px; margin-top: 12px; max-width: 340px; }
.vc101-phase-count { font-family: var(--mono); font-size: 11.5px; color: var(--faint); flex: none; }
.vc101-phase-progress.is-complete .vc101-bar > span { background: var(--good); }
.vc101-phase-progress.is-complete .vc101-phase-count { color: var(--good-text); }
.vc101-weekend .vc101-bar > span { background: var(--spark); }
.vc101-weekend .vc101-phase-progress.is-complete .vc101-bar > span { background: var(--good); }

/* Stage-card states: completed, opened-but-not-done, and up-next */
.vc101-stage.is-seen .vc101-check::before { content: "◦"; color: var(--faint); font-size: 15px; }
.vc101-stage.is-next { border-color: var(--brand); }
.vc101-stage.is-next .vc101-check::before { content: "▸"; color: var(--brand); font-size: 12px; }

/* Stage page: mini bar under the byline */
.vc101-mini { display: flex; align-items: center; gap: 10px; margin-top: 14px; max-width: 320px; }
.vc101-mini-label { font-family: var(--mono); font-size: 11px; color: var(--faint); flex: none; }

/* Stage page: the completion action */
.vc101-done-row {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 40px; padding-top: 24px; border-top: 1px solid var(--line);
}
.vc101-done-state { font-family: var(--mono); font-size: 13px; font-weight: 600; color: var(--good-text); }
.vc101-undo {
  font-family: var(--mono); font-size: 11.5px; color: var(--faint);
  background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline;
}
.vc101-undo:hover { color: var(--spark); }

/* ---------- Coding Guide search ---------- */
/* The guide's header: title block left, search top-right. `align-items:start`
   keeps the box pinned to the top of the row instead of floating to the middle
   of a title block that is three lines taller than it is. */
.kb-head {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(260px, 340px);
  gap: 16px 40px; align-items: start; margin-bottom: 4px;
}
.kb-head-text { min-width: 0; }
.kb-head .kb-search { margin: 0; max-width: none; }
@media (max-width: 900px) {
  /* Stacked, and the search goes LAST: on a phone the listing below is quicker
     to skim than this field is to type into. */
  .kb-head { grid-template-columns: 1fr; }
  .kb-head .kb-search { margin: 0 0 24px; max-width: 60ch; }
}

.kb-search { margin: 0 0 32px; max-width: 60ch; }
.kb-search-label {
  display: block; font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 8px;
}
.kb-search-row { display: flex; gap: 8px; align-items: stretch; }
.kb-search input[type="search"] {
  flex: 1; min-width: 0;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius); color: var(--ink);
  font-family: var(--sans); font-size: 15px; padding: 11px 14px;
  -webkit-appearance: none; appearance: none;
}
.kb-search input[type="search"]::placeholder { color: var(--faint); }
.kb-search input[type="search"]:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft);
}
.kb-search-clear, .kb-inline-clear {
  font-family: var(--mono); font-size: 12px; color: var(--muted);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius); padding: 0 14px; cursor: pointer; white-space: nowrap;
}
.kb-inline-clear { padding: 1px 6px; border: 0; background: none; color: var(--brand); text-decoration: underline; }
.kb-search-clear:hover { border-color: var(--brand); color: var(--brand); }
.kb-search-count {
  font-family: var(--mono); font-size: 11.5px; color: var(--faint);
  margin: 8px 0 0; min-height: 1em;
}

/* Matched body text under a result */
.kb-snippet {
  font-size: 13px !important; color: var(--muted) !important;
  border-left: 2px solid var(--line-strong); padding-left: 10px; margin-top: 4px !important;
}
.kb-snippet mark {
  background: var(--brand-soft); color: var(--ink);
  border-radius: 3px; padding: 0 2px; font-weight: 600;
}

/* =========================================================================
   Certificate directory + certificate pages  (.cd-*)
   Specs: DIRECTORY-SPEC.md / BADGE-SPEC.md in the App-Hub-Local repo.

   The visual rules here are load-bearing, not decorative:

   · RIGOR TIER and LISTING TYPE share no color, border, shape or position.
     `tier` (gold/standard) is audit rigor and sits with the audit facts in a
     bordered, accented box. `listing` (free/enhanced) only ever appears as the
     owner-content section, which is deliberately the QUIETEST block on the
     page — muted surface, no accent. If "enhanced" ever looked richer than the
     verdict, money would appear to buy a rating.

   · EXPIRED uses .cd-stale — a flat neutral, never the pass green. An expired
     certificate must not be able to look current even at a glance.

   · Everything uses design tokens only, so dark mode (the :root override near
     the top of this file) applies with no extra rules.
   ========================================================================= */

/* ---- notices (loading / empty / not-live / error) ---- */
.cd-note {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px; margin: 20px 0; font-size: 15px; color: var(--muted);
  background: var(--surface-2);
}
.cd-note strong { color: var(--ink); }
.cd-note-bad { border-color: var(--warn); background: color-mix(in srgb, var(--warn) 8%, transparent); }

/* ---- directory controls ---- */
.cd-controls { margin: 26px 0 22px; }
.cd-search-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cd-search-label {
  font-family: var(--mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: .1em; color: var(--faint);
}
.cd-search-row input {
  flex: 1; min-width: 220px; background: var(--surface-2);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  color: var(--ink); font-family: var(--sans); font-size: 15px; padding: 10px 13px;
}
.cd-search-row input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.cd-count { font-family: var(--mono); font-size: 12px; color: var(--faint); }

.cd-filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.cd-filters label {
  font-family: var(--mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: .1em; color: var(--faint); margin-left: 6px;
}
.cd-filters label:first-child { margin-left: 0; }
.cd-filters select {
  background: var(--surface-2); border: 1px solid var(--line-strong);
  border-radius: var(--radius); color: var(--ink); font-size: 14px; padding: 8px 10px;
}
.cd-controls-note { font-size: 13px; color: var(--faint); margin: 12px 0 0; max-width: 62ch; }

/* ---- directory grid ---- */
.cd-grid {
  display: grid; gap: 14px; margin: 8px 0 34px;
  grid-template-columns: repeat(auto-fill, minmax(min(268px, 100%), 1fr));
}
.cd-empty { color: var(--faint); font-size: 15px; }

/* A free and an enhanced listing render the SAME card. No variant exists. */
.cd-card {
  display: block; text-decoration: none; color: inherit;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px 17px; box-shadow: var(--shadow);
  transition: border-color .15s ease, transform .15s ease;
}
.cd-card:hover { border-color: var(--brand); transform: translateY(-2px); }
.cd-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.cd-card-top h3 { margin: 0; font-size: 17px; line-height: 1.3; color: var(--ink); }
.cd-card-meta {
  display: flex; gap: 10px; flex-wrap: wrap; margin-top: 8px;
  font-family: var(--mono); font-size: 11.5px; color: var(--faint);
}
.cd-card-tier { color: var(--muted); }
.cd-card-findings { margin: 10px 0 0; font-size: 13px; color: var(--muted); }
.cd-card-fresh { margin: 8px 0 0; font-size: 12.5px; display: flex; align-items: center; gap: 6px; }
/* "has caveats" marker on a directory card (DIRECTORY-SPEC v2 rule 3). The
   strong variant is a failed citation check — the audit could not be located in
   the code it describes — which is a warning about the verdict's foundation, not
   about coverage, so it gets the alert color rather than the caution one. */
.cd-card-caveat { margin: 6px 0 0; font-size: 12.5px; color: var(--warn-text); }
.cd-card-caveat-strong { color: var(--spark); font-weight: 500; }
.cd-card-date { margin: 10px 0 0; font-family: var(--mono); font-size: 11.5px; color: var(--faint); }

/* verdict pill on cards */
.cd-pill {
  flex: none; font-family: var(--mono); font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  padding: 4px 9px; border-radius: 999px; border: 1px solid var(--line-strong); color: var(--muted);
}
.cd-pill.cd-ok   { border-color: var(--good); color: var(--good-text); background: color-mix(in srgb, var(--good) 10%, transparent); }
.cd-pill.cd-warn { border-color: var(--warn); color: var(--warn-text); background: color-mix(in srgb, var(--warn) 12%, transparent); }
.cd-pill.cd-bad  { border-color: var(--spark); color: var(--spark); background: var(--spark-soft); }

/* ---- the Self-assessed block (ph113tsk15): subordinate to the verdict ----
   Smaller type, a dashed neutral rule, no rating colour, no left accent.
   Nothing here may borrow .cd-verdict / .cd-pill / .cd-ok — it is not the badge. */
.cd-self-assessed {
  border: 1px dashed var(--line-strong); border-radius: var(--radius);
  padding: 12px 14px; margin: 18px 0; background: transparent;
  font-size: 13.5px; color: var(--muted);
}
.cd-sa-label {
  margin: 0; font-family: var(--mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: .1em; color: var(--faint);
}
.cd-sa-facts, .cd-sa-statement { margin: 6px 0 0; }
.cd-sa-receipt { margin: 6px 0 0; font-size: 11.5px; color: var(--faint); }

/* ---- the verdict block (verdict + freshness + caveats, together) ---- */
.cd-verdict {
  border: 1px solid var(--line-strong); border-left-width: 4px;
  border-radius: var(--radius); padding: 20px 22px; margin: 22px 0;
  background: var(--surface);
}
.cd-verdict.cd-ok    { border-left-color: var(--good); }
.cd-verdict.cd-warn  { border-left-color: var(--warn); }
.cd-verdict.cd-bad   { border-left-color: var(--spark); }
.cd-verdict.cd-none  { border-left-color: var(--line-strong); }
/* Expired: flat neutral. Never the pass color, whatever the rating was. */
.cd-verdict.cd-stale { border-left-color: var(--faint); background: var(--surface-2); }

.cd-verdict-top { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.cd-rating { font-size: 27px; font-weight: 600; color: var(--ink); line-height: 1.1; }
.cd-standard {
  font-family: var(--mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: .1em; color: var(--faint);
}
.cd-score { font-family: var(--mono); font-size: 20px; font-weight: 700; color: var(--ink); }
.cd-score i { font-style: normal; font-size: 13px; font-weight: 400; color: var(--faint); }

.cd-expired-flag {
  margin: 12px 0 0; padding: 11px 13px; border-radius: 6px;
  background: color-mix(in srgb, var(--faint) 14%, transparent);
  color: var(--ink); font-size: 14.5px;
}
.cd-label   { margin: 10px 0 0; font-size: 16px; color: var(--ink); }
.cd-summary { margin: 8px 0 0; font-size: 14.5px; color: var(--muted); }

.cd-fresh { margin: 14px 0 0; font-size: 13.5px; color: var(--muted); display: flex; gap: 7px; align-items: baseline; }
.cd-fresh strong { color: var(--ink); }
.cd-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--faint); flex: none; transform: translateY(4px); }
.cd-fresh-ok   .cd-dot, .cd-card-fresh.cd-fresh-ok   .cd-dot { background: var(--good); }
.cd-fresh-warn .cd-dot, .cd-card-fresh.cd-fresh-warn .cd-dot { background: var(--warn); }
.cd-fresh-bad  .cd-dot, .cd-card-fresh.cd-fresh-bad  .cd-dot { background: var(--spark); }
.cd-card-fresh.cd-fresh-warn { color: var(--warn); }
.cd-card-fresh.cd-fresh-bad  { color: var(--spark); }

/* Caveats are inside the verdict box on purpose — they are part of the verdict,
   not an appendix to it. */
.cd-caveats { margin: 16px 0 0; padding-top: 14px; border-top: 1px solid var(--line); }
.cd-caveats h4 {
  margin: 0 0 8px; font-family: var(--mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: .1em; color: var(--faint);
}
.cd-caveats ul { margin: 0; padding-left: 18px; }
.cd-caveats li { font-size: 13.5px; color: var(--muted); margin-bottom: 7px; line-height: 1.6; }
.cd-caveat-detail {
  display: block; font-family: var(--mono); font-size: 11.5px;
  color: var(--faint); margin-top: 3px;
}
/* A failed citation check outranks every other caveat, so it does not read as
   one more gray bullet in a list: it takes the alert color and full weight.
   BADGE-SPEC v2 numbers this rule zero precisely because burying it is the one
   failure that would discredit everything else on the certificate. */
.cd-caveats li.cd-cav-strong {
  color: var(--ink); font-weight: 500;
  border-left: 3px solid var(--spark); padding-left: 10px;
  margin-left: -18px; list-style: none;
}

/* ---- rigor tier (audit thoroughness — NOT a listing type) ---- */
.cd-tier {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px; margin: 0 0 22px; background: var(--surface-2);
}
.cd-tier-gold { border-color: var(--star); background: color-mix(in srgb, var(--star) 8%, transparent); }
.cd-tier-mark {
  font-family: var(--mono); font-size: 11.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em; color: var(--muted);
}
.cd-tier-gold .cd-tier-mark { color: var(--star); }
.cd-tier-what { margin: 8px 0 0; font-size: 13.5px; color: var(--muted); line-height: 1.6; }
.cd-tier-detail { margin: 8px 0 0; font-size: 13px; color: var(--muted); }
.cd-model { font-family: var(--mono); font-size: 12.5px; color: var(--ink); border-bottom: 1px dotted var(--line-strong); cursor: help; }

/* ---- findings + areas ---- */
.cd-cols { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); margin: 0 0 22px; }
.cd-findings h3, .cd-areas h3, .cd-facts h3, .cd-request h3 { font-size: 15px; margin: 0 0 10px; color: var(--ink); }
.cd-findings-row { display: flex; gap: 10px; flex-wrap: wrap; }
.cd-finding {
  flex: 1; min-width: 76px; text-align: center; padding: 12px 8px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
}
.cd-finding b { display: block; font-family: var(--mono); font-size: 22px; color: var(--ink); }
.cd-finding span { font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--faint); }
.cd-f-bad  b { color: var(--spark); }
.cd-f-warn b { color: var(--warn); }

.cd-rubric { margin: -4px 0 8px; font-family: var(--mono); font-size: 11.5px; color: var(--faint); }
.cd-arealist { list-style: none; margin: 0; padding: 0; }
.cd-arealist li {
  display: flex; justify-content: space-between; gap: 12px; align-items: baseline;
  padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 13.5px;
}
.cd-area-name { color: var(--muted); }
.cd-area-level { flex: none; font-family: var(--mono); font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
.cd-l-ok   { color: var(--good); }
.cd-l-warn { color: var(--warn); }
.cd-l-bad  { color: var(--spark); }
/* "Not graded" reads as absent information, not as a grade of any kind. */
.cd-l-none { color: var(--faint); font-style: italic; font-weight: 400; }

/* ---- audit record table ---- */
.cd-facts { margin: 0 0 22px; }
.cd-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.cd-table th, .cd-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); }
.cd-table th { width: 42%; font-weight: 500; color: var(--faint); }
.cd-table td { color: var(--ink); }

/* ---- report request ---- */
.cd-request {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 18px 20px; margin: 0 0 26px; background: var(--surface-2);
}
.cd-request p { font-size: 14px; color: var(--muted); margin: 0 0 14px; }

/* ---- owner-supplied content (enhanced listings only) --------------------
   The quietest block on the page, by design. It carries no accent color and
   no border treatment that could read as a mark of quality, because our
   certification mark is elsewhere on this same page. */
.cd-owner { margin: 34px 0 26px; }
.cd-owner-boundary { border-top: 2px dashed var(--line-strong); margin-bottom: 16px; }
.cd-owner-label {
  display: inline-block; font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em; color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--line-strong);
  border-radius: 999px; padding: 4px 11px;
}
.cd-owner h2 { font-size: 19px; margin: 12px 0 6px; color: var(--ink); }
.cd-owner-note { font-size: 13px; color: var(--faint); margin: 0 0 14px; max-width: 66ch; }
.cd-owner-headline { font-size: 16px; color: var(--ink); margin: 0 0 10px; }
.cd-owner p { font-size: 14.5px; color: var(--muted); line-height: 1.7; margin: 0 0 12px; }
.cd-owner-shots { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); margin: 14px 0; }
.cd-owner-shots img { width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--radius); display: block; }
.cd-owner-links { list-style: none; margin: 12px 0; padding: 0; display: flex; gap: 14px; flex-wrap: wrap; }
.cd-owner-links a { font-size: 14px; color: var(--brand); }
.cd-owner-contact { font-size: 13.5px; color: var(--muted); }

/* ---- certificate page chrome ---- */
.cd-back { margin: 22px 0 0; font-size: 14px; }
.cd-cert-head { margin: 10px 0 0; }
.cd-cert-head h1 { margin: 6px 0 0; }
.cd-canonical { font-size: 12.5px; color: var(--faint); margin: 26px 0 40px; line-height: 1.7; }
.cd-canonical a { word-break: break-all; }

/* Expired tag — sits beside the verdict word at comparable weight so the skim
   cannot come away with just "Pass". Neutral by design: it is a statement of
   state, not a severity. */
.cd-expired-tag {
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink); background: color-mix(in srgb, var(--faint) 22%, transparent);
  border: 1px solid var(--faint); border-radius: 999px; padding: 4px 11px;
}
/* The freshness line wraps as a unit on narrow screens instead of breaking the
   label away from its dot. */
.cd-fresh { flex-wrap: wrap; }
.cd-fresh strong { white-space: nowrap; }

/* ---------- Hero workspace diagram ----------
   A stylized view of the workspace, NOT a screenshot. It shows the shape of the
   tool — your apps on the left, your app running beside what it is actually
   doing — and deliberately contains no verdicts, scores or finding counts,
   because inventing those would be the exact overclaim the product refuses.
   Built from tokens so it themes itself and never goes stale with the UI. */
.hero-shot { margin-top: 40px; perspective: 1400px; }
.hs-win {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; overflow: hidden; box-shadow: var(--shadow);
  transform: rotateX(2.2deg); transform-origin: top center;
}
.hs-bar {
  display: flex; align-items: center; gap: 7px;
  padding: 11px 14px; background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}
.hs-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--line-strong); flex: none; }
.hs-dot:first-child { background: var(--spark); opacity: .75; }
.hs-dot:nth-child(2) { background: var(--star); opacity: .75; }
.hs-dot:nth-child(3) { background: var(--good); opacity: .75; }
.hs-title {
  margin-left: 10px; font-family: var(--mono); font-size: 11.5px;
  color: var(--faint); letter-spacing: .04em;
}
.hs-body { display: grid; grid-template-columns: 168px 1fr; min-height: 236px; }
.hs-rail {
  border-right: 1px solid var(--line); padding: 14px 12px;
  display: flex; flex-direction: column; gap: 9px; background: var(--surface-2);
}
.hs-rail-label, .hs-pane-label {
  font-family: var(--mono); font-size: 10px; text-transform: uppercase;
  letter-spacing: .12em; color: var(--faint);
}
.hs-app {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 11px; color: var(--faint);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 7px; padding: 8px 10px;
}
.hs-app i { width: 7px; height: 7px; border-radius: 50%; background: var(--line-strong); flex: none; }
.hs-app.is-live { color: var(--ink); border-color: color-mix(in srgb, var(--good) 45%, var(--line)); }
.hs-app.is-live i { background: var(--good); }
.hs-main { display: grid; grid-template-columns: 1fr 1fr; }
.hs-pane { padding: 16px 16px 18px; display: flex; flex-direction: column; gap: 9px; }
.hs-pane-alt { border-left: 1px solid var(--line); background: var(--surface-2); }
.hs-line { height: 9px; border-radius: 5px; background: var(--line); display: block; }
.hs-line.w40 { width: 40%; } .hs-line.w45 { width: 45%; } .hs-line.w55 { width: 55%; }
.hs-line.w60 { width: 60%; } .hs-line.w65 { width: 65%; } .hs-line.w70 { width: 70%; }
.hs-line.w75 { width: 75%; } .hs-line.w90 { width: 90%; }
.hs-block {
  height: 54px; border-radius: 8px; margin: 3px 0;
  background: linear-gradient(120deg, var(--brand-soft), var(--spark-soft));
  border: 1px solid var(--line);
}
.hs-row { display: flex; align-items: center; gap: 10px; }
.hs-row em {
  font-style: normal; font-family: var(--mono); font-size: 10.5px;
  color: var(--brand); min-width: 58px;
}
/* Phones: the two panes stop being readable side by side, and the rail is the
   least informative part of the diagram, so drop it rather than shrink
   everything into illegibility. */
@media (max-width: 700px) {
  .hs-body { grid-template-columns: 1fr; }
  .hs-rail { display: none; }
  .hs-main { grid-template-columns: 1fr; }
  .hs-pane-alt { border-left: 0; border-top: 1px solid var(--line); }
  .hs-win { transform: none; }
}

/* ---------- Touch and legibility on phones ----------
   Found by sweeping 12 pages x 9 widths and measuring rendered boxes rather
   than eyeballing screenshots. Only the genuine defects are here: the checkbox
   inputs the sweep also flagged sit inside 39-57px <label>s, so the label is
   already the tap target and enlarging the box would be cosmetic churn. */

/* A 16px-tall range track is hard to hit and harder to drag with a thumb. The
   input keeps its size on pointer devices; touch gets a taller hit area and a
   bigger thumb. */
@media (pointer: coarse), (max-width: 560px) {
  .signup-form input[type="range"] { height: 34px; }
  .signup-form input[type="range"]::-webkit-slider-thumb { width: 24px; height: 24px; }
  .signup-form input[type="range"]::-moz-range-thumb { width: 24px; height: 24px; }
}

/* 11px prose is below comfortable reading size on a phone. These two carry real
   sentences — what a plan includes, and the caveat under it — so they scale up
   on small screens. .eyebrow stays 11.5px on purpose: it is a short tracked
   label, not something anyone reads a paragraph of. */
@media (max-width: 560px) {
  .plan-inc { font-size: 12.5px; }
  .plan-foot { font-size: 13px; }
  .plan-list li { font-size: 14px; }
}

/* The two remaining sub-24px controls the sweep found. WCAG 2.2 AA (2.5.8) sets
   24x24 CSS px as the floor for a pointer target; 15px and 29px buttons with no
   padding fail or barely scrape it, and both sit on pages a phone user reaches
   from the course. Sized on touch only, so the desktop density is unchanged. */
@media (pointer: coarse), (max-width: 560px) {
  .vc101-reset { min-height: 34px; padding: 8px 12px; }
  .market-filters button { min-height: 36px; padding: 9px 14px; }
}

/* ---------- Skip to content (WCAG 2.4.1) ----------
   First focusable element on every page. Off-screen until focused, so it costs
   the visual design nothing and saves a keyboard or switch-control user from
   tabbing the entire nav on all 74 pages before reaching what they came for. */
/* FIXED, not absolute. Absolute positions against the document, so once the
   page is scrolled the link is parked far above the viewport and tabbing never
   reveals it — it only worked at scroll position 0. Fixed puts it where the
   user actually is. */
.skip-link {
  position: fixed; left: 8px; top: -60px; z-index: 100;
  background: var(--brand); color: var(--paper); font-weight: 600; font-size: 14px;
  padding: 10px 16px; border-radius: var(--radius); text-decoration: none;
  transition: top .15s ease;
}
.skip-link:focus { top: 8px; text-decoration: none; outline: 2px solid var(--ink); outline-offset: 2px; }
/* The main region is a scroll target for the link above. scroll-margin keeps
   the heading clear of the sticky header once it lands. */
#content { scroll-margin-top: 76px; }

/* Card titles are h2 rather than h3: they sit directly under the page's h1, so
   h3 skipped a level for anyone navigating by heading. They keep the visual
   weight the design intends — rank is a document-structure decision, not a
   typographic one. */
.kb-item .kb-title, .kb-item h2.kb-title {
  color: var(--ink); font-size: 19px; margin: 8px 0 6px;
  letter-spacing: -0.015em; font-weight: 640;
}

/* ═══ THE CODE-BLOCK SHOWCASE ══════════════════════════════════════════════
   One card per block: the picture, then what it is, who it is for, and the way
   in. A card WITHOUT a picture is not a shorter version of one with — the body
   simply starts at the top of the card, so a section part-way through having its
   art installed still looks deliberate. That is why the art is a plain child
   rather than a fixed-height band with a placeholder in it. */
.blk-showgrid {
  display: grid; gap: 18px; margin-top: 18px;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
}
.blk-show {
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 14px; background: var(--surface);
  box-shadow: var(--shadow); overflow: hidden;
  transition: transform 0.18s ease, border-color 0.18s ease;
}
.blk-show:hover { transform: translateY(-3px); border-color: var(--line-strong); }
/* The picture rides in the button row, at the right, at a size that reads as a
   mark rather than a scene (owner, 2026-09-03). Its ground is keyed out, so it
   sits directly on the card surface with no band of its own. */
.blk-show-foot {
  margin-top: auto; display: flex; align-items: center; justify-content: space-between;
  gap: 14px; padding-top: 6px;
}
.blk-show-icon {
  display: block; height: 62px; width: auto; flex: none;
  /* The render is a wide landscape; capping the width stops a squarer future one
     from crowding the button off its own row. */
  max-width: 130px; object-fit: contain;
}
.blk-show-body { display: flex; flex-direction: column; gap: 8px; padding: 18px 20px 20px; flex: 1; }
.blk-show-body .tag {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--faint); align-self: flex-start;
}
.blk-show-body h3 { margin: 0; font-size: 1.12rem; letter-spacing: -0.015em; text-wrap: balance; }
.blk-show-sum { margin: 0; color: var(--muted); font-size: 14.5px; line-height: 1.55; }
/* The questionnaire label, quoted. `Who it's for` is a label rather than a
   heading because a card is not a section, and eighteen more headings would
   flood the page's outline for anyone navigating by heading. */
.blk-show-who {
  margin: 4px 0 0; font-size: 13.5px; color: var(--ink);
  border-left: 2px solid var(--accent-green); padding-left: 10px;
}
.blk-show-wholab {
  display: block; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--accent-green); margin-bottom: 2px;
}
.blk-show-stack { margin: 0; font-family: var(--mono); font-size: 11.5px; color: var(--faint); }
/* margin-top on the ROW, not the button — the row is what pins to the bottom of
   the card, so cards with summaries of different lengths still line their
   buttons up. */
.blk-show-more { flex: none; }
@media (max-width: 560px) {
  .blk-showgrid { grid-template-columns: 1fr; }
  .blk-show-icon { height: 52px; }
}

/* ═══ THE BLOCK PAGE HEADER ════════════════════════════════════════════════
   Title and details on the left, the picture on the right. The text column is
   capped in ch rather than fr so the lede keeps a readable measure whatever the
   picture does, and the picture column is content-sized so a page WITHOUT one
   is not a narrow column with a hole beside it — the grid simply has one cell. */
.blk-head {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 360px);
  gap: 32px; align-items: start; margin-bottom: 6px;
}
.blk-head-say > :first-child { margin-top: 0; }
.blk-head-art { display: block; width: 100%; height: auto; margin-top: 18px; }
@media (max-width: 820px) {
  /* One column, and the picture goes BELOW the words rather than above them.
     Stacking it above would push the h1 — the thing a reader arrived for, and
     the thing a search result promised them — off the top of a phone screen. */
  .blk-head { grid-template-columns: 1fr; gap: 4px; }
  .blk-head-art { max-width: 420px; margin: 6px 0 10px; }
}

/* This block used to be a `prefers-color-scheme: dark` override forcing dark ink
   onto a brightened button, because .btn was filled with --brand (#7b95ff) and
   white on that is 2.77:1. .btn is now filled with --brand-2, which takes a
   white label at 6.23:1, so the inversion is gone and there is nothing left to
   override — the rule below simply restates the ghost button's ink at the end
   of the file, where the earlier `.btn.ghost { color: var(--ink) }` would
   otherwise be the only declaration and this is where a reader looks for it. */
.btn.ghost, .btn.ghost:hover { color: var(--ink); }

/* Revoked outranks every other certificate state: it is the only one where the
   ISSUER pulled the certificate, so it gets the alert color rather than the
   neutral treatment expiry and withdrawal share. A dispute is deliberately
   NEUTRAL — an open dispute means someone contested the verdict and it is being
   reviewed, and coloring it as a failure would convict on accusation. */
.cd-revoked-tag {
  color: var(--paper); background: var(--spark); border-color: var(--spark);
}
.cd-disputed-tag {
  color: var(--ink); background: color-mix(in srgb, var(--warn) 18%, transparent);
  border-color: var(--warn);
}
.cd-caveats li.cd-cav-revoked, .cd-caveats li.cd-cav-withdrawn {
  color: var(--ink); font-weight: 500;
  border-left: 3px solid var(--spark); padding-left: 10px;
  margin-left: -18px; list-style: none;
}
.cd-caveats li.cd-cav-withdrawn { border-left-color: var(--faint); }

/* =========================================================================
   Feature request board (.fr-*)

   The vote button is the largest interactive element on each row on purpose:
   voting is the action the board exists for, and the count IS the ranking, so
   it should be readable at a glance down the column.
   ========================================================================= */
.fr-board { margin: 26px 0 40px; }
.fr-board-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.fr-board-head h2 { margin: 0; font-size: 20px; }
.fr-count { font-family: var(--mono); font-size: 12px; color: var(--faint); }

.fr-list { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 12px; }
.fr-item {
  display: flex; gap: 14px; align-items: flex-start;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow);
}
.fr-empty {
  color: var(--muted); font-size: 15px; background: var(--surface-2);
  border: 1px dashed var(--line-strong); border-radius: var(--radius);
  padding: 20px 22px; text-align: center;
}

/* Comfortably above the WCAG 2.2 AA 24px floor, and a real target on a phone. */
.fr-vote {
  flex: none; display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-width: 56px; min-height: 56px; padding: 8px 10px; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--line-strong);
  border-radius: var(--radius); color: var(--muted);
  font-family: var(--mono); transition: border-color .12s ease, color .12s ease;
}
.fr-vote:hover:not(:disabled) { border-color: var(--brand); color: var(--brand); }
.fr-vote:disabled { cursor: default; }
.fr-vote.is-voted { border-color: var(--good); color: var(--good-text); background: color-mix(in srgb, var(--good) 8%, transparent); }
.fr-vote-arrow { font-size: 12px; line-height: 1; }
.fr-vote-count { font-size: 17px; font-weight: 700; color: var(--ink); }
.fr-vote.is-voted .fr-vote-count { color: var(--good-text); }

.fr-body { min-width: 0; }
.fr-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.fr-head h3 { margin: 0; font-size: 16.5px; color: var(--ink); overflow-wrap: anywhere; }
.fr-detail { margin: 7px 0 0; font-size: 14.5px; color: var(--muted); line-height: 1.6; overflow-wrap: anywhere; }
.fr-meta { margin: 9px 0 0; display: flex; gap: 12px; flex-wrap: wrap; font-family: var(--mono); font-size: 11.5px; color: var(--faint); }
.fr-area { text-transform: uppercase; letter-spacing: .08em; }

.fr-status {
  flex: none; font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em;
  padding: 3px 9px; border-radius: 999px; border: 1px solid var(--line-strong); color: var(--muted);
}
/* Planned and Shipped read as progress, so they carry the positive color;
   Open is neutral because "nobody has committed to this yet" is the truth. */
.fr-status.fr-planned { border-color: var(--brand); color: var(--brand); background: var(--brand-soft); }
.fr-status.fr-shipped { border-color: var(--good); color: var(--good-text); background: color-mix(in srgb, var(--good) 10%, transparent); }

.fr-form-wrap { margin: 34px 0 0; padding-top: 26px; border-top: 1px solid var(--line); }
.fr-form-wrap h2 { font-size: 20px; margin: 0 0 8px; }
.fr-form-note { font-size: 14px; color: var(--muted); margin: 0 0 18px; max-width: 62ch; }
.fr-opt { font-family: var(--sans); text-transform: none; letter-spacing: 0; color: var(--faint); font-size: 12px; }

@media (max-width: 560px) {
  .fr-item { padding: 14px; gap: 12px; }
  .fr-vote { min-width: 50px; }
}

/* =========================================================================
   The workflow recommender  (.wf-*)

   Three steps that reveal one another. Each is a real section rather than a
   tab, so the whole thing prints, deep-links and degrades to a long page if
   the script never runs.
   ========================================================================= */
.wf-step { margin: 34px 0; }
.wf-step h2 { display: flex; align-items: baseline; gap: 12px; }
.wf-num {
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  color: var(--surface); background: var(--brand);
  width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center; flex: none;
}
.wf-sub { color: var(--muted); margin-top: -6px; }

/* --- step 1: stage cards --- */
.wf-stages { display: grid; gap: 12px; margin-top: 18px; }
@media (min-width: 700px) { .wf-stages { grid-template-columns: 1fr 1fr; } }
.wf-stage {
  text-align: left; cursor: pointer; font: inherit;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius); padding: 16px 18px;
  display: block; width: 100%;
}
.wf-stage:hover { border-color: var(--brand); }
.wf-stage:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* Selection is a border AND a tint AND a mark — never color alone, which is
   invisible to anyone who cannot distinguish it. */
.wf-stage.is-on { border-color: var(--brand); background: var(--brand-soft); }
.wf-stage.is-on .wf-stage-label::after { content: " ✓"; color: var(--brand); }
.wf-stage-label { display: block; font-weight: 600; color: var(--ink); }
.wf-stage-blurb { display: block; font-size: 14px; color: var(--muted); margin-top: 4px; }

/* --- step 2: questions --- */
.wf-q { border: 0; padding: 0; margin: 0 0 22px; }
.wf-q legend { font-weight: 600; color: var(--ink); padding: 0; margin-bottom: 10px; }
.wf-opts { display: flex; flex-wrap: wrap; gap: 8px; }
.wf-opt {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  border: 1px solid var(--line-strong); border-radius: 20px;
  padding: 9px 14px; font-size: 14px; color: var(--muted);
  background: var(--surface);
}
.wf-opt:hover { border-color: var(--brand); }
.wf-opt:has(input:checked) { border-color: var(--brand); background: var(--brand-soft); color: var(--ink); font-weight: 500; }
.wf-opt:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }

/* --- step 3: the report --- */
.wf-lede { font-size: 17px; color: var(--muted); }
.wf-insights { margin: 26px 0; }
.wf-insight {
  border-left: 3px solid var(--spark); padding: 2px 0 2px 16px; margin: 0 0 18px;
}
.wf-insight h4 { margin: 0 0 4px; font-size: 15.5px; color: var(--ink); }
.wf-insight p { margin: 0; color: var(--muted); font-size: 14.5px; }
.wf-list-h { margin-top: 30px; }
.wf-list { margin: 0; padding-left: 22px; }
.wf-item { margin-bottom: 16px; }
.wf-item-t { font-weight: 600; }
.wf-item-why { margin: 3px 0 0; color: var(--muted); font-size: 14px; }
.wf-after { margin-top: 24px; color: var(--muted); }

/* --- the email step --- */
.wf-capture {
  margin-top: 34px; padding: 22px; border-radius: var(--radius);
  background: var(--surface-2); border: 1px solid var(--line-strong);
}
.wf-capture h3 { margin-top: 0; }
.wf-capture-lede { color: var(--muted); }
/* The plain-dealing paragraph. Quieter than the pitch above it on purpose —
   it is there to be true, not to persuade. */
.wf-capture-honest {
  font-size: 13.5px; color: var(--faint);
  border-left: 2px solid var(--line-strong); padding-left: 12px; margin: 14px 0 18px;
}
.wf-opt-label, .wf-opt.wf-optional { font-weight: 400; }
.wf-restart { margin-top: 26px; }
.wf-link {
  font: inherit; font-size: 14px; background: none; border: 0; padding: 0;
  color: var(--brand); text-decoration: underline; cursor: pointer;
}

/* Recommender: the email gate and the report actions */
.wf-req-note { color: var(--faint); font-weight: 400; }
.wf-fine { font-size: 12.5px; color: var(--faint); margin-top: 12px; }
.wf-report-actions { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; margin-bottom: 18px; }
/* The report ON SCREEN sits on the dark design; ON PAPER it is still the white
   document (ph32tsk71). workflow-report.js injects the Playground's report
   chrome — a #fff ground with #14181f ink — and that port stays exactly as the
   app has it, because scripts/report-chrome-port-check.mjs holds it to the
   app's white, print-first rule. What changed is only where the site paints it:
   in a browser window that white ground was a 736x1345 light slab in the middle
   of a dark page (measured at 1280x800; 303x2311 at 375x740), which is the
   "large white section" the owner reported on 2026-09-10. So on SCREEN the
   report is a --surface card in the site's tokens, and print — the reason it
   was white — gets the untouched white document, because this whole block is
   inside @media screen. The notices sit in a --paper well rather than on a pale
   tint: any tint over --surface is LIGHTER than it, and the check below forbids
   exactly that. Checked by `npm run check:site-stuck-dark`. */
#wf-report { margin-top: 4px; }
@media screen {
  #wf-report .vcr { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
  #wf-report .vcr h1, #wf-report .vcr h2, #wf-report .vcr h3,
  #wf-report .vcr ol.plan a { color: var(--ink); }
  #wf-report .vcr .cover { border-bottom-color: var(--line-strong); }
  #wf-report .vcr h2, #wf-report .vcr .tile, #wf-report .vcr footer { border-color: var(--line); }
  #wf-report .vcr .eyebrow, #wf-report .vcr .cover .sub, #wf-report .vcr .verdict .blurb,
  #wf-report .vcr .summary, #wf-report .vcr .tile .l, #wf-report .vcr .f-area,
  #wf-report .vcr .f-detail, #wf-report .vcr .phase .goal, #wf-report .vcr ol.plan,
  #wf-report .vcr .why, #wf-report .vcr footer { color: var(--muted); }
  #wf-report .vcr .notice { background: var(--paper); color: var(--muted); border-left-color: var(--warn); }
  #wf-report .vcr .notice.clean { background: var(--paper); border-left-color: var(--good); }
  #wf-report .vcr .finding { border-left-color: var(--line-strong); }
  #wf-report .vcr .finding.major { border-left-color: var(--warn); }
}
@media print {
  body > *:not(main) { display: none !important; }
  main > *:not(#wf-step-report) { display: none !important; }
  #wf-step-report > h2, .wf-report-actions { display: none !important; }
}

/* =========================================================
   Pre-built code blocks — one page per block, generated by
   scripts/build-blocks.mjs. Tokens only; no new colors.
   The audit panel is the page's argument, so it is the only
   element allowed to raise its voice.
   ========================================================= */
.blk-page { padding-top: 56px; padding-bottom: 72px; }
.blk-page h1 { font-size: clamp(28px, 4.2vw, 42px); letter-spacing: -0.02em; margin: 10px 0 14px; }
.blk-page section { margin-top: 40px; }
.blk-page h2 { font-size: clamp(19px, 2.4vw, 25px); letter-spacing: -0.015em; margin: 0 0 12px; }
.blk-page h3 { font-size: 15px; margin: 0 0 8px; }
.blk-page p, .blk-page li { max-width: 72ch; }
.blk-page .lede { font-size: 17px; line-height: 1.6; color: var(--muted); }

.blk-fulltitle { font-size: 15.5px; color: var(--muted); margin: 0 0 12px; max-width: 72ch; line-height: 1.5; }
.blk-forwhen { font-size: 15px; color: var(--ink); margin: 0 0 10px; }
.blk-forwhen b { color: var(--brand); font-weight: 600; }
.blk-rebuilt {
  font-family: var(--mono); font-size: 12.5px; color: var(--muted);
  border-left: 3px solid var(--spark); padding: 6px 0 6px 14px; margin: 18px 0 0;
}
.blk-rebuilt b { color: var(--ink); }

/* The verdict. Four states, and the ones that are not a pass have to be as
   legible as the one that is — a page that whispers CAUTION is the claim-and-
   behavior gap the audit exists to catch. */
.blk-audit {
  margin-top: 36px; padding: 20px 22px;
  border: 1px solid var(--line-strong); border-left-width: 4px; border-radius: 14px;
  background: var(--surface);
}
.blk-audit-h {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--faint); margin: 0 0 6px;
}
.blk-audit-verdict { font-size: 18px; font-weight: 700; margin: 0 0 8px; }
.blk-audit p { margin: 0 0 8px; }
.blk-audit-when { font-family: var(--mono); font-size: 12px; color: var(--faint); margin: 8px 0 0; }
.blk-audit.is-pass { border-left-color: var(--good-text); }
.blk-audit.is-pass .blk-audit-verdict { color: var(--good-text); }
.blk-audit.is-caution { border-left-color: var(--spark); }
.blk-audit.is-caution .blk-audit-verdict { color: var(--spark-text); }
.blk-audit.is-ungraded, .blk-audit.is-none, .blk-audit.is-expired { border-left-color: var(--faint); }
.blk-audit.is-ungraded .blk-audit-verdict,
.blk-audit.is-none .blk-audit-verdict,
.blk-audit.is-expired .blk-audit-verdict { color: var(--muted); }

.blk-findings { list-style: none; padding: 0; margin: 0; display: grid; gap: 10px; }
.blk-findings li {
  padding: 10px 14px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface); display: flex; gap: 12px; align-items: baseline;
  justify-content: space-between; flex-wrap: wrap;
}
.blk-findings b { font-weight: 600; font-size: 14.5px; }
.blk-sev {
  font-family: var(--mono); font-size: 11px; color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap;
}

.blk-verified, .blk-todo, .blk-config { display: grid; gap: 8px; padding-left: 20px; }
.blk-verified li, .blk-todo li, .blk-config li { line-height: 1.55; }
.blk-todo li { color: var(--muted); }

.blk-stacks { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.blk-stack { border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px; background: var(--surface); }
.blk-stack h3 { font-family: var(--mono); font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--faint); }
.blk-stack ul { margin: 0; padding-left: 18px; }
.blk-stack li { font-family: var(--mono); font-size: 12.5px; }
.blk-note { font-size: 13px; color: var(--faint); margin: 10px 0 0; }

.blk-cta {
  border: 1px solid var(--line-strong); border-radius: 14px;
  padding: 20px 22px; background: var(--surface-2);
}
.blk-cta-top { margin-top: 28px; }
.blk-steps { margin: 12px 0 16px; padding-left: 20px; display: grid; gap: 8px; }
.blk-actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 4px 0 0; }

.blk-faq { margin: 0; }
.blk-faq dt { font-weight: 600; margin-top: 14px; }
.blk-faq dd { margin: 6px 0 0; color: var(--muted); max-width: 72ch; }
.blk-back { margin-top: 44px; padding-top: 20px; border-top: 1px solid var(--line); font-size: 14px; }

.blk-groupnote { color: var(--muted); margin: 0 0 16px; max-width: 72ch; }
.blk-card .tag { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; }

/* Related-blocks cluster + audit verdict quote on block pages (ph32tsk50/51) */
.blk-related-list { list-style: none; padding: 0; margin: 10px 0 0; display: grid; gap: 8px; }
.blk-related-list li { color: var(--faint); }
.blk-related-list a { font-weight: 600; }
.blk-audit-quote { margin: 0; padding: 2px 0 2px 14px; border-left: 3px solid var(--accent-green); font-style: normal; }
.blk-audit-quote cite { display: block; margin-top: 6px; font-size: .85em; font-style: normal; color: var(--faint); }

/* =========================================================================
   TEACHING FIGURES (ph32tsk67)
   Diagrams for the 101 course and the guide. Measured before writing these:
   31 lesson pages and 34 guide articles contained ZERO figures inside <main> —
   no img, svg, figure or canvas. The prose is good and entirely unillustrated,
   which is the course's largest comprehension gap.

   THE ONE RULE THAT SHAPES ALL OF IT: line 173 of this file is a global kill
   switch — `* { animation:none!important; transition:none!important }` under
   prefers-reduced-motion. So a from-state written in plain CSS (opacity:0, ready
   to transition in) would never transition for a reduced-motion reader: it would
   be INVISIBLE CONTENT, permanently. The default markup is therefore the
   COMPLETE, FINISHED drawing, and the from-state is added by JS only after
   matchMedia('(prefers-reduced-motion: no-preference)') has agreed. No JS, no
   motion preference, old browser, script blocked — every one of those paths
   renders the whole figure. Motion is the enhancement; the picture is the
   content. See figureReveal() in assets/site.js — NOT vibecoder101.js, which is generated
   by build-101.mjs and would wipe it.
   ========================================================================= */
.fig { margin: 26px 0; padding: 18px 16px 14px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
.fig figcaption { margin-top: 14px; color: var(--muted); font-size: .92em; line-height: 1.5; }
/* THE FIGURES ARE HTML, NOT SVG. The first cut drew both as <svg viewBox> pictures,
   and a viewBox scales its TEXT with its width: on a 375px phone the 12px labels
   rendered at ~6px. Boxes, borders and dashed lines are things CSS draws for
   free, and text in a div reflows instead of shrinking — so every label below is
   a real font size on every screen. */
.fig .fig-label { font: 600 13px/1.3 var(--mono); color: var(--ink); }
.fig .fig-sub { font: 500 12.5px/1.4 var(--mono); color: var(--muted); }

/* --- the stack, as layers (101: what-is-a-stack) --- */
.stack-fig .stack { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.stack-fig .layer { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; padding: 12px 18px;
  background: var(--surface-2); border: 1.5px solid color-mix(in srgb, var(--brand) 55%, var(--line-strong)); border-radius: 10px; }
.stack-fig .layer .fig-sub { flex: 1 1 18ch; }
/* Optional layers: the EDGE is what goes quiet, never the words. The first cut
   dimmed the label and sub-label to 55% opacity, which put the sub-label at
   3.13:1 — under AA — to say "optional". A dashed, dimmer border and the word
   itself say it at full contrast. */
.stack-fig .layer.is-optional { border-style: dashed; border-color: color-mix(in srgb, var(--brand) 30%, var(--line)); }
.stack-fig .layer .opt { font: 700 10.5px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; font-style: normal; color: var(--faint); margin-left: auto; }

/* The opt-in from-state. `.will-move` is added by JS ONLY when the reader has
   not asked for reduced motion; `.in` follows when the figure is scrolled to. */
.fig.will-move .layer { opacity: 0; transform: translateY(16px); }
.fig.will-move.in .layer {
  opacity: 1; transform: none;
  transition: opacity .45s var(--ease) calc(var(--i) * 95ms), transform .6s var(--glide) calc(var(--i) * 95ms);
}

/* --- reads vs attacks (101: the-audit) -----------------------------------
   The one claim on the site that a reader most reasonably doubts is "it
   attacks, not just reads". This draws the difference as a sequence rather than
   asserting it again in prose. Deliberately austere: two lanes, mono labels, no
   characters, no glow, no easing bounce. It has to read as documentation, not
   as an advert — a diagram that looks persuasive is doing the opposite of what
   this product sells. The captions QUOTE the backed claim and do not extend it:
   nothing here says the audit blocks anything, only that it sends a real
   request and records what came back.
   BOTH LANES END IN A RESULT. The first cut left lane A's result slot empty, so
   the picture showed one lane with an answer and one without — the contrast it
   exists to draw was only in the caption. Now lane A ends in a quoted opinion
   in a dashed pill and lane B in a recorded status code in a solid one. */
.lanes-fig .lane + .lane { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.lanes-fig .lane-tag { margin: 0 0 10px; font: 700 11px/1.3 var(--mono); letter-spacing: .12em; text-transform: uppercase; }
.lanes-fig .lane-a .lane-tag { color: var(--faint); }
.lanes-fig .lane-b .lane-tag { color: var(--ink); }
.lanes-fig .chain { display: grid; grid-template-columns: auto minmax(18px, 1fr) auto minmax(18px, 1fr) auto; align-items: center; column-gap: 8px; }
.lanes-fig .chain .fig-sub { text-align: center; max-width: 24ch; }
.lanes-fig .wire { display: block; height: 0; border-top: 1.5px dashed var(--line-strong); }
.lanes-fig .code { display: grid; gap: 6px; width: 132px; padding: 10px 10px; border-radius: 6px; background: color-mix(in srgb, var(--faint) 14%, transparent); }
.lanes-fig .code i { display: block; height: 6px; border-radius: 3px; background: var(--faint); opacity: .45; }
.lanes-fig .code i:nth-child(1) { width: 58%; }
.lanes-fig .code i:nth-child(2) { width: 90%; }
.lanes-fig .code i:nth-child(3) { width: 72%; }
.lanes-fig .code i:nth-child(4) { width: 100%; }
.lanes-fig .pill { display: inline-block; padding: 7px 14px; border-radius: 999px; white-space: nowrap;
  font: 700 12px/1.2 var(--mono); color: var(--ink); background: var(--surface-2); border: 1.5px solid var(--line-strong); }
.lanes-fig .pill.claim { border-style: dashed; color: var(--muted); font-weight: 500; font-style: italic; }
.lanes-fig .pill.hit { border-color: var(--spark); color: var(--spark); }
.lanes-fig .pill.back { border-color: var(--good); color: var(--good); }
.lanes-fig .door-box { display: inline-block; padding: 9px 16px; border-radius: 8px; white-space: nowrap;
  font: 500 12.5px/1.2 var(--mono); color: var(--muted); background: var(--surface-2); border: 1.5px solid var(--spark); }
.lanes-fig .verdict { display: flex; align-items: flex-start; gap: 10px; margin: 14px 0 0; font: 500 12.5px/1.4 var(--mono); color: var(--muted); }
.lanes-fig .verdict-dot { flex: none; width: 10px; height: 10px; margin-top: 4px; border-radius: 50%; background: var(--good); }
/* Narrow screens: each chain stands on end. The wires turn vertical and every
   pill keeps its full-size type — nothing here is scaled, only re-stacked. */
@media (max-width: 560px) {
  .lanes-fig .chain { grid-template-columns: 1fr; justify-items: start; row-gap: 6px; }
  .lanes-fig .chain .fig-sub { text-align: left; max-width: none; }
  .lanes-fig .wire { height: 16px; width: 0; border-top: 0; border-left: 1.5px dashed var(--line-strong); margin-left: 22px; }
}

/* =========================================================================
   FREE TOOLS (ph45tsk11) — /free-tools/
   The front doors to the credit-free tools. Same construction as the
   directory block above: tokens only, no new colors, and every rule pairs a
   foreground with a ground the palette gate already measures. Nothing here is
   page-local, so `npm run test:a11y-website` reaches all of it — an inline
   <style> block would be invisible to that gate, which is why there is none.
   ========================================================================= */

.ft-page { padding-bottom: 20px; }
.ft-crumb { margin: 0 0 6px; font-size: 13.5px; }
.ft-crumb a { color: var(--muted); }
.ft-benefit {
  margin: 10px 0 26px; max-width: 68ch; font-size: 16.5px; line-height: 1.6; color: var(--muted);
}

/* ---- the set's index ---- */
.ft-grid {
  display: grid; gap: 14px; margin: 22px 0 34px;
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
}
.ft-card {
  display: block; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); padding: 20px 20px 18px; color: var(--muted);
  transition: border-color .2s var(--ease), transform .2s var(--ease);
}
a.ft-card:hover { border-color: var(--brand); transform: translateY(-2px); text-decoration: none; }
.ft-card h2 { margin: 0 0 8px; font-size: 18px; line-height: 1.3; color: var(--ink); }
.ft-card p { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--muted); }
.ft-card-go {
  display: inline-block; margin-top: 14px; font-family: var(--mono); font-size: 12px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--brand);
}

/* ---- the form ---- */
.ft-form { display: grid; gap: 16px; margin: 0 0 26px; max-width: 860px; }
.ft-label { display: grid; gap: 8px; font-size: 14px; line-height: 1.5; color: var(--muted); }
.ft-textarea, .ft-input {
  width: 100%; background: var(--surface-2); border: 1px solid var(--line-strong);
  border-radius: var(--radius); color: var(--ink); font-size: 14.5px; padding: 12px 14px;
}
.ft-textarea { font-family: var(--mono); line-height: 1.5; resize: vertical; }
.ft-input { font-family: var(--sans); }
.ft-textarea:focus, .ft-input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.ft-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.ft-status { font-family: var(--mono); font-size: 12.5px; color: var(--faint); }
.ft-receipts { margin: 0; font-size: 13px; line-height: 1.55; color: var(--faint); max-width: 68ch; }

/* ---- what it reads / does not read ---- */
.ft-section { margin: 30px 0 0; }
.ft-section h2 { font-size: 19px; margin: 0 0 10px; }
.ft-scope { margin: 0 0 12px; color: var(--muted); font-size: 15px; line-height: 1.6; max-width: 72ch; }
.ft-measures, .ft-limits { margin: 0; padding-left: 20px; max-width: 78ch; }
.ft-measures li, .ft-limits li { margin: 0 0 8px; color: var(--muted); font-size: 14.5px; line-height: 1.6; }
.ft-weight {
  font-family: var(--mono); font-size: 11.5px; color: var(--faint);
  border: 1px solid var(--line); border-radius: 999px; padding: 2px 8px; margin-left: 6px;
  white-space: nowrap;
}

/* ---- the result ---- */
.ft-result { margin: 8px 0 0; }
.ft-card--result { border-color: var(--line-strong); padding: 22px; }
.ft-result-head h2 { margin: 0; font-size: 20px; color: var(--ink); }
.ft-ran { margin: 4px 0 14px; font-family: var(--mono); font-size: 11.5px; color: var(--faint); }
.ft-result-scope { margin: 0 0 16px; font-size: 14.5px; line-height: 1.6; color: var(--muted); }
.ft-wall h2, .ft-measured h2 { font-size: 18px; margin: 22px 0 8px; color: var(--ink); }
.ft-wall h3 { font-size: 14px; margin: 16px 0 6px; color: var(--ink); }
.ft-gave { margin: 0 0 8px; font-size: 16px; line-height: 1.55; color: var(--ink); }
.ft-list { margin: 0; padding-left: 18px; }
.ft-list li { margin: 0 0 7px; font-size: 14px; line-height: 1.6; color: var(--muted); }
.ft-dl { margin: 0; }
.ft-dl dt {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--faint); margin: 10px 0 3px;
}
.ft-dl dd { margin: 0 0 4px; font-size: 14px; line-height: 1.6; color: var(--muted); }
.ft-none { color: var(--faint); font-style: italic; }
.ft-pre {
  margin: 4px 0 0; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2); color: var(--muted); font-family: var(--mono); font-size: 12.5px;
  line-height: 1.5; overflow-x: auto; white-space: pre-wrap; word-break: break-word;
}
.ft-receipt-list { margin: 0; padding-left: 18px; }
.ft-receipt-list li { margin: 0 0 6px; font-size: 14px; color: var(--muted); }
.ft-receipt-list b { color: var(--ink); }
.ft-download { margin: 22px 0 0; }
/* The free QR code generator (ph98tsk98). The code sits on its own background
   color, so the preview box adds no fill of its own. */
.qr-options { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.qr-color { width: 64px; height: 40px; padding: 2px; border: 1px solid var(--line-strong); border-radius: 8px; background: transparent; cursor: pointer; }
.qr-out { margin: 0 0 14px; }
.qr-out svg { display: block; width: 260px; max-width: 100%; height: auto; }

/* DARK TEXT ON THE BRIGHT FILLS (ph32tsk70).
   --brand #7b95ff and --spark #ff6b4a are LIGHT fills: white text on them measures
   2.77:1 and 2.82:1, and WCAG 1.4.3 needs 4.5:1 for text. Every rule that painted
   white on one of them now uses --paper, measured with scripts/lib/contrast.mjs:
     --paper on --brand 6.78:1   --paper on --spark 6.66:1
   --paper rather than the hand-typed #17182b that .vc101-weekend already used,
   because it is a token and it measures higher (6.31:1 / 6.20:1 for #17182b).
   Found when ph32tsk66 taught test:a11y-website to read every rule, not just the
   token pairs, and reported these as four failures ("the same pair is written by
   3 more rules"). The badge chip carries its color on the rule that owns its background, and its
   two lines use `color: inherit`. Two traps sit here. A color written in a
   separate rule from its fill is measured against every ground it could land on,
   including the dark page, so it reads as dark-on-dark. And simply DELETING the
   earned-state overrides would be worse: `.vc101-badgechip b` sets --muted and
   would beat plain inheritance, painting gray text on the blue fill. `inherit`
   beats the base rule on specificity and resolves to --paper. The secondary line
   gives up its old 0.82 fade; bold versus regular carries the hierarchy. */
