/* XYPN LIVE 2026 handout companion — shared stylesheet.
   Palette and type are fixed by the brief: Work Sans (headings), Open Sans
   (body); navy #1B2A49, blue #2850BE, ground #F7F8FA, warning red #C0392B. */

:root {
  --navy: #1B2A49;
  --navy-ink: #182238;
  --navy-soft: #2B3D63;
  --blue: #2850BE;
  --blue-dark: #1c3c94;
  --ground: #F7F8FA;
  --paper: #FFFFFF;
  --line: #DDE2EA;
  --line-soft: #E9ECF2;
  --text: #232C3D;
  --text-soft: #57627A;
  --warn: #C0392B;
  --warn-ground: #FBEAE8;

  /* Vendor badge colors — one consistent system used on both the vendor table and the
     mobile vendor cards (see badge()/sortLevelRank() in vendors.js): green for a plain
     yes, a muted grey-red for no, grey for unknown/not applicable, amber for anything
     that needs a second look (partial support, SMS-only). */
  --badge-yes-bg: #E3EEE1; --badge-yes-fg: #2E6A3E;
  --badge-no-bg: #EFE2E1; --badge-no-fg: #8C5049;
  --badge-unknown-bg: var(--line-soft); --badge-unknown-fg: var(--text-soft);
  --badge-amber-bg: #FBF0D6; --badge-amber-fg: #8A6113;

  --font-head: "Work Sans", "Helvetica Neue", Arial, sans-serif;
  --font-body: "Open Sans", "Segoe UI", Arial, sans-serif;
  --font-mono: "SF Mono", "Menlo", "Consolas", monospace;

  --measure: 40rem; /* ~68ch at body size */
  --topbar-h: 56px;
  --progress-h: 3px;
  --radius: 3px;
  --radius-card: 6px;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
@media (min-width: 640px) {
  body { font-size: 17px; }
}

h1, h2, h3, h4 {
  font-family: var(--font-head);
  color: var(--navy);
  line-height: 1.2;
  font-weight: 600;
  margin: 0 0 0.5em;
}

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

a { color: var(--blue); text-decoration-thickness: 1px; text-underline-offset: 0.15em; }
a:hover { color: var(--blue-dark); }

/* ---------- Inline content links (guide body) ----------
   Not underlined at rest; underline appears on hover/focus so the prose stays clean
   but the affordance is unambiguous. Visited state is a slightly muted blue-violet so
   a reader can see, at a glance, which of many outbound links they've already opened. */
.doc-lead a, .main a {
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.doc-lead a:hover, .doc-lead a:focus-visible,
.main a:hover, .main a:focus-visible {
  text-decoration: underline;
}
.doc-lead a:visited, .main a:visited {
  color: #5B4E96;
}
/* Small inline-SVG external-link glyph after any outbound content link, drawn with a
   CSS-embedded data-URI so it needs no extra request and no per-link markup. Buttons
   (.source-btn, .vendor-btn, .cta-btn) carry their own literal inline <svg> instead —
   excluded here so the icon never doubles up. */
.doc-lead a[target="_blank"]:not(.source-btn):not(.cta-btn)::after,
.main a[target="_blank"]:not(.source-btn):not(.cta-btn)::after {
  content: "";
  display: inline-block;
  width: 0.68em;
  height: 0.68em;
  margin-left: 0.2em;
  vertical-align: baseline;
  background-repeat: no-repeat;
  background-size: contain;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232850BE' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
}

p { margin: 0 0 1em; }
ul, ol { margin: 0 0 1em; padding-left: 1.3em; }
li { margin-bottom: 0.35em; }

img { max-width: 100%; display: block; }

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

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

.skip-link {
  position: absolute; left: -999px; top: 0;
  background: var(--navy); color: #fff; padding: 0.6em 1em;
  z-index: 200; border-radius: 0 0 4px 0;
}
.skip-link:focus { left: 0; }

/* ---------- Top bar (shared: guide + vendors) ---------- */

.topbar {
  position: sticky; top: 0; z-index: 50;
  background: var(--navy);
  color: #fff;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0 0.9rem;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
@media (max-width: 480px) {
  .topbar { gap: 0.5rem; padding: 0 0.6rem; }
}

.topbar__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px; height: 38px;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.25);
  border-radius: var(--radius);
  color: #fff;
  cursor: pointer;
  flex: none;
}
.topbar__toggle span,
.topbar__toggle::before,
.topbar__toggle::after {
  content: ""; display: block; width: 16px; height: 2px; background: #fff;
}
.topbar__toggle::before { box-shadow: 0 -5px 0 #fff, 0 5px 0 #fff; }

.topbar__logo { flex: none; display: flex; align-items: center; min-width: 0; overflow: hidden; }
.topbar__logo img { height: 20px; width: auto; }
@media (max-width: 480px) { .topbar__logo img { height: 17px; } }

.topbar__search-wrap {
  position: relative;
  flex: 1 1 120px;
  min-width: 64px;
  max-width: 30rem;
}

.topbar__search {
  width: 100%;
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: var(--radius);
  color: #fff;
  font-family: var(--font-body);
  font-size: 0.95rem;
  padding: 0.5em 0.8em;
}
.topbar__search::placeholder { color: rgba(255,255,255,0.65); }
.topbar__search:focus { background: rgba(255,255,255,0.18); }

.topbar__actions { display: flex; align-items: center; gap: 0.5rem; flex: none; margin-left: auto; }

.topbar__btn { /* legacy hook kept for anything still targeting it via JS */ }

/* Platform-filter pill — replaces the old plain "Filter" button. Always shows the
   current choice (see guide.js updateFilterPill) so it reads as state, not a command. */
.topbar__pill {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.82rem;
  color: #fff;
  background: rgba(255,255,255,0.1);
  border: 1px solid rgba(255,255,255,0.35);
  border-radius: 999px;
  padding: 0.42em 0.85em;
  cursor: pointer;
  white-space: nowrap;
}
.topbar__pill:hover { background: rgba(255,255,255,0.18); }

/* Vendor lookup — a plain nav link in the topbar, not a boxed button competing with
   search or the pill. */
.topbar__link {
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: rgba(255,255,255,0.88);
  background: none;
  border: none;
  padding: 0.45em 0.2em;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.topbar__link:hover { color: #fff; border-bottom-color: rgba(255,255,255,0.5); }
@media (max-width: 560px) {
  .topbar__link--vendors { display: none; }
}

/* Book-a-call — the one solid, unmissable button in the top bar of every page. */
.topbar__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  background: var(--blue);
  color: #fff;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.82rem;
  border: none;
  border-radius: 999px;
  padding: 0.5em 0.95em;
  text-decoration: none;
  white-space: nowrap;
  min-height: 36px;
}
.topbar__cta:hover, .topbar__cta:focus-visible { background: var(--blue-dark); color: #fff; }
.topbar__cta-icon { flex: none; }
@media (max-width: 480px) {
  .topbar__cta { padding: 0.5em; gap: 0; }
  .topbar__cta-label { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
}

/* ---------- Reading progress bar ---------- */

.progress {
  position: sticky;
  top: var(--topbar-h);
  z-index: 49;
  height: var(--progress-h);
  background: var(--line-soft);
}
.progress__bar {
  height: 100%;
  width: 0%;
  background: var(--blue);
}
@media (prefers-reduced-motion: no-preference) {
  .progress__bar { transition: width 0.12s linear; }
}

/* ---------- Search results dropdown ---------- */

.search-results {
  position: absolute;
  top: calc(100% + 6px);
  left: 0; right: 0;
  background: var(--paper);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(20, 30, 55, 0.18);
  max-height: 70vh;
  overflow-y: auto;
  z-index: 60;
  display: none;
}
.search-results.is-open { display: block; }
.search-result {
  display: block;
  padding: 0.6em 0.9em;
  border-bottom: 1px solid var(--line-soft);
  text-decoration: none;
  color: var(--text);
}
.search-result:last-child { border-bottom: none; }
.search-result:hover, .search-result.is-active { background: var(--ground); }
.search-result__heading {
  font-family: var(--font-head);
  font-weight: 600;
  color: var(--navy);
  font-size: 0.9rem;
}
.search-result__chapter { color: var(--text-soft); font-size: 0.78rem; margin-top: 0.1em; }
.search-result__snippet { font-size: 0.85rem; margin-top: 0.25em; color: var(--text-soft); }
.search-result__snippet mark { background: #fde9c8; color: inherit; padding: 0 0.1em; }
.search-empty { padding: 0.9em; color: var(--text-soft); font-size: 0.9rem; }

/* ---------- Layout: sidebar + main ---------- */

.layout {
  display: block;
}
@media (min-width: 900px) {
  .layout {
    display: grid;
    grid-template-columns: 260px minmax(0, 1fr);
    align-items: start;
  }
}

.toc-backdrop {
  display: none;
  position: fixed; inset: 0;
  background: rgba(15, 20, 35, 0.45);
  z-index: 69;
}
.toc-backdrop.is-open { display: block; }

.toc {
  background: var(--paper);
  border-right: 1px solid var(--line);
}
@media (max-width: 899px) {
  .toc {
    position: fixed;
    top: 0; bottom: 0; left: 0;
    width: 85%; max-width: 320px;
    z-index: 70;
    transform: translateX(-101%);
    transition: transform 0.22s ease;
    overflow-y: auto;
    padding-top: calc(var(--topbar-h) + var(--progress-h));
  }
  .toc.is-open { transform: translateX(0); }
}
@media (min-width: 900px) {
  .toc {
    position: sticky;
    top: calc(var(--topbar-h) + var(--progress-h));
    height: calc(100vh - var(--topbar-h) - var(--progress-h));
    overflow-y: auto;
  }
}
@media (prefers-reduced-motion: reduce) {
  .toc { transition: none; }
}

.toc__close {
  display: block;
  margin: 0.7rem 0.9rem 0;
  background: none; border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.3em 0.7em;
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--text-soft);
}
@media (min-width: 900px) { .toc__close { display: none; } }

/* Vendor lookup + Book a call read as a paired pair of controls at the top of the
   sidebar: same box (width, height, radius), outlined vs. solid so they're clearly
   two different kinds of action without competing for attention. */
.toc__vendors-link, .toc__book-link {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-height: 44px;
  margin: 0.7rem 0.9rem 0;
  padding: 0.5em 0.7em;
  border-radius: var(--radius);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.85rem;
  text-decoration: none;
  text-align: center;
}
.toc__vendors-link {
  border: 1.5px solid var(--line);
  color: var(--navy);
}
.toc__vendors-link:hover, .toc__vendors-link:focus-visible { border-color: var(--blue); color: var(--blue); }

.toc__book-link {
  margin-bottom: 0.9rem;
  background: var(--blue);
  color: #fff;
}
.toc__book-link:hover, .toc__book-link:focus-visible { background: var(--blue-dark); }

.toc__list { list-style: none; margin: 0; padding: 0.9rem 0 2rem; }
.toc__chapter { margin-bottom: 0.15rem; }
.toc__chapter-link {
  display: flex;
  gap: 0.6em;
  align-items: baseline;
  padding: 0.4em 0.9rem;
  text-decoration: none;
  color: var(--navy);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.92rem;
}
.toc__chapter-num {
  font-variant-numeric: tabular-nums;
  color: var(--blue);
  font-weight: 600;
  min-width: 1.3em;
}
.toc__subs { list-style: none; margin: 0 0 0.5em; padding: 0; }
.toc__subs a {
  display: block;
  padding: 0.3em 0.9rem 0.3em 2.4rem;
  font-size: 0.85rem;
  color: var(--text-soft);
  text-decoration: none;
  border-left: 2px solid transparent;
}
.toc__subs a:hover, .toc__subs a.is-current {
  color: var(--navy);
  border-left-color: var(--blue);
  background: var(--ground);
}

/* ---------- Main content ---------- */

.main {
  min-width: 0;
  padding: 1.6rem 1.1rem 5rem;
}
@media (min-width: 640px) {
  .main { padding: 2.2rem 2rem 6rem; }
}

/* ---------- Masthead (guide opening) ---------- */

.masthead {
  max-width: var(--measure);
  margin: 0 auto 1.8rem;
}
.masthead__eyebrow {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.8rem;
  color: var(--blue);
  margin: 0 0 0.7em;
}
.masthead h1 {
  font-size: clamp(1.55rem, 4vw, 2rem);
  margin-bottom: 0.4em;
}
.masthead p { max-width: 34rem; }
.masthead p:first-of-type { color: var(--navy); font-family: var(--font-head); font-weight: 600; font-size: 0.95rem; margin-bottom: 0.5em; }
.masthead p:first-of-type + p { color: var(--text-soft); font-size: 0.95rem; }

/* ---------- Chapter map ---------- */

.chapter-map {
  max-width: 56rem;
  margin: 0 auto 2.2rem;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.7rem;
}
@media (min-width: 560px) {
  .chapter-map { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 900px) {
  .chapter-map { grid-template-columns: repeat(3, 1fr); }
}
.chapter-map__card {
  display: block;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 0.95em 1.1em;
  text-decoration: none;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
@media (prefers-reduced-motion: reduce) { .chapter-map__card { transition: none; } }
.chapter-map__card:hover, .chapter-map__card:focus-visible {
  border-color: var(--blue);
}
.chapter-map__num {
  display: block;
  font-family: var(--font-head);
  font-weight: 600;
  color: var(--blue);
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  margin-bottom: 0.3em;
}
.chapter-map__name {
  display: block;
  font-family: var(--font-head);
  font-weight: 600;
  color: var(--navy);
  font-size: 1rem;
  margin-bottom: 0.3em;
}
.chapter-map__gloss {
  margin: 0;
  font-size: 0.85rem;
  font-style: italic;
  color: var(--text-soft);
  line-height: 1.4;
}
.chapter-map__gloss--plain { font-style: normal; }

/* ---------- Platform chooser (first-run panel + collapsed summary bar) ---------- */

.chooser {
  max-width: var(--measure);
  margin: 0 auto 2.6rem;
}
.chooser__summary {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.6em 0.9em;
  font-size: 0.9rem;
  color: var(--text-soft);
}
.chooser__summary p { margin: 0; }
.chooser__change {
  margin-left: 0.5em;
  background: none;
  border: none;
  padding: 0;
  color: var(--blue);
  font-family: var(--font-body);
  font-size: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
.chooser__change:hover { color: var(--blue-dark); }

.chooser__panel {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 1.3rem 1.3rem 1.5rem;
}
.chooser__panel h2 { font-size: 1.05rem; margin-bottom: 0.3em; }
.chooser__lede { color: var(--text-soft); font-size: 0.9rem; margin-bottom: 1.2em; }
.chooser__group { margin-bottom: 1.1em; }
.chooser__group:last-child { margin-bottom: 0; }
.chooser__group-label {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--navy);
  margin: 0 0 0.5em;
}
.chooser__cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.6em;
}
@media (max-width: 520px) {
  .chooser__cards { grid-template-columns: 1fr 1fr; }
}
.chooser__card {
  position: relative;
  min-height: 48px;
  background: var(--ground);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  padding: 0.7em 0.8em 0.7em 2.2em;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--navy);
  text-align: left;
  cursor: pointer;
}
.chooser__card::before {
  content: "";
  position: absolute;
  left: 0.75em;
  top: 50%;
  transform: translateY(-50%);
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 1.5px solid var(--line);
  background: var(--paper);
}
.chooser__card:hover { border-color: var(--blue); }
.chooser__card[aria-checked="true"] {
  background: #EAF0FB;
  border-color: var(--blue);
  color: var(--blue-dark);
}
.chooser__card[aria-checked="true"]::before {
  border-color: var(--blue);
  background: var(--blue);
  box-shadow: inset 0 0 0 3px var(--paper);
}

/* ---------- Chapter opener ---------- */

.chapter {
  max-width: var(--measure);
  margin: 0 auto 3.4rem;
  scroll-margin-top: calc(var(--topbar-h) + var(--progress-h) + 12px);
}
.chapter-opener { margin-bottom: 1.5em; }
.chapter-opener__band {
  background: var(--navy);
  color: #fff;
  border-radius: var(--radius-card);
  padding: 1.1em 1.3em;
  display: flex;
  align-items: baseline;
  gap: 0.6em;
}
.chapter-opener__num {
  font-family: var(--font-head);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: 1rem;
  color: #9FB4E0;
}
.chapter-opener__band h2 { margin: 0; color: #fff; font-size: clamp(1.25rem, 3vw, 1.55rem); }
.chapter-opener__quote {
  margin: 0.8em 0.2em 0;
  padding: 0;
  font-family: var(--font-head);
  font-style: italic;
  font-size: 0.98rem;
  color: var(--navy);
}
.chapter-opener__quote::before { content: "\201C"; }
.chapter-opener__quote::after { content: "\201D"; }

/* ---------- Aside blockquotes ----------
   A markdown `>` blockquote in the guide body (the CIS Controls callout, the Okta/Duo
   aside, etc.) is a step aside from the talk's prose — not another paragraph, and not
   a how-to panel. Jonathan review note 2026-09-24: give it its own register — a small-
   caps "Aside" label, hairline top/bottom rules (not a left rule; that's reserved for
   .manual-quote's "your manual says" callout below and for .chapter-opener__quote,
   which this rule explicitly excludes), slightly smaller type, and extra margin so it
   reads as set apart from the surrounding flow rather than just another block. */
blockquote:not(.chapter-opener__quote) {
  margin: 1.8em 0;
  padding: 1em 0 1.15em;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
blockquote:not(.chapter-opener__quote)::before {
  content: "Aside";
  display: block;
  font-family: var(--font-head);
  font-variant-caps: small-caps;
  font-weight: 700;
  letter-spacing: 0.03em;
  font-size: 0.82rem;
  color: var(--blue-dark);
  margin: 0 0 0.65em;
}
blockquote:not(.chapter-opener__quote) p {
  font-size: 0.95rem;
  color: var(--text-soft);
  margin: 0 0 0.6em;
}
blockquote:not(.chapter-opener__quote) p:last-child { margin-bottom: 0; }
blockquote:not(.chapter-opener__quote) strong { color: var(--navy); }

.subsection {
  margin-bottom: 2.6rem;
  scroll-margin-top: calc(var(--topbar-h) + var(--progress-h) + 12px);
}
.subsection h3 { font-size: 1.2rem; margin-bottom: 0.7em; }

.block { margin: 0 0 1em; }
.block.is-hit { animation: hit-flash 1.8s ease; }
@keyframes hit-flash {
  0% { background: #fde9c8; }
  100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .block.is-hit { animation: none; background: #fde9c8; }
}

.manual-quote {
  border-left: 3px solid var(--navy);
  background: var(--paper);
  padding: 0.7em 1em;
  margin: 0 0 1.3em;
  font-family: var(--font-head);
  font-size: 0.98rem;
  color: var(--navy);
}
.manual-quote p { margin: 0; }
.manual-quote strong { color: var(--blue); }

.warn { border-left: 3px solid var(--warn); background: var(--warn-ground); padding: 0.7em 1em; }

/* ---------- How-to panels ----------
   Every subsection splits into two registers: recap (the talk's prose, default typography,
   no styling of its own) and "how to do it" (this panel) — see build_blocks()/classify_howto()
   in build.py for what lands in each. Panel background is the light blue #F4F8FE, distinct on
   purpose from the plain grey page ground (#F7F8FA, same as .cta-card's aside) so the how-to
   register reads as its own thing rather than "another aside card" — the CTA cards stay the
   only other user of this blue. Tighter line-height and a smaller base size than the page's
   prose (.howto-panel__body) signal "reference/instruction," not "read this like the talk." */
.howto-panel {
  background: #F4F8FE;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 1.1rem 1.2rem 1.3rem;
  margin: 0 0 1.4em;
}
.howto-panel__label {
  display: flex;
  align-items: center;
  gap: 0.45em;
  margin: 0 0 0.9em;
  font-family: var(--font-head);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--blue-dark);
}
.howto-panel__icon { flex: none; color: var(--blue-dark); }
.howto-panel__body {
  font-size: 0.94rem;
  line-height: 1.52;
}
.howto-panel__body .block:last-child,
.howto-panel__body .howto-strip:last-child .block:last-child { margin-bottom: 0; }
.howto-panel__body ol, .howto-panel__body ul { padding-left: 1.3em; }
.howto-panel__body li { margin-bottom: 0.55em; }
.howto-panel__body li:last-child { margin-bottom: 0; }
.howto-panel__body pre { font-size: 0.82rem; }

/* A platform-gated (data-only) run inside the panel — chips label which platform(s) the strip
   below them applies to. Filtering still works exactly as elsewhere: filter.js queries every
   [data-only] element on the page, and the individual .block divs inside the strip keep their
   own data-only too, so hiding is unaffected by this extra wrapper. */
.howto-strip { margin: 0 0 1.1em; }
.howto-strip:last-child { margin-bottom: 0; }
.howto-strip + .howto-strip { border-top: 1px dashed var(--line); padding-top: 1em; }
.howto-strip__label { display: flex; flex-wrap: wrap; gap: 0.4em; margin: 0 0 0.6em; }
.howto-strip__chip {
  display: inline-block;
  font-family: var(--font-head);
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--navy);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.2em 0.7em;
}

/* ---------- Source buttons ----------
   A paragraph/list item that's ONLY a link starting "Microsoft:"/"Google:"/"Apple:"/
   "Official:" (see SOURCE_BTN_RE in build.py) renders as this outlined button instead
   of an inline link, so a how-to step can end with an obvious "go to the vendor's own
   instructions" control. Same visual family as .vendor-btn--secondary. */
.source-btn-wrap { margin: 0 0 1em; }
li.source-btn-wrap { list-style: none; margin-left: -1.3em; }
.source-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  min-height: 44px;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--blue);
  background: var(--paper);
  border: 1.5px solid var(--blue);
  border-radius: var(--radius);
  padding: 0.55em 0.95em;
  text-decoration: none !important;
  border-bottom: none !important;
}
.source-btn:hover, .source-btn:focus-visible {
  background: #EAF0FB;
  color: var(--blue-dark);
  border-color: var(--blue-dark);
}
.source-btn:visited { color: var(--blue) !important; }
.source-btn .icon-external { flex: none; }

.table-wrap { overflow-x: auto; margin: 0 0 1.3em; -webkit-overflow-scrolling: touch; }
table { border-collapse: collapse; width: 100%; font-size: 0.92rem; }

/* AIDEV-NOTE: full-width table breakout (Jonathan review note 2026-09-24, task 3) —
   .chapter/.subsection cap prose at var(--measure) (640px), but a wide reference table
   (AV lab gear, pricing, inventory) reads better using more of the content column.
   Scoped to *direct* children of .chapter/.subsection so a table nested inside a
   .howto-panel (e.g. the "Network, layer 3" DNS-resolver table) keeps the panel's own
   narrower width — breaking out there would blow past the panel's boxed background.
   Available width is computed as 100vw minus the sidebar (260px, see .layout) minus
   .main's own left/right padding (2rem each = 64px) at the >=900px breakpoint where
   the sidebar becomes a fixed column. This is NOT the classic "100vw + 50%/translateX"
   centered-breakout hack: that centers on the full viewport, not on .main's actual
   (sidebar-offset) content box, and would render visibly off-center here. The 16px
   beyond the 324px sidebar+padding is slack for the "100vw includes the scrollbar"
   quirk some browsers have, so this can never push the whole page into horizontal
   scroll. The margin math uses "100%" (the table-wrap's real containing-block width,
   i.e. .chapter's actual rendered width) rather than var(--measure) directly, so it
   also degrades correctly on the rare viewport where .chapter itself is already
   narrower than 640px (margin resolves to 0 — nothing to break out into).
   Selector note: build_blocks() in build.py wraps every rendered block (including a
   table's .table-wrap) in its own <div class="block">, so a recap table's real DOM
   shape is .chapter/.subsection > .block > .table-wrap, not a direct .table-wrap
   child — hence the two-level child combinator below. A table inside a .howto-panel
   is nested three-plus levels deep (…howto-panel > howto-panel__body > .block >
   .table-wrap), so it never matches this selector and correctly keeps the panel's
   own narrower width. */
@media (min-width: 900px) {
  .chapter > .block > .table-wrap, .subsection > .block > .table-wrap {
    --table-avail: calc(100vw - 340px);
    width: min(1100px, var(--table-avail));
    margin-left: calc((100% - min(1100px, var(--table-avail))) / 2);
    margin-right: calc((100% - min(1100px, var(--table-avail))) / 2);
  }
}
th, td { text-align: left; padding: 0.5em 0.7em; border-bottom: 1px solid var(--line); vertical-align: top; }
th { font-family: var(--font-head); color: var(--navy); background: var(--line-soft); white-space: nowrap; }

pre {
  position: relative;
  background: var(--navy-ink);
  color: #E6EAF2;
  padding: 1em 1em;
  border-radius: var(--radius);
  overflow-x: auto;
  font-size: 0.85rem;
  margin: 0 0 1.3em;
}
code { font-family: var(--font-mono); }
p code, li code { background: var(--line-soft); padding: 0.1em 0.35em; border-radius: 2px; font-size: 0.88em; }
pre code { background: none; padding: 0; }

.code-copy {
  position: absolute;
  top: 0.5em; right: 0.5em;
  font-family: var(--font-body);
  font-size: 0.75rem;
  background: rgba(255,255,255,0.1);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.3);
  border-radius: var(--radius);
  padding: 0.3em 0.6em;
  cursor: pointer;
}
.code-copy:hover { background: rgba(255,255,255,0.2); }

.talk-slide {
  margin: 0 0 1.4em;
  max-width: 420px;
}
/* AIDEV-NOTE: root cause of the old misshapen thumbnails — the <img> carried
   width="1280" height="720" HTML attributes, which the UA maps to CSS width/height
   presentational hints. Only "width" was ever overridden (by the global `img{
   max-width:100%}` rule); "height" stayed pinned at the literal 720px attribute value,
   so a narrow .talk-slide (max-width:420px) squashed the box to 420x720 instead of
   420x236. Explicit width:100%/height:auto here wins the cascade over those hints and
   lets the real image ratio (always 16:9 — see build.py SLIDE_TARGET_WIDTH) drive the
   box; the width/height attributes now just reserve the correct-ratio space pre-load. */
/* Border/radius live here, not on the <img> — see the AIDEV-NOTE in build.py above the
   figure markup: a border on the sized <img> itself would skew its own box slightly
   away from true 16:9 at small widths. line-height:0 avoids the few px of inline-flow
   gap a block-level img would otherwise leave under the frame's bottom edge. */
.talk-slide__frame {
  display: block;
  line-height: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 2px 8px rgba(27, 42, 73, 0.1);
}
.talk-slide img {
  display: block;
  width: 100%;
  height: auto;
  cursor: zoom-in;
}
/* Desktop: float the "from the talk" figure beside the subsection's opening
   paragraphs instead of stacking it full-width. Requires the figure to be the first
   child laid out in .subsection (see the AIDEV-NOTE in build.py's render_guide_content)
   — text after it wraps naturally. .subsection gets a clearfix so a short subsection
   never lets the next subsection's heading creep up beside a tall floated image. */
@media (min-width: 760px) {
  .talk-slide {
    float: right;
    width: 46%;
    max-width: 340px;
    margin: 0 0 1em 1.6em;
  }
}
.subsection::after { content: ""; display: table; clear: both; }

.talk-slide--landing { float: none; width: 100%; max-width: 30rem; margin: 0 auto; }

/* ---------- Markdown images in the guide body (![alt](assets/img/...) in HANDOUT.md) ----------
   Default: centered, framed the same way as the "from the talk" slide figures, at the
   content column's width. `badge:`-prefixed alt text (stripped before render, see
   render_markdown_images in build.py) is a small marker meant to sit beside its
   paragraph, not stack full-width — floats right on desktop, centered/unfloated on
   mobile like a normal inline figure. */
.doc-img {
  margin: 0 auto 1.4em;
  text-align: center;
}
.doc-img img {
  display: inline-block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 2px 8px rgba(27, 42, 73, 0.1);
}
/* AIDEV-NOTE: max-width (not a fixed width) on purpose — the current placeholder badge
   is a small square-ish graphic, but a portrait replacement (../media/handout-images/
   just-say-no.jpg, Jonathan review note 2026-09-24) needs to shrink-wrap to its own
   aspect ratio rather than being forced to a fixed box. */
.doc-img--badge {
  max-width: 260px;
}
@media (min-width: 760px) {
  .doc-img--badge {
    float: right;
    max-width: 260px;
    margin: 0.2em 0 1em 1.6em;
  }
}

/* ---------- State cybersecurity-rule tile map (render_state_map in build.py) ----------
   50-state + DC tile cartogram at hard-coded grid positions (STATE_GRID in build.py),
   standard 8-row x 11-col abbreviation-grid layout. One shared popover: absolutely
   positioned near the tile from 640px up (assets/js/statemap.js positions it), static
   full-width under the grid below that — same element, CSS just changes its layout
   mode, so content and behavior stay in one place regardless of breakpoint. */
.state-map {
  max-width: var(--measure);
  margin: 1.6rem auto 2rem;
  position: relative;
}
.state-map__grid {
  display: grid;
  grid-template-columns: repeat(11, 1fr);
  gap: 3px;
  max-width: 30rem;
}
.state-tile {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 2px;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.62rem;
  letter-spacing: 0.01em;
  cursor: pointer;
  padding: 0;
  position: relative;
}
.state-tile--adopted { background: var(--navy); color: #fff; }
.state-tile--similar { background: var(--blue); color: #fff; }
.state-tile--not_found, .state-tile--unknown { background: var(--line-soft); color: var(--text-soft); }
.state-tile--low-confidence::after {
  content: "";
  position: absolute;
  bottom: 3px; right: 3px;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--text-soft);
}
.state-tile:hover, .state-tile:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 1px;
  z-index: 1;
}

.state-map__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.1rem;
  margin-top: 0.9rem;
  font-size: 0.85rem;
  color: var(--text-soft);
}
.state-map__legend-item { display: inline-flex; align-items: center; gap: 0.4em; }
.state-map__swatch { width: 12px; height: 12px; border-radius: 2px; display: inline-block; flex: none; }
.state-map__swatch--adopted { background: var(--navy); }
.state-map__swatch--similar { background: var(--blue); }
.state-map__swatch--not_found { background: var(--line-soft); }

.state-map__caption {
  font-size: 0.82rem;
  color: var(--text-soft);
  margin: 0.6em 0 0;
  max-width: 30rem;
}

.state-map__popover {
  position: absolute;
  z-index: 20;
  width: 230px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: 0 6px 20px rgba(27, 42, 73, 0.18);
  padding: 0.8em 0.9em;
  font-size: 0.85rem;
}
.state-map__popover-name { font-family: var(--font-head); font-weight: 700; color: var(--navy); margin: 0 0 0.2em; }
.state-map__popover-status { font-weight: 600; color: var(--blue); }
.state-map__popover-citation { color: var(--text); margin: 0 0 0.4em; }
.state-map__popover-effective { color: var(--text-soft); font-size: 0.8rem; margin: 0 0 0.4em; }
.state-map__popover-link { font-weight: 600; }

@media (max-width: 639px) {
  .state-map__grid { max-width: 100%; gap: 2px; }
  .state-tile { font-size: 0.5rem; }
  .state-map__popover {
    position: static;
    width: 100%;
    box-sizing: border-box;
    margin-top: 0.8em;
    box-shadow: none;
  }
}

/* ---------- Lightbox ---------- */

.lightbox {
  display: none;
  position: fixed; inset: 0;
  background: rgba(10, 14, 25, 0.86);
  z-index: 90;
  align-items: center;
  justify-content: center;
  padding: 2rem 1rem;
}
.lightbox.is-open { display: flex; }
.lightbox img { max-width: 100%; max-height: 100%; border-radius: 4px; }
.lightbox__close {
  position: absolute; top: 1rem; right: 1rem;
  background: rgba(255,255,255,0.15);
  color: #fff; border: 1px solid rgba(255,255,255,0.4);
  border-radius: var(--radius);
  width: 38px; height: 38px;
  font-size: 1.1rem;
  cursor: pointer;
}

/* ---------- Landing / gate page ---------- */

.gate-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.gate-top {
  background: var(--navy);
  padding: 1.2rem 1.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  position: relative;
}
.gate-top img { height: 24px; }
.gate-top__cta {
  position: absolute;
  right: 1.2rem;
  top: 50%;
  transform: translateY(-50%);
  background: var(--blue);
  color: #fff;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.82rem;
  border-radius: 999px;
  padding: 0.5em 0.95em;
  text-decoration: none;
  white-space: nowrap;
}
.gate-top__cta:hover { background: var(--blue-dark); color: #fff; }
@media (max-width: 480px) {
  .gate-top__cta { right: 0.7rem; font-size: 0.76rem; padding: 0.45em 0.7em; }
}

.gate-main {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2.2rem 1.2rem 3rem;
}
.gate-card {
  width: 100%;
  max-width: 30rem;
}
.gate-card h1 {
  font-size: clamp(1.6rem, 5vw, 2.1rem);
  margin-bottom: 0.5em;
}
.gate-card .sub {
  color: var(--text-soft);
  font-size: 1.05rem;
  margin-bottom: 1.6em;
}
.gate-form { display: grid; gap: 0.9rem; }
.gate-field label {
  display: block;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--navy);
  margin-bottom: 0.35em;
}
.gate-field input {
  width: 100%;
  font-family: var(--font-body);
  font-size: 1rem;
  padding: 0.65em 0.8em;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}
.gate-field input:focus { border-color: var(--blue); }
.gate-row { display: grid; gap: 0.9rem; grid-template-columns: 1fr; }
@media (min-width: 480px) {
  .gate-row { grid-template-columns: 1fr 1fr; }
}
.gate-error {
  color: var(--warn);
  font-size: 0.85rem;
  display: none;
}
.gate-error.is-visible { display: block; }
.gate-consent {
  font-size: 0.8rem;
  color: var(--text-soft);
  margin: 0.2em 0 0.2em;
}
.gate-submit {
  background: var(--blue);
  color: #fff;
  border: none;
  border-radius: var(--radius);
  padding: 0.8em 1.1em;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 1rem;
  cursor: pointer;
}
.gate-submit:hover { background: var(--blue-dark); }
.gate-submit[disabled] { opacity: 0.6; cursor: progress; }

.gate-foot {
  text-align: center;
  padding: 1.2rem;
  color: var(--text-soft);
  font-size: 0.8rem;
}

/* ---------- Landing preview ("what's inside") ---------- */

.landing-preview {
  max-width: 56rem;
  margin: 0 auto;
  padding: 0 1.2rem 3rem;
}
.landing-preview__label {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--text-soft);
  text-align: center;
  margin-bottom: 1.1em;
}
.landing-preview .chapter-map { margin-bottom: 2rem; }
.landing-preview .chapter-map__card { pointer-events: none; }

/* ---------- 404 page ---------- */

.error-page {
  flex: 1;
  max-width: 32rem;
  margin: 0 auto;
  padding: 3rem 1.2rem 4rem;
  text-align: center;
}
.error-page__code {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.9rem;
  letter-spacing: 0.04em;
  color: var(--blue);
  margin-bottom: 0.8em;
}
.error-page h1 { font-size: 1.6rem; }
.error-page p { color: var(--text-soft); }
.error-page__actions {
  margin-top: 1.4rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
  align-items: center;
}
.error-page__actions .topbar__link { color: var(--blue); border-bottom-color: transparent; }
.error-page__actions .topbar__link:hover { color: var(--blue-dark); border-bottom-color: var(--blue-dark); }

/* ---------- Vendors page ---------- */

.vendors-main { max-width: 56rem; margin: 0 auto; padding: 1.8rem 1.1rem 5rem; }
@media (min-width: 640px) { .vendors-main { padding: 2.4rem 1.8rem 6rem; } }
.vendors-main h1 { font-size: 1.6rem; }
.vendors-intro { color: var(--text-soft); max-width: 42rem; margin-bottom: 1.4rem; }

/* ---------- Vendor tabs (Passkeys & MFA / SOC 2 & security reports) ----------
   Real buttons with role="tab", state mirrored to the URL hash by vendors.js
   (#passkeys / #soc2) so either lookup is linkable directly. Underline-style active
   indicator, not a filled pill — this sits above two chip rows that already use the
   filled-pill treatment, so a tab needed a visually distinct "which page am I on"
   signal rather than competing with the filter chips below it. */
.vendor-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em 1.6em;
  border-bottom: 1px solid var(--line);
  margin-bottom: 1.4rem;
}
.vendor-tab {
  background: none;
  border: none;
  border-bottom: 2.5px solid transparent;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.98rem;
  color: var(--text-soft);
  padding: 0.15em 0.05em 0.7em;
  cursor: pointer;
  white-space: nowrap;
}
.vendor-tab:hover { color: var(--navy); }
.vendor-tab[aria-selected="true"] { color: var(--navy); border-bottom-color: var(--blue); }
.vendor-tab:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.vendor-panel[hidden] { display: none; }
.vendor-count--soc2-static { color: var(--navy); font-weight: 600; margin-bottom: 1rem; }

/* Notes column: an always-visible muted line on the mobile card (.vendor-row__note,
   defined below), a collapsed <details> under the vendor name on the desktop table so
   one long note doesn't set every row's height. */
.vendor-table__notes { margin-top: 0.25em; }
.vendor-table__notes summary {
  font-size: 0.78rem;
  color: var(--text-soft);
  cursor: pointer;
  list-style: none;
}
.vendor-table__notes summary::-webkit-details-marker { display: none; }
.vendor-table__notes summary::after { content: " +"; }
.vendor-table__notes[open] summary::after { content: " \2212"; }
.vendor-table__notes summary:hover, .vendor-table__notes summary:focus-visible { text-decoration: underline; color: var(--navy); }
.vendor-table__notes div { font-size: 0.8rem; color: var(--text-soft); margin-top: 0.3em; max-width: 22rem; }

.vendor-search {
  width: 100%;
  font-size: 1rem;
  padding: 0.7em 0.9em;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: 0.9rem;
  background: var(--paper);
}

.vendor-chips { display: flex; flex-wrap: wrap; gap: 0.5em; margin-bottom: 1.3rem; }
.vendor-chip {
  font-family: var(--font-head);
  font-size: 0.82rem;
  padding: 0.4em 0.85em;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  color: var(--navy);
  cursor: pointer;
}
.vendor-chip.is-active { background: var(--blue); border-color: var(--blue); color: #fff; }

.vendor-count { color: var(--text-soft); font-size: 0.85rem; margin-bottom: 0.8rem; }

.vendor-list { display: grid; gap: 0.8rem; }
.vendor-row {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.9em 1.1em;
}
.vendor-row__top { display: flex; justify-content: space-between; align-items: baseline; gap: 0.8em; flex-wrap: wrap; }
.vendor-row__name { font-family: var(--font-head); font-weight: 600; color: var(--navy); font-size: 1.02rem; }
.vendor-row__category { color: var(--text-soft); font-size: 0.82rem; }
.vendor-row__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5em 1.2em;
  margin-top: 0.6em;
  font-size: 0.9rem;
}
@media (min-width: 620px) {
  .vendor-row__grid { grid-template-columns: 1fr 1fr; }
}
.vendor-row__label { font-family: var(--font-head); font-weight: 600; font-size: 0.78rem; color: var(--text-soft); }
.vendor-badge {
  display: inline-block;
  font-size: 0.72rem;
  font-family: var(--font-head);
  font-weight: 600;
  padding: 0.15em 0.55em;
  border-radius: 999px;
  margin-left: 0.4em;
}
.vendor-badge--yes { background: var(--badge-yes-bg); color: var(--badge-yes-fg); }
.vendor-badge--no { background: var(--badge-no-bg); color: var(--badge-no-fg); }
.vendor-badge--partial, .vendor-badge--sms { background: var(--badge-amber-bg); color: var(--badge-amber-fg); }
.vendor-badge--unknown { background: var(--badge-unknown-bg); color: var(--badge-unknown-fg); }
/* SOC 2 tab only: a trust page that contradicts itself or won't render enough to
   confirm anything — same grey as "unknown" plus a trailing dot, echoing the state
   map's low-confidence marker (.state-tile--low-confidence) rather than inventing a
   new visual language for "we couldn't tell." */
.vendor-badge--unresolved { background: var(--badge-unknown-bg); color: var(--badge-unknown-fg); }
.vendor-badge--unresolved::after {
  content: "";
  display: inline-block;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: currentColor;
  margin-left: 0.4em;
  vertical-align: middle;
  opacity: 0.7;
}
.vendor-empty { color: var(--text-soft); padding: 2rem 0; text-align: center; }

/* ---------- Vendor table (desktop) ----------
   Sortable by Vendor / Passkeys / SSO (see vendors.js sortBy). Shown from 860px up;
   the existing .vendor-list cards remain the mobile presentation (see the media query
   pair at the end of this section) — vendors.js renders both markup trees every call
   and CSS decides which one is visible, so there is one source of truth per render. */
.vendor-table-wrap { display: none; overflow-x: auto; margin-bottom: 1rem; }
.vendor-table { width: 100%; border-collapse: collapse; background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-card); overflow: hidden; }
.vendor-table th, .vendor-table td { text-align: left; padding: 0.7em 0.9em; border-bottom: 1px solid var(--line); vertical-align: top; font-size: 0.88rem; }
.vendor-table thead th { background: var(--ground); font-family: var(--font-head); color: var(--navy); font-weight: 600; white-space: nowrap; }
.vendor-table tbody tr:last-child td { border-bottom: none; }
.vendor-table tbody tr:hover { background: var(--ground); }
.vendor-table__sort {
  background: none; border: none; padding: 0; margin: 0;
  font: inherit; color: inherit; cursor: pointer;
  display: inline-flex; align-items: center; gap: 0.3em;
}
.vendor-table__sort::after {
  content: "";
  width: 0; height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid var(--text-soft);
  opacity: 0.5;
}
.vendor-table__sort[data-dir="asc"]::after { border-top: none; border-bottom: 5px solid var(--blue); opacity: 1; }
.vendor-table__sort[data-dir="desc"]::after { border-top: 5px solid var(--blue); opacity: 1; }
.vendor-table__name { font-family: var(--font-head); font-weight: 600; color: var(--navy); }
.vendor-table__category { color: var(--text-soft); font-size: 0.82rem; }
.vendor-table .vendor-badge { margin-left: 0; margin-bottom: 0.3em; }
.vendor-table__cell-note { color: var(--text-soft); font-size: 0.82rem; }
@media (min-width: 860px) {
  .vendor-table-wrap { display: block; }
  .vendor-list { display: none; }
}

/* ---------- Vendor article buttons ----------
   Real buttons, not links, for the vendor's own KB article — see kbButtonHtml() in
   vendors.js. One primary (solid) button per vendor for the passkey/MFA article; any
   further links (none in today's data, see vendors.js note) get --secondary. */
.vendor-row__cta { margin-top: 0.7em; }
.vendor-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  min-height: 44px;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.92rem;
  border-radius: var(--radius);
  padding: 0.65em 1em;
  text-decoration: none;
  cursor: pointer;
}
.vendor-btn + .vendor-btn { margin-left: 0.6em; margin-top: 0.5em; }
.vendor-btn--primary {
  background: var(--blue);
  color: #fff;
  border: 1.5px solid var(--blue);
}
.vendor-btn--primary:hover, .vendor-btn--primary:focus-visible {
  background: var(--blue-dark);
  border-color: var(--blue-dark);
  color: #fff;
}
.vendor-btn--secondary {
  background: var(--paper);
  color: var(--blue);
  border: 1.5px solid var(--blue);
}
.vendor-btn--secondary:hover, .vendor-btn--secondary:focus-visible {
  background: #EAF0FB;
  color: var(--blue-dark);
  border-color: var(--blue-dark);
}
.vendor-btn .icon-external { flex: none; }
.vendor-chip-muted {
  display: inline-block;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.82rem;
  color: var(--text-soft);
  background: var(--line-soft);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.45em 0.9em;
}
.vendor-row__note { font-size: 0.8rem; color: var(--text-soft); margin-top: 0.4em; }
@media (max-width: 480px) {
  .vendor-btn, .vendor-chip-muted { display: flex; width: 100%; justify-content: center; }
  .vendor-btn + .vendor-btn { margin-left: 0; }
}

.confidence-note {
  font-size: 0.78rem;
  color: var(--text-soft);
  margin-top: 0.15em;
}

/* ---------- CTA component (checklist / Reg S-P guide) ----------
   One reusable two-card block, built once in build.py's render_cta_block() and placed
   by chapter title (see CTA_AFTER_CHAPTERS) plus once on /vendors/ via {{CTA}}.
   Deliberately calm/off-content: light blue ground, no gradients, navy text — reads as
   "an aside", not as more handout prose. Cards stack on mobile, sit side-by-side from
   640px up. */
.cta-block {
  max-width: var(--measure);
  margin: 0 auto 2.6rem;
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}
@media (min-width: 640px) {
  .cta-block { grid-template-columns: 1fr 1fr; }
  /* The book-a-call card spans the full row under the two document cards (Jonathan, 2026-09-24). */
  .cta-card--call { grid-column: 1 / -1; }
}
.cta-card {
  background: #F4F8FE;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.2rem 1.3rem;
  display: flex;
  gap: 1rem;
  align-items: flex-start;
}
@media (max-width: 480px) {
  .cta-card { flex-direction: column; }
}
/* The document-cover cards show the linked PDF/checklist's own cover (checklist-cover.jpg
   / guide-cover.png, see CTA_CARDS in build.py); the "book a call" card has no cover so
   it gets an inline-SVG calendar mark (CALENDAR_ICON_SVG) in the same slot — sized to
   match, so all three cards read as one row even though only two carry a real image. */
.cta-card__media {
  flex: none;
  width: 96px;
}
@media (min-width: 481px) {
  .cta-card__media { width: 120px; }
}
.cta-card__cover {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  box-shadow: 0 2px 8px rgba(27, 42, 73, 0.14);
}
.cta-card__glyph {
  display: block;
  margin: 0.4em auto 0;
  color: var(--blue);
}
.cta-card__body { flex: 1; min-width: 0; }
.cta-card h3 {
  font-size: 1.02rem;
  color: var(--navy);
  margin-bottom: 0.45em;
}
.cta-card p {
  color: var(--text-soft);
  font-size: 0.9rem;
  margin-bottom: 1.1em;
}
.cta-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  min-height: 44px;
  background: var(--blue);
  color: #fff !important;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.92rem;
  border: none;
  border-radius: var(--radius);
  padding: 0.7em 1.1em;
  text-decoration: none !important;
  border-bottom: none !important;
}
.cta-btn:hover, .cta-btn:focus-visible { background: var(--blue-dark); }
.cta-btn .icon-external { flex: none; }
.cta-more-link {
  max-width: var(--measure);
  margin: -1.4rem auto 2.6rem;
  font-size: 0.9rem;
  text-align: center;
}
/* On /vendors/ the block sits in .vendors-main, which has its own (wider) measure —
   let it use the same max-width as the vendor list rather than the guide's --measure. */
.vendors-main .cta-block,
.vendors-main .cta-more-link {
  max-width: none;
  margin-top: 2rem;
}

/* ---------- Tech tips inline signup ----------
   One reusable form (render_techtips_block() in build.py), placed at the end of
   /guide/ before the footer and at the bottom of /vendors/ — posts to the same
   /api/unlock function as the gate, tagged list:"techtips" (see assets/js/techtips.js
   and functions/api/unlock.js). Calm, off-content styling like the CTA block, but its
   own identity (no card grid) so it doesn't read as a third CTA card. */
.techtips {
  max-width: var(--measure);
  margin: 0 auto 2.6rem;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 1.3rem 1.4rem;
}
.vendors-main .techtips { max-width: none; margin-top: 2rem; }
.techtips h3 { font-size: 1.02rem; margin-bottom: 0.3em; }
.techtips p { color: var(--text-soft); font-size: 0.9rem; margin-bottom: 1em; }
.techtips__form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7em;
}
.techtips__form input[type="email"] {
  flex: 1 1 14rem;
  min-height: 44px;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.6em 0.8em;
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--text);
  background: var(--ground);
}
.techtips__form input[type="email"]:focus-visible { background: var(--paper); }
.techtips__submit {
  flex: none;
  min-height: 44px;
  background: var(--blue);
  color: #fff;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.92rem;
  border: none;
  border-radius: var(--radius);
  padding: 0.7em 1.2em;
  cursor: pointer;
}
.techtips__submit:hover, .techtips__submit:focus-visible { background: var(--blue-dark); }
.techtips__submit[disabled] { opacity: 0.65; cursor: progress; }
.techtips__success {
  color: var(--navy);
  font-family: var(--font-head);
  font-weight: 600;
  margin: 0;
}

/* ---------- Review mode (Jonathan-only, ?review=1, see assets/js/review.js) ----------
   Never present unless review.js decides it's active (query param or sessionStorage) —
   these rules only ever apply once review.js has already added .review-mode to <html>,
   so there's no separate "hide in production" flag to keep in sync here. */
.review-mode .review-target {
  cursor: pointer;
  transition: background-color 0.1s ease, outline-color 0.1s ease;
  outline: 1px dashed transparent;
  outline-offset: 2px;
}
.review-mode .review-target:hover {
  background-color: rgba(40, 80, 190, 0.06);
  outline-color: var(--blue);
}
.review-marker {
  display: inline-block;
  color: #C99A1E;
  font-size: 0.7em;
  margin-left: 0.4em;
  vertical-align: middle;
  cursor: help;
}
.review-popover {
  position: absolute;
  z-index: 10000;
  width: 280px;
  max-width: calc(100vw - 16px);
  background: var(--paper);
  border: 1px solid var(--navy);
  border-radius: var(--radius-card);
  box-shadow: 0 6px 20px rgba(27, 42, 73, 0.25);
  padding: 0.7em;
}
.review-popover__text {
  width: 100%;
  font-family: var(--font-body);
  font-size: 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.5em;
  resize: vertical;
  box-sizing: border-box;
}
.review-popover__actions {
  display: flex;
  gap: 0.5em;
  margin-top: 0.5em;
  justify-content: flex-end;
}
.review-popover__actions button {
  font-family: var(--font-head);
  font-size: 0.82rem;
  font-weight: 600;
  border-radius: var(--radius);
  padding: 0.4em 0.8em;
  cursor: pointer;
  border: 1px solid var(--line);
  background: var(--ground);
  color: var(--text);
}
.review-popover__save {
  background: var(--blue) !important;
  border-color: var(--blue) !important;
  color: #fff !important;
}
.review-popover__save:hover, .review-popover__save:focus-visible { background: var(--blue-dark) !important; }
.review-download {
  position: fixed;
  right: 12px;
  bottom: 12px;
  z-index: 9999;
  background: var(--navy);
  color: #fff !important;
  font-family: var(--font-head);
  font-size: 0.78rem;
  font-weight: 600;
  text-decoration: none !important;
  border-radius: var(--radius);
  padding: 0.5em 0.9em;
  box-shadow: 0 2px 8px rgba(27, 42, 73, 0.25);
}
.review-download:hover, .review-download:focus-visible { background: var(--navy-soft); }

/* ---------- "How this was made" (Jonathan's 90-second video, 2026-09-24) ----------
   Sits under the masthead, before the chapter map, in the SAME column as the masthead
   (--measure, centered) so the intro shares one left edge. Was a wide left-aligned
   two-column block until 2026-09-28; Jonathan: "its placement is bad". Nothing autoplays. */
.made {
  max-width: var(--measure);
  margin: 0 auto 2.4rem;
}
.made h2 { margin: 0 0 0.7rem; font-size: 1.15rem; }
.made__video { margin: 0 0 0.7rem; }
.made__video video { display: block; width: 100%; height: auto; background: #000; }
.made p { margin: 0; color: var(--text-soft, #4B5563); font-size: 0.95rem; }

/* signup.js: a CTA button after a confirmed one-click request */
.cta-btn--sent { background: #1f7a4d; border-color: #1f7a4d; pointer-events: none; }
.cta-btn--sent svg { display: none; }
