:root {
  /* 2026-06-13 Charcoal Ember design refresh (PR — demo-lab calm-confident pass).
     - Body bg shifted from #09090C (panic-black) to #0E1014 (warm
       charcoal, in GitHub-dark range) so reading the demo feels like
       reading documentation rather than staring at a CRT.
     - --ink-muted bumped from #928E82 → #A6A199 to clear WCAG AA on
       bg-card / bg-alt / bg-raised (was failing on .controls keys
       3.77, .probe-summary-detail
       4.34, footer-heading 6.07-vs-7-AAA).
     - --ink-subtle bumped #898581 → #9C978E for AAA on bg + AA on
       bg-card (was 4.30 on hist-toggle).
     - --line / --line-soft warmed slightly for the new bg.
     - New --chip-neutral-* tokens for non-semantic chrome (ambient
       beacon/secret chips, cluster registered idle states) so red /
       green stay reserved for genuine pass / fail / blocked-ok. */
  --bg:             #1a1616;
  --bg-alt:         #242020;
  --bg-card:        #3e3636;
  --bg-raised:      #483e3e;
  --ink:            #ece4e0;
  --ink-soft:       #b0a8a2;
  --ink-muted:      #887e78;
  --ink-subtle:     #605856;
  --ink-ghost:      #352e2e;
  --line:           #5a4e4e;
  --line-soft:      #3a3434;
  --line-dashed:    rgba(236, 228, 224, 0.14);
  --accent:         #60b0d0;
  --accent-ink:     #80c8e8;
  --accent-wash:    #1a2830;
  --protected:      #78c07a;
  --protected-ink:  #98d89a;
  --protected-wash: #1a2a1a;
  --warn:           #e0a040;
  --warn-ink:       #e8b868;
  --warn-wash:      #2a2010;
  --mark:           #e0a040;
  /* Neutral chip palette — for "active but not load-bearing" chrome
     (beacon-relay idle, secret-store idle, ambient packet rail). The
     prior amber pulse on every "active" verdict diluted the load-
     bearing amber on the step-in-flight call-to-action. */
  --chip-neutral:        #C0BCB0;
  --chip-neutral-bg:     rgba(166, 161, 153, 0.08);
  --chip-neutral-border: rgba(166, 161, 153, 0.30);
  --tfg:            #e8e0dc;
  --tfm:            #887e78;
  --tfd:            #605856;
  --tbg:            #1a1616;
  --tbg2:           #242020;
  --tbd:            #3a3434;
  --radius:         7px;
  --radius-lg:      10px;
  --container:      1200px;

  /* Brand vars — added 2026-05-01 per cloudtaser-io-website#239 brand refresh.
     Used ONLY in nav lockup mark, favicon, OG image, footer mark.
     Body palette (--bg, --ink, etc.) unchanged — cleared 2026-04-28 a11y audit. */
  --brand-bg-dark:    #1a1616;
  --brand-bg-light:   #fafaf7;
  --brand-ink-dark:   #ece4e0;
  --brand-ink-light:  #0a0a0a;
  --brand-term-live:  #78c07a;
  --brand-term-warn:  #e0a040;
  --brand-term-error: #e8685e;
}

* {
  box-sizing: border-box; margin: 0; padding: 0;
  scrollbar-color: var(--bg-raised) transparent;
  scrollbar-width: thin;
}
html { scroll-behavior: smooth; font-size: clamp(16px, 0.6vw + 6px, 20px); }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--bg-raised);
  border-radius: 8px;
  border: 2px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--ink-muted);
  background-clip: content-box;
}
::-webkit-scrollbar-corner { background: transparent; }
::selection { background: var(--accent); color: var(--bg); }
:focus-visible { outline: 2px dashed var(--mark); outline-offset: 3px; border-radius: 2px; }

em { font-style: normal; color: var(--mark); font-weight: inherit; }

.redacted { font-family: 'Redacted', 'IBM Plex Mono', monospace; letter-spacing: 0.02em; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  opacity: 0.25;
  background-image: url("data:image/svg+xml;utf8,<svg viewBox='0 0 240 240' xmlns='http://www.w3.org/2000/svg'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.92' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.95  0 0 0 0 0.91  0 0 0 0 0.85  0 0 0 0.05 0'/></filter><rect width='240' height='240' filter='url(%23n)'/></svg>");
  mix-blend-mode: screen;
  /* GPU layer promotion: composites the fixed noise overlay on its own
     layer so the browser avoids re-rastering on every repaint/scroll.
     Eliminates per-frame SVG filter cost on low-end GPUs and mobile. */
  will-change: transform;
  transform: translateZ(0);
}

.grid-bg {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, var(--line-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line-soft) 1px, transparent 1px);
  background-size: 64px 64px;
  pointer-events: none;
  mask-image: radial-gradient(ellipse at center, black 40%, transparent 80%);
  -webkit-mask-image: radial-gradient(ellipse at center, black 40%, transparent 80%);
  opacity: 0.6;
  z-index: 0;
}

.container {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 32px;
  position: relative;
  z-index: 3;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }

code, pre, .mono { font-family: 'IBM Plex Mono', ui-monospace, monospace; }

.with-corners { position: relative; }
.with-corners::before,
.with-corners::after,
.with-corners > .c-bl,
.with-corners > .c-br {
  content: "+";
  position: absolute;
  color: var(--ink-muted);
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 400;
  font-size: 14px;
  line-height: 1;
  pointer-events: none;
  opacity: 0.6;
}
.with-corners::before { top: -7px; left: -7px; }
.with-corners::after { top: -7px; right: -7px; }
.with-corners > .c-bl { bottom: -7px; left: -7px; }
.with-corners > .c-br { bottom: -7px; right: -7px; }

/* NAV */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(14, 15, 17, 0.85);
  backdrop-filter: saturate(150%) blur(16px);
  -webkit-backdrop-filter: saturate(150%) blur(16px);
  border-bottom: 1px solid var(--line);
}
.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64px;
}
.logo {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -0.01em;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.logo:hover { text-decoration: none; }
.logo-mark {
  display: inline-block;
  width: 24px;
  height: 24px;
  color: var(--ink);
  flex-shrink: 0;
}
.logo:hover .logo-mark { color: var(--ink); }
.footer-brand .logo-mark { width: 16px; height: 16px; }
@keyframes breathe {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
.nav-links {
  display: flex;
  gap: 28px;
  align-items: center;
  list-style: none;
}
.nav-links a {
  color: var(--ink-muted);
  font-size: 13.5px;
  font-weight: 400;
  font-family: 'IBM Plex Mono', monospace;
  letter-spacing: 0.01em;
}
.nav-links a:hover { color: var(--ink); text-decoration: none; }
.nav-links a.active { color: var(--mark); }
.nav-links a.nav-cta {
  background: var(--mark);
  color: var(--bg);
  padding: 8px 14px;
  border-radius: var(--radius);
  font-size: 13px;
  font-weight: 600;
  font-family: 'IBM Plex Mono', monospace;
}
.nav-links a.nav-cta:hover { background: var(--ink); color: var(--bg); text-decoration: none; }
.nav-toggle {
  display: none;
  background: transparent;
  border: 1px dashed var(--line);
  border-radius: 2px;
  width: 36px;
  height: 32px;
  cursor: pointer;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 0 8px;
}
.nav-toggle span { display: block; height: 1.5px; background: var(--ink); }
@media (max-width: 860px) {
  .nav-toggle { display: inline-flex; }
  .nav-links {
    position: fixed;
    inset: 64px 0 auto 0;
    background: var(--bg);
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 16px 32px 24px;
    border-bottom: 1px dashed var(--line);
    transform: translateY(-120%);
    transition: transform 0.25s ease;
  }
  .nav-links.open { transform: translateY(0); }
  .nav-links li { border-bottom: 1px dashed var(--line); }
  .nav-links a { display: block; padding: 14px 0; font-size: 15px; }
  .nav-cta { display: inline-block; width: fit-content; margin-top: 12px; }
}

/* HERO */
.hero {
  padding: 80px 0 0;
  position: relative;
  border-bottom: 1px dashed var(--line);
}
.hero-grid-bg {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, var(--line-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line-soft) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(ellipse 70% 60% at 20% 30%, black 0%, transparent 80%);
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at 20% 30%, black 0%, transparent 80%);
  opacity: 0.5;
  pointer-events: none;
  z-index: 0;
}
.eyebrow {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-muted);
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 40px;
}
.eyebrow::before { content: ""; height: 1px; background: var(--line); flex: 0 0 40px; }
.eyebrow .ver { color: var(--mark); margin-left: auto; font-size: 10px; letter-spacing: 0.14em; }
.hero-masthead {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 56px;
  align-items: start;
  margin-bottom: 56px;
}
.hero h1 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(38px, 5vw, 68px);
  line-height: 1.02;
  letter-spacing: -0.025em;
  margin-bottom: 28px;
  max-width: 20ch;
  color: var(--ink);
}
.hero h1 em {
  font-weight: 500;
  color: var(--mark);
  letter-spacing: -0.02em;
}
.hero-sub {
  font-size: 17px;
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 56ch;
  margin-bottom: 36px;
}
.hero-sub strong { font-weight: 500; color: var(--ink); }
.hero-sub .underline-accent {
  background-image: linear-gradient(transparent 96%, var(--mark) 96%);
  background-position: 0 90%;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  padding: 0 2px;
}
.hero-ctas { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 40px; }
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 12px 20px;
  border-radius: var(--radius);
  font-size: 14px;
  font-weight: 500;
  transition: all 0.18s ease;
  border: 1px dashed transparent;
  cursor: pointer;
  font-family: 'IBM Plex Mono', monospace;
  letter-spacing: 0.02em;
  position: relative;
  text-decoration: none;
}
.btn-primary { background: var(--mark); color: var(--bg); border-color: var(--mark); }
.btn-primary:hover { background: transparent; color: var(--mark); border-style: dashed; text-decoration: none; }
.btn-secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink-muted);
  border-style: dashed;
}
.btn-secondary:hover { border-color: var(--ink); border-style: solid; background: var(--ink); color: var(--bg); text-decoration: none; }
.btn .arrow { transition: transform 0.18s ease; display: inline-block; }
.btn:hover .arrow { transform: translateX(3px); }

/* Demo under construction — disabled CTA variant.
   Used when cloudtaser-ebpf#163 + adversarial CI have not landed yet.
   Tooltip surfaces the reason without apologising.
   aria-disabled=true is set on the element in HTML; pointer-events:none
   prevents hover flicker; the tooltip is always-visible on focus (no JS).
   WCAG AA: --ink-subtle (#9C978E) on --bg-card (#1F2530) = 4.65:1 > 4.5 */
.btn-disabled-construction {
  background: var(--bg-card);
  color: var(--ink-subtle);
  border-color: var(--line);
  border-style: dashed;
  cursor: not-allowed;
  pointer-events: none;
  position: relative;
  text-decoration: none;
}
.btn-disabled-construction:hover,
.btn-disabled-construction:focus {
  background: var(--bg-card);
  color: var(--ink-subtle);
  border-color: var(--line);
  text-decoration: none;
}
/* Tooltip shown on focus (keyboard nav) via sibling span */
.btn-disabled-construction-wrap {
  position: relative;
  display: inline-block;
}
.btn-disabled-construction-wrap .btn-disabled-tooltip {
  display: none;
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--bg-raised);
  color: var(--ink-soft);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  line-height: 1.5;
  padding: 8px 12px;
  white-space: nowrap;
  z-index: 200;
  pointer-events: none;
}
.btn-disabled-construction-wrap:hover .btn-disabled-tooltip,
.btn-disabled-construction-wrap:focus-within .btn-disabled-tooltip {
  display: block;
}

/* Nav CTA disabled variant — matches .nav-cta sizing */
.nav-cta-disabled {
  background: var(--bg-card);
  color: var(--ink-subtle);
  padding: 8px 14px;
  border-radius: var(--radius);
  font-size: 13px;
  font-weight: 600;
  font-family: 'IBM Plex Mono', monospace;
  border: 1px dashed var(--line);
  cursor: not-allowed;
  pointer-events: none;
  display: inline-block;
  text-decoration: none;
}
.nav-cta-disabled:hover,
.nav-cta-disabled:focus { text-decoration: none; color: var(--ink-subtle); }
/* Tooltip for nav CTA disabled */
.nav-cta-disabled-wrap {
  position: relative;
  display: inline-block;
}
.nav-cta-disabled-wrap .btn-disabled-tooltip {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  background: var(--bg-raised);
  color: var(--ink-soft);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  line-height: 1.5;
  padding: 8px 12px;
  white-space: nowrap;
  z-index: 200;
  pointer-events: none;
  min-width: 240px;
}
.nav-cta-disabled-wrap:hover .btn-disabled-tooltip,
.nav-cta-disabled-wrap:focus-within .btn-disabled-tooltip {
  display: block;
}
@media (max-width: 860px) {
  .nav-cta-disabled-wrap .btn-disabled-tooltip { right: auto; left: 0; min-width: 220px; }
}

/* Demo-lab construction banner */
.demo-construction-banner {
  background: var(--bg-alt);
  border-bottom: 1px dashed var(--line);
  padding: 14px 0;
}
.demo-construction-banner-inner {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 24px;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 16px;
}
.demo-construction-banner-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--warn-ink);
  flex-shrink: 0;
}
.demo-construction-banner-text {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--ink-muted);
  line-height: 1.6;
}
.demo-construction-banner-text a {
  color: var(--ink-soft);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.demo-construction-banner-text a:hover { color: var(--ink); }

/* Demo maintenance overlay — toggled by DEMO_MAINTENANCE constant in
   demo-lab.html inline JS. Covers the interactive demo area with a
   translucent backdrop and a centered status message. Removing the
   .demo-maintenance-active class from .demo-live (or setting
   DEMO_MAINTENANCE = false) restores the demo instantly. */
.demo-maintenance-overlay {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 50;
  background: rgba(14, 16, 20, 0.88);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border-radius: var(--radius-lg);
}
.demo-live.demo-maintenance-active {
  position: relative;
}
.demo-live.demo-maintenance-active .demo-maintenance-overlay {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 48px 32px;
}
.demo-maintenance-status {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--warn-ink);
}
.demo-maintenance-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--warn);
  animation: maintenance-pulse 2.4s ease-in-out infinite;
}
@keyframes maintenance-pulse {
  0%, 100% { opacity: 0.4; box-shadow: 0 0 0 0 rgba(232, 153, 112, 0); }
  50%      { opacity: 1;   box-shadow: 0 0 8px 2px rgba(232, 153, 112, 0.25); }
}
.demo-maintenance-heading {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(20px, 3vw, 28px);
  color: var(--ink);
  text-align: center;
  line-height: 1.3;
  max-width: 36ch;
}
.demo-maintenance-body {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 15px;
  color: var(--ink-soft);
  text-align: center;
  line-height: 1.6;
  max-width: 48ch;
}
.demo-maintenance-body a {
  color: var(--accent-ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.demo-maintenance-body a:hover { color: var(--ink); }
/* When maintenance is active, dim all interactive children behind the overlay */
.demo-live.demo-maintenance-active .strip-stack,
.demo-live.demo-maintenance-active .controls,
.demo-live.demo-maintenance-active .demo-probe,
.demo-live.demo-maintenance-active .aha-conversion-box {
  pointer-events: none;
  user-select: none;
}
@media (prefers-reduced-motion: reduce) {
  .demo-maintenance-dot { animation: none; opacity: 0.7; }
}

.marginalia {
  border-left: 1px dashed var(--line);
  padding-left: 24px;
  padding-top: 64px;
}
.marginalia-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-subtle);
  margin-bottom: 16px;
}
.marginalia-note {
  font-family: 'IBM Plex Sans', sans-serif;
  font-weight: 400;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-muted);
  margin-bottom: 22px;
}
.marginalia-note strong {
  color: var(--ink);
  font-weight: 500;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.04em;
  display: inline-block;
  padding: 1px 6px;
  background: var(--bg-raised);
  border: 1px dashed var(--line);
  border-radius: 2px;
  margin-right: 4px;
}
.marginalia-divider { width: 28px; height: 1px; background: var(--line); margin: 18px 0; }
@media (max-width: 900px) {
  .hero-masthead { grid-template-columns: 1fr; gap: 24px; }
  .marginalia { border-left: none; border-top: 1px dashed var(--line); padding-left: 0; padding-top: 24px; }
}

.compliance {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 20px 0;
  border-top: 1px dashed var(--line);
  border-bottom: 1px dashed var(--line);
  margin-bottom: 40px;
  flex-wrap: wrap;
}
.compliance-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-subtle);
}
.compliance-badges { display: flex; gap: 6px; flex-wrap: wrap; }
.badge {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.06em;
  padding: 4px 9px;
  border: 1px dashed var(--line);
  border-radius: 2px;
  color: var(--ink-soft);
  background: var(--bg-card);
  display: inline-block;
}
.badge:hover { border-style: solid; border-color: var(--mark); color: var(--mark); text-decoration: none; }
.badge.planned { color: var(--warn); border-color: var(--warn); }

.stack-strip {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  padding: 18px 24px;
  display: grid;
  grid-template-columns: auto repeat(4, 1fr);
  grid-template-rows: auto auto;
  gap: 6px 22px;
  align-items: center;
  margin-bottom: 56px;
  font-family: 'IBM Plex Mono', monospace;
  position: relative;
}
@media (min-width: 901px) {
  .stack-strip > .stack-indicator { grid-row: 1 / span 2; align-self: center; }
  .stack-strip > div:not(.stack-indicator) {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: 1 / span 2;
    row-gap: 5px;
  }
  .stack-strip .stack-cell-label { margin-bottom: 0; }
}
.stack-indicator {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-right: 22px;
  border-right: 1px dashed var(--line);
}
.stack-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--protected);
  box-shadow: 0 0 0 0 rgba(109, 214, 155, 0.5);
  animation: stack-pulse 2.2s ease-out infinite;
}
@keyframes stack-pulse {
  0% { box-shadow: 0 0 0 0 rgba(109, 214, 155, 0.7); }
  70% { box-shadow: 0 0 0 8px rgba(109, 214, 155, 0); }
  100% { box-shadow: 0 0 0 0 rgba(109, 214, 155, 0); }
}
.stack-label { font-size: 10px; letter-spacing: 0.18em; color: var(--ink-soft); }
.stack-cell-label { font-size: 9.5px; letter-spacing: 0.2em; color: var(--ink-muted); text-transform: uppercase; margin-bottom: 5px; }
.stack-cell-value { font-size: 13.5px; color: var(--ink); letter-spacing: 0.01em; font-weight: 500; }
.stack-cell-value .accent { color: var(--mark); }
@media (max-width: 900px) {
  .stack-strip { grid-template-columns: 1fr 1fr; gap: 16px 20px; padding: 18px; }
  .stack-indicator { grid-column: 1 / -1; border-right: none; border-bottom: 1px dashed var(--line); padding-bottom: 14px; padding-right: 0; }
}

.hero-diagram-wrap {
  padding: 44px 40px 40px;
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  position: relative;
  margin-bottom: 96px;
}
.hero-diagram-wrap::before,
.hero-diagram-wrap::after,
.hero-diagram-wrap .c-bl,
.hero-diagram-wrap .c-br {
  content: "+";
  position: absolute;
  color: var(--ink-muted);
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 400;
  font-size: 16px;
  line-height: 1;
  pointer-events: none;
}
.hero-diagram-wrap::before { top: -8px; left: -8px; }
.hero-diagram-wrap::after { top: -8px; right: -8px; }
.hero-diagram-wrap .c-bl { bottom: -8px; left: -8px; }
.hero-diagram-wrap .c-br { bottom: -8px; right: -8px; }

.diagram-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 28px;
  padding-bottom: 16px;
  border-bottom: 1px dashed var(--line);
}
.diagram-title {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.2em;
  color: var(--ink-muted);
  text-transform: uppercase;
}
.diagram-title .fig { color: var(--mark); }
.diagram-state {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  color: var(--protected);
}
.diagram-state-dot {
  width: 6px;
  height: 6px;
  background: var(--protected);
  border-radius: 50%;
  animation: stack-pulse 2s ease-out infinite;
}
.hero-diagram { width: 100%; height: auto; display: block; }

.ebpf-ring { animation: ring-breathe 3.6s ease-in-out infinite; }
@keyframes ring-breathe {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}
.memfd-glow { animation: memfd-glow 2.6s ease-in-out infinite; }
@keyframes memfd-glow {
  0%, 100% { fill: #133024; }
  50% { fill: #1A432F; }
}
.packet-secret {
  offset-path: path("M 870 205 L 770 205 L 670 230 L 448 216 L 300 266");
  offset-rotate: 0deg;
  animation: packet-flow 3.6s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
.packet-secret.p2 { animation-delay: 1.2s; }
.packet-secret.p3 { animation-delay: 2.4s; }
@keyframes packet-flow {
  0%   { offset-distance: 0%;   opacity: 0; }
  8%   { opacity: 1; }
  88%  { opacity: 1; }
  100% { offset-distance: 100%; opacity: 0; }
}
.packet-cipher {
  offset-path: path("M 300 266 L 388 266 L 448 284 L 525 240");
  offset-rotate: 0deg;
  animation: packet-flow 2.8s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
.packet-cipher.c2 { animation-delay: 0.9s; }
.packet-cipher.c3 { animation-delay: 1.8s; }

/* SECTION */
.section {
  padding: 120px 0;
  border-bottom: 1px dashed var(--line);
  position: relative;
  overflow: hidden;
}
.section-alt { background: var(--bg-alt); }
.section-ghost-num {
  position: absolute;
  top: 32px;
  right: -20px;
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: 280px;
  line-height: 0.8;
  color: transparent;
  -webkit-text-stroke: 1px var(--ink-ghost);
  letter-spacing: -0.04em;
  pointer-events: none;
  user-select: none;
  z-index: 0;
}
.section > .container { position: relative; z-index: 2; }
.section-head {
  display: grid;
  grid-template-columns: 150px 1fr;
  gap: 32px;
  align-items: baseline;
  margin-bottom: 60px;
}
.section-num-small {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  letter-spacing: 0.14em;
  color: var(--ink-muted);
  padding-top: 12px;
  position: relative;
  text-transform: uppercase;
}
.section-num-small::before {
  content: "§";
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 18px;
  margin-right: 6px;
  color: var(--mark);
  vertical-align: -2px;
}
.section-head-content { max-width: 66ch; }
.section-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mark);
  margin-bottom: 22px;
  display: inline-block;
  padding: 3px 10px;
  border: 1px dashed var(--mark);
  border-radius: 2px;
}
.section-title {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(30px, 4vw, 56px);
  line-height: 1.02;
  letter-spacing: -0.028em;
  margin-bottom: 20px;
  color: var(--ink);
}
.section-title em { font-weight: 500;  color: var(--mark); }
.section-lede { font-size: 17px; color: var(--ink-soft); line-height: 1.55; max-width: 62ch; }
.section-lede code {
  background: var(--bg-raised);
  padding: 1px 7px;
  border-radius: 3px;
  font-size: 14.5px;
  border: 1px dashed var(--line);
  color: var(--ink);
}
@media (max-width: 820px) {
  .section-head { grid-template-columns: 1fr; gap: 12px; }
  .section-ghost-num { font-size: 180px; right: -10px; top: 0; }
}

/* POSITIONING */
.positioning-statement {
  padding: 72px 0;
  border-top: 1px dashed var(--line);
  border-bottom: 1px dashed var(--line);
  background: var(--bg-alt);
}
.positioning-inner {
  display: grid;
  grid-template-columns: minmax(260px, 0.92fr) minmax(320px, 1.08fr);
  gap: 48px;
  align-items: start;
}
.positioning-statement h2 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(28px, 3vw, 46px);
  line-height: 1.05;
  color: var(--ink);
  max-width: 13ch;
}
.positioning-copy {
  border-left: 1px dashed var(--line);
  padding-left: 32px;
}
.positioning-copy p {
  color: var(--ink-soft);
  font-size: 17px;
  line-height: 1.65;
  max-width: 64ch;
}
.positioning-copy p + p { margin-top: 18px; }

/* PROBLEM */
.problem-grid { display: grid; grid-template-columns: 1.5fr 1fr; gap: 64px; align-items: start; }
.problem-body p { font-size: 16.5px; color: var(--ink-soft); margin-bottom: 20px; line-height: 1.65; }
.problem-body strong { color: var(--ink); font-weight: 500; }
.problem-body code {
  font-family: 'IBM Plex Mono', monospace;
  background: var(--bg-raised);
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 13.5px;
  border: 1px solid var(--line);
  color: var(--mark);
}
.pullquote {
  background: var(--warn-wash);
  border-left: 2px solid var(--warn);
  padding: 28px 26px 24px;
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  margin-bottom: 28px;
  position: relative;
}
.pullquote-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--warn-ink);
  margin-bottom: 12px;
}
.pullquote-text {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: 20px;
  line-height: 1.34;
  color: var(--ink);
  margin-bottom: 14px;
  letter-spacing: -0.01em;
}
.pullquote-cite { font-size: 12px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; }
.fact-list { list-style: none; border-top: 1px dashed var(--line); }
.fact-list li {
  padding: 16px 0;
  border-bottom: 1px dashed var(--line);
  display: grid;
  grid-template-columns: 70px 1fr;
  gap: 16px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-soft);
}
.fact-list .year {
  font-family: 'IBM Plex Mono', monospace;
  color: var(--warn);
  font-size: 12.5px;
  letter-spacing: 0.04em;
  padding-top: 2px;
}
.fact-list strong { color: var(--ink); font-weight: 500; display: inline; margin-right: 4px; }
@media (max-width: 900px) { .problem-grid { grid-template-columns: 1fr; gap: 40px; } }

/* CAT PANEL */
.cat-panel {
  margin-top: 64px;
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: 44px;
  align-items: center;
  padding: 40px;
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  position: relative;
}
.cat-panel::before {
  content: "INTERLUDE · FIG. 03";
  position: absolute;
  top: -9px;
  left: 32px;
  background: var(--bg);
  padding: 0 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.2em;
  color: var(--ink-muted);
}
.cat-img-wrap {
  position: relative;
  background: var(--bg-raised);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  padding: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cat-img-wrap img {
  max-width: 100%;
  height: auto;
  display: block;
  filter: contrast(1.05) brightness(0.95);
  border-radius: 4px;
}
.cat-text { display: flex; flex-direction: column; gap: 8px; }
.cat-quote {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(24px, 3.2vw, 36px);
  line-height: 1.2;
  letter-spacing: -0.018em;
  color: var(--ink);
  margin-bottom: 12px;
}
.cat-quote em { color: var(--mark); }
.cat-caption {
  font-size: 14.5px;
  color: var(--ink-muted);
  line-height: 1.55;
  border-left: 1px dashed var(--line);
  padding-left: 18px;
}
@media (max-width: 740px) {
  .cat-panel { grid-template-columns: 1fr; gap: 24px; padding: 28px; }
}

/* PILLARS */
.pillars { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
.pillar {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  padding: 36px 32px;
  position: relative;
  border-radius: var(--radius-lg);
  transition: border-color 0.2s ease;
}
.pillar:hover { border-color: var(--mark); border-style: solid; }
.pillar::before,
.pillar::after,
.pillar > .c-bl,
.pillar > .c-br {
  content: "+";
  position: absolute;
  color: var(--ink-muted);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  line-height: 1;
}
.pillar::before { top: -6px; left: -6px; }
.pillar::after { top: -6px; right: -6px; }
.pillar > .c-bl { bottom: -6px; left: -6px; }
.pillar > .c-br { bottom: -6px; right: -6px; }
.pillar-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 20px;
  padding-bottom: 14px;
  border-bottom: 1px dashed var(--line);
}
.pillar-num {
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 500;
  font-size: 22px;
  color: var(--mark);
  line-height: 1;
  letter-spacing: 0.02em;
}
.pillar-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  color: var(--ink-muted);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.pillar h3 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: 22px;
  line-height: 1.2;
  letter-spacing: -0.02em;
  margin-bottom: 14px;
  color: var(--ink);
}
.pillar h3 em {  color: var(--mark); }
.pillar p { font-size: 14.5px; line-height: 1.6; color: var(--ink-muted); margin-bottom: 18px; }
.pillar p code {
  font-size: 12.5px;
  background: var(--bg-raised);
  padding: 1px 5px;
  border-radius: 3px;
  color: var(--ink);
  border: 1px solid var(--line);
}
.pillar .tech { display: flex; flex-wrap: wrap; gap: 6px; }
.tech-tag {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  padding: 3px 8px;
  background: var(--bg-raised);
  border: 1px dashed var(--line);
  border-radius: 2px;
  color: var(--ink-soft);
}
@media (max-width: 780px) { .pillars { grid-template-columns: 1fr; } }

/* ATTACK VECTORS */
.attacks-wrap { margin-top: 20px; }
.attacks-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
@media (max-width: 980px) { .attacks-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 500px) { .attacks-grid { grid-template-columns: 1fr; } }
.attack {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  padding: 22px 20px;
  position: relative;
  transition: all 0.2s ease;
  overflow: hidden;
}
.attack::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--warn);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s ease;
}
.attack:hover { border-color: var(--protected); border-style: solid; background: var(--bg-raised); }
.attack:hover::before { background: var(--protected); transform: scaleX(1); }
.attack-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.attack-vector {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  color: var(--ink);
  font-weight: 500;
  letter-spacing: -0.01em;
}
.attack-status {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  letter-spacing: 0.18em;
  color: var(--warn);
  padding: 2px 7px;
  border: 1px dashed var(--warn);
  border-radius: 2px;
  transition: all 0.3s ease;
}
.attack:hover .attack-status { color: var(--protected); border-color: var(--protected); border-style: solid; }
.attack-desc { font-size: 13px; color: var(--ink-muted); line-height: 1.45; margin-bottom: 14px; }
.attack-block { padding-top: 12px; border-top: 1px dashed var(--line); }
.attack-block-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  letter-spacing: 0.2em;
  color: var(--ink-muted);
  margin-bottom: 6px;
  text-transform: uppercase;
}
.attack-block-mech { font-family: 'IBM Plex Mono', monospace; font-size: 12.5px; color: var(--protected); line-height: 1.4; }
.attacks-legend {
  margin-top: 20px;
  padding: 16px 20px;
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  display: flex;
  align-items: center;
  gap: 28px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  color: var(--ink-muted);
  letter-spacing: 0.04em;
  flex-wrap: wrap;
}
.attacks-legend strong { color: var(--ink); font-weight: 500; }
.legend-swatch { display: inline-flex; align-items: center; gap: 8px; }
.swatch { width: 10px; height: 10px; border-radius: 50%; }
.swatch.warn { background: var(--warn); }
.swatch.prot { background: var(--protected); }

.attack-callout {
  margin-top: 36px;
  padding: 36px 40px;
  background: var(--bg-raised);
  border: 1px dashed var(--protected);
  border-radius: var(--radius-lg);
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 28px;
  align-items: start;
  position: relative;
}
.attack-callout::before { content: "+"; position: absolute; top: -7px; left: -7px; color: var(--protected); font-family: 'IBM Plex Mono', monospace; }
.attack-callout::after { content: "+"; position: absolute; top: -7px; right: -7px; color: var(--protected); font-family: 'IBM Plex Mono', monospace; }
.callout-icon { font-family: 'IBM Plex Sans', system-ui, sans-serif; font-weight: 600; font-size: 44px; line-height: 1; color: var(--protected); }
.callout-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--protected);
  margin-bottom: 10px;
}
.attack-callout p { font-size: 15.5px; line-height: 1.6; color: var(--ink-soft); max-width: 70ch; }
.attack-callout p strong { color: var(--ink); }
.attack-callout p a { color: var(--mark); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.attack-callout p a:hover { text-decoration-thickness: 2px; }
.attack-callout code {
  background: var(--bg);
  padding: 1px 6px;
  border-radius: 3px;
  color: var(--mark);
  font-size: 13.5px;
  border: 1px solid var(--line);
}
@media (max-width: 820px) { .attack-callout { grid-template-columns: 1fr; gap: 14px; padding: 28px; } }

/* MANIFESTO */
.manifesto {
  padding: 140px 0;
  background: var(--bg);
  border-top: 1px dashed var(--mark);
  border-bottom: 1px dashed var(--mark);
  position: relative;
  overflow: hidden;
}
.manifesto::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, rgba(229, 193, 74, 0.05) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(229, 193, 74, 0.05) 1px, transparent 1px);
  background-size: 80px 80px;
  mask-image: radial-gradient(ellipse at center, black 30%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse at center, black 30%, transparent 75%);
  pointer-events: none;
}
.manifesto-inner {
  max-width: 1000px;
  margin: 0 auto;
  padding: 0 32px;
  position: relative;
  z-index: 2;
  text-align: center;
}
.manifesto-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--mark);
  margin-bottom: 36px;
  display: inline-block;
  padding: 5px 14px;
  border: 1px dashed var(--mark);
  border-radius: 2px;
}
.manifesto-text {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(34px, 5.5vw, 76px);
  line-height: 1.05;
  letter-spacing: -0.03em;
  color: var(--ink);
}
.manifesto-text em { font-weight: 500;  color: var(--mark); }
.manifesto-signature {
  margin-top: 44px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  letter-spacing: 0.14em;
  color: var(--ink-muted);
}
.manifesto-signature::before { content: "— "; color: var(--mark); }

/* COMPARE TABLE */
.compare-wrap {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  overflow-x: auto;
  position: relative;
}
.compare-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  min-width: 720px;
}
.compare-table thead th {
  padding: 20px 24px;
  text-align: left;
  border-bottom: 1px solid var(--ink-muted);
  background: var(--bg-raised);
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 500;
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
}
.compare-table thead th:first-child { width: 28%; }
.compare-table thead th.cloudtaser-col { background: var(--bg); color: var(--mark); border-bottom-color: var(--mark); }
.compare-table thead th.cloudtaser-col::before { content: "●  "; color: var(--mark); }
.compare-table tbody td {
  padding: 16px 24px;
  border-bottom: 1px dashed var(--line);
  vertical-align: top;
  line-height: 1.5;
  color: var(--ink-soft);
}
.compare-table tbody tr:last-child td { border-bottom: none; }
.compare-table tbody tr:hover { background: rgba(229, 193, 74, 0.03); }
.compare-table td:first-child { font-weight: 500; color: var(--ink); }
.compare-table td.neg { color: var(--ink-muted); }
.compare-table td.neg::before { content: "— "; color: var(--warn); font-weight: 600; font-family: 'IBM Plex Mono', monospace; }
.compare-table td.pos { color: var(--ink); }
.compare-table td.pos::before { content: "✓ "; color: var(--protected); font-weight: 600; font-family: 'IBM Plex Mono', monospace; }
.compare-table td.partial { color: var(--ink); }
.compare-table td.partial::before { content: "◐ "; color: var(--mark); font-weight: 600; font-family: 'IBM Plex Mono', monospace; }
.compare-table code {
  font-size: 12px;
  background: var(--bg);
  padding: 1px 5px;
  border-radius: 3px;
  color: var(--mark);
  border: 1px solid var(--line);
}
.compare-table a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.compare-table a:hover { color: var(--mark); text-decoration-thickness: 2px; }
.ref-content table a, .ref-section table a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.ref-content table a:hover, .ref-section table a:hover { color: var(--mark); text-decoration-thickness: 2px; }

/* INSTALL */
.install-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; }
.code-block {
  background: var(--bg);
  color: var(--ink);
  border-radius: var(--radius-lg);
  border: 1px dashed var(--line);
  overflow: hidden;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  line-height: 1.7;
  position: relative;
}
.code-block.with-marks::before { content: "+"; position: absolute; top: -7px; left: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.code-block.with-marks::after { content: "+"; position: absolute; top: -7px; right: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.code-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 18px;
  background: var(--bg-card);
  border-bottom: 1px dashed var(--line);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.code-copy {
  background: transparent;
  color: var(--ink-soft);
  border: 1px dashed var(--line);
  font-family: inherit;
  font-size: 10.5px;
  padding: 4px 10px;
  border-radius: 2px;
  cursor: pointer;
  letter-spacing: 0.08em;
  transition: all 0.15s ease;
}
.code-copy:hover { background: var(--ink); color: var(--bg); border-color: var(--ink); border-style: solid; }
.code-copy.copied { background: var(--protected); color: var(--bg); border-color: var(--protected); border-style: solid; }
.code-body { padding: 22px; overflow-x: auto; }
.code-body .cmt { color: var(--ink-muted); }
.code-body .kw { color: var(--accent); }
.code-body .str { color: var(--protected); }
.code-body .num { color: var(--mark); }
.code-body .prompt { color: var(--mark); opacity: 0.7; user-select: none; }

.install-steps { display: flex; flex-direction: column; gap: 28px; }
.install-step { display: grid; grid-template-columns: 36px 1fr; gap: 18px; }
.step-num {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px dashed var(--mark);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  font-weight: 500;
  color: var(--mark);
}
.install-step h3 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 19px;
  margin-bottom: 6px;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.install-step p { font-size: 14px; color: var(--ink-muted); line-height: 1.6; }
.install-step code {
  font-size: 12.5px;
  background: var(--bg-card);
  padding: 1px 6px;
  border-radius: 3px;
  color: var(--mark);
  border: 1px solid var(--line);
}
@media (max-width: 860px) { .install-grid { grid-template-columns: 1fr; } }

/* AUDIENCES */
.audiences { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.audience {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  padding: 32px 28px;
  transition: all 0.2s ease;
  position: relative;
}
.audience:hover { border-style: solid; border-color: var(--mark); }
.audience-icon { width: 32px; height: 32px; margin-bottom: 22px; color: var(--mark); }
.audience h3 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 20px;
  margin-bottom: 10px;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.audience p { font-size: 13.5px; color: var(--ink-muted); line-height: 1.55; }
@media (max-width: 900px) { .audiences { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 540px) { .audiences { grid-template-columns: 1fr; } }

/* BIG CTA */
.big-cta { padding: 120px 0; text-align: center; border-bottom: 1px dashed var(--line); }
.big-cta .serif-huge {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(36px, 5.5vw, 78px);
  line-height: 1.02;
  letter-spacing: -0.03em;
  max-width: 20ch;
  margin: 0 auto 24px;
  color: var(--ink);
}
.big-cta .serif-huge em { font-weight: 500;  color: var(--mark); }
.big-cta .sub { font-size: 17px; color: var(--ink-soft); max-width: 52ch; margin: 0 auto 36px; }
.big-cta .hero-ctas { justify-content: center; }

/* FOOTER */
.footer {
  padding: 64px 0 32px;
  font-size: 13.5px;
  color: var(--ink-muted);
  border-top: 1px dashed var(--line);
}
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 40px; margin-bottom: 48px; }
.footer-brand .logo { margin-bottom: 16px; }
.footer-brand p { max-width: 36ch; font-size: 13.5px; color: var(--ink-muted); line-height: 1.55; }
.footer h5,
.footer .footer-heading {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 16px;
  font-weight: 500;
}
.footer ul { list-style: none; }
.footer li { margin-bottom: 10px; }
.footer li a { color: var(--ink-soft); font-size: 13.5px; }
.footer li a:hover { color: var(--ink); }
.footer-bottom {
  padding-top: 24px;
  border-top: 1px dashed var(--line);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  font-size: 12px;
  color: var(--ink-muted);
  font-family: 'IBM Plex Mono', monospace;
  letter-spacing: 0.04em;
}
.footer-bottom .serif-signature { font-family: 'IBM Plex Sans', system-ui, sans-serif; color: var(--ink-muted); letter-spacing: 0; }
.footer-analytics-note { margin-top: 20px; font-size: 11px; color: var(--ink-subtle); line-height: 1.55; max-width: 80ch; }
@media (max-width: 800px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .footer-grid { grid-template-columns: 1fr; } }

/* REVEAL / DRAW */
.reveal { opacity: 0; transform: translateY(18px); transition: opacity 0.8s ease, transform 0.8s ease; }
.reveal.in { opacity: 1; transform: none; }
.drawline {
  position: relative;
  display: block;
  height: 1px;
  background: transparent;
  overflow: hidden;
  width: 100%;
  margin: 64px 0;
}
.drawline::after {
  content: "";
  position: absolute;
  inset: 0;
  border-top: 1px dashed var(--line);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1.4s ease;
}
.drawline.in::after { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .packet-secret, .packet-cipher, .ebpf-ring, .memfd-glow, .stack-dot, .diagram-state-dot { animation: none !important; }
  .drawline::after { transform: scaleX(1); transition: none; }
  html { scroll-behavior: auto; }
}

/* VISIBILITY PANEL */
.visibility-panel {
  margin-bottom: 96px;
  padding: 40px 48px;
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 40px;
  align-items: stretch;
}
.visibility-panel::before,
.visibility-panel::after,
.visibility-panel > .c-bl,
.visibility-panel > .c-br {
  content: "+";
  position: absolute;
  color: var(--ink-muted);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 14px;
  line-height: 1;
  pointer-events: none;
}
.visibility-panel::before { top: -8px; left: -8px; }
.visibility-panel::after  { top: -8px; right: -8px; }
.visibility-panel > .c-bl { bottom: -8px; left: -8px; }
.visibility-panel > .c-br { bottom: -8px; right: -8px; }
.visibility-head {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-bottom: 18px;
  padding-bottom: 12px;
  border-bottom: 1px dashed var(--line);
  display: flex;
  align-items: center;
  gap: 10px;
}
.visibility-head.left-h { color: var(--protected); }
.visibility-head.right-h { color: var(--warn); }
.visibility-head::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.visibility-lines {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13.5px;
  line-height: 2.0;
  color: var(--ink);
}
.visibility-lines .key { color: var(--mark); }
.visibility-lines.right {
  font-family: 'Redacted', 'IBM Plex Mono', monospace;
  font-size: 17px;
  letter-spacing: 0.04em;
  color: var(--warn);
  line-height: 1.55;
}
.visibility-lines.right .key {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13.5px;
  letter-spacing: 0.02em;
  color: var(--warn);
  opacity: 0.75;
}
.visibility-arrow {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  color: var(--mark);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 0 8px;
  border-left: 1px dashed var(--line);
  border-right: 1px dashed var(--line);
  white-space: nowrap;
  min-width: 120px;
}
.visibility-arrow .arrow-glyph { font-size: 20px; letter-spacing: 0; color: var(--mark); }
.visibility-arrow .arrow-sub {
  font-size: 9.5px;
  letter-spacing: 0.18em;
  color: var(--ink-muted);
  text-transform: uppercase;
}
.visibility-foot {
  grid-column: 1 / -1;
  padding-top: 20px;
  margin-top: 12px;
  border-top: 1px dashed var(--line);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--ink-muted);
  letter-spacing: 0.04em;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.visibility-foot .cta {
  color: var(--mark);
  text-decoration: none;
  border-bottom: 1px dashed var(--mark);
  padding-bottom: 1px;
}
.visibility-foot .cta:hover { color: var(--ink); border-color: var(--ink); text-decoration: none; }
@media (max-width: 760px) {
  .visibility-panel {
    grid-template-columns: 1fr;
    padding: 28px 24px;
    gap: 28px;
    margin-bottom: 64px;
  }
  .visibility-arrow {
    border-left: none;
    border-right: none;
    border-top: 1px dashed var(--line);
    border-bottom: 1px dashed var(--line);
    padding: 12px 0;
    flex-direction: row;
    min-width: auto;
  }
  .visibility-lines { font-size: 12.5px; }
  .visibility-lines.right { font-size: 15px; }
}

/* TOC (reference pages) */
.toc {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  padding: 28px 32px;
  position: relative;
  max-width: 820px;
}
.toc::before { content: "+"; position: absolute; top: -7px; left: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.toc::after { content: "+"; position: absolute; top: -7px; right: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.toc-head {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.22em;
  color: var(--mark);
  margin-bottom: 18px;
  padding-bottom: 14px;
  border-bottom: 1px dashed var(--line);
  text-transform: uppercase;
}
.toc-list { list-style: none; padding: 0; margin: 0; }
.toc-list li { border-bottom: 1px dashed var(--line); }
.toc-list li:last-child { border-bottom: none; }
.toc-list a {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  gap: 16px;
  padding: 14px 0;
  align-items: baseline;
  color: var(--ink);
}
.toc-list a:hover { text-decoration: none; }
.toc-list a:hover .toc-title { color: var(--mark); }
.toc-num { font-family: 'IBM Plex Mono', monospace; font-size: 11px; letter-spacing: 0.14em; color: var(--ink-subtle); }
.toc-title {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 19px;
  color: var(--ink);
  letter-spacing: -0.015em;
  transition: color 0.2s;
}
.toc-meta { font-family: 'IBM Plex Mono', monospace; font-size: 11.5px; color: var(--ink-muted); letter-spacing: 0.04em; text-align: right; }
@media (max-width: 640px) {
  .toc { padding: 22px; }
  .toc-list a { grid-template-columns: 32px 1fr; gap: 12px; padding: 12px 0; }
  .toc-meta { grid-column: 2; font-size: 10.5px; margin-top: 2px; text-align: left; }
  .toc-title { font-size: 17px; }
}

/* REFERENCE SECTIONS */
.ref-section { padding: 100px 0; border-bottom: 1px dashed var(--line); position: relative; overflow: hidden; }
.ref-section.alt { background: var(--bg-alt); }
.ref-grid { display: grid; grid-template-columns: 240px 1fr; gap: 56px; align-items: start; }
.ref-sidebar { position: sticky; top: 96px; padding-top: 8px; }
.ref-sidebar-num {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: 72px;
  color: transparent;
  -webkit-text-stroke: 1px var(--mark);
  line-height: 1;
  letter-spacing: -0.04em;
  margin-bottom: 14px;
}
.ref-sidebar-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 24px;
}
.ref-sidebar-specs { border-top: 1px dashed var(--line); }
.ref-sidebar-specs dt {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-top: 14px;
  margin-bottom: 4px;
}
.ref-sidebar-specs dd {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  color: var(--ink);
  margin-bottom: 10px;
  line-height: 1.45;
}
.ref-content h2 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(32px, 4vw, 54px);
  line-height: 1.05;
  letter-spacing: -0.025em;
  margin-bottom: 20px;
  color: var(--ink);
}
.ref-content h2 em {  color: var(--mark); }
.ref-content h3 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 22px;
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 40px 0 16px;
  padding-top: 28px;
  border-top: 1px dashed var(--line);
}
.ref-content h3:first-of-type { border-top: none; padding-top: 0; margin-top: 32px; }
.ref-content .lede {
  font-size: 18px;
  color: var(--ink-soft);
  line-height: 1.55;
  margin-bottom: 28px;
  padding-bottom: 24px;
  border-bottom: 1px dashed var(--line);
}
.ref-content p { font-size: 15.5px; color: var(--ink-soft); line-height: 1.7; margin-bottom: 18px; }
.ref-content p strong { color: var(--ink); font-weight: 500; }
.ref-content p code, .ref-content li code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  background: var(--bg-raised);
  padding: 1px 6px;
  border-radius: 3px;
  border: 1px solid var(--line);
  color: var(--mark);
}
.ref-content ul { list-style: none; margin: 16px 0 24px; padding: 0; }
.ref-content ul li {
  padding: 8px 0 8px 22px;
  position: relative;
  font-size: 15px;
  color: var(--ink-soft);
  line-height: 1.6;
}
.ref-content ul li::before {
  content: "→";
  position: absolute;
  left: 0; top: 8px;
  color: var(--mark);
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 500;
}
.ref-content ul li strong { color: var(--ink); }

.ref-figure {
  margin: 32px 0;
  padding: 24px;
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  position: relative;
}
.ref-figure::before { content: "+"; position: absolute; top: -7px; left: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.ref-figure::after { content: "+"; position: absolute; top: -7px; right: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.ref-figure-caption {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px dashed var(--line);
}
.ref-figure-caption .fig { color: var(--mark); margin-right: 8px; }
.ref-figure pre {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  color: var(--ink);
  line-height: 1.6;
  overflow-x: auto;
  white-space: pre;
}
.ref-figure pre .cmt { color: var(--ink-subtle); }
.ref-figure pre .kw { color: var(--accent); }
.ref-figure pre .str { color: var(--protected); }
.ref-figure pre .m { color: var(--mark); }
@media (max-width: 900px) {
  .ref-grid { grid-template-columns: 1fr; gap: 32px; }
  .ref-sidebar { position: static; border-bottom: 1px dashed var(--line); padding-bottom: 24px; }
  .ref-sidebar-num { font-size: 56px; }
  .ref-section { padding: 72px 0; }
  .ref-content h2 { font-size: clamp(26px, 6vw, 34px); }
  .ref-content .lede { font-size: 15.5px; }
  .ref-content p { font-size: 14.5px; }
}

/* PLATFORMS */
.platforms { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.platform {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  padding: 24px;
}
.platform-name {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--mark);
  text-transform: uppercase;
  margin-bottom: 14px;
}
.platform h4 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 18px;
  color: var(--ink);
  margin-bottom: 10px;
  letter-spacing: -0.01em;
}
.platform p { font-size: 13.5px; color: var(--ink-muted); line-height: 1.55; }
.platform p code { font-family: 'IBM Plex Mono', monospace; font-size: 12px; color: var(--ink); }
@media (max-width: 780px) { .platforms { grid-template-columns: 1fr; } }

/* GUARANTEES */
.guarantees { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.guarantee {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  padding: 28px 26px;
  position: relative;
}
.guarantee.do { border-color: var(--protected); }
.guarantee.dont { border-color: var(--warn); }
.guarantee-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-bottom: 18px;
  padding-bottom: 12px;
  border-bottom: 1px dashed var(--line);
}
.guarantee.do .guarantee-label { color: var(--protected); }
.guarantee.dont .guarantee-label { color: var(--warn); }
.guarantee h4 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 17px;
  color: var(--ink);
  margin: 12px 0 6px;
  letter-spacing: -0.01em;
}
.guarantee p { font-size: 13.5px; color: var(--ink-muted); line-height: 1.55; margin-bottom: 12px; }
@media (max-width: 780px) { .guarantees { grid-template-columns: 1fr; } }

/* SCENARIO (use-cases) */
.scenario {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  padding: 32px;
  position: relative;
  margin-bottom: 24px;
}
.scenario::before { content: "+"; position: absolute; top: -7px; left: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.scenario::after { content: "+"; position: absolute; top: -7px; right: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.scenario-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mark);
  margin-bottom: 12px;
}
.scenario h3 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 24px;
  color: var(--ink);
  margin-bottom: 16px;
  line-height: 1.2;
  letter-spacing: -0.015em;
}
.scenario h3 em { color: var(--mark); }
.scenario p { font-size: 15px; color: var(--ink-soft); line-height: 1.65; margin-bottom: 14px; }
.scenario p:last-child { margin-bottom: 0; }
.scenario ul { list-style: none; margin: 14px 0 8px; padding: 0; }
.scenario li {
  padding: 6px 0 6px 22px;
  position: relative;
  font-size: 14.5px;
  color: var(--ink-soft);
  line-height: 1.6;
}
.scenario li::before {
  content: "→";
  position: absolute;
  left: 0; top: 6px;
  color: var(--mark);
  font-family: 'IBM Plex Mono', monospace;
}
.scenario li strong { color: var(--ink); font-weight: 500; }
.scenario code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  background: var(--bg-raised);
  padding: 1px 6px;
  border-radius: 3px;
  color: var(--mark);
  border: 1px solid var(--line);
}

/* NEWS */
.news-grid { display: flex; flex-direction: column; gap: 0; margin-top: 24px; }
.news-item {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 32px;
  padding: 28px 0;
  border-top: 1px dashed var(--line);
  align-items: start;
}
.news-item:last-child { border-bottom: 1px dashed var(--line); }
.news-date {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  letter-spacing: 0.12em;
  color: var(--mark);
  text-transform: uppercase;
  padding-top: 4px;
}
.news-title {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 22px;
  color: var(--ink);
  margin-bottom: 10px;
  line-height: 1.25;
  letter-spacing: -0.015em;
}
.news-body { font-size: 15px; color: var(--ink-soft); line-height: 1.65; }
.news-body a { color: var(--accent); }
.news-body a:hover { color: var(--mark); }
@media (max-width: 740px) {
  .news-item { grid-template-columns: 1fr; gap: 10px; padding: 24px 0; }
  .news-title { font-size: 19px; }
  .news-body { font-size: 14.5px; }
}

/* ROADMAP */
.roadmap { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; margin-top: 28px; }
.roadmap-item {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  padding: 24px 26px;
  position: relative;
}
.roadmap-item .milestone-date {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mark);
  margin-bottom: 10px;
}
.roadmap-item h4 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: 18px;
  color: var(--ink);
  margin-bottom: 8px;
  letter-spacing: -0.01em;
}
.roadmap-item p { font-size: 13.5px; color: var(--ink-muted); line-height: 1.6; }
@media (max-width: 740px) { .roadmap { grid-template-columns: 1fr; } }

/* CONTACT */
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 32px; }
.contact-card {
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  padding: 32px;
  position: relative;
}
.contact-card::before { content: "+"; position: absolute; top: -7px; left: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.contact-card::after { content: "+"; position: absolute; top: -7px; right: -7px; color: var(--ink-muted); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.contact-card h3 {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mark);
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px dashed var(--line);
}
.contact-card .email-big {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 400;
  font-size: clamp(22px, 2.6vw, 30px);
  color: var(--ink);
  letter-spacing: -0.015em;
  display: inline-block;
  padding-bottom: 3px;
  border-bottom: 1px dashed var(--mark);
}
.contact-card .email-big:hover { color: var(--mark); text-decoration: none; }
.contact-card dl { display: grid; grid-template-columns: auto 1fr; gap: 10px 20px; margin-top: 10px; }
.contact-card dt { font-family: 'IBM Plex Mono', monospace; font-size: 11.5px; color: var(--ink-muted); }
.contact-card dd { font-size: 14px; color: var(--ink); }
.contact-card dd a { color: var(--accent); }
.contact-card p { font-size: 14.5px; color: var(--ink-soft); line-height: 1.6; margin-bottom: 10px; }
.contact-card p strong { color: var(--ink); }
@media (max-width: 740px) { .contact-grid { grid-template-columns: 1fr; } }

/* CONTACT BOOKING */
.contact-booking {
  padding: 60px 0;
  border-bottom: 1px solid var(--line);
}
.contact-booking h2 {
  font-size: 28px;
  margin: 0 0 12px;
  color: var(--ink);
}
.contact-booking-lede {
  color: var(--ink-soft);
  margin: 0 0 32px;
  max-width: 60ch;
}
.appointment-embed {
  width: 100%;
  max-width: 760px;
  aspect-ratio: 4 / 3;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
}
.appointment-embed iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
.appointment-embed-unconfigured {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 200px;
  font-family: 'IBM Plex Mono', var(--mono, monospace);
  color: var(--ink-muted);
  font-size: 13px;
}
.appointment-embed-unconfigured iframe { display: none; }
.appointment-embed-unconfigured::after {
  content: 'Booking widget not yet configured. See data-booking-src in contact.html or email hello@cloudtaser.io.';
  padding: 24px;
  text-align: center;
  max-width: 80%;
}
.contact-booking-fallback {
  margin-top: 16px;
  color: var(--ink-muted);
  font-size: 14px;
}

/* UTILITIES */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.skip-link {
  position: absolute;
  top: -40px;
  left: 8px;
  background: var(--mark);
  color: var(--bg);
  padding: 8px 14px;
  border-radius: var(--radius);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  z-index: 200;
  transition: top 0.2s;
}
.skip-link:focus { top: 8px; text-decoration: none; }

.anchor-nav { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 24px; }
.anchor-nav a {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 6px 12px;
  border: 1px dashed var(--line);
  border-radius: 2px;
  color: var(--ink-soft);
  background: var(--bg-card);
}
.anchor-nav a:hover { border-style: solid; border-color: var(--mark); color: var(--mark); text-decoration: none; }

/* MOBILE */
@media (max-width: 900px) {
  .container { padding: 0 24px; }
  .hero { padding: 56px 0 0; }
  .hero h1 { font-size: clamp(30px, 7.5vw, 44px); margin-bottom: 22px; }
  .hero-sub { font-size: 15.5px; margin-bottom: 28px; }
  .eyebrow { margin-bottom: 28px; font-size: 10px; }
  .eyebrow { flex-wrap: wrap; gap: 6px 10px; }
  .eyebrow .ver { margin-left: 0; }
  .btn { padding: 11px 18px; font-size: 13px; }
  .section { padding: 80px 0; }
  .section-head { margin-bottom: 40px; }
  .section-ghost-num { font-size: 140px; top: 16px; right: -10px; opacity: 0.6; }
  .section-title { font-size: clamp(26px, 6vw, 36px); }
  .section-lede { font-size: 15.5px; }
  .section-label { font-size: 10px; padding: 3px 8px; }
  .positioning-statement { padding: 56px 0; }
  .positioning-inner { grid-template-columns: 1fr; gap: 24px; }
  .positioning-statement h2 { max-width: 16ch; }
  .positioning-copy { border-left: 0; border-top: 1px dashed var(--line); padding: 24px 0 0; }
  .positioning-copy p { font-size: 15.5px; }
  .stack-strip { padding: 16px; gap: 14px 18px; grid-template-columns: 1fr 1fr; }
  .stack-cell-label { font-size: 9px; }
  .stack-cell-value { font-size: 12.5px; }
  .hero-diagram-wrap { padding: 32px 16px 24px; overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: 64px; }
  .hero-diagram-wrap .hero-diagram { min-width: 820px; }
  .diagram-head { flex-wrap: wrap; gap: 10px; }
  .diagram-title { font-size: 9.5px; letter-spacing: 0.14em; }
  .problem-body p { font-size: 15.5px; }
  .pullquote { padding: 22px 20px 18px; }
  .pullquote-text { font-size: 17px; }
  .cat-quote { font-size: clamp(20px, 5vw, 28px); }
  .cat-caption { font-size: 13.5px; padding-left: 14px; }
  .pillars { grid-template-columns: 1fr; gap: 10px; }
  .pillar { padding: 28px 24px; }
  .pillar h3 { font-size: 19px; }
  .attacks-grid { grid-template-columns: 1fr; gap: 10px; }
  .attack { padding: 20px 18px; }
  .attack-desc { min-height: auto; margin-bottom: 12px; }
  .attacks-legend { gap: 16px 22px; padding: 14px 18px; font-size: 11px; }
  .attack-callout { padding: 26px 22px; grid-template-columns: 1fr; gap: 12px; }
  .attack-callout p { font-size: 14.5px; }
  .manifesto { padding: 80px 0; }
  .manifesto-inner { padding: 0 24px; }
  .manifesto-text { font-size: clamp(26px, 7vw, 42px); }
  .manifesto-label { margin-bottom: 24px; font-size: 10px; }
  .manifesto-signature { margin-top: 28px; font-size: 10.5px; }
  .compare-table { font-size: 13px; }
  .compare-table thead th { padding: 16px 18px; font-size: 10.5px; }
  .compare-table tbody td { padding: 14px 18px; }
  .install-grid { grid-template-columns: 1fr; gap: 32px; }
  .install-step h3 { font-size: 17px; }
  .install-step p { font-size: 13.5px; }
  .code-body { padding: 18px; font-size: 12px; }
  .code-header { padding: 10px 16px; font-size: 10px; }
  .audiences { grid-template-columns: 1fr; gap: 10px; }
  .audience { padding: 26px 22px; }
  .big-cta { padding: 80px 0; }
  .big-cta .serif-huge { font-size: clamp(30px, 7vw, 46px); margin-bottom: 18px; }
  .big-cta .sub { font-size: 15.5px; margin-bottom: 28px; }
  .footer { padding: 48px 0 28px; }
  .footer-grid { gap: 32px; margin-bottom: 36px; }
  .footer-bottom { flex-direction: column; align-items: flex-start; font-size: 11px; }
  .scenario { padding: 24px 22px; }
  .scenario h3 { font-size: 20px; }
  .scenario p { font-size: 14.5px; }
}

@media (max-width: 540px) {
  .container { padding: 0 20px; }
  .nav-inner { height: 56px; }
  .logo { font-size: 18px; }
  .section-ghost-num { font-size: 110px; right: -6px; }
  .compliance { padding: 14px 0; }
  .compliance-label { font-size: 9.5px; }
  .badge { font-size: 9.5px; padding: 3px 7px; }
  .fact-list li { grid-template-columns: 60px 1fr; gap: 12px; font-size: 13px; }
  .fact-list .year { font-size: 11.5px; }
  .hero-diagram-wrap .hero-diagram { min-width: 720px; }
}

@media (max-width: 900px) {
  .hero-diagram-wrap { position: relative; }
  .hero-diagram-wrap .scroll-hint {
    display: flex;
    align-items: center;
    gap: 8px;
    position: absolute;
    bottom: 10px;
    right: 14px;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 9.5px;
    color: var(--ink-subtle);
    letter-spacing: 0.12em;
    pointer-events: none;
    background: var(--bg-card);
    padding: 3px 8px;
    border-radius: 2px;
  }
}
@media (min-width: 901px) { .hero-diagram-wrap .scroll-hint { display: none; } }

/* -------- Three pillars of sovereignty + CC cost table (issue #77) -------- */
.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;
}

.pillars.pillars-three { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 980px) { .pillars.pillars-three { grid-template-columns: 1fr; } }

.pillar-substrate {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px dashed var(--border);
  display: flex;
  gap: 10px;
  align-items: baseline;
  font-size: 13px;
  color: var(--ink-muted);
}
.pillar-substrate .substrate-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink-muted);
  flex-shrink: 0;
}
.pillar-substrate .substrate-value { flex: 1; }
.pillar-substrate .substrate-value code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--accent);
  background: transparent;
  padding: 0;
}
.pillar-substrate-required .substrate-value strong { color: var(--mark); }

.pillar.pillar-cc { border-color: var(--mark); }
.pillar.pillar-cc .pillar-label { color: var(--mark); }

.cc-table th { text-align: left; }
.cc-table tbody th {
  padding: 18px 22px;
  font-weight: 500;
  color: var(--ink);
  border-bottom: 1px solid var(--border);
  background: var(--bg);
}
.cc-table tbody tr:last-child th { border-bottom: none; }
.cc-footnote {
  margin-top: 16px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--ink-muted);
  letter-spacing: 0.02em;
  line-height: 1.6;
}

/* DPIA scope block used on compliance.html */
.dpia-scope {
  margin-top: 28px;
  padding: 22px 26px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--mark);
  background: var(--bg-card);
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--ink-muted);
}
.dpia-scope .dpia-label {
  display: block;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--mark);
  margin-bottom: 10px;
}
.dpia-scope strong { color: var(--ink); }

.precond-list { list-style: none; padding: 0; margin: 28px 0 0; display: grid; gap: 20px; }
.precond-list > li {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 20px;
  padding: 22px 24px;
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-left: 3px solid var(--mark);
  align-items: start;
}
.precond-list .precond-num {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 30px;
  color: var(--mark);
  line-height: 1;
  padding-top: 2px;
}
.precond-list h3 {
  font-size: 18px;
  font-weight: 500;
  color: var(--ink);
  margin: 0 0 8px;
  letter-spacing: -0.01em;
}
.precond-list h3 code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 14px;
  color: var(--accent);
  background: transparent;
  padding: 0;
}
.precond-list p {
  font-size: 14.5px;
  line-height: 1.65;
  color: var(--ink-muted);
  margin: 0;
}
.precond-list p code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  color: var(--ink);
  background: var(--bg-raised);
  padding: 1px 5px;
  border-radius: 2px;
}
.precond-list strong { color: var(--ink); }

.scope-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
  margin-top: 28px;
}
.scope-item {
  padding: 22px 24px;
  background: var(--bg-card);
  border: 1px dashed var(--line);
}
.scope-item h3 {
  font-size: 16px;
  font-weight: 500;
  color: var(--ink);
  margin: 0 0 10px;
  letter-spacing: -0.005em;
}
.scope-item p {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink-muted);
  margin: 0;
}
.scope-item p code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--ink);
  background: var(--bg-raised);
  padding: 1px 5px;
  border-radius: 2px;
}
.scope-item a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.scope-item a:hover { color: var(--mark); }

@media (max-width: 780px) {
  .cc-table th, .cc-table td { padding: 12px 14px; font-size: 12.5px; }
  .pillar-substrate { font-size: 12.5px; }
  .precond-list > li { grid-template-columns: 40px 1fr; gap: 14px; padding: 18px 18px; }
  .precond-list .precond-num { font-size: 24px; }
  .scope-grid { grid-template-columns: 1fr; gap: 12px; }
  .scope-item { padding: 18px 18px; }
}

/* =========================================================================
   Demo Lab page — /demo-lab.html
   Unlisted, private. Three-option selection grid + interactive demo layout.
   ========================================================================= */

.demo-options { display: grid; gap: 32px; margin-top: 20px; }
.demo-option {
  position: relative;
  padding: 40px 44px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  /* On narrow viewports sub-content (sketch-diagram, iframes) must not
     widen the article past the grid column. min-width:0 lets grid/flex
     children shrink below their content; overflow:hidden stops any
     overdraw from the sketch-body's inner scrolling element. */
  min-width: 0;
  overflow: hidden;
}
.pane-body-live iframe { max-width: 100%; }
.demo-option:nth-of-type(2) { border-left-color: var(--mark); }
.demo-option:nth-of-type(3) { border-left-color: var(--protected); }

.option-head {
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: 24px;
  align-items: start;
  margin-bottom: 28px;
  padding-bottom: 24px;
  border-bottom: 1px dashed var(--line);
}
.option-num {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 44px;
  font-weight: 500;
  color: var(--accent);
  line-height: 1;
  letter-spacing: -0.02em;
}
.demo-option:nth-of-type(2) .option-num { color: var(--mark); }
.demo-option:nth-of-type(3) .option-num { color: var(--protected); }

.option-meta { display: flex; flex-direction: column; gap: 8px; }
.option-kind {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.option-title {
  font-size: clamp(26px, 3vw, 36px);
  font-weight: 500;
  color: var(--ink);
  margin: 0;
  letter-spacing: -0.015em;
  line-height: 1.15;
}
.option-title em { color: var(--mark); font-style: normal; }
.option-sub {
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink-soft);
  margin: 4px 0 0;
  max-width: 70ch;
}
.option-sub code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  color: var(--ink);
  background: var(--bg-raised);
  padding: 1px 5px;
  border-radius: 2px;
}
.option-sub a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.option-sub a:hover { color: var(--mark); }
.option-body { display: flex; flex-direction: column; gap: 24px; }
.option-footnote {
  font-size: 13px;
  color: var(--ink-muted);
  line-height: 1.6;
  margin: 8px 0 0;
}

/* Inline "pill" highlight for Next button reference in prose */
.pill {
  display: inline-block;
  padding: 1px 8px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mark);
  border: 1px solid var(--mark);
  border-radius: 3px;
}

/* ---- Option 01 - asciinema placeholder (restored in v3) ---- */
.asciinema-frame {
  min-height: 280px;
  background: #0B0C0F;
  border: 1px dashed var(--line);
  border-radius: 4px;
  position: relative;
  overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.cast-placeholder {
  padding: 40px;
  text-align: center;
  max-width: 60ch;
}
.cast-placeholder-badge {
  display: inline-block;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.18em;
  color: var(--warn);
  padding: 2px 8px;
  border: 1px dashed var(--warn);
  border-radius: 2px;
  margin-bottom: 18px;
}
.cast-placeholder-title {
  font-size: 20px;
  color: var(--ink);
  margin-bottom: 10px;
  font-weight: 500;
}
.cast-placeholder-sub {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink-muted);
  margin-bottom: 14px;
}
.cast-placeholder-sub code {
  font-family: 'IBM Plex Mono', monospace;
  color: var(--ink);
  font-size: 12px;
  background: var(--bg-raised);
  padding: 1px 5px;
  border-radius: 2px;
}
.cast-placeholder-hint {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  color: var(--ink-subtle);
  line-height: 1.5;
  padding-top: 12px;
  border-top: 1px dashed var(--line);
}
.cast-placeholder-hint code {
  color: var(--ink-muted);
  font-size: 11px;
}

/* Honest-disclosure callout on Option 02 body (visible without expanding the sketch) */
.disclosure-note {
  padding: 14px 18px;
  background: var(--bg-raised);
  border: 1px dashed var(--line);
  border-left: 3px solid var(--warn);
  border-radius: 3px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-soft);
}
.disclosure-note .disclosure-label {
  display: block;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--warn);
  margin-bottom: 6px;
}
.disclosure-note p { margin: 0; }
/* Multi-paragraph variant of disclosure-note (cloudtaser-demo#402): when
   the note carries production-topology + deviation + CTA paragraphs,
   restore vertical rhythm so the three blocks read as distinct beats. */
.disclosure-note p + p { margin-top: 12px; }
.disclosure-note .disclosure-cta { margin-top: 16px; }
.disclosure-note .disclosure-cta a { font-weight: 500; }
.disclosure-note code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--ink);
  background: var(--bg-card);
  padding: 1px 5px;
  border-radius: 2px;
}
.disclosure-note strong { color: var(--ink); }
.disclosure-note em { color: var(--ink); font-style: italic; }
.disclosure-note a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.disclosure-note a:hover { color: var(--mark); }

/* ---- Option 02 - live demo (watch or drive) ---- */
/* Role status pill — replaces the old top-of-page role banner.
   Lives in .controls (left side, before the Next button); JS sets
   data-role on .controls to drive the dot colour + pulse. The Next
   button itself is the single morphing CTA (claim → step → release). */
.role-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 999px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  background: var(--bg-alt);
  border: 1px solid var(--line);
  white-space: nowrap;
  flex-shrink: 0;
}
.role-status-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--ink-muted);
  flex-shrink: 0;
}

/* Per-role colouring — driven by data-role on .controls so we don't
   need a class on the pill itself. */
.controls[data-role="available"] .role-status {
  color: var(--protected-ink);
  border-color: rgba(109,214,155,0.45);
  background: rgba(109,214,155,0.08);
}
.controls[data-role="available"] .role-status-dot {
  background: var(--protected);
  animation: pulse 1.6s ease-in-out infinite;
}
.controls[data-role="host"] .role-status {
  color: var(--mark);
  border-color: rgba(229,193,74,0.45);
  background: rgba(229,193,74,0.08);
}
.controls[data-role="host"] .role-status-dot { background: var(--mark); }
.controls[data-role="watching"] .role-status {
  color: var(--accent-ink);
  border-color: rgba(125,168,255,0.40);
  background: rgba(125,168,255,0.08);
}
.controls[data-role="watching"] .role-status-dot {
  background: var(--accent);
  animation: pulse 2.4s ease-in-out infinite;
}
.controls[data-role="resetting"] .role-status {
  color: var(--warn-ink);
  border-color: rgba(232,153,112,0.45);
  background: rgba(232,153,112,0.10);
}
.controls[data-role="resetting"] .role-status-dot {
  background: var(--warn);
  animation: pulse 0.8s ease-in-out infinite;
}

/* Claim-state Next button: subtle outer pulse to compensate for the
   removed banner — without it the page-load CTA can blend into the
   strip card. Disabled when reduced motion is preferred. */
.controls[data-role="available"] .btn.primary:not(:disabled) {
  animation: claim-pulse 2.4s ease-in-out infinite;
}
@keyframes claim-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(229,193,74,0.00); }
  50%      { box-shadow: 0 0 0 4px rgba(229,193,74,0.18), 0 0 22px rgba(229,193,74,0.22); }
}
@media (prefers-reduced-motion: reduce) {
  .controls[data-role="available"] .btn.primary { animation: none; }
  .controls .role-status-dot { animation: none !important; }
}
/* Spinner shown during "Resetting demo for you…" claim transition */
.role-spinner {
  display: inline-block;
  width: 12px;
  height: 12px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  vertical-align: middle;
  margin-left: 6px;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

/* Hint rendered inside the disabled Next button for non-drivers --
   clarifies "this belongs to the active driver" without looking like
   an active CTA. */
.demo-next-btn .btn-hint {
  font-style: normal;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-left: 8px;
  font-weight: 400;
}
/* ---- Interactive demo ---- */
.demo-status-banner {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: 14px;
  align-items: start;
  padding: 14px 18px;
  background: var(--warn-wash);
  border: 1px solid var(--warn);
  border-radius: 3px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--warn-ink);
}
.demo-status-banner .status-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--warn);
  margin-top: 6px;
  animation: status-pulse 2s ease-in-out infinite;
}
.demo-status-banner a { color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.demo-status-banner a:hover { color: var(--mark); }
@keyframes status-pulse {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}

.terminal-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  transition: grid-template-columns 0.25s ease;
}
/* Driver-toggled + SSE-broadcast expansion. Stacks all three panes
   full-width so log / kubectl output is easier to read. */
.terminal-grid.expanded {
  grid-template-columns: 1fr;
}
/* When expanded, applyExpanded() moves .demo-controls INTO .terminal-grid
   between the cluster pane and the beacon pane, so Next / probe / End
   sit right under the action (cluster pane) instead of at the page
   bottom. In non-expanded (3-col) mode controls stay as a sibling after
   the grid, via the same function. */
.terminal-grid.expanded > .demo-controls {
  grid-column: 1 / -1;
  margin: 0;
}
.terminal-grid.expanded .terminal-pane {
  min-height: 280px;
}
.terminal-pane {
  background: #07080A;
  border: 1px solid rgba(255,255,255,0.22);
  border-top: 1px solid rgba(255,255,255,0.32);
  border-radius: 3px;
  min-height: 220px;
  display: flex; flex-direction: column;
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.08), inset 0 1px 0 rgba(255,255,255,0.08), 0 4px 20px rgba(0,0,0,0.7);
}
.pane-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 14px;
  background: var(--bg-card);
  border-bottom: 1px dashed rgba(255,255,255,0.18);
  font-family: 'IBM Plex Mono', monospace;
}
.pane-title {
  display: flex; align-items: center; gap: 8px;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  color: var(--ink);
}
.pane-dot { width: 7px; height: 7px; border-radius: 50%; }
.pane-dot-cluster { background: var(--accent); }
.pane-dot-beacon  { background: var(--mark); }
.pane-dot-secret  { background: var(--protected); }
.pane-role {
  font-size: 9.5px;
  color: var(--ink-muted);
  letter-spacing: 0.08em;
}
.pane-role .endpoint-label {
  font-family: 'IBM Plex Mono', monospace;
  color: var(--ink-soft);
  letter-spacing: 0.04em;
}
.pane-body {
  flex: 1;
  padding: 14px 16px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--ink);
  position: relative;
  overflow: auto;
}
.pane-body-mock { opacity: 0.55; }
.pane-body-live {
  padding: 0;
  overflow: hidden;
  background: #07080A;
  min-height: 220px;
}
.pane-body-live iframe {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 220px;
  border: 0;
  background: #07080A;
}
.mock-line { white-space: pre; }
.mock-dim { color: var(--ink-muted); }
.mock-log { color: var(--protected-ink); }
.mock-log-time { color: var(--ink-subtle); padding-right: 10px; }
.mock-prompt { color: var(--protected); padding-right: 6px; }
.mock-cmd { color: var(--ink); }
.mock-cursor { color: var(--ink); animation: mock-cursor 1.2s steps(1) infinite; }
@keyframes mock-cursor {
  0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; }
}
.mock-overlay {
  position: absolute;
  right: 10px; bottom: 10px;
  font-size: 9.5px;
  letter-spacing: 0.08em;
  color: var(--ink-subtle);
  text-transform: uppercase;
  background: rgba(7, 8, 10, 0.8);
  padding: 2px 6px;
  border: 1px dashed var(--line);
  border-radius: 2px;
}

/* Attention-grab flash for the secret-store pane during the 5s auto-run window.
   Not triggered yet; hooks available for Phase B.4 wiring. */
.terminal-pane.flashing {
  animation: pane-flash 0.8s ease-in-out infinite;
}
@keyframes pane-flash {
  0%, 100% { border-color: var(--line); box-shadow: none; }
  50%      { border-color: var(--mark); box-shadow: 0 0 22px rgba(229, 193, 74, 0.4); }
}

.demo-controls {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  background: var(--bg-raised);
  border: 1px dashed var(--line);
  border-radius: 3px;
}
.demo-controls-primary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.demo-next-btn {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.02em;
  padding: 10px 22px;
  background: var(--mark);
  color: #0E0F11;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  transition: background 0.15s, opacity 0.15s;
}
.demo-next-btn:hover:not(:disabled) { background: var(--mark-ink, #F2D673); }
.demo-next-btn:disabled { opacity: 0.38; cursor: not-allowed; }
.demo-next-btn .arrow { margin-left: 6px; }
.demo-step-indicator {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  color: var(--ink-muted);
  letter-spacing: 0.04em;
}
.demo-step-indicator em { color: var(--ink); font-style: normal; }
.demo-step-indicator .step-sep { color: var(--ink-subtle); padding: 0 4px; }

.demo-expand-btn {
  margin-left: auto;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  letter-spacing: 0.05em;
  padding: 8px 14px;
  background: transparent;
  color: var(--ink-muted);
  border: 1px dashed var(--line);
  border-radius: 3px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.demo-expand-btn:hover:not(:disabled) {
  color: var(--ink);
  border-color: var(--mark);
  border-style: solid;
}
.demo-expand-btn:disabled { opacity: 0.38; cursor: not-allowed; }
.demo-expand-btn.active {
  color: var(--mark);
  border-color: var(--mark);
  border-style: solid;
  background: color-mix(in srgb, var(--bg-card) 80%, var(--mark) 10%);
}

.demo-release-btn {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  letter-spacing: 0.05em;
  padding: 8px 14px;
  background: transparent;
  color: var(--ink-muted);
  border: 1px dashed var(--line);
  border-radius: 3px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.demo-release-btn:hover:not(:disabled) {
  color: var(--warn);
  border-color: var(--warn);
  border-style: solid;
}
.demo-release-btn:disabled { opacity: 0.38; cursor: not-allowed; }

/* -----------------------------------------------------------------------
   Probe panel — Phase 1: Run-all button + sequential checklist

   Post 2026-04-28 refactor: the embedded (non-fullscreen) layout uses
   the same single-container visual treatment as fullscreen — border +
   radius + flush header — for consistency. The fullscreen-specific
   override below tunes flex sizing for the right-column grid track.
   ----------------------------------------------------------------------- */
.demo-probe {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin-top: 12px;
  background: var(--bg-alt);
  /* border removed -- founder feedback (#801). bg-alt provides
     sufficient separation from the strip column. */
  border-radius: var(--radius);
  overflow: hidden;
  /* position:relative establishes the containing block for the
     absolute-positioned .callout-column descendant (#537 — strip-Y
     aligned overlay; supersedes #292's in-flow stack). */
  position: relative;
}

/* Hide the right-column probe/YAML panel when the demo is not in
   fullscreen mode. The container is only meaningful once the user has
   engaged the demo (body.demo-fullscreen); on normal page-load it is
   just empty visual chrome that would confuse visitors scanning the
   product marketing page. In fullscreen the rule below is overridden
   by `body.demo-fullscreen .demo-probe { grid-area: probes; … }` which
   sets an explicit display:flex context, so the `!important` is not
   required — specificity of the fullscreen selector wins anyway. */
body:not(.demo-fullscreen) .demo-probe { display: none; }

/* Run all probes button */
.probe-run-all-btn {
  align-self: flex-start;
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 13px;
  font-weight: 500;
  padding: 9px 20px;
  background: var(--accent);
  color: #0E0F11;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  transition: background 0.15s, opacity 0.15s;
}
.probe-run-all-btn:hover:not(:disabled) { background: var(--accent-ink); }
.probe-run-all-btn:disabled { opacity: 0.38; cursor: not-allowed; }
.probe-run-all-btn .arrow { margin-left: 6px; }

/* probe-hint and its legend selectors removed 2026-04-28 — element no
   longer in the DOM. drive.py check_probe_hint_removed asserts this. */


/* Probe summary bar — rendered above checklist after all probes complete.
   `:not([hidden])` keeps the HTML `hidden` attribute working — without
   it, `display: flex` overrides the user-agent `display: none` and an
   empty bordered bar shows before any probe has run. */
.probe-summary:not([hidden]) {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 0;
  padding: 8px 12px;
  margin: 8px 12px 0;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 3px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-muted);
}
/* Keeps score + detail stats on one baseline regardless of probe-column
   width.  The warn-msg div (width: 100%) still wraps to its own row
   via flex-wrap: wrap on the parent.
   gap: 6px gives consistent inter-stat spacing instead of relying on
   HTML whitespace nodes between <span> flex items (fragile across
   browsers).  flex-wrap: nowrap is the default but stated explicitly
   so the inline constraint is obvious.  (#353) */
.probe-summary-stats-row {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: nowrap;
  gap: 6px;
  white-space: nowrap;
}
.probe-summary-warn {
  border-color: var(--warn);
  background: var(--warn-wash);
}
.probe-summary-score {
  color: var(--protected-ink);
  font-weight: 500;
}
.probe-summary-sep    { color: var(--line); }
.probe-summary-detail { color: var(--ink-muted); }
.probe-summary-fail {
  color: var(--warn-ink);
  font-weight: 500;
}
.probe-summary-warn-msg {
  width: 100%;
  margin-top: 2px;
  font-size: 10.5px;
  color: var(--warn-ink);
}

/* Checklist container.

   IMPORTANT: the [hidden] rule MUST come first / be respected so the
   right-column mutex (description-pane vs probes-pane) actually mutexes.
   Without it, `display: flex` overrides the user-agent `display: none`
   that comes from the HTML `hidden` attribute and the probes panel
   bleeds through under the description box (regression observed
   2026-04-28 on PR #189). Pair: `.yaml-preview-pane[hidden]` rule. */
/* probe-checklist: visible only at step 7 (#801). Steps 0-6 show the
   callout column alone; probes appear at the eBPF enforcement moment.
   data-step on .demo-live is the single source of truth. The callout-
   column is a sibling and toggles independently (JS hidden attribute). */
.demo-live:not([data-step="7"]) .probe-checklist {
  display: none;
}
.probe-checklist {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Inner padding so rows don't sit flush against the .demo-probe
     container edge. Fullscreen overrides this with its own padding
     (8px 12px 10px) to leave room for overflow scrollbars. */
  padding: 8px 12px 10px;
}

/* Individual probe row — wire-diagram card layout.

   2026-05-12 redesign: uniform 3-row card matching founder wire-diagram spec.
     Row 1 (header):  [icon] [tag PROOF?]  ...spacer...  [status pill]
     Row 2 (identifier): short mono identifier of what is being tested
     Row 3 (affordance): "expand for details" toggle text
     Expandable area (hidden): full label, description prose, command output

   All cards have the same collapsed height regardless of description length.
   Left-border accent carries semantic color (green/red/amber); other
   borders are transparent when collapsed, subtle var(--line) when
   expanded. Fail state is the exception — full red border always. */
.probe-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 10px 6px;
  border-radius: 3px;
  background: var(--bg-card);
  /* Collapsed: only left accent border visible; other borders transparent.
     Expanded state gets a subtle full border via .probe-row-expanded. */
  border: 1px solid transparent;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  line-height: 1.2;
  transition: padding 0.15s ease, background 0.15s ease;
}
/* 2026-04-28 audit — climax-calm pass: 15 probe rows lit
   simultaneously with full-opacity green washes was a "Christmas
   tree" — the eye couldn't land on any single outcome. Strategy:
   keep semantic color in the LEFT BORDER + ICON + PILL TEXT only;
   row backgrounds drop to a single neutral so the grid reads as a
   calm settled list with subtle accent edges. The green wash on
   blocked-ok / pass becomes a 2px left border (load-bearing
   semantic) rather than a full row tint. Pending opacity bumped
   from 0.6 to 0.85 — at 0.6 the rows were below WCAG AA on their
   summary pill (#575554 on #1c2027 = 2.20). */
.probe-row {
  border-left-width: 2px;
  border-left-style: solid;
}
/* No row-level opacity dim — pending rows looked dim because of
   color, not because of opacity (opacity at <1 multiplies the cmd
   line's color and tanks contrast, e.g. .85*--ink-muted on bg-card
   was AA-failing 3.64 at 10px). Pending state is visually
   distinguishable by:
   - the dim icon (·)
   - the lighter pill text "pending"
   - no left-border accent (line-color, not protected/mark)
   That's enough hierarchical signal without opacity blur. */
/* State borders — only the LEFT accent carries semantic color.
   Top/right/bottom stay transparent when collapsed (the .probe-row
   base sets border: 1px solid transparent). The expanded state
   (.probe-row-expanded) brings the other three sides in as var(--line).
   Closes cloudtaser-demo#803 — thin left-border accent per state. */
.probe-row-pending  { border-left-color: var(--line); }
.probe-row-running  {
  border-left-color: var(--mark);
  background: color-mix(in srgb, var(--bg-card) 92%, var(--mark) 8%);
}
.probe-row-pass {
  border-left-color: var(--protected);
}
.probe-row-blocked-ok {
  border-left-color: var(--protected);
}
/* Infra-error: amber/gold accent — "we could not reach the target"
   is an operational notice, NOT a security failure. Visually distinct
   from fail (red border + warn-wash) so a prospect never mistakes
   "infrastructure temporarily unavailable" for "the security claim
   is violated." Slightly stronger --mark tint than the running state
   (12% vs 8%) so the row reads as settled, not in-flight. */
.probe-row-infra-error {
  border-left-color: var(--mark);
  background: color-mix(in srgb, var(--bg-card) 88%, var(--mark) 12%);
}
/* Fail gets full border — failures must stand out. */
.probe-row-fail {
  border-color: var(--warn);
  background: var(--warn-wash);
}

/* P1-3: Proof probes -- the 4 rows that prove the data-sovereignty claim.
   Stronger left border (4px vs 2px) in the accent gold so they read as
   "these are the ones that matter" without disrupting the calm grid.
   Subtle background gradient fades from a faint gold wash on the left
   to transparent, reinforcing the border accent without Christmas-tree.
   The label gets a weight bump (600) so the eye lands there first. */
.probe-row[data-proof="true"] {
  border-left-width: 4px;
  border-left-color: var(--mark);
  background: linear-gradient(to right, rgba(229, 193, 74, 0.06) 0%, transparent 18%);
}
.probe-row[data-proof="true"] .probe-row-identifier {
  font-weight: 600;
  color: var(--ink);
}
/* Failed proof probe — higher specificity than both .probe-row-fail and
   .probe-row[data-proof="true"] so the red alarm wins over the gold proof
   marker. A failed proof probe is the most alarming state in the grid —
   it means the data-sovereignty claim failed verification. The 4px left
   border thickness is preserved (proof marker) but coloured red. */
.probe-row-fail[data-proof="true"] {
  border-color: var(--warn);
  border-left-color: var(--warn);
  border-left-width: 4px;
  background: var(--warn-wash);
}
/* Infra-error on proof probe — keeps the 4px proof marker in amber
   (same as the base proof accent). Not red — infra-error is operational,
   not a security-claim violation. */
.probe-row-infra-error[data-proof="true"] {
  border-left-width: 4px;
  border-left-color: var(--mark);
  background: color-mix(in srgb, var(--bg-card) 88%, var(--mark) 12%);
}
/* When completed (pass or blocked-ok), proof rows show the left border
   in protected green (same semantic as other completed rows) but keep
   the extra width -- the thickness IS the proof marker. */
.probe-row-pass[data-proof="true"],
.probe-row-blocked-ok[data-proof="true"] {
  border-left-width: 4px;
  border-left-color: var(--protected);
  background: linear-gradient(to right, rgba(109, 214, 155, 0.05) 0%, transparent 18%);
}
/* Proof tag pill -- small inline "PROOF" badge next to the category tag.
   CSS ::after pseudo-element, no HTML change needed. Calm uppercase, 7px,
   matches the tag aesthetic. */
.probe-row[data-proof="true"] .probe-row-tag::after {
  content: "PROOF";
  display: inline-block;
  margin-left: 4px;
  font-size: 7px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--mark);
  background: rgba(229, 193, 74, 0.10);
  border: 1px solid rgba(229, 193, 74, 0.25);
  border-radius: 2px;
  padding: 0 3px;
  vertical-align: middle;
  line-height: 1.6;
}
/* Completed proof rows swap badge color to green for visual consistency */
.probe-row-pass[data-proof="true"] .probe-row-tag::after,
.probe-row-blocked-ok[data-proof="true"] .probe-row-tag::after {
  color: var(--protected);
  background: rgba(109, 214, 155, 0.10);
  border-color: rgba(109, 214, 155, 0.25);
}

/* Posture/summary card — the cc_protection_summary row. Yellow
   highlight so it reads as the "bottom line" scorecard among the
   15 probe cards. Same structural treatment as other cards but with
   the gold accent that proof probes use. Closes #813. */
.probe-row[data-posture="true"] {
  border-left-width: 4px;
  border-left-color: var(--mark);
  border-color: rgba(229, 193, 74, 0.30);
  background: linear-gradient(to right, rgba(229, 193, 74, 0.08) 0%, rgba(229, 193, 74, 0.03) 40%, transparent 100%);
}
.probe-row[data-posture="true"] .probe-row-identifier {
  font-weight: 600;
  color: var(--ink);
}
.probe-row[data-posture="true"] .probe-row-tag {
  color: var(--mark);
  background: rgba(229, 193, 74, 0.12);
}
/* Completed posture card — swap to green border like proof probes */
.probe-row-pass[data-posture="true"],
.probe-row-blocked-ok[data-posture="true"] {
  border-left-color: var(--protected);
  border-color: rgba(109, 214, 155, 0.30);
  background: linear-gradient(to right, rgba(109, 214, 155, 0.06) 0%, rgba(109, 214, 155, 0.02) 40%, transparent 100%);
}
/* Failed posture card — red wins over gold */
.probe-row-fail[data-posture="true"] {
  border-color: var(--warn);
  border-left-color: var(--warn);
  border-left-width: 4px;
  background: var(--warn-wash);
}

.probe-row-icon {
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  text-align: center;
  flex-shrink: 0;
  width: 14px;
}
.probe-row-pending  .probe-row-icon { color: var(--ink-subtle); }
.probe-row-running  .probe-row-icon { color: var(--mark); }
.probe-row-pass     .probe-row-icon { color: var(--protected); }
.probe-row-blocked-ok .probe-row-icon { color: var(--protected); }
.probe-row-fail     .probe-row-icon { color: var(--warn); }

/* Category tag — short uppercase hint (MEMORY, CC, AUDIT, etc.)
   in the card header row. Sits left-aligned after the icon so the
   CISO eye scans [icon] [tag] ... [status]. (#813 card redesign) */
.probe-row-tag {
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding: 1px 5px;
  background: color-mix(in srgb, var(--bg-card) 90%, white 10%);
  border-radius: 2px;
  white-space: nowrap;
  line-height: 1;
}
.probe-row-pass .probe-row-tag,
.probe-row-blocked-ok .probe-row-tag {
  color: var(--protected-ink);
  background: color-mix(in srgb, var(--bg-card) 90%, var(--protected) 10%);
}
.probe-row-fail .probe-row-tag {
  color: var(--warn-ink);
  background: color-mix(in srgb, var(--bg-card) 90%, var(--warn) 10%);
}

/* Label — now inside the expandable area, shows full verbose text
   when expanded. No longer visible in collapsed state. */
.probe-row-label {
  color: var(--ink-soft);
  font-size: 10.5px;
  letter-spacing: 0.01em;
  line-height: 1.35;
  margin-bottom: 4px;
}

/* Status pill — right-aligned in the header row. (#813 card layout) */
.probe-row-summary {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 2px;
  white-space: nowrap;
  margin-left: auto;
}
/* Pill colors — 2026-04-28 audit: the prior `--ink-subtle` on
   bg-card produced 2.20 contrast at 10px (failing AA). The new
   `--ink-muted` (#A6A199) on bg-card (#1F2530) reaches 6.9 — clears
   AA + nearly AAA. Bold weight on pass/blocked/fail pills pairs
   semantic color with typography so colorblind viewers still get the
   verdict from weight + glyph + text. */
.probe-row-pending  .probe-row-summary { color: var(--ink-muted); font-weight: 400; }
.probe-row-running  .probe-row-summary { color: var(--mark); font-weight: 600; }
.probe-row-pass     .probe-row-summary { color: var(--protected-ink); font-weight: 600; }
.probe-row-blocked-ok .probe-row-summary { color: var(--protected-ink); font-weight: 600; }
.probe-row-fail     .probe-row-summary { color: var(--warn-ink); font-weight: 600; }

/* Probe-row description — inside the expandable area. Shows full
   prose explanation when the card is expanded. */
.probe-row-desc {
  font-family: inherit;
  font-size: 10px;
  color: var(--ink-muted);
  letter-spacing: 0;
  margin-top: 4px;
  line-height: 1.4;
}
.probe-row-desc:empty { display: none; }

/* One-line summary — visible inside expanded area. */
.probe-row-desc-summary {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* Full multiline desc — revealed alongside desc-summary on expand. */
.probe-row-desc-full {
  white-space: normal;
  margin-top: 4px;
  line-height: 1.5;
  font-size: 10px;
}

/* Click affordance — makes the row feel interactive. */
.probe-row {
  cursor: pointer;
}
.probe-row:hover {
  background: color-mix(in srgb, var(--bg-card) 97%, white 3%);
}
/* Keyboard focus — visible ring for tab-navigating users.
   :focus-visible so mouse clicks don't trigger the ring. */
.probe-row:focus-visible {
  outline: 2px solid var(--mark);
  outline-offset: 1px;
}

/* --- Wire-diagram: header row layout --- */
.probe-row-header {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 16px; /* normalise across proof (PROOF badge) and non-proof rows */
}

/* --- Wire-diagram: identifier (row 2) --- */
.probe-row-identifier {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--ink);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- Wire-diagram: expand affordance (row 3) --- */
.probe-row-expand-affordance {
  font-size: 10px;
  color: var(--ink-subtle);
  letter-spacing: 0.04em;
}
.probe-row-expand-affordance::before {
  content: "\25B8\00A0";
  font-size: 8px;
}
.probe-row-expanded .probe-row-expand-affordance::before {
  content: "\25BE\00A0";
}

/* --- Wire-diagram: expandable area (hidden when collapsed) --- */
.probe-row-expandable {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--line);
}

/* Uniform collapsed height (#351) — all 15 probe cards share the same
   height in the collapsed state so the grid reads as a settled, aligned
   list rather than a ragged column. The value (72px) is derived from:
   14px vertical padding + 16px header + 4px gap + 14px identifier +
   4px gap + 12px expand affordance + a few pixels of slack. Only
   applies when NOT expanded; the expanded state lets the card grow to
   accommodate the expandable area + output prose. */
.probe-row:not(.probe-row-expanded) {
  min-height: 72px;
}

/* Expanded state — reveal the expandable area with subtle chrome. The
   expanded card gets thin top/right/bottom borders; left border stays
   under state-rule control (pass=green, fail=red, etc.). (#813) */
.probe-row-expanded {
  padding: 8px 10px 8px;
  border-top-color: var(--line);
  border-right-color: var(--line);
  border-bottom-color: var(--line);
}

/* Expandable output details — inside the expandable area.
   Revealed when the card is expanded. (#813) */
.probe-row-details {
  margin-top: 2px;
}
.probe-row-expand {
  font-size: 9.5px;
  color: var(--ink-muted);
  letter-spacing: 0.06em;
  cursor: pointer;
  list-style: none;
  user-select: none;
  white-space: nowrap;
}
/* When open, ensure correct spacing for the output pre. */
details.probe-row-details[open] {
  margin-top: 4px;
}
.probe-row-expand::-webkit-details-marker { display: none; }
.probe-row-expand::before { content: "▸ "; color: var(--ink-subtle); }
details[open] > .probe-row-expand::before { content: "▾ "; }
.probe-row-output {
  margin-top: 6px;
  padding: 8px 10px;
  background: #07080A;
  border: 1px dashed var(--line);
  border-radius: 2px;
  font-size: 10.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 200px;
  overflow-y: auto;
}

/* -----------------------------------------------------------------------
   Pane header — (i) popover replacing inline endpoint detail
   ----------------------------------------------------------------------- */
.pane-info {
  position: relative;
  display: flex;
  align-items: center;
}

/* <details> wraps the (i) button + popover body */
.pane-popover {
  position: relative;
}

/* The (i) summary = the visible button */
.pane-info-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1px solid var(--ink-muted);
  background: transparent;
  color: var(--ink-muted);
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 9.5px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  list-style: none;
  user-select: none;
  transition: border-color 0.12s, color 0.12s;
  letter-spacing: 0;
}
.pane-info-btn::-webkit-details-marker { display: none; }
.pane-popover[open] > .pane-info-btn,
.pane-info-btn:hover {
  border-color: var(--accent);
  color: var(--accent-ink);
}

/* Popover card */
.pane-popover-body {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 20;
  width: 240px;
  padding: 10px 14px;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0,0,0,0.45);
  pointer-events: none;
}
.pane-popover[open] > .pane-popover-body { pointer-events: auto; }
.popover-host {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--ink);
  letter-spacing: 0.04em;
  margin-bottom: 6px;
}
.popover-meta {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  color: var(--ink-muted);
  letter-spacing: 0.04em;
  line-height: 1.6;
}

/* Architecture sketch (collapsible) */
.architecture-sketch {
  margin-top: 8px;
  border: 1px dashed var(--line);
  border-radius: 3px;
  background: var(--bg-alt);
}
.architecture-sketch > summary {
  padding: 14px 18px;
  cursor: pointer;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--ink);
  user-select: none;
  list-style: none;
}
.architecture-sketch > summary::-webkit-details-marker { display: none; }
.architecture-sketch > summary::before {
  content: "▸ ";
  color: var(--mark);
  margin-right: 6px;
  transition: transform 0.15s;
  display: inline-block;
}
.architecture-sketch[open] > summary::before {
  content: "▾ ";
}
.architecture-sketch > summary:hover { color: var(--mark); }
.sketch-body {
  padding: 8px 26px 28px;
  border-top: 1px dashed var(--line);
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--ink-soft);
}
.sketch-body h4 {
  font-size: 13px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--mark);
  margin: 22px 0 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 500;
}
.sketch-body h4:first-child { margin-top: 10px; }
.sketch-diagram {
  background: #07080A;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 16px 18px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--ink);
  overflow-x: auto;
  margin: 0 0 14px;
  max-width: 100%;
  min-width: 0;
}
.sketch-list {
  margin: 0;
  padding-left: 22px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.sketch-list li { padding-left: 4px; }
.sketch-list code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--ink);
  background: var(--bg-card);
  padding: 1px 5px;
  border-radius: 2px;
}
.sketch-list strong { color: var(--ink); font-weight: 500; }
.sketch-list a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }

/* ==========================================================================
   Phase 4 — Architecture sketch: HTML/CSS table replacing ASCII boxes
   ========================================================================== */

/* Browser → Cloudflare flow diagram */
.sketch-flow {
  background: #07080A;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 16px 18px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--ink);
  margin: 0 0 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.sketch-flow-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.sketch-flow-node {
  color: var(--ink);
  font-weight: 500;
  white-space: nowrap;
}
.sketch-flow-label {
  color: var(--ink-muted);
}
.sketch-flow-arrow {
  color: var(--ink-subtle);
  padding-left: 4px;
  font-size: 11px;
}
.sketch-flow-connections {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 16px;
  row-gap: 3px;
  padding-left: 16px;
  border-left: 1px solid var(--line);
}
.sketch-flow-connections code {
  color: var(--accent-ink);
  font-size: 11px;
}
.sketch-flow-meta {
  color: var(--ink-muted);
  font-size: 10.5px;
  align-self: center;
}

/* VM topology table */
.sketch-table {
  width: 100%;
  border-collapse: collapse;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--ink);
  background: #07080A;
  border: 1px solid var(--line);
  border-radius: 3px;
  margin: 0 0 14px;
  overflow: hidden;
}
.sketch-table th,
.sketch-table td {
  padding: 9px 14px;
  border: 1px solid var(--line);
  vertical-align: top;
  text-align: left;
}
.sketch-table thead th {
  background: var(--bg-raised);
  color: var(--ink-muted);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 500;
  border-bottom: 1px solid var(--line);
}
.sketch-table tbody th {
  white-space: nowrap;
  color: var(--ink);
  font-weight: 500;
  background: var(--bg-card);
  vertical-align: middle;
}
.sketch-table tfoot td {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 12px;
  color: var(--ink-soft);
  line-height: 1.6;
  border-top: 1px solid var(--line);
  background: var(--bg-raised);
}
.sketch-table tfoot a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.sketch-table tfoot strong { color: var(--ink); font-weight: 500; }
.sketch-cost {
  color: var(--ink-muted);
  font-size: 10.5px;
  display: block;
  margin-top: 2px;
}
.sketch-badge {
  display: inline-block;
  font-size: 8.5px;
  letter-spacing: 0.1em;
  padding: 0 5px;
  border-radius: 2px;
  font-weight: 600;
  margin-right: 5px;
  vertical-align: middle;
}
.sketch-badge-us {
  background: var(--accent-wash);
  color: var(--accent-ink);
  border: 1px solid var(--accent);
}
.sketch-badge-eu {
  background: var(--protected-wash);
  color: var(--protected-ink);
  border: 1px solid var(--protected);
}

/* ---- Option 03 · DIY grid ---- */
.diy-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
}
.diy-panel {
  background: var(--bg-raised);
  border: 1px dashed var(--line);
  padding: 22px 24px;
  border-radius: 3px;
}
.diy-panel-wide { grid-column: 1 / -1; }
.diy-panel h4 {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mark);
  margin: 0 0 14px;
  font-weight: 500;
}
.diy-list {
  margin: 0; padding-left: 20px;
  display: flex; flex-direction: column; gap: 8px;
  font-size: 13.5px; line-height: 1.6; color: var(--ink-soft);
}
.diy-list code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--ink);
  background: var(--bg-card);
  padding: 1px 5px;
  border-radius: 2px;
}
.diy-list em { color: var(--ink); font-style: normal; }
.diy-list a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.diy-list a:hover { color: var(--mark); }

/* ---- "Read more" tail inside the architecture sketch <details> ---- */
.sketch-deep-link {
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px dashed var(--line);
  font-size: 13px;
  color: var(--ink-muted);
}
.sketch-deep-link a {
  color: var(--accent);
  text-decoration: none;
  transition: color 0.15s;
}
.sketch-deep-link a:hover { color: var(--accent-ink); }


/* ==========================================================================
   Phase 2 — Topology strip
   ========================================================================== */
.topo-strip {
  display: flex;
  align-items: center;
  height: 40px;
  background: var(--bg-card);
  border: 1px solid rgba(255,255,255,0.22);
  border-top: 1px solid rgba(255,255,255,0.32);
  border-radius: 3px;
  padding: 0 16px;
  margin-bottom: 10px;
  gap: 0;
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.07), inset 0 1px 0 rgba(255,255,255,0.08), 0 2px 12px rgba(0,0,0,0.6);
}

/* Node wrapper — holds the <details> popover trigger */
.topo-node {
  position: relative;
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

/* <details> element — no default styling */
.topo-popover { position: relative; }

/* The clickable summary = dot + label + region */
.topo-node-inner {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  list-style: none;
  user-select: none;
  padding: 4px 6px;
  border-radius: 3px;
  transition: background 0.12s;
}
.topo-node-inner::-webkit-details-marker { display: none; }
.topo-node-inner:hover { background: var(--bg-raised); }
.topo-popover[open] > .topo-node-inner { background: var(--bg-raised); }

.topo-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.topo-dot-cluster { background: var(--accent); }
.topo-dot-beacon  { background: var(--mark); }
.topo-dot-secret  { background: var(--protected); }

.topo-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  letter-spacing: 0.14em;
  color: var(--ink);
  white-space: nowrap;
}

.topo-region {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 8.5px;
  letter-spacing: 0.1em;
  color: var(--ink);
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 0 4px;
  white-space: nowrap;
}

/* Connector: stretches between nodes, hosts the animated packet */
.topo-connector {
  flex: 1;
  position: relative;
  height: 40px;
  display: flex;
  align-items: center;
  min-width: 40px;
}

.topo-line {
  width: 100%;
  height: 1px;
  background: rgba(255,255,255,0.35);
}

/* Traveling packet dot — hidden by default, animates only when .topo-active */
.topo-packet {
  position: absolute;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--mark);
  box-shadow: 0 0 8px var(--mark);
  left: 0;
  opacity: 0;
  pointer-events: none;
}

.topo-packet-2 {
  background: var(--protected);
  box-shadow: 0 0 8px var(--protected);
}

/* Activate animation when parent has .topo-active */
/* Both sub-classes are forward-compatible hooks set by Round 2B JS */
.topo-strip.topo-active .topo-packet,
.topo-strip.topo-active-handshake .topo-packet,      /* set by Round 2B JS */
.topo-strip.topo-active-busy .topo-packet {          /* set by Round 2B JS */
  animation: topo-travel 1.8s ease-in-out infinite;
}
.topo-strip.topo-active .topo-packet-2,
.topo-strip.topo-active-handshake .topo-packet-2,    /* set by Round 2B JS */
.topo-strip.topo-active-busy .topo-packet-2 {        /* set by Round 2B JS */
  animation: topo-travel 1.8s ease-in-out 0.9s infinite;
}

/* Handshake phase (step 2): slow trickle — dim opacity to signal idle traffic */
.topo-strip.topo-active-handshake .topo-packet {     /* set by Round 2B JS */
  animation: topo-travel-dim 3.6s ease-in-out infinite;
}
.topo-strip.topo-active-handshake .topo-packet-2 {   /* set by Round 2B JS */
  animation: topo-travel-dim 3.6s ease-in-out 1.8s infinite;
}

@keyframes topo-travel {
  0%   { left: 0;    opacity: 0; }
  8%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { left: calc(100% - 6px); opacity: 0; }
}

@keyframes topo-travel-dim {
  0%   { left: 0;    opacity: 0; }
  8%   { opacity: 0.35; }
  92%  { opacity: 0.35; }
  100% { left: calc(100% - 6px); opacity: 0; }
}

/* Dot pulse — subtle breathe on all three topology dots when the strip is active */
@keyframes topo-dot-breathe {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50%       { box-shadow: 0 0 6px 2px var(--dot-glow, currentColor); }
}
.topo-strip.topo-active .topo-dot-cluster,
.topo-strip.topo-active-handshake .topo-dot-cluster,  /* set by Round 2B JS */
.topo-strip.topo-active-busy .topo-dot-cluster {      /* set by Round 2B JS */
  --dot-glow: var(--accent);
  animation: topo-dot-breathe 2.4s ease-in-out infinite;
}
.topo-strip.topo-active .topo-dot-beacon,
.topo-strip.topo-active-handshake .topo-dot-beacon,   /* set by Round 2B JS */
.topo-strip.topo-active-busy .topo-dot-beacon {       /* set by Round 2B JS */
  --dot-glow: var(--mark);
  animation: topo-dot-breathe 2.4s ease-in-out 0.4s infinite;
}
.topo-strip.topo-active .topo-dot-secret,
.topo-strip.topo-active-handshake .topo-dot-secret,   /* set by Round 2B JS */
.topo-strip.topo-active-busy .topo-dot-secret {       /* set by Round 2B JS */
  --dot-glow: var(--protected);
  animation: topo-dot-breathe 2.4s ease-in-out 0.8s infinite;
}

/* Topology popover card — anchored below, same style as pane popovers */
.topo-popover-body {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 20;
  width: 260px;
  padding: 10px 14px;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0,0,0,0.45);
  pointer-events: none;
}
.topo-popover[open] > .topo-popover-body { pointer-events: auto; }

/* Encryption note row inside topology popovers */
.popover-enc {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--line);
  font-size: 11px;
  line-height: 1.55;
  color: var(--ink-muted);
  font-style: italic;
}

/* Right-align last node's popover so it doesn't clip off screen */
.topo-popover-left .topo-popover-body {
  left: auto;
  right: 0;
}

/* ==========================================================================
   Phase 2 — Demo live layout: cluster full-width, beacon below
   ========================================================================== */
/* Demo container is the focal "card" of /demo-lab. Elevated above the
 * page bg so the strip-stack visually owns the viewport. Surrounding
 * chrome (tab-intro, role banner, architecture-sketch) recedes via
 * --ink-muted text and a flatter background — see overrides below.
 *
 * Background sits between --bg-alt (#0F1013, used by the strip bodies
 * inside) and --bg-card (#252B35, too bright). The intermediate tone
 * makes strips appear as darker islands inside a brighter card → natural
 * depth perception without breaking the dark theme. */
.demo-live {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 28px;
  margin-bottom: 36px;
  padding: 22px 20px 20px;
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 40px -16px rgba(0,0,0,0.65), 0 1px 0 rgba(255,255,255,0.04) inset;
}

/* Cluster pane: full width
 *
 * Active max-height is set to 360px — enough for ~12 visible terminal rows at
 * the current font-size, covering the longest helm-install output without the
 * empty black void seen when only 1-3 lines have emitted. Commands that emit
 * more lines scroll naturally inside xterm.js (wheel-scroll enabled).
 *
 * The terminal background is controlled via the xterm.js theme option in JS
 * (var(--bg)). Tracked separately.
 */
.demo-live > .terminal-pane[data-pane="cluster"] {
  min-height: 340px;
  max-height: 360px;
  transition: max-height 300ms ease;
  position: relative;
}

/* Idle collapse — demo not yet claimed. The xterm pane is hidden (visibility:hidden
   + height:0) so the SSE stream stays connected but no terminal background bleeds
   through. The overlay becomes block-flow content instead of absolutely-positioned
   so it sizes the pane naturally (~80-100px with padding).
   max-height constraint is removed — the idle card content drives the height.
   Transition back to active: class removed → xterm pane returns → max-height 360px
   animates in. */
.demo-live > .terminal-pane[data-pane="cluster"].cluster-pane-idle {
  min-height: 0;
}
.demo-live > .terminal-pane[data-pane="cluster"].cluster-pane-idle .pane-body-live {
  min-height: 0;
  height: 0;
  overflow: hidden;
}
.demo-live > .terminal-pane[data-pane="cluster"].cluster-pane-idle .pane-body-live iframe {
  visibility: hidden;
  height: 0;
}



/* ==========================================================================
   Phase 2 — Secret register widget (always visible; state-machine content)
   States A/B/C/D are controlled entirely by JS via .secret-widget-state-*
   classes on .secret-register-widget — no CSS display gates.
   ========================================================================== */
.secret-register-widget {
  border: 1px solid rgba(255,255,255,0.22);
  border-top: 1px solid rgba(255,255,255,0.32);
  border-radius: 3px;
  background: #07080A;
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.08), inset 0 1px 0 rgba(255,255,255,0.08), 0 4px 20px rgba(0,0,0,0.7);
}

/* Pane-style header — matches .pane-head on cluster/beacon panes */
.secret-widget-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 14px;
  background: var(--bg-card);
  border-bottom: 1px dashed rgba(255,255,255,0.18);
  font-family: 'IBM Plex Mono', monospace;
}
.secret-widget-head-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  color: var(--ink);
}

/* Widget body — minimum height keeps the card stable as content changes */
.secret-widget-stage {
  min-height: 80px;
  position: relative;
}

/* Content panels within the stage */
.secret-widget-panel {
  padding: 14px 16px;
}
/* Form panel has its own internal padding via .secret-widget-heading/.secret-widget-body */
.secret-widget-panel-form {
  padding: 0;
}
/* Done panel: the inner .secret-widget-done handles its own padding */
.secret-widget-panel-done {
  padding: 0;
}
.secret-widget-panel[hidden] { display: none; }

/* State A — idle / pre-step-3 */
.secret-widget-panel-idle .secret-widget-idle-host {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  margin-bottom: 6px;
}
.secret-widget-panel-idle .secret-widget-idle-hint {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  color: var(--ink-muted);
  letter-spacing: 0.02em;
}

/* State B — step 3 anticipation */
.secret-widget-panel-anticipate .secret-widget-anticipate-fp {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--ink-soft);
  letter-spacing: 0.04em;
  margin-bottom: 6px;
  word-break: break-all;
}
.secret-widget-panel-anticipate .secret-widget-anticipate-hint {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  color: var(--ink-muted);
  letter-spacing: 0.02em;
}

/* State C — active register form. The .secret-widget-inner sits inside
   panel-form; no extra border needed since .secret-register-widget already
   has one. The pending tint is kept to signal "action required". */
.secret-widget-inner {
  background: transparent;
  overflow: visible;
}

.secret-widget-pending {
  background: color-mix(in srgb, #07080A 90%, var(--protected) 10%);
}

.secret-widget-heading {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  letter-spacing: 0.08em;
  color: var(--ink);
  border-bottom: 1px dashed var(--line);
}

.secret-widget-body {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  flex-wrap: wrap;
}

.secret-widget-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
}

.secret-widget-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--ink-muted);
  white-space: nowrap;
  flex-shrink: 0;
}

.secret-widget-input {
  flex: 1;
  min-width: 0;
  background: #07080A;
  border: 1px solid var(--line);
  border-radius: 2px;
  color: var(--ink-soft);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  padding: 6px 10px;
  outline: none;
  cursor: text;
  letter-spacing: 0.04em;
}
.secret-widget-input:focus { border-color: var(--protected); box-shadow: 0 0 0 2px rgba(109,214,155,0.12); }
.secret-widget-input:invalid:not(:placeholder-shown) { border-color: var(--danger, #e05252); }

/* Inline validation error for fingerprint UUID mismatch */
.fingerprint-error {
  font-size: 11px;
  color: var(--danger, #e05252);
  font-family: 'IBM Plex Mono', monospace;
  margin-top: 4px;
  padding: 0 2px;
}
.fingerprint-error[hidden] { display: none; }

/* Reset-failed banner (cloudtaser-demo#370). Shown when the SSE
   `reset_failed` event arrives — orchestrator gave up after two
   reset-demo.sh attempts. Banner persists until the next successful
   `reset` event or page reload. Style is danger-red border + dark
   background, with the stderr tail in a monospaced pre block so
   newlines render as the operator wrote them. */
.reset-failed-banner {
  margin: 8px 0 12px;
  border: 1px solid var(--danger, #e05252);
  background: rgba(224, 82, 82, 0.08);
  border-radius: 6px;
  padding: 10px 14px;
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 13px;
  color: var(--text-primary, #e6e8eb);
}
.reset-failed-banner[hidden] { display: none; }
.reset-failed-banner-inner { display: flex; flex-direction: column; gap: 6px; }
.reset-failed-banner-title { color: var(--danger, #e05252); font-weight: 600; }
.reset-failed-banner-hint { font-size: 12px; opacity: 0.85; }
.reset-failed-banner-tail {
  margin: 0;
  padding: 8px 10px;
  background: rgba(0, 0, 0, 0.35);
  border-radius: 4px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  line-height: 1.45;
  color: var(--text-secondary, #c8ccd1);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 200px;
  overflow-y: auto;
}

.secret-register-btn {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 13px;
  font-weight: 500;
  padding: 8px 20px;
  background: var(--protected);
  color: #07080A;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, opacity 0.15s;
}
.secret-register-btn:hover:not(:disabled) { background: var(--protected-ink); }
.secret-register-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.secret-register-btn .arrow { margin-left: 6px; }

/* Done state — inside panel-done; the parent panel's [hidden] gates visibility,
   so the inner .secret-widget-done card is always rendered as flex when visible. */
.secret-widget-done {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border-color: var(--protected);
  background: var(--protected-wash);
}

.secret-widget-ok {
  font-size: 18px;
  color: var(--protected);
  flex-shrink: 0;
}

.secret-widget-done-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.secret-widget-done-text strong {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--protected-ink);
  font-weight: 500;
}
.secret-widget-done-meta {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  color: var(--ink-muted);
  letter-spacing: 0.06em;
}

/* RPC output from the secret-store WebSocket captured and shown inline in
   the done card so the driver sees "Cluster registered. Fingerprint: …" */
.secret-widget-rpc-output {
  margin-top: 8px;
  padding: 7px 10px;
  background: #07080A;
  border: 1px dashed var(--line);
  border-radius: 2px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  line-height: 1.55;
  color: var(--ink-soft);
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 120px;
  overflow-y: auto;
}

/* ==========================================================================
   Phase 2 — Beacon collapsible strip
   ========================================================================== */
.beacon-strip {
  border: 1px solid rgba(255,255,255,0.22);
  border-radius: 3px;
  background: #07080A;
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.08), 0 4px 20px rgba(0,0,0,0.7);
}

/* Beacon strip header: outer container (div[role="group"]) */
.beacon-strip-header {
  display: flex;
  align-items: center;
  background: var(--bg-card);
  border-bottom: 1px dashed transparent;
  transition: border-color 0.12s;
}
.beacon-strip[aria-label] .beacon-strip-body:not([hidden]) ~ * { border-top-color: var(--line); }
.beacon-strip-header:has(+ .beacon-strip-body:not([hidden])) { border-bottom-color: var(--line); }

/* The actual toggle button */
.beacon-strip-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  padding: 10px 14px;
  background: transparent;
  border: none;
  cursor: pointer;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.12em;
  color: var(--ink-muted);
  text-align: left;
  transition: color 0.12s;
}
.beacon-strip-toggle:hover { color: var(--ink); }
.beacon-strip-toggle[aria-expanded="true"] { color: var(--ink); }

.beacon-strip-chevron {
  font-size: 10px;
  color: var(--mark);
  flex-shrink: 0;
  line-height: 1;
}

.beacon-strip-title {
  color: inherit;
  letter-spacing: 0.16em;
}

.beacon-strip-subtitle {
  font-size: 9.5px;
  color: var(--ink-subtle);
  letter-spacing: 0.06em;
  font-weight: 400;
}
.beacon-strip-subtitle::before {
  content: "\B7";
  padding: 0 5px;
  color: var(--ink-ghost);
}

/* Actions: (i) info popover + external terminal link, right-aligned */
.beacon-strip-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 14px 0 4px;
  flex-shrink: 0;
}

/* Full terminal link */
.beacon-strip-fullterm {
  font-size: 9.5px;
  letter-spacing: 0.08em;
  color: var(--ink-muted);
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.12s;
}
.beacon-strip-fullterm:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

/* Expanded body — 200px iframe */
.beacon-strip-body {
  border-top: 1px dashed var(--line);
}

.beacon-strip-frame {
  height: 200px;
  min-height: 200px;
  padding: 0;
  overflow: hidden;
}

.beacon-strip-frame iframe {
  display: block;
  width: 100%;
  height: 200px;
  border: 0;
  background: #07080A;
}

/* ==========================================================================
   Phase 2 — terminal-grid is gone; cleanup remnant responsive rules
   ========================================================================== */

/* ---- Responsive ---- */
@media (max-width: 780px) {
  .demo-option { padding: 28px 22px; }
  .option-head { grid-template-columns: 56px 1fr; gap: 16px; }
  .option-num { font-size: 34px; }
  .terminal-pane { min-height: 180px; }
  .demo-live > .terminal-pane[data-pane="cluster"] { min-height: 240px; max-height: 320px; }
  .demo-live > .terminal-pane[data-pane="cluster"].cluster-pane-idle { min-height: 0; }
  .diy-grid { grid-template-columns: 1fr; }
  .demo-controls-primary { flex-direction: column; align-items: stretch; gap: 8px; }
  .probe-run-all-btn { align-self: stretch; }
  .pane-popover-body { right: auto; left: 0; }
  .role-status { font-size: 11px; padding: 5px 10px; }
  .sketch-body { padding: 8px 16px 20px; }

  /* Topology strip: compact labels on small screens */
  .topo-label { display: none; }
  .topo-region { display: none; }
  .topo-dot { width: 10px; height: 10px; }
  .topo-node-inner { padding: 4px; }

  /* Beacon strip: hide external terminal link */
  .beacon-strip-fullterm { display: none; }

  /* Secret widget: stack vertically */
  .secret-widget-body { flex-direction: column; align-items: stretch; }
  .secret-register-btn { align-self: flex-end; }
  .topo-popover-body { width: 200px; }
  .topo-popover-left .topo-popover-body { right: 0; left: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .topo-strip.topo-active .topo-packet,
  .topo-strip.topo-active .topo-packet-2,
  .topo-strip.topo-active-handshake .topo-packet,
  .topo-strip.topo-active-handshake .topo-packet-2,
  .topo-strip.topo-active-busy .topo-packet,
  .topo-strip.topo-active-busy .topo-packet-2 { animation: none; opacity: 0; }
  .topo-dot-cluster, .topo-dot-beacon, .topo-dot-secret { animation: none !important; }
}

/* ==========================================================================
   Phase 3 — Tab navigation (demo-lab.html)
   WAI-ARIA Authoring Practices 1.2 — "Tabs with Automatic Activation"
   Keyboard: Arrow Left/Right move focus + activate; Home/End jump to
   first/last; Tab leaves the tablist to the panel.
   ========================================================================== */

/* The overall tab widget wrapper */
.demo-tab-widget {
  margin-top: 8px;
}

/* Tab bar */
.demo-tablist {
  display: flex;
  gap: 0;
  border-bottom: 2px solid var(--line);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Individual tab button */
.demo-tab-btn {
  position: relative;
  padding: 12px 24px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
  white-space: nowrap;
}
.demo-tab-btn:hover { color: var(--ink); }
.demo-tab-btn[aria-selected="true"] {
  color: var(--ink);
  border-bottom-color: var(--mark);
}

/* Selected dot for the active tab (the ● in "Try it ●") */
.demo-tab-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--protected);
  box-shadow: 0 0 6px rgba(109, 214, 155, 0.5);
  margin-left: 6px;
  vertical-align: middle;
  animation: breathe 2.4s ease-in-out infinite;
}
/* Only show the dot on the "Try it" tab when it's selected */
.demo-tab-btn:not([aria-selected="true"]) .demo-tab-dot { display: none; }

/* Tab panels */
.demo-tab-panel {
  padding: 20px 0 0;
}
/* Hidden panels: visually hidden AND removed from accessibility tree */
.demo-tab-panel[hidden] { display: none; }

/* Shared code styling for tab intro paragraphs */
.demo-tab-panel .tab-intro code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  color: var(--ink);
  background: var(--bg-raised);
  padding: 1px 5px;
  border-radius: 2px;
}

/* Panel for "Deploy yourself": same as old Option 03 body */
.demo-tab-deploy {
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.demo-tab-deploy .tab-intro {
  font-size: 15px;
  line-height: 1.65;
  color: var(--ink-soft);
  max-width: 74ch;
}
.demo-tab-deploy .tab-intro a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.demo-tab-deploy .tab-intro a:hover { color: var(--mark); }

/* Tab sub-heading (replaces old option-kind label) */
.tab-kind {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 16px;
}

/* Responsive: tabs wrap / scroll on small screens */
@media (max-width: 640px) {
  .demo-tablist {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    gap: 0;
    scrollbar-width: none;
  }
  .demo-tablist::-webkit-scrollbar { display: none; }
  .demo-tab-btn { padding: 10px 18px; font-size: 10.5px; }
  .demo-tab-panel { padding-top: 16px; }
}

/* ==========================================================================
   Phase 3 — Technology page: static 3-pane "step 6" showcase
   ========================================================================== */

.tech-demo-section {
  padding: 100px 0;
  border-bottom: 1px dashed var(--line);
  background: var(--bg-alt);
}

.tech-demo-intro {
  max-width: 1100px;
  margin: 0 auto 48px;
  padding: 0 32px;
}

.tech-demo-intro .section-num-small {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mark);
  margin-bottom: 10px;
}

.tech-demo-intro h2 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 500;
  font-size: clamp(28px, 3.8vw, 46px);
  letter-spacing: -0.025em;
  line-height: 1.06;
  color: var(--ink);
  margin-bottom: 14px;
}
.tech-demo-intro h2 em { color: var(--mark); }

.tech-demo-intro p {
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 68ch;
}

/* Three-pane grid */
.tech-demo-panes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 32px;
}

.tech-demo-pane {
  background: #07080A;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 3px;
  display: flex;
  flex-direction: column;
  min-height: 340px;
  overflow: hidden;
}

.tech-demo-pane-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  background: var(--bg-card);
  border-bottom: 1px dashed rgba(255,255,255,0.18);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  color: var(--ink);
  flex-shrink: 0;
}

.tech-demo-pane-body {
  flex: 1;
  padding: 14px 16px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  line-height: 1.7;
  color: var(--ink);
  overflow: auto;
}

/* ANSI colour simulation — no real escape codes; just semantic spans */
.ansi-dim     { color: var(--ink-muted); }
.ansi-cyan    { color: #5DD8D8; }
.ansi-yellow  { color: var(--mark); }
.ansi-green   { color: var(--protected); }
.ansi-blue    { color: var(--accent); }
.ansi-white   { color: var(--ink); }
.ansi-prompt  { color: var(--protected); }
.ansi-bold    { font-weight: 600; color: var(--ink); }

/* Caption row */
.tech-demo-caption {
  max-width: 1100px;
  margin: 20px auto 0;
  padding: 0 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

.tech-demo-caption-text {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
}
.tech-demo-caption-text strong { color: var(--ink); }

.tech-demo-caption-link {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11.5px;
  letter-spacing: 0.06em;
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px dashed var(--accent);
  transition: color 0.15s, border-color 0.15s;
  white-space: nowrap;
}
.tech-demo-caption-link:hover { color: var(--mark); border-color: var(--mark); text-decoration: none; }

/* Step badge over the pane headers */
.tech-demo-step-badge {
  margin-left: auto;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mark);
  background: transparent;
  border: 1px dashed var(--mark);
  border-radius: 2px;
  padding: 1px 6px;
}

/* Responsive: stack panes vertically on narrow screens */
@media (max-width: 900px) {
  .tech-demo-section { padding: 72px 0; }
  .tech-demo-panes { grid-template-columns: 1fr; gap: 10px; }
  .tech-demo-pane { min-height: 200px; }
  .tech-demo-intro { padding: 0 24px; margin-bottom: 32px; }
  .tech-demo-panes { padding: 0 24px; }
  .tech-demo-caption { padding: 0 24px; }
}

@media (max-width: 540px) {
  .tech-demo-intro { padding: 0 20px; }
  .tech-demo-panes { padding: 0 20px; }
  .tech-demo-caption { padding: 0 20px; }
}

/* ==========================================================================
   Technology §09 — Demo infrastructure section
   ========================================================================== */
.demo-infra-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px 56px;
}
@media (max-width: 780px) {
  .demo-infra-grid { grid-template-columns: 1fr; gap: 32px; }
}

.demo-infra-block {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.demo-infra-block-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-subtle);
}

.demo-infra-body {
  font-size: 14px;
  line-height: 1.7;
  color: var(--ink-soft);
  margin: 0;
}
.demo-infra-body code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  color: var(--ink-muted);
  background: var(--bg-card);
  padding: 1px 4px;
  border-radius: 2px;
}
.demo-infra-body a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px dashed var(--accent);
}
.demo-infra-body a:hover { color: var(--mark); border-color: var(--mark); }

.demo-infra-vm-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.demo-infra-vm {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: 14px 16px;
  background: var(--bg-card);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

.demo-infra-vm-badge {
  flex-shrink: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  letter-spacing: 0.14em;
  padding: 2px 7px;
  border-radius: 2px;
  margin-top: 2px;
  border: 1px solid transparent;
}
.demo-infra-vm-badge-us {
  color: var(--accent);
  background: color-mix(in srgb, transparent 88%, var(--accent) 12%);
  border-color: color-mix(in srgb, transparent 60%, var(--accent) 40%);
}
.demo-infra-vm-badge-eu {
  color: var(--protected);
  background: color-mix(in srgb, transparent 88%, var(--protected) 12%);
  border-color: color-mix(in srgb, transparent 60%, var(--protected) 40%);
}

.demo-infra-vm-detail { flex: 1; min-width: 0; }
.demo-infra-vm-host {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  color: var(--ink);
  margin-bottom: 4px;
}
.demo-infra-vm-spec {
  font-size: 12px;
  color: var(--ink-muted);
  line-height: 1.5;
}
.demo-infra-vm-cost {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--mark);
  margin-top: 6px;
  letter-spacing: 0.04em;
}

.demo-infra-total {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink-muted);
  margin: 0;
}

.demo-infra-caveat {
  font-size: 12.5px;
  line-height: 1.65;
  color: var(--ink-subtle);
  margin: 0;
  padding: 12px 14px;
  background: var(--bg-alt);
  border-left: 2px solid var(--mark);
  border-radius: 0 var(--radius) var(--radius) 0;
}
.demo-infra-caveat strong { color: var(--ink-muted); }
.demo-infra-caveat a { color: var(--ink-muted); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }

/* =============================================================================
   Demo Lab v19 redesign — edge-rail strip stack, verdict chips, controls bar
   Replaces the v5 proof-dashboard. Dead card/proof-grid/sov-strip rules removed.
   ============================================================================= */

/* Shared keyframe animations */
@keyframes glow-amber { 50% { box-shadow: 0 0 8px var(--mark); } }
@keyframes glow-green { 50% { box-shadow: 0 0 8px var(--protected); } }
@keyframes glow-blue  { 50% { box-shadow: 0 0 8px var(--accent); } }
@keyframes pulse      { 50% { opacity: 0.35; } }

/* ========== STRIP STACK — edge rail on left border ========== */
.strip-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 4px;
  position: relative;
}

/* The edge rail — 4px column on left border; packets travel its full height */
.edge-rail {
  position: absolute;
  left: -2px; top: 0; bottom: 0;
  width: 4px;
  pointer-events: none;
  z-index: 5;
}
.edge-rail .epkt {
  position: absolute;
  left: 50%;
  width: 10px; height: 10px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  opacity: 0;
  pointer-events: none;
}
.edge-rail .epkt.amber {
  background: var(--mark);
  box-shadow: 0 0 10px var(--mark), 0 0 20px rgba(229,193,74,0.55), 0 0 36px rgba(229,193,74,0.25);
}
.edge-rail .epkt.green {
  background: var(--protected);
  box-shadow: 0 0 10px var(--protected), 0 0 20px rgba(109,214,155,0.55), 0 0 36px rgba(109,214,155,0.25);
}
/* blue — ambient data-flow packets; smaller + dimmer than amber/green dramatic bursts */
.edge-rail .epkt.blue {
  width: 7px; height: 7px;
  background: var(--accent);
  box-shadow: 0 0 6px var(--accent), 0 0 14px rgba(125,168,255,0.30);
  opacity: 0;
}
/* trailing streak */
.edge-rail .epkt::after {
  content: "";
  position: absolute;
  left: 50%; bottom: 100%;
  width: 2px; height: 38px;
  transform: translateX(-50%);
  border-radius: 1px;
  pointer-events: none;
}
.edge-rail .epkt.amber::after { background: linear-gradient(to top, rgba(229,193,74,0.65), transparent); }
.edge-rail .epkt.green::after { background: linear-gradient(to top, rgba(109,214,155,0.65), transparent); }
.edge-rail .epkt.blue::after  { background: linear-gradient(to top, rgba(125,168,255,0.40), transparent); }
.edge-rail .epkt.flying-down { animation: edge-down 2.6s cubic-bezier(0.4, 0, 0.6, 1) forwards; }
.edge-rail .epkt.flying-up   { animation: edge-up   2.6s cubic-bezier(0.4, 0, 0.6, 1) forwards; }
.edge-rail .epkt.flying-up::after { bottom: auto; top: 100%; }
.edge-rail .epkt.flying-up.amber::after { background: linear-gradient(to bottom, rgba(229,193,74,0.65), transparent); }
.edge-rail .epkt.flying-up.green::after { background: linear-gradient(to bottom, rgba(109,214,155,0.65), transparent); }
.edge-rail .epkt.flying-up.blue::after  { background: linear-gradient(to bottom, rgba(125,168,255,0.40), transparent); }

@keyframes edge-down {
  0%   { top: 0%;   opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
  10%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  90%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { top: 100%; opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
}
@keyframes edge-up {
  0%   { top: 100%; opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
  10%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  90%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { top: 0%;   opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
}

/* Strip border tint during packet flight */
.strip-stack.flowing .strip { border-left-color: rgba(229,193,74,0.45); transition: border-left-color 0.3s; }
.strip-stack.flowing.green .strip { border-left-color: rgba(109,214,155,0.45); }
.strip { transition: border-color 0.5s, background 0.5s; }

/* ========== STRIP BASE ========== */
.strip {
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-top-color: rgba(255,255,255,0.18);
  border-radius: var(--radius);
  font-family: 'IBM Plex Mono', monospace;
  position: relative;
  z-index: 1;
}
.strip.is-cluster {
  background: #07080A;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.03), 0 4px 16px rgba(0,0,0,0.4);
}

/* Verdict states — drive border + chip via data-verdict on .strip */
.strip[data-verdict="exposed"] {
  border-color: rgba(232,153,112,0.45);
  border-top-color: rgba(232,153,112,0.55);
}
.strip[data-verdict="exposed"].is-cluster {
  background: linear-gradient(180deg, rgba(232,153,112,0.04), transparent 30%) #07080A;
}
.strip[data-verdict="active"] {
  border-color: rgba(229,193,74,0.50);
  border-top-color: rgba(229,193,74,0.60);
}
.strip[data-verdict="secured"] {
  border-color: rgba(109,214,155,0.45);
  border-top-color: rgba(109,214,155,0.55);
}
.strip[data-verdict="secured"].is-cluster {
  background: linear-gradient(180deg, rgba(109,214,155,0.04), transparent 30%) #07080A;
}

/* Active-strip focus glow — 2026-04-28 audit: the amber pulse fired
 * on every "active" strip (beacon + secret + cluster all have
 * data-verdict="active" at various steps) producing three concurrent
 * amber halos. The principle is "one primary actor per moment"; the
 * cluster pane is where steps actually run, so only it gets the
 * amber pulse. Beacon + secret strips with data-verdict="active"
 * stay still (chrome-neutral chip; no border halo).
 * Disabled with prefers-reduced-motion. */
.strip[data-verdict="active"].is-cluster {
  animation: strip-active-glow-cluster 2.4s ease-in-out infinite;
}
@keyframes strip-active-glow-cluster {
  0%, 100% { box-shadow: 0 0 0 1px rgba(255,255,255,0.03), 0 4px 16px rgba(0,0,0,0.4); }
  50%      { box-shadow: 0 0 0 1px rgba(229,193,74,0.30), 0 4px 16px rgba(0,0,0,0.4), 0 0 24px rgba(229,193,74,0.20); }
}
@media (prefers-reduced-motion: reduce) {
  .strip[data-verdict="active"].is-cluster { animation: none; }
}

/* ========== STRIP HEAD ========== */
.strip-head {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  padding: 11px 14px;
  background: var(--bg-card);
  border: none;
  border-bottom: 1px dashed rgba(255,255,255,0.18);
  font-size: 11px; letter-spacing: 0.16em;
  width: 100%;
  text-align: left;
  color: var(--ink);
  cursor: pointer;
  user-select: none;
  transition: background 0.15s;
  overflow: hidden;
  min-height: 46px;
}
.strip-head { cursor: default; }
.strip-head.collapsed { border-bottom-color: transparent; }
.strip-head .sdot {
  width: 8px; height: 8px; border-radius: 50%;
  flex-shrink: 0;
  position: relative; z-index: 2;
}
/* 2026-04-28 audit: the three coloured sdots used to glow on
   independent infinite loops, producing three concurrent pulses on
   every render. Reduced to one motion source: the cluster sdot
   pulses (the live actor); beacon + secret sdots are static chips.
   2026-05-01 (cloudtaser-demo#434, #439): sdot colour now tracks
   the strip's data-verdict, so all three boxes are blue in the
   idle "all systems ready" state, then individually transition to
   yellow (active / connecting) and green (secured / established) as
   the demo progresses. The cluster sdot keeps its glow animation
   since it's the primary actor; beacon + secret sdots remain
   static. The base rule below is the IDLE colour (data-verdict="");
   the data-verdict overrides specialise per state. */
.is-cluster .sdot { background: var(--accent); animation: glow-blue 2.4s ease-in-out infinite; }
.is-beacon  .sdot { background: var(--accent); }
.is-secret  .sdot { background: var(--accent); }
.strip[data-verdict="active"]  .sdot { background: var(--mark); }
.strip[data-verdict="secured"] .sdot { background: var(--protected); }
.strip[data-verdict="exposed"] .sdot { background: var(--warn); }
/* Cluster sdot keeps its glow animation under all verdicts — the
   animation channel is independent of colour, and switching it off
   per-verdict would lose the "cluster is the live actor" signal.
   The animation just changes hue to match the dot. */
.strip[data-verdict="active"].is-cluster  .sdot { animation: glow-amber 2.4s ease-in-out infinite; }
.strip[data-verdict="secured"].is-cluster .sdot { animation: glow-green 2.4s ease-in-out infinite; }
.strip-head .sttl { color: var(--ink); position: relative; z-index: 2; flex-shrink: 0; }
.strip-head .sreg {
  color: var(--ink-soft);
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: 2px; padding: 0 5px;
  font-size: 11px; letter-spacing: 0.10em;
  position: relative; z-index: 2;
  flex-shrink: 0;
}

/* Verdict chip — the new dashboard primitive */
.strip-head .verdict {
  position: relative; z-index: 2;
  display: inline-flex; align-items: center; gap: 7px;
  margin-left: auto;
  padding: 4px 10px;
  border-radius: 2px;
  font-size: 11px;
  letter-spacing: 0.04em;
  font-family: 'IBM Plex Mono', monospace;
  border: 1px solid var(--line);
  background: var(--bg-alt);
  color: var(--ink-muted);
  height: 24px;
  transition: color 0.4s, border-color 0.4s, background 0.4s;
  min-width: 0;
  max-width: 360px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.strip-head .verdict .vdot {
  width: 7px; height: 7px; border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.strip[data-verdict="exposed"] .strip-head .verdict {
  color: var(--warn-ink);
  border-color: rgba(232,153,112,0.45);
  background: rgba(232,153,112,0.10);
}
/* "active" was amber-everywhere — a rarely-load-bearing state painted
   in the same color reserved for "step in flight on the cluster."
   2026-04-28 audit: switch active to neutral chrome so amber on the
   cluster pane during a step is visually unique. The cluster strip
   keeps its amber pulse via .strip[data-verdict="active"] above; this
   only changes the chip text + chip background + vdot color, not the
   strip-level glow.  Beacon + secret "active" states are de-facto
   ambient ("mTLS · 1 conn", "no cluster bound") and don't deserve
   the visual weight. */
.strip[data-verdict="active"] .strip-head .verdict {
  color: var(--chip-neutral);
  border-color: var(--chip-neutral-border);
  background: var(--chip-neutral-bg);
}
.strip[data-verdict="active"] .strip-head .verdict .vdot { animation: pulse 1.4s ease-in-out infinite; }
/* Cluster pane is the only place "active" should read as the live
   work-in-flight signal — restore amber there and keep it as the
   single semantic landing point for "step running right now". */
.strip[data-verdict="active"].is-cluster .strip-head .verdict {
  color: var(--mark);
  border-color: rgba(229,193,74,0.45);
  background: rgba(229,193,74,0.10);
}
.strip[data-verdict="secured"] .strip-head .verdict {
  color: var(--protected-ink);
  border-color: rgba(109,214,155,0.45);
  background: rgba(109,214,155,0.10);
}

/* ========== STRIP BODIES — fixed heights ========== */
:root {
  /* Height of all three strip-bodies in idle state — sized to fit a
     single neutral idle-line (font 12.5px × 1.45 line-height ≈ 18px +
     8px×2 vertical padding = ~34px; kept at 72px for visual balance). */
  --idle-strip-h: 72px;
}
.strip-body {
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--ink-soft);
  font-family: 'IBM Plex Mono', monospace;
  overflow: hidden;
  transform-origin: top;
  transform: scaleY(1);
  opacity: 1;
  transition: transform 0.7s cubic-bezier(0.25, 0.1, 0.25, 1),
              opacity   0.5s ease,
              max-height 0.7s cubic-bezier(0.25, 0.1, 0.25, 1);
  max-height: 400px;
}
.strip-body.collapsed {
  transform: scaleY(0);
  opacity: 0;
  max-height: 0;
  pointer-events: none;
}
.strip-body > .body-inner { padding: 14px 16px; }

/* Cluster body: xterm.js SSE terminal, 280px, always open */
.strip-body.cluster-body {
  height: 340px;
  max-height: 340px;
  padding: 0;
}
.strip-body.cluster-body > .body-inner { height: 100%; overflow-y: auto; }
.strip-body.cluster-body .xterm-container {
  width: 100%; height: 100%;
  display: block;
}

/* Cluster idle state — collapse the strip-body to the fixed placeholder
   height so all three strips are visually identical at idle.
   Hide the xterm container; show the in-page idle placeholder. */
.strip-body.cluster-body.cluster-idle {
  height: var(--idle-strip-h);
  max-height: var(--idle-strip-h);
}
.strip-body.cluster-body.cluster-idle .xterm-container { display: none; }
.strip-body.cluster-body.cluster-idle .idle-terminal { display: flex; }
.strip-body.cluster-body .idle-terminal { display: none; }

/* Beacon body: 120px */
.strip-body.beacon-body {
  height: 120px;
  max-height: 120px;
  color: var(--ink-muted);
}
.strip-body.beacon-body > .body-inner { height: 100%; overflow-y: auto; }
.strip-body.beacon-body.collapsed { height: 0; }
.strip-body.beacon-body .xterm-container {
  width: 100%; height: 100%;
  display: block;
}
/* Beacon idle state — collapse to fixed placeholder height. */
.strip-body.beacon-body.beacon-idle {
  height: var(--idle-strip-h);
  max-height: var(--idle-strip-h);
}
.strip-body.beacon-body.beacon-idle .xterm-container { display: none; }
.strip-body.beacon-body.beacon-idle .idle-terminal { display: flex; }
.strip-body.beacon-body .idle-terminal { display: none; }

/* Secret body: 200px */
.strip-body.secret-body {
  height: 200px;
  max-height: 200px;
  position: relative;
}
.strip-body.secret-body > .body-inner { height: 100%; overflow-y: auto; }
.strip-body.secret-body.collapsed { height: 0; }
/* Secret idle state — collapse to fixed placeholder height; hide
   the .body-inner state-machine (panels A/B/C/D/L). */
.strip-body.secret-body.secret-idle {
  height: var(--idle-strip-h);
  max-height: var(--idle-strip-h);
}
.strip-body.secret-body.secret-idle > .body-inner { display: none; }
.strip-body.secret-body.secret-idle .idle-terminal { display: flex; }
.strip-body.secret-body .idle-terminal { display: none; }

/* Terminal text styles */
.pb-prompt { color: var(--protected); user-select: none; }
.pb-cmd    { color: var(--ink); }
.pb-out    { color: var(--ink-soft); }
.pb-mute   { color: var(--ink-subtle); }
.pb-block  { color: var(--warn); }
.pb-good   { color: var(--protected-ink); }
.pb-cursor {
  display: inline-block; width: 8px; height: 14px;
  background: var(--ink); margin-left: 2px;
  animation: cursor 1.2s steps(1) infinite;
  vertical-align: text-bottom;
}
@keyframes cursor { 50% { opacity: 0; } }

/* Flash animation for auto-run pane notification */
.strip-body.flashing {
  animation: pane-flash 0.8s ease-in-out infinite;
}

/* ========== IDLE TERMINAL PLACEHOLDER ==========
   Single neutral idle line shown in each strip-body (cluster / beacon /
   secret) when no session is active. Per-strip visibility is gated by
   `.cluster-idle`, `.beacon-idle`, and `.secret-idle` classes on the
   strip-body — see the cluster-body / beacon-body / secret-body blocks
   above. Default is `display: none`; the idle class flips to `flex`. */
.idle-terminal {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: var(--bg);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  line-height: 1.45;
  padding: 8px 14px;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 0;
  z-index: 1;
}
/* Each strip-body needs `position: relative` so .idle-terminal's
   `position: absolute; inset: 0` fills it. cluster-body / beacon-body
   already had no position; explicit relative on each idle path keeps the
   absolute placeholder bounded to its own strip. */
.strip-body.cluster-body,
.strip-body.beacon-body,
.strip-body.secret-body { position: relative; }
.idle-terminal .idle-line {
  color: #8a8a8a;
  font-style: italic;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
  width: 100%;
}

/* ========== CLIENT-LAGGING STATE (plan-990 P1.7) ==========
   Visual feedback for the SSE `client_lagging` event. When the demo
   backend's broadcastSSE drops a client whose per-channel ring buffer
   filled, the affected pane's xterm is dimmed and a small toast surfaces
   "stream catching up…" for 5s. EventSource auto-reconnects under the
   hood; this state is purely a UI hint so the operator knows why output
   briefly paused. Silently no-op until the cloudtaser-cli broadcastSSE
   half ships (sibling-owned PR).

   The toast sits inside each strip-body / ss-live-panel next to the
   xterm container; all three parents (cluster-body, beacon-body,
   secret-body) already have `position: relative` for the idle-terminal
   overlay, so absolute positioning here is safe. */
/* term.element (xterm.js) is itself the .xterm container element, so the
   class lands on .xterm directly. Match both shapes — self-match for the
   common case, descendant fallback if the class is ever applied to an
   outer wrapper. */
.xterm.xterm-lagging,
.xterm-lagging .xterm {
  opacity: 0.45;
  transition: opacity 200ms ease-in-out;
}
.xterm-lagging-toast {
  position: absolute;
  top: 6px;
  right: 8px;
  z-index: 3;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  color: var(--ink-soft);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0.02em;
  pointer-events: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}
.xterm-lagging-toast[hidden] { display: none; }

/* ========== CONTROLS BAR ========== */
.controls {
  display: flex; gap: 12px; align-items: center;
  padding: 14px 16px;
  background: var(--bg-raised);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  flex-wrap: wrap;
  min-height: 70px;
}
.next-group {
  display: flex; align-items: center;
  gap: 12px;
}
.next-progress {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 0 4px;
  height: 36px;
}
.next-progress .npd {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--line);
  transition: background 0.3s, transform 0.2s;
}
.next-progress .npd.done   { background: var(--protected); }
.next-progress .npd.active { background: var(--mark); animation: pulse 1.6s ease-in-out infinite; }
/* Reverse-drain pulse (#804): brief scale-down on the dot being emptied
   during reverseStepDots(). The existing transition: transform 0.2s on
   .npd handles the tween; .reversing sets the target scale. JS removes
   .reversing after 280ms so the dot springs back to full size. */
.next-progress .npd.reversing { transform: scale(0.55); }

.btn {
  padding: 9px 16px;
  border-radius: 2px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.01em;
  border: 1px solid var(--line);
  background: var(--bg-card);
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s, opacity 0.12s;
  height: 36px;
}
.btn:hover:not(:disabled) { background: var(--bg-raised); border-color: var(--ink-muted); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn.primary {
  background: var(--mark); color: var(--bg); border-color: var(--mark);
}
.btn.primary:hover:not(:disabled) {
  background: #F5D055;
  border-color: #F5D055;
  box-shadow: 0 0 0 3px rgba(229,193,74,0.22), 0 0 18px rgba(229,193,74,0.18);
}
.btn.ghost { background: transparent; color: var(--ink-soft); }
.btn.ghost:hover:not(:disabled) { color: var(--ink); background: var(--bg-card); }
.btn.icon-only {
  width: 36px;
  padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 15px;
  line-height: 1;
}
.btn .arrow { padding-left: 4px; }
/* The controls-bar primary CTA (#demo-next) must never wrap to two lines.
   The button has a fixed height of 36px; if the label wraps the text
   overflows and renders as two rows of text. white-space: nowrap prevents
   this for all inflight labels ("Step N running", "Run all probes →",
   "Release demo", etc.) in both normal and fullscreen contexts. The
   fullscreen override below (body.demo-fullscreen .controls .btn.primary)
   adds max-width + .btn-text ellipsis on top of this base rule to handle
   the longest idle labels ("6. Restart — secret lands in memfd") without
   pushing End+reset off-screen. */
.controls .btn.primary { white-space: nowrap; }

/* Tooltip pattern */
[data-tooltip] { position: relative; }
[data-tooltip]::before, [data-tooltip]::after {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s ease, transform 0.12s ease;
  z-index: 50;
}
[data-tooltip]::before {
  content: attr(data-tooltip);
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translate(-50%, 4px);
  background: var(--bg-card);
  color: var(--ink);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.06em;
  padding: 5px 9px;
  border-radius: 2px;
  border: 1px solid var(--line);
  white-space: nowrap;
  box-shadow: 0 4px 12px rgba(0,0,0,0.4);
}
[data-tooltip]::after {
  content: "";
  bottom: calc(100% + 4px);
  left: 50%;
  transform: translate(-50%, 4px);
  width: 0; height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 5px solid var(--line);
}
[data-tooltip]:hover::before, [data-tooltip]:hover::after,
[data-tooltip]:focus-visible::before, [data-tooltip]:focus-visible::after {
  opacity: 1;
  transform: translate(-50%, 0);
}

.controls .spacer { flex: 1; }
.controls .next-label {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px; letter-spacing: 0.02em;
  color: var(--ink-soft);
  /* No fixed width — flex within .controls. The max-widths on .val
     children cap individual cells so a long step name doesn't push
     End+reset off-screen. */
  max-width: 720px;
  flex-shrink: 1;
  height: 36px;
  white-space: nowrap;
  overflow: hidden;
}
.controls .next-label .key {
  color: var(--ink-muted); font-size: 10.5px;
  letter-spacing: 0.14em; text-transform: uppercase;
  flex-shrink: 0;
}
.controls .next-label .val {
  color: var(--ink); font-style: normal;
  overflow: hidden; text-overflow: ellipsis;
  flex-shrink: 1; min-width: 0;
}
.controls .next-label .val.now      { max-width: 220px; }
.controls .next-label .val.next     { max-width: 220px; }
.controls .next-label .val.watching { min-width: 14px; text-align: right; }
/* watching-group is a single span wrapping the key+val pair; give it flex
   layout so the gap between "watching" label and count matches the sibling
   key/val pairs that rely on the parent .next-label gap. Without this the
   two inner spans are inline-adjacent and render as "watching1" not
   "watching 1". (cloudtaser-io-website fix/text-spacing-button-wrap-microux) */
#watching-group { display: inline-flex; align-items: center; gap: 6px; }
.controls .role-tag {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--mark);
  padding: 6px 12px;
  border-radius: 2px;
  background: rgba(229,193,74,0.10);
  border: 1px solid rgba(229,193,74,0.30);
  height: 36px;
  display: inline-flex; align-items: center;
}
.playback-group {
  display: inline-flex;
  gap: 8px;
  align-items: center;
  flex-shrink: 0;
}
.loop-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px;
  border-radius: 2px;
  border: 1px solid var(--line);
  background: var(--bg-card);
  color: var(--ink-soft);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.06em;
  height: 36px;
  cursor: pointer;
}
.loop-toggle.playing {
  color: var(--mark);
  border-color: rgba(229,193,74,0.40);
  background: rgba(229,193,74,0.08);
}
.loop-toggle .ldot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.loop-toggle.playing .ldot { animation: pulse 1.6s ease-in-out infinite; }

/* P0-3: Fullscreen toggle button -- ghost styling distinct from "End + reset"
   so there is zero risk of misclick. The button uses a muted icon + small
   label, visually subordinate to End+reset but always reachable. Hidden when
   there is no active session (same idle-state rule as the release button).
   The icon swaps between expand and collapse glyphs to reflect state. */
.fullscreen-toggle {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-subtle);
  height: 36px;
  border-color: transparent;
}
.fullscreen-toggle:hover:not(:disabled) {
  color: var(--ink-soft);
  background: var(--bg-card);
  border-color: var(--line);
}
.fullscreen-toggle-icon {
  font-size: 14px;
  line-height: 1;
}
.fullscreen-toggle-label {
  white-space: nowrap;
}
/* Hide when no session active (idle state) -- matches End+reset behavior.
   The .playback-group parent is already hidden at role=available, but this
   rule is a safety net for any future layout refactor. */
.demo-live[data-role="available"] .fullscreen-toggle { display: none; }

/* ========== IDLE-STATE CONTROLS BAR (cloudtaser-demo#434) ==========
   At role=available (no driver) the only useful affordance is the
   "Start the demo" claim button — the step-progress dots, now/next/
   watching key-vals, End+reset button, fingerprint pill, elapsed
   counter are all session-state widgets that read as noise pre-claim.
   Hide them until someone claims so the page reads "three terminals
   + one CTA" instead of "three terminals + a wall of dormant chrome".
   updateRoleBanner mirrors data-role from .controls onto .demo-live
   so the strip-stack siblings can be targeted by the same CSS selector. */
.demo-live[data-role="available"] .controls .next-progress,
.demo-live[data-role="available"] .controls .next-label,
.demo-live[data-role="available"] .controls .playback-group,
.demo-live[data-role="available"] .controls .cluster-id-pill,
.demo-live[data-role="available"] .controls .step-elapsed {
  display: none;
}
/* Centre the lone "Start the demo" button now that the .next-label /
   .next-progress siblings are hidden — without this the button hugs
   the left edge of the controls bar and the right side reads as a
   blank gap. */
.demo-live[data-role="available"] .controls {
  justify-content: center;
}
.demo-live[data-role="available"] .controls .next-group {
  flex: 0 0 auto;
}
.demo-live[data-role="available"] .controls .spacer {
  display: none;
}

/* ========== SECRET STORE STRIP CONTENT ========== */
.ss-cli {
  background: var(--bg);
  border-radius: 2px;
  padding: 12px 14px 16px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  line-height: 1.6;
  color: var(--ink-soft);
  /* Reserve space for the full form lifecycle (input + autofill hint +
     success banner) so showing/hiding those sub-elements does not shift
     the controls bar below the strip stack. Without this, the autofill
     hint appearance + success-pop insertion mutate the .body-inner
     scroll height and cause visible CLS on the Next button text. (#810) */
  min-height: 152px;
}
.ss-cli .ss-prompt { color: var(--protected); }
.ss-cli .ss-cmd { color: var(--ink); }
.ss-cli .ss-mute { color: var(--ink-subtle); }
.ss-form {
  /* Allow the row to wrap when the panel-C width drops below the
     UUID + button + glyph footprint. Without flex-wrap the input
     was forced to shrink (flex:1, min-width:0) and the visible
     UUID got truncated to maybe 60% of its 36-char body — caught
     2026-04-28 in user feedback ("half of the input field is shown").
     With flex-wrap:wrap the input keeps its 38ch min-width and the
     button drops to a second row when there isn't enough space. */
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-top: 8px;
  padding: 8px 10px;
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: 2px;
}
.ss-form .ss-glyph { color: var(--mark); flex-shrink: 0; font-size: 14px; }
.ss-form input {
  /* min-width 38ch keeps the full 36-char UUID + 2-char buffer
     visible WITHOUT horizontal scroll on viewports where panel-C
     is wider than ~36ch + gap + button + glyph. The UUID format
     is fixed-length 8-4-4-4-12 dashes-hex (36 visible chars), so
     reserving 38ch gives a comfortable read with a one-char nudge
     either side. flex:1 1 38ch lets the input grow to fill any
     extra width, and lets the wrapping (set on .ss-form above)
     drop the button onto a second row when the panel is narrow. */
  flex: 1 1 38ch; min-width: 38ch;
  background: transparent; border: none;
  color: var(--ink);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px; letter-spacing: 0.02em;
  padding: 4px 0; outline: none;
}
.ss-form input::placeholder { color: var(--ink-muted); }
.ss-form input.invalid { color: var(--warn); }
.ss-form button {
  background: var(--mark); color: var(--bg);
  border: 1px solid var(--mark); border-radius: 2px;
  padding: 4px 12px; font-size: 12px; font-weight: 500; letter-spacing: 0.04em;
  flex-shrink: 0; cursor: pointer; font-family: inherit;
  transition: filter 0.12s, opacity 0.12s;
}
.ss-form button:hover:not(:disabled) { filter: brightness(1.08); }
.ss-form button:disabled { opacity: 0.4; cursor: not-allowed; }
/* On very narrow viewports (mobile portrait, panel-C squeezed below
   ~440px in demo-fullscreen) the 38ch min-width on the input would
   overflow horizontally. Drop min-width to 0 and let it span the
   full row; the button stretches under it. The threshold is set
   so that on a 1280px desktop the form stays single-row (panel-C
   is ~700-900px there), and only collapses on truly tight columns. */
@media (max-width: 440px) {
  .ss-form input { min-width: 0; flex: 1 1 100%; }
  .ss-form button { width: 100%; }
}
.ss-success {
  margin-top: 8px;
  padding: 8px 10px;
  background: rgba(109,214,155,0.08);
  border: 1px solid rgba(109,214,155,0.30);
  border-left: 2px solid var(--protected);
  border-radius: 2px;
  color: var(--protected-ink);
  font-size: 11.5px; letter-spacing: 0.04em;
  display: none;
}
.ss-success.show { display: block; animation: success-pop 0.4s ease-out; }
@keyframes success-pop {
  from { transform: scale(0.96); opacity: 0; }
  to   { transform: scale(1);    opacity: 1; }
}

/* secret store idle panel */
.ss-idle-panel, .ss-anticipate-panel, .ss-done-panel {
  user-select: none;
  padding: 14px 16px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  color: var(--ink-muted);
}
.ss-idle-panel .ss-host { color: var(--ink-soft); font-size: 12px; }
.ss-idle-panel .ss-hint { color: var(--ink-subtle); font-size: 11.5px; margin-top: 4px; }
.ss-anticipate-fp { color: var(--mark); font-size: 13px; word-break: break-all; }
.ss-anticipate-hint { color: var(--ink-subtle); font-size: 11.5px; margin-top: 4px; }
/* `:not([hidden])` keeps the HTML `hidden` attribute working — without
   it, `display: flex` overrides the user-agent `display: none` and the
   "Cluster registered" panel shows even at steps 0-3 before any
   registration has happened. Same bug class as `.probe-summary` (PR #149). */
.ss-done-panel:not([hidden]) { display: flex; align-items: center; gap: 12px; }
.ss-done-ok { color: var(--protected); font-size: 18px; flex-shrink: 0; }
.ss-done-text strong { color: var(--protected-ink); }
.ss-done-meta { color: var(--ink-muted); font-size: 11px; display: block; margin-top: 2px; }
.ss-rpc-output { font-size: 11px; color: var(--ink-subtle); margin-top: 6px; white-space: pre-wrap; }
.ss-watcher-note { color: var(--mark); font-size: 11.5px; margin-top: 4px; display: none; }
.ss-watcher-note.show { display: block; }

/* Panel L — post-register live SSE terminal (vault audit + bridge journal).
   Fills the secret-body so viewers see live activity instead of the static
   "Cluster registered" confirmation once the demo advances past step 4. */
.ss-live-panel {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}
.ss-live-panel .xterm-container {
  width: 100%;
  height: 100%;
  display: block;
}
.ss-live-panel[hidden] { display: none; }

/* Panel S — collapsed success banner (#secret-success-banner).
   Hidden by default; shown via CSS attribute selector once the host
   completes registration and JS sets data-step3-completed="true"
   on .demo-live. Stays visible for steps 4-7 so the CISO eye is not
   drawn back to dead input UI during later demo beats. The register
   form (panel-C / .ss-cli) is simultaneously suppressed so the banner
   replaces it cleanly.
   Visibility is driven entirely by the data attribute; the `hidden`
   HTML attribute is never toggled on this element. */
.ss-success-banner {
  display: none;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--protected-ink);
  background: rgba(109, 214, 155, 0.06);
  border-left: 2px solid var(--protected);
  user-select: none;
}
.demo-live[data-step3-completed="true"] .ss-success-banner {
  display: flex;
  animation: success-pop 0.35s ease-out;
}
/* Suppress the register form (panel-C) when step 3 is complete.
   renderSecretWidget() already hides it via the `hidden` attribute,
   but this CSS provides defence-in-depth against future refactors. */
.demo-live[data-step3-completed="true"] .ss-cli {
  display: none !important;
}
.ss-success-banner-ok {
  color: var(--protected);
  font-size: 14px;
  flex-shrink: 0;
  line-height: 1;
}
.ss-success-banner-text {
  color: var(--protected-ink);
  font-weight: 500;
  letter-spacing: 0.02em;
}
.ss-success-banner-fp {
  color: var(--ink-muted);
  font-size: 11px;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* fingerprint error message */
.fingerprint-error {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px; color: var(--warn);
  padding: 4px 10px 0;
}
.fingerprint-error[hidden] { display: none; }

/* Auto-fill hint — shown below the register form when the input was
   populated from state.fingerprint rather than typed by the user.
   Uses --ink-subtle to keep it clearly secondary to the value itself.
   margin-bottom: 14px gives breathing room before the success output
   area below — closes cloudtaser-demo#802. */
.fingerprint-autofill-hint {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--ink-subtle);
  padding: 4px 10px 0;
  margin-bottom: 14px;
  letter-spacing: 0.03em;
}
.fingerprint-autofill-hint[hidden] { display: none; }

/* ========== BEACON STRIP CONTENT ========== */
.bb-time { color: var(--ink-subtle); }
.bb-conn { color: var(--mark); }
.bb-tag  { color: var(--protected-ink); }
.bb-line { display: block; font-size: 11.5px; line-height: 1.7; }

/* ========== RESPONSIVE ========== */
@media (prefers-reduced-motion: reduce) {
  .edge-rail .epkt,
  .edge-rail .epkt.flying-down,
  .edge-rail .epkt.flying-up { animation: none; opacity: 0; }
  .strip-body { transition: none; }
  .strip-head .verdict { transition: none; }
}
@media (max-width: 840px) {
  .strip-stack { min-height: auto; }
  .strip-body.cluster-body { height: 240px; max-height: 240px; }
  .strip-body.beacon-body, .strip-body.secret-body { height: auto; max-height: 200px; }
  .strip-body.beacon-body.collapsed, .strip-body.secret-body.collapsed { height: 0; max-height: 0; }
  .strip-head .verdict { max-width: 220px; }
}
@media (max-width: 720px) {
  .strip-head { flex-wrap: wrap; gap: 6px; min-height: auto; padding-bottom: 8px; }
  .strip-head .verdict { margin-left: 0; flex-basis: 100%; max-width: none; }
  .controls { flex-direction: column; align-items: stretch; }
  .controls .next-label { flex-basis: 100%; order: 99; width: auto; }
  .next-group { flex-wrap: wrap; }
}
.demo-infra-caveat a:hover { color: var(--ink-soft); }

/* =============================================================================
   Demo Lab — full-tab mode
   When state.driver_session_id is non-empty (any active session — host OR
   watcher), <body> gets the .demo-fullscreen class and the .demo-live card
   pins to the viewport. Layout becomes a 2-column grid: strips stacked on
   the left, probe checklist scrolling on the right, controls bar spanning
   both at the bottom. Cluster history is preserved (xterm.js scrollback
   buffer is intact on resize). On resize, vh/vw + minmax tracks reflow
   automatically; the FitAddon re-fits terminal columns and rows to the
   new container dimensions.
   Reverts cleanly when state.driver_session_id becomes empty (release/reset).
   ============================================================================= */

/* When fullscreen, hide all page chrome other than the demo card so the
   browser tab is wholly devoted to the demo. The demo card itself escapes
   .container constraints via position:fixed below.
   Uses :has() to scope only to /demo-lab.html where .demo-live exists. */
body.demo-fullscreen {
  overflow: hidden; /* prevent body scroll behind the fixed card */
}
body.demo-fullscreen .nav,
body.demo-fullscreen .hero,
body.demo-fullscreen #demo-lab-tabs > .container > h2,
body.demo-fullscreen .demo-tab-list,
body.demo-fullscreen #demo-lab-tabs .tab-kind,
body.demo-fullscreen #demo-lab-tabs .tab-intro,
body.demo-fullscreen .architecture-sketch,
body.demo-fullscreen .demo-infra-caveat,
body.demo-fullscreen .post-cta-recap,
body.demo-fullscreen footer,
body.demo-fullscreen .footer {
  display: none !important;
}

/* The card itself — fills the viewport */
body.demo-fullscreen .demo-live {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  margin: 0;
  padding: 10px;
  border: none;
  border-radius: 0;
  box-shadow: none;
  background: var(--bg);
  z-index: 1000;
  /* Grid: left column flex (capped at 1600px to prevent ultrawide waste),
     right column "what's happening" / probes panel (min 280px so callout
     text doesn't squeeze-wrap at narrow two-column widths).
     The right column spans BOTH rows
     (strips + controls bar) so its full height equals the left column's
     strip stack PLUS the controls bar — the user explicitly asked for
     this in 2026-04-28 feedback ("step description box right, box4
     should be full height as 3 boxes and next bar combined").
     The CI-bot banner row was dropped 2026-04-28 (banner removed). */
  display: grid;
  grid-template-columns: min(80vw, 1600px) minmax(280px, 1fr);
  grid-template-rows: minmax(0, 1fr) auto;
  grid-template-areas:
    "strips   probes"
    "controls probes";
  gap: 8px 0;
}

/* Strip stack lives in the left column and consumes all available height */
body.demo-fullscreen .strip-stack {
  grid-area: strips;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* Distribute vertical space between cluster (largest), beacon (small),
     secret (medium). flex-grow ratios mirror the wireframe. */
}

/* Probe checklist column — scrolls independently on the right.

   Post 2026-04-28 "double rectangle" refactor: the outer .demo-probe
   container's visual frame (border, radius, bg, gap=0, overflow:hidden)
   is set on the BASE rule above so embedded + fullscreen layouts share
   the same single-container look. This fullscreen-specific rule only
   handles the grid placement and margin reset. */
body.demo-fullscreen .demo-probe {
  grid-area: probes;
  min-height: 0;
  margin-top: 0 !important;
}
body.demo-fullscreen .demo-probe .probe-summary:not([hidden]) {
  flex-shrink: 0;
  /* margin is set by the base .probe-summary rule (8px 12px 0). */
}
body.demo-fullscreen .demo-probe .probe-checklist:not([hidden]) {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 8px 12px 10px;
  /* 200ms fade-in to match the description<->probes mode swap. */
  animation: right-column-fade-in 200ms ease;
}

/* Controls live in the left column only (under the strip stack), matching
   the strip-stack width. The probes column sits beside them, full-height. */
body.demo-fullscreen .controls {
  grid-area: controls;
  flex-shrink: 0;
  /* Force one-row layout — no wrap, even when the Next button label is
     long ("6. Restart — secret lands in memfd" / "7. Prove it (15
     probes)"). Truncation rules below cap the button width so End+reset
     never gets pushed to a second row. */
  flex-wrap: nowrap;
}
/* Primary CTA sizing in fullscreen — button hugs its text content.
   Previous fixed min-width: 300px / max-width: 380px kept the button
   wider than short labels ("Step 3 running", "Resetting") needed.
   Switched to width: fit-content so the button is exactly as wide as
   its longest visible label at any moment + standard padding.
   max-width: 380px retained as an upper safety cap.
   The .btn-text span retains overflow: hidden as a safety net only. */
body.demo-fullscreen .controls .btn.primary {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  width: fit-content;
  /* min-width prevents labels like "5. Annotate for injection" and
     "3. Register at secret store" from being flex-crushed below a
     readable width when the controls bar is space-constrained.
     210px comfortably fits the longest step labels at 13px mono
     (≈165px text + 32px padding) without forcing a fixed wide box
     for short inflight labels ("Step 3 running", "Resetting").
     Closes #349 — steps 3/5/6 label truncation. */
  min-width: 210px;
  max-width: 380px;
}
body.demo-fullscreen .controls .btn.primary .btn-text {
  /* Safety net: ellipsis still set in case a future label exceeds the
     380px cap. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 1 1 auto;
}
body.demo-fullscreen .controls .btn.primary .arrow {
  flex-shrink: 0;
}
/* Also keep next-group itself from forcing a wrap if the right-side
   labels run long — the spacer between next-group and the End+reset
   button consumes any remaining width. */
body.demo-fullscreen .controls .next-group {
  flex-shrink: 1;
  min-width: 0;
}

/* CI-bot banner CSS removed 2026-04-28 — element no longer in the
   DOM. drive.py check_cibot_banner_removed asserts both id + class
   are gone. The grid track 'cibot' is also dropped below in the
   .demo-fullscreen .demo-live grid template. */

/* History accordion is gated by [hidden] attribute until first SSE event;
   fullscreen does not override the reveal so the host/watcher sees events
   after claiming. No additional rule needed here. */

/* Strip flex distribution — cluster grows largest (driver pane), beacon
   smallest, secret middle. Override fixed heights from the embedded layout. */
body.demo-fullscreen .strip {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
body.demo-fullscreen .strip.is-cluster {
  flex: 5 1 0;
}
body.demo-fullscreen .strip.is-beacon {
  flex: 2 1 0;
}
body.demo-fullscreen .strip.is-secret {
  flex: 3 1 0;
}
body.demo-fullscreen .strip-head {
  flex-shrink: 0;
}
/* Strip bodies grow to fill remaining space; xterm container inside is 100% height. */
body.demo-fullscreen .strip-body {
  flex: 1 1 auto;
  min-height: 0;
  height: auto !important;
  max-height: none !important;
}
body.demo-fullscreen .strip-body.cluster-body,
body.demo-fullscreen .strip-body.beacon-body,
body.demo-fullscreen .strip-body.secret-body {
  height: auto !important;
  max-height: none !important;
}
/* Cluster idle overlay only shows when no session — by definition not in
   fullscreen mode. But guard anyway. */
body.demo-fullscreen .strip-body.cluster-body.cluster-idle {
  height: 0 !important;
  max-height: 0 !important;
}
body.demo-fullscreen .strip-body .xterm-container {
  width: 100%;
  height: 100%;
  display: block;
}
/* Secret body's nested .body-inner needs the same flex treatment so the
   live vault SSE terminal (panel L) actually fills the strip in fullscreen. */
body.demo-fullscreen .secret-body > .body-inner {
  height: 100%;
  display: flex;
  flex-direction: column;
}
body.demo-fullscreen .secret-body > .body-inner > div:not([hidden]) {
  flex: 1 1 auto;
  min-height: 0;
}
body.demo-fullscreen #secret-panel-live:not([hidden]) {
  display: flex;
}
body.demo-fullscreen #secret-panel-live .xterm-container {
  flex: 1 1 auto;
  min-height: 0;
}

/* Mobile / small viewports: collapse to a single-column stacked layout
   with probes below the strips so the strip widths aren't crushed.
   CI-bot banner row stays at the very top; controls drop to the bottom. */
@media (max-width: 880px) {
  body.demo-fullscreen .demo-live {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: min(60vh, 340px) auto auto;
    grid-template-areas:
      "strips"
      "probes"
      "controls";
  }
  body.demo-fullscreen .demo-probe .probe-checklist:not([hidden]) {
    max-height: 260px;
  }
}

/* Accessibility: when prefers-reduced-motion, no transition on the layout
   switch — instant. */
@media (prefers-reduced-motion: reduce) {
  body.demo-fullscreen .demo-live { transition: none; }
}

/* CI-bot banner CSS removed 2026-04-28 — banner element no longer in
   the DOM. drive.py check_cibot_banner_removed asserts the absence. */

/* Right-column mode-swap fade — used by the probes panel
   (probe-checklist) when it appears. 200ms matches the user
   feedback. prefers-reduced-motion respected via media query below. */
@keyframes right-column-fade-in {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  body.demo-fullscreen .demo-probe .probe-checklist:not([hidden]) {
    animation: none !important;
  }
}

/* =============================================================================
   Demo CTA — appears only after step 7 probes complete.

   2026-04-28 design audit: the demo persuades technical buyers when
   the design is restrained.  A modal / sticky / popup CTA reads as
   marketing-coded and trips the trained-skepticism radar.  Instead:
   one muted aside in the right column, gated by :has() so it appears
   ONLY when the probe summary is already visible (i.e. probes have
   run + verdicts are settled).  No JS hookup needed.  Two channels:
   email as the calmer default, calendar as the explicit second
   option.  Both rel=noopener — the demo tab keeps its session if the
   visitor opens either link.

   Accessibility: <aside> with aria-label, semantic <a> with mailto:
   for the email link, AAA contrast on the link text.
   ============================================================================= */
.demo-cta {
  display: none;
}
/* Show only when the probe-summary is unhidden (post run-all).
   `:has` is supported in all browsers we target (Chrome 105+,
   Firefox 121+, Safari 15.4+); demo-lab is a modern-browser-only
   surface (depends on grid + container queries already), so this
   feature gate is fine. */
.demo-probe:has(#probe-summary:not([hidden])) .demo-cta {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 14px;
  margin: 8px 12px 12px;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: rgba(166, 161, 153, 0.04);
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  /* 200ms fade-in matches the right-column mode-swap timing. */
  animation: demo-cta-fade 380ms ease;
  flex-shrink: 0;
}
@keyframes demo-cta-fade {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .demo-probe:has(#probe-summary:not([hidden])) .demo-cta { animation: none; }
}
.demo-cta-intro {
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-soft);
  margin: 0 0 4px;
  letter-spacing: 0.01em;
}
.demo-cta-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12.5px;
  letter-spacing: 0.02em;
  /* AAA-contrast peer-to-peer link tone — not a "button", not a
     marketing CTA. The arrow is the only visual nod that this is
     interactive. Underline-on-hover keeps the chrome calm at rest. */
  color: var(--ink);
  text-decoration: none;
  padding: 2px 0;
}
.demo-cta-link:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--mark);
}
.demo-cta-secondary {
  color: var(--ink-soft);
  font-size: 12px;
}
.demo-cta-arrow {
  color: var(--ink-muted);
  transition: transform 0.15s ease;
}
.demo-cta-link:hover .demo-cta-arrow { transform: translateX(2px); color: var(--mark); }

/* ------------------------------------------------------------------ */
/* demo-lab UX polish — 2026-04-30                                     */
/* ------------------------------------------------------------------ */

/* Item 5: Cluster fingerprint pill in controls bar */
.cluster-id-pill {
  display: inline-flex; align-items: center;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.04em;
  color: var(--ink-muted);
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 0 8px;
  height: 24px;
  white-space: nowrap;
  cursor: default;
  flex-shrink: 0;
  /* Hidden until JS populates it from state.fingerprint */
  transition: opacity 0.2s;
}
.cluster-id-pill[hidden] { display: none !important; }

/* Item 6: Step elapsed counter next to .next-label */
.step-elapsed {
  display: inline-flex; align-items: center;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.04em;
  color: var(--ink-muted);
  flex-shrink: 0;
  /* Hidden when step is not inflight */
  transition: opacity 0.2s;
}
.step-elapsed[hidden] { display: none !important; }

/* ==========================================================================
   Nested architecture-sketch (cloudtaser-demo#401). Dev-internal
   subsections (orchestrator state machine, probe whitelist, RBAC,
   GKE rationale) collapsed by default to keep the prospect-reading
   path short. Inherits the outer .architecture-sketch frame so it
   reads as a one-step-deeper drill-down, not a competing block.
   ========================================================================== */
.architecture-sketch-nested {
  margin: 18px 0 8px;
  background: var(--bg-card, var(--bg-raised));
}
.architecture-sketch-nested > summary {
  font-size: 11.5px;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
}
.architecture-sketch-nested > summary:hover { color: var(--mark); }
.architecture-sketch-nested .sketch-body {
  padding: 8px 22px 22px;
}

/* Post-CTA recap (noise audit #246) — brief anchor text between the CTA
   panel and the engineering-deep-dive accordion. Muted so it does not
   compete with the CTA but readable for scanners who want confirmation. */
.post-cta-recap {
  margin: 16px 0 24px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink-soft);
}
.post-cta-recap a { color: var(--mark); text-decoration: underline; }
.post-cta-recap a:hover { color: var(--ink); }

/* ============================================================================
   Focus target (cloudtaser-demo#445, supersedes #436)
   ----------------------------------------------------------------------------
   A cyan box-shadow pulse on the per-step focal element. Replaces the
   earlier floating-emoji design (#234) — see demo-lab.html `STEP_FOCUS`
   block for the rationale. The glow is rendered as `box-shadow` on the
   element itself, so it survives any layout reflow (no JS reposition
   needed) and traces the host element's corners via `border-radius:
   inherit`. z-index is NOT needed — box-shadow extends outward without
   establishing a stacking context for the host.

   Cyan (#4DD9E0) is chosen deliberately: yellow would clash with the dot
   state-machine's "active" colour (#E5C14A); cyan reads as "look here"
   without competing with the strip-verdict palette.
   ============================================================================ */
.focus-target {
  border-radius: inherit;
  animation: focus-pulse 2s ease-in-out infinite;
}

/* Selector-specificity overrides — without these, more-specific
   per-element animations on the same `box-shadow` property win the
   cascade and the cyan focus pulse never paints. Each rule below
   matches the host element's own selector PLUS .focus-target so it
   sits one notch higher in specificity than the host's default
   animation rule.

   1. #demo-next in claim state has `.controls[data-role="available"]
      .btn.primary:not(:disabled) { animation: claim-pulse }` — the
      claim outer pulse. When focus-target lands on the same button,
      cyan wins.
   2. The cluster strip with data-verdict="active" runs
      `strip-active-glow-cluster` (amber). When the cluster strip is
      ALSO the focal target, cyan wins. */
.controls[data-role="available"] .btn.primary.focus-target,
.controls[data-role="available"] .btn.primary:not(:disabled).focus-target {
  animation: focus-pulse 2s ease-in-out infinite;
}
.strip[data-verdict="active"].is-cluster.focus-target {
  animation: focus-pulse 2s ease-in-out infinite;
}

@keyframes focus-pulse {
  0%, 100% {
    box-shadow:
      0 0 0 2px rgba(77, 217, 224, 0.40),
      0 0 16px 4px rgba(77, 217, 224, 0.25);
  }
  50% {
    box-shadow:
      0 0 0 2px rgba(77, 217, 224, 0.85),
      0 0 28px 8px rgba(77, 217, 224, 0.55);
  }
}

@media (prefers-reduced-motion: reduce) {
  .focus-target {
    animation: none;
    box-shadow: 0 0 0 2px rgba(77, 217, 224, 0.7);
  }
}

/* ============================================================================
   Phase narration callouts — typed terminal-style text inside box4 (the
   right column / .demo-probe), positioned at the same Y centre as the
   matching strip in the LEFT column.

   Architecture (post-#306, supersedes #537/#292):
   - .callout-column   : absolute, occupies the right-column vertical
                         region matching the LEFT-column .strip-stack.
                         top and height are CSS constants (not JS-computed):
                         top: 0; bottom: 78px (controls-bar trim).
                         pointer-events:none so the right column stays
                         interactive. Hidden by setRightColumnMode when
                         probes panel takes over (#282 contract intact).
                         ALSO hidden when the visitor explicitly expands
                         the "View manifest" toggle — deliberate
                         inspection mode where the YAML body fills the
                         right column and callout overlay would re-create
                         the bug.jpg visual collision (#292 fix preserved
                         for that branch only).
   - .callout-subzone  : absolute within .callout-column. top is a CSS
                         percentage (calc) derived from flex 5:2:3 strip
                         ratios + 6px gap. transform: translateY(-50%)
                         centres the box on that line regardless of text
                         length. No JS required — see #callout-subzone-*
                         ID rules below.
   - .callout-box      : the visible text + cursor container. Hidden by
                         default (opacity:0). JS drives opacity transitions
                         to appear / fade out.
   - .callout-text     : the typed text (plain var(--ink) — max contrast,
                         no accent colour).
   - .callout-cursor   : blinking pipe `|` during typing; hidden on done.
   ============================================================================ */

/* Callout column: absolute overlay on the right column (.demo-probe).
   Starts at top:0 (rc-header removed in #781); bottom:78px trims
   the controls-bar + gap region so the column height matches the
   strip-stack. The JS-driven syncCalloutLayoutToStrips() that
   previously computed top/height in pixels has been removed
   (PR #306): horizontal YAML scroll is not being pursued, so the box
   dimensions are stable and can be expressed as CSS constants. */
.callout-column {
  position: absolute;
  /* #781: rc-header removed, callout-column now starts from top.
     Bottom: anchored so the column ends at the strip-stack bottom. */
  top: 0;
  left: 0;
  right: 0;
  bottom: 78px;
  pointer-events: none;
  z-index: 5;
  overflow: visible; /* allow .info-bit honesty footers to extend past the column bottom anchor (PR #305 contract) */
}
.callout-column[hidden] { display: none; }
/* Show callouts only when a demo session is active (inside demo-fullscreen).
   Outside the live demo the column is empty and just adds vertical noise. */
body:not(.demo-fullscreen) .callout-column { display: none; }
/* #537: while a "View manifest" toggle is expanded the manifest body
   crosses into the strip-stack-equivalent Y region; suppress the
   absolute callout overlay for the duration so the user reading raw
   YAML doesn't get a translucent dark callout box rendered on top of
   it. body class is set/cleared by markCalloutsForManifestExpanded
   from the toggle click handler. */
body.demo-manifest-expanded .callout-column { display: none; }

/* Sub-zone: absolute within .callout-column. Fixed percentage `top`
   values align each zone to the vertical centre of its matching strip.

   Derivation (let S = strip-stack height, C = callout-column height):

   Post #781 the callout-column starts at top:0 inside .demo-probe,
   which shares the same grid-row top as .strip-stack. The 1px border
   on .demo-probe offsets the containing block by 1px; bottom:78px
   trims the controls-bar region so C ≈ S.

   Strip flex distribution is 5:2:3 (cluster:beacon:secret) with a 6px
   gap between strips (fullscreen). Strip centres relative to strip-
   stack top (= callout-column top ± 1px):
     cluster_centre = (S - 12) / 4           ≈ 0.25 S
     beacon_centre  = (6/10)(S - 12) + 6     ≈ 0.60 S
     secret_centre  = (17/20)(S - 12) + 12   ≈ 0.85 S

   As a percentage of C (≈ S):
     cluster ≈ 24.5%   (verified: zone.centreY = strip.centreY ± 12px)
     beacon  ≈ 59.5%
     secret  ≈ 85%

   translateY(-50%) centres the box on the computed line regardless of
   text height. */
.callout-subzone {
  position: absolute;
  left: 0;
  right: 0;
  transform: translateY(-50%);
}
/* #325: percentages corrected — previous values (17/52/78) were
   derived from the stale H = S - 46 formula left over from the
   rc-header era. With top:0 the column spans the full strip-stack
   height and the strip-centre ratios (5:2:3 flex) map directly. */
#callout-subzone-cluster { top: 24.5%; }
#callout-subzone-beacon  { top: 59.5%; }
#callout-subzone-secret  { top: 85%; }

/* Visible callout box — left-accent bar inside box4, reads like a
   teleprompter cue aligned to the active pane strip.
   #285: at idle (no .callout-visible) the box renders fully transparent
   so released sessions show neutral right-column boxes, not lingering
   green-tinted frames. The visual frame (background + borders) is
   conditional on .callout-visible — opacity alone left the dark bg
   visible at idle in some viewports. */
.callout-box {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 8px 12px 7px 14px;
  background: transparent;
  border-left: 2px solid transparent;
  border-bottom: 1px solid transparent;
  margin: 0 8px;
  border-radius: 0 2px 2px 0;
  opacity: 0;
  transition: opacity 0.3s ease-out, background 0.3s ease-out, border-color 0.3s ease-out;
  will-change: opacity;
  /* text rendering: crisp on dark bg */
  -webkit-font-smoothing: antialiased;
  min-height: 44px;
  /* Width fills the available column space; max-width previously 340px
     was a guard against horizontal YAML scroll that is no longer planned.
     Now cemented at a comfortable prose width relative to the column. */
  max-width: 92%;
}
.callout-box.callout-visible {
  opacity: 1;
  background: rgba(26, 22, 14, 0.88);
  border-left-color: rgba(229, 193, 74, 0.55);
  border-bottom-color: rgba(229, 193, 74, 0.12);
}

/* Typed text — sans-serif narration; distinct from cold monospace terminal output */
.callout-text {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--ink);           /* #EFE9D9 — max contrast on dark bg */
  letter-spacing: 0;
  white-space: pre-wrap;
  /* overflow-wrap: break-word breaks long words at any character boundary,
     which produces awkward mid-word splits on hyphenated terms like
     "EU-jurisdiction". Use word-break: normal to preserve word boundaries
     and let hyphens: auto suggest breaks only when no natural break point
     exists. overflow-wrap: break-word is kept as the last-resort fallback
     for truly unbreakable URLs or identifiers. */
  word-break: normal;
  overflow-wrap: break-word;
  hyphens: auto;
}

/* Blinking cursor pipe — shown while typing, hidden after */
.callout-cursor {
  display: inline-block;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
  margin-left: 1px;
  animation: callout-blink 0.9s step-start infinite;
  line-height: 1.5;
}
.callout-cursor.callout-cursor-hidden {
  visibility: hidden;
  animation: none;
}

@keyframes callout-blink {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0; }
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
  .callout-cursor {
    animation: none;
    opacity: 1;
  }
  .callout-box {
    transition: none;
  }
}

/* ============================================================================
   Callout content wrap + info-bits (#295)

   .callout-content-wrap wraps the .callout-box and any appended .info-bit
   inside each .callout-subzone. Uses column flex so the info-bit flows
   directly below the callout-box without displacing the subzone's
   translateY(-50%) anchor — the subzone height grows downward only,
   never upward, so the strip-Y-centre contract from #537 is preserved.

   Info-bit area constraint (spec §1): each shape's rendered height
   is kept at or below the typical callout-box height (~40px) by
   fixed or max-height sizing on the shape wrappers.
   ============================================================================ */

.callout-content-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* ── Base info-bit ── */
.info-bit {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 10px;
  color: var(--ink-soft);
  background: rgba(14, 16, 20, 0.60);
  border-left: 2px solid rgba(239, 233, 217, 0.12);
  border-bottom: 1px solid rgba(239, 233, 217, 0.05);
  margin: 0 8px;
  padding: 5px 10px;
  border-radius: 0 2px 2px 0;
  pointer-events: none;
  -webkit-font-smoothing: antialiased;
}

/* ── Metric ── */
.info-metric {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 5px 12px;
  max-height: 48px;
}
.info-metric-value {
  font-size: 22px;
  font-weight: 600;
  line-height: 1;
  color: var(--ink);
  letter-spacing: -0.02em;
}
.info-metric-label {
  font-size: 9px;
  font-weight: 500;
  color: var(--ink-soft);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.info-metric-sub {
  font-size: 9px;
  color: var(--ink-muted);
  letter-spacing: 0.02em;
}

/* ── Icon row ── */
.info-icon-row {
  display: flex;
  flex-direction: row;
  gap: 14px;
  list-style: none;
  padding: 5px 12px;
  max-height: 52px;
}
.info-icon-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  color: var(--ink-soft);
}
.info-icon-item svg {
  flex-shrink: 0;
}
.info-icon-item b {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-weight: 500;
  font-size: 8px;
  letter-spacing: 0.04em;
  white-space: nowrap;
  color: var(--ink-muted);
}

/* ── Mini list ── */
.info-mini-list ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.info-mini-list li {
  font-size: 10px;
  color: var(--ink-soft);
  line-height: 1.4;
  white-space: pre;
}
.info-list-mark {
  font-size: 10px;
  color: var(--ink-muted);
}

/* ── Code snippet ── */
.info-code pre {
  margin: 0;
  padding: 0;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 9.5px;
  line-height: 1.5;
  color: var(--ink-soft);
  white-space: pre;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* ── Mini diagram ── */
.info-mini-diagram {
  display: flex;
  align-items: center;
  padding: 4px 12px;
}
.info-mini-diagram svg {
  display: block;
  max-width: 100%;
  color: var(--ink-muted);
}

/* Visibility: inherited from parent .callout-column hidden states.
   No additional rules needed — info-bits live inside .callout-content-wrap
   inside .callout-subzone inside .callout-column. When .callout-column
   is hidden (probes mode, manifest-expanded), info-bits are hidden for free. */
