/* =========================================================
   SWE Learning Module — Style Guide (Light Mode)
   ========================================================= */

:root {
  /* Background */
  --bg: #F0F5F5;
  --bg-mid: #F5FAFA;
  --card: #FFFFFF;
  --card-hover: #E8F0EF;

  /* Text */
  --text-1: #0F1F1E;
  --text-2: #505252;
  --text-3: #334E4D;

  /* Teal accent */
  --accent-bg: #E0F2F0;
  --accent-light: #7BBDB8;
  --accent: #0C7A70;
  --accent-dim: #085C54;

  /* Border */
  --border: #C8D8D7;
  --border-hover: #A0BFBD;

  /* Other accents (tbd) */
  --accent-amber: #9B6400;
  --accent-amber-bg: #F5E6C8;
  --accent-violet: #5B4AAF;
  --accent-violet-bg: #E5E0F7;
  --accent-blue: #1D6FA4;
  --accent-blue-bg: #D6E8F5;
  --accent-rose: #A3344A;
  --accent-rose-bg: #F5D5DC;



  /* Fonts */
  --font-heading: 'Source Serif 4', Georgia, serif;
  --font-body: 'Inter', sans-serif;
  --font-equation: 'Cambria Math', Cambria, serif;

  /* Type scale */
  --fs-h1: 40px;
  --fs-h2: 24px;
  --fs-body: 15px;
  --fs-body-sm: 12px;
  --fs-label: 10px;
  --fs-caption: 10px;
  --fs-nav-logo: 12px;
  --fs-button: 12px;
  --fs-formula: 18px;

  /* Layout */
  --nav-height: 64px;
  --drawer-width: 200px;
  --ref-panel-width: 280px;
  --content-max-width: 760px;

  /* Misc */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --transition: 0.2s ease;
}

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

html {
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--text-2);
  background-color: var(--bg);
  line-height: 1.6;
}

ul,
ol {
  list-style: none;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font-family: inherit;
  cursor: pointer;
  background: none;
  border: none;
}

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

/* ---------- Typography ---------- */
h1,
h2,
h3 {
  font-family: var(--font-heading);
  font-weight: 400;
  color: var(--text-1);
}

h1 {
  font-size: var(--fs-h1);
  line-height: 1.2;
}

h2 {
  font-size: var(--fs-h2);
  line-height: 1.3;
}

.eyebrow {
  font-family: var(--font-body);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

.body-text {
  font-size: var(--fs-body);
  color: var(--text-3);
}

.body-text-sm {
  font-size: var(--fs-body-sm);
  color: var(--text-2);
}

.caption {
  font-size: var(--fs-caption);
  color: var(--text-2);
}

.formula {
  font-family: var(--font-equation);
  font-size: var(--fs-formula);
}

.divider {
  border: none;
  border-top: 1px solid var(--border);
  margin: 2.5rem 0;
}

/* ---------- Layout helpers ---------- */
.wrap {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* =========================================================
   Navbar
   ========================================================= */
.navbar {
  position: sticky;
  top: 0;
  z-index: 300;
  height: var(--nav-height);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 2rem;
  background: var(--bg-mid);
  border-bottom: 1px solid var(--border);
}

.nav-logo {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--font-body);
  font-size: var(--fs-nav-logo);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--accent);
}

.nav-logo-icon {
  display: block;
  width: auto;
  height: 81px;
  transition: transform var(--transition);
}

.nav-logo:hover .nav-logo-icon {
  transform: scale(1.08);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 2rem;
}

.nav-links a {
  font-size: var(--fs-body-sm);
  color: var(--text-2);
  transition: color var(--transition);
}

.nav-links a:hover,
.nav-links a.active {
  color: var(--text-1);
}

/* =========================================================
   Buttons
   ========================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-size: var(--fs-button);
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}

/* Primary button */
.btn-primary {
  padding: 11px 24px;
  background: var(--accent);
  color: #FFFFFF;
}

.btn-primary:hover {
  background: var(--accent-dim);
}

.btn-primary:disabled {
  background: var(--border-hover);
  color: var(--accent-bg);
}

/* Ghost button */
.btn-ghost {
  padding: 11px 20px;
  background: transparent;
  color: var(--text-3);
  border-color: var(--border-hover);
}

.btn-ghost:hover {
  background: var(--card-hover);
  color: var(--text-1);
  border-color: var(--accent-light);
}

.btn-ghost:disabled {
  color: var(--border-hover);
  border-color: var(--border);
}

/* Next section button */
.btn-next {
  padding: 9px 18px;
  background: var(--accent-bg);
  color: var(--accent);
  border-color: var(--accent-light);
}

.btn-next:hover {
  background: #C8E8E4;
  color: var(--accent-dim);
  border-color: var(--accent);
}

.btn-next:disabled {
  background: var(--bg);
  color: var(--border-hover);
  border-color: var(--border);
}

/* Drawer / panel slider buttons (36x36) */
.icon-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--card);
  border: 1px solid var(--border);
  color: var(--text-3);
  transition: background var(--transition), border-color var(--transition);
}

.icon-btn:hover {
  background: var(--card-hover);
  border-color: var(--border-hover);
}

.button-row {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* =========================================================
   Hero (Home)
   ========================================================= */
.hero {
  padding: 4rem 0 2rem;
}

.hero-heading {
  margin-bottom: 1.75rem;
}

.hero-grid {
  display: flex;
  align-items: flex-start;
  gap: 2.5rem;
}

.hero-copy {
  flex: 1;
  min-width: 0;
}

.hero-media {
  flex: 1;
  min-width: 0;
}

.hero h1 {
  margin: 0.75rem 0 0;
}

.text-accent {
  color: var(--accent);
}

.hero .body-text {
  max-width: 52ch;
  margin-bottom: 1.75rem;
}

.hero-figure {
  margin: 0;
}

/* ---- Hero illustration (heart + wave animation) ---- */
.hero-media {
  cursor: pointer;
}

.hero-illustration {
  display: block;
  width: 100%;
  height: auto;
}

.hero-illustration #main {
  transform-box: fill-box;
  transform-origin: center;
}

.hero-media:hover .hero-illustration #main {
  animation: heroHeartbeat 1.1s ease-in-out infinite;
}

@keyframes heroHeartbeat {
  0% {
    transform: scale(1);
  }

  18% {
    transform: scale(1.015);
  }

  32% {
    transform: scale(1);
  }

  55% {
    transform: scale(1.01);
  }

  72% {
    transform: scale(1);
  }

  100% {
    transform: scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-media:hover .hero-illustration #main {
    animation: none;
  }
}

/* ---- Tutorial help button ----
   Matches the rendered height of .btn-primary/.btn-ghost in the same
   row (11px vertical padding + 1px border + a 12px/1.6 line box =
   43.2px) so the circle lines up with the pill buttons beside it. */
.help-btn {
  font-family: var(--font-heading);
  font-size: 15px;
  font-weight: 600;
  width: 43.2px;
  height: 43.2px;
}

/* =========================================================
   Panel / Block
   ========================================================= */
.panel {
  background: var(--card);
  border: 0.5px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1.125rem;
  background: var(--bg-mid);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}

.panel-label {
  font-size: var(--fs-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}

.panel-tags {
  display: flex;
  gap: 6px;
}

.panel-body {
  padding: 1.125rem;
}

/* =========================================================
   Learning Objectives (Home)
   ========================================================= */
.learning-objectives h2 {
  margin: 0.5rem 0 1.25rem;
}

.objectives-panel {
  display: flex;
  flex-direction: column;
}

.objective-item {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 1rem 1.125rem;
  border-bottom: 1px solid var(--border);
}

.objective-item:last-child {
  border-bottom: none;
}

.objective-marker {
  flex-shrink: 0;
  width: 8px;
  height: 8px;
  margin-top: 0.4rem;
  border-radius: 50%;
  border: 1.5px solid var(--border-hover);
}

/* =========================================================
   Tags
   ========================================================= */
.tag {
  display: inline-block;
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-bg);
  border: 1px solid var(--accent);
  border-radius: 3px;
  padding: 2px 7px;
}

/* =========================================================
   Module cards (Home)
   ========================================================= */
.modules h2 {
  margin: 0.5rem 0 1.25rem;
}

.sections-overview h2 {
  margin: 0.5rem 0 1.25rem;
}

.module-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
}

.module-card {
  background: var(--card);
  border: 0.5px solid var(--border);
  border-radius: var(--radius-lg);
  border-top: 3px solid var(--accent);
  padding: 1.125rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  transition: background var(--transition);
}

.module-card:hover {
  background: var(--card-hover);
}

.module-card.active {
  background: var(--accent-bg);
  border-top-color: var(--accent);
}

.module-card.active:hover {
  background: #C8E8E4;
}

.module-card-header {
  display: flex;
  align-items: flex-start;
  gap: 0.875rem;
}

.module-icon {
  flex-shrink: 0;
  width: 72px;
  height: 72px;
  border-radius: var(--radius-md);
  object-fit: contain;
  transition: transform var(--transition);
}

.module-card:hover .module-icon {
  transform: scale(1.08);
}

.module-card-heading {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.25rem;
}

.module-label {
  font-size: var(--fs-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}

.module-card h3 {
  font-family: var(--font-heading);
  font-size: 18px;
  font-weight: 400;
  color: var(--text-1);
}

/* =========================================================
   Footer
   ========================================================= */
.site-footer {
  border-top: 1px solid var(--border);
  padding: 1.5rem 0;
  margin-top: 2.5rem;
}

.site-footer p {
  font-size: var(--fs-caption);
  color: var(--text-2);
  text-align: center;
}

/* =========================================================
   Module page layout
   ========================================================= */
.module-layout {
  display: flex;
  align-items: flex-start;
}

/* ---- Left navigation drawer ---- */
.nav-drawer {
  position: sticky;
  top: var(--nav-height);
  height: calc(100vh - var(--nav-height));
  width: var(--drawer-width);
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  background: var(--bg-mid);
  overflow-y: auto;
  transition: width var(--transition);
}

.nav-drawer.closed {
  width: 0;
  overflow: hidden;
  border-right: none;
}

/* Toggle button lives outside the drawer so it is never clipped by
   the drawer's overflow:hidden when collapsed. */
.drawer-toggle {
  position: fixed;
  top: calc(var(--nav-height) + 1rem);
  left: calc(var(--drawer-width) - 18px);
  z-index: 296;
  transition: left var(--transition);
}

.drawer-toggle.collapsed {
  left: 6px;
}

/* Tooltip on the collapsed toggle buttons: a small floating label
   naming what's inside, not the content itself — so it can't conflict
   with open/close state the way expanding the real drawer/panel did.
   Pure CSS, no JS: it only ever shows/hides on :hover/:focus-visible
   and never touches the .closed class. No need to set position here —
   both .drawer-toggle and .ref-toggle are already position:fixed,
   which already establishes a containing block for the ::after below;
   redeclaring position at equal specificity would just be a coin-flip
   in the cascade over which rule wins. */
.nav-drawer.closed [data-tooltip]::after,
.ref-panel.closed [data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(15, 31, 30, 0.9);
  color: #fff;
  font-size: var(--fs-caption);
  font-weight: 600;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition);
  pointer-events: none;
  z-index: 297;
}

.nav-drawer.closed [data-tooltip]::after {
  left: calc(100% + 10px);
}

.ref-panel.closed [data-tooltip]::after {
  right: calc(100% + 10px);
}

.nav-drawer.closed [data-tooltip]:hover::after,
.nav-drawer.closed [data-tooltip]:focus-visible::after,
.ref-panel.closed [data-tooltip]:hover::after,
.ref-panel.closed [data-tooltip]:focus-visible::after {
  opacity: 1;
  visibility: visible;
}

.drawer-nav {
  padding: 1.5rem 1.25rem;
  white-space: nowrap;
}

.drawer-title {
  font-size: var(--fs-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: 0.75rem;
}

.drawer-nav ul {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.drawer-nav li a {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.6rem 0.5rem;
  font-size: var(--fs-body-sm);
  color: var(--text-2);
  line-height: 1.4;
  white-space: normal;
  border-radius: var(--radius-sm);
  transition: background var(--transition), color var(--transition);
}

.drawer-nav li a:hover {
  background: var(--card-hover);
}

.drawer-nav li a.active {
  color: var(--accent);
  font-weight: 600;
}

.drawer-bullet {
  width: 8px;
  height: 8px;
  margin-top: 0.3em;
  border-radius: 50%;
  border: 1.5px solid var(--border-hover);
  flex-shrink: 0;
}

.drawer-nav li a.active .drawer-bullet {
  background: var(--accent);
  border-color: var(--accent);
}

/* ---- Main content ---- */
.module-main {
  flex: 1;
  min-width: 0;
  padding: 2.5rem 3rem;
}

/* ---- Module-wide introduction (module3-1.html only) ---- */
.module-intro {
  padding-bottom: 2rem;
  margin-bottom: 2.5rem;
  border-bottom: 1px solid var(--border);
}

.module-intro h1 {
  margin: 0.75rem 0 1rem;
}

.module-intro .body-text {
  max-width: 62ch;
}

.sections-panel {
  margin-top: 1.5rem;
  max-width: 420px;
}

.section-list li {
  border-bottom: 1px solid var(--border);
}

.section-list li:last-child {
  border-bottom: none;
}

.section-list-link {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.75rem 1.125rem;
  font-size: var(--fs-body-sm);
  color: var(--text-2);
  transition: background var(--transition), color var(--transition);
}

.section-list-link:hover {
  background: var(--card-hover);
  color: var(--text-1);
}

.bullet {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--border-hover);
  flex-shrink: 0;
}

.module-section {
  padding-bottom: 1.5rem;
}

.module-section h2 {
  margin: 0.5rem 0 1rem;
}

.module-section .body-text {
  margin-bottom: 1rem;
}

.interactive-panel {
  margin: 1.5rem 0;
}

.interactive-panel .panel-body {
  min-height: 160px;
}

/* =========================================================
   Wave Types Interactive (module 3-1)
   ========================================================= */
.wave-row {
  margin-bottom: 1rem;
}

.wave-row-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 0.4rem;
}

.wave-row-label {
  font-size: var(--fs-body-sm);
  font-weight: 600;
}

.wave-row-speed {
  font-family: var(--font-equation);
  font-size: var(--fs-body-sm);
  color: var(--text-2);
}

/* A plain HTML row instead of on-canvas text/arrow — it sits above the
   dot grid entirely, so it's never competing for space or contrast
   with the animated dots. */
.wave-propagation-note {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.4rem;
  font-size: var(--fs-caption);
  color: var(--text-2);
  white-space: nowrap;
}

.wave-propagation-arrow {
  flex: 1;
  min-width: 24px;
  height: 2px;
  background: var(--accent-bg);
  position: relative;
}

.wave-propagation-arrow::after {
  content: '';
  position: absolute;
  right: -1px;
  top: 50%;
  transform: translateY(-50%);
  border: 5px solid transparent;
  border-right: 0;
  border-left: 6px solid var(--accent-bg);
}

.wave-row[data-wave="compressional"] .wave-propagation-arrow {
  background: var(--accent-violet-bg);
}

.wave-row[data-wave="compressional"] .wave-propagation-arrow::after {
  border-left-color: var(--accent-violet-bg);
}

.wave-canvas {
  width: 100%;
  height: 140px;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
}

.wave-canvas-caption {
  font-size: var(--fs-caption);
  color: var(--text-2);
  margin-top: 0.5rem;
}

.wave-controls {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.wave-play-toggle {
  flex-shrink: 0;
  font-size: var(--fs-body-sm);
  letter-spacing: 0.02em;
}

.wave-slider-group {
  flex: 1;
  min-width: 0;
}

.wave-slider-labels {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-size: var(--fs-body-sm);
  color: var(--text-3);
  margin-bottom: 0.4rem;
}

.wave-slider-labels span:last-child {
  font-weight: 600;
}

/* Soft/medium/stiff each get their own color so the label's color
   itself hints at where the slider sits, not just its text. Medium is
   a literal blend of the soft and stiff colors — a midpoint, not a
   separate palette color — so it stays visually "between" as the
   slider moves. */
#stiffnessValueLabel[data-level="soft"] {
  color: var(--accent);
}

#stiffnessValueLabel[data-level="medium"] {
  color: color-mix(in srgb, var(--accent), var(--accent-amber));
}

#stiffnessValueLabel[data-level="stiff"] {
  color: var(--accent-amber);
}

/* Slider — 3px track, 13px thumb, 15% accent ring on hover/focus,
   per the style guide's SLIDER spec. */
.wave-slider {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 3px;
  background: var(--border);
  border-radius: 2px;
}

.wave-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 rgba(12, 122, 112, 0.15);
  transition: box-shadow var(--transition);
  cursor: pointer;
}

.wave-slider:hover::-webkit-slider-thumb,
.wave-slider:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 3px rgba(12, 122, 112, 0.15);
}

.wave-slider::-moz-range-thumb {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  box-shadow: 0 0 0 0 rgba(12, 122, 112, 0.15);
  transition: box-shadow var(--transition);
  cursor: pointer;
}

.wave-slider:hover::-moz-range-thumb,
.wave-slider:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 3px rgba(12, 122, 112, 0.15);
}

.wave-slider::-moz-range-track {
  height: 3px;
  background: var(--border);
  border-radius: 2px;
}

.wave-slider-endpoints {
  display: flex;
  justify-content: space-between;
  font-size: var(--fs-caption);
  color: var(--text-2);
  margin-top: 0.35rem;
}

.wave-note {
  font-size: var(--fs-caption);
  color: var(--text-2);
  margin-top: 0.85rem;
}

@media (max-width: 520px) {
  .wave-controls {
    flex-direction: column;
    align-items: stretch;
  }
}

/* =========================================================
   ARF Push Pulse Interactive (module 3-2)
   Layer reveal order: beam (top-down clip) -> focal zone
   (fade/scale) -> displacement dot (pop) -> shear-wave pulse. xyz
   axis, medium and probe are static. The tissue's funnel-shaped
   cutout mask (see #arf-mask in the SVG) is clipped to the same
   growing rect as the beam, so it only opens where the beam has
   actually reached. The shear-wave arms are driven entirely from
   script.js (initArfInteractive): the "hump" (particle displacement)
   and its leading arrow are repositioned every frame so they travel
   outward from the dot to the tissue edge, then settle back to a
   flat line — reused for both the intro pulse and every click.
   ========================================================= */
.arf-interactive {
  padding-top: 0.875rem;
}

.arf-stage {
  position: relative;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  overflow: hidden;
}

.arf-svg {
  display: block;
  width: 100%;
  height: auto;
}

.arf-hit-area {
  cursor: pointer;
}

.arf-annotations {
  pointer-events: none;
}

.arf-label {
  font-family: var(--font-body);
  font-size: var(--fs-h2);
  font-weight: 600;
}

.arf-label-neutral {
  fill: #3f5150;
}

.arf-label-blue {
  fill: #1d6fa4;
}

.arf-label-accent {
  fill: #0c7a70;
}

.arf-leader {
  fill: none;
  stroke: #3f5150;
  stroke-width: 2.5;
  stroke-dasharray: 18 14;
}

.arf-leader-blue {
  stroke: #1d6fa4;
}

.arf-leader-accent {
  stroke: #0c7a70;
}

.arf-leader-dot {
  fill: #3f5150;
}

.arf-leader-dot-blue {
  fill: #1d6fa4;
}

.arf-leader-dot-neutral {
  fill: #3f5150;
}

.arf-leader-dot-accent {
  fill: #0c7a70;
}

/* ---- Base (hidden) state for the animated layers ---- */
.arf-svg #arfBeamRevealRect {
  height: 0;
}

.arf-svg #focalZone {
  opacity: 0;
  transform: scale(0.55);
  transform-box: fill-box;
  transform-origin: center;
}

.arf-svg #dot {
  opacity: 0;
  transform: scale(0);
  transform-box: fill-box;
  transform-origin: center;
}

.arf-label-group.arf-label-beam,
.arf-label-group.arf-label-focal,
.arf-label-group.arf-label-shear {
  opacity: 0;
}

/* ---- Playing state: staggered reveal ---- */
.arf-stage.playing .arf-svg #arfBeamRevealRect {
  animation: arfBeamReveal 1.1s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

.arf-stage.playing .arf-svg #focalZone {
  animation: arfFadeScaleIn 0.5s ease-out 1.05s forwards;
}

.arf-stage.playing .arf-svg #dot {
  animation: arfDotPop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) 1.5s forwards;
}

.arf-stage.playing .arf-label-group.arf-label-beam {
  animation: arfFadeIn 0.4s ease-out 1.15s forwards;
}

.arf-stage.playing .arf-label-group.arf-label-focal {
  animation: arfFadeIn 0.4s ease-out 1.55s forwards;
}

/* The shear-wave pulse timing is dynamic (JS-driven, replayable per
   click), so its label fades in via inline style from script.js
   rather than a fixed animation-delay. */

/* ---- Reduced motion: show the end state instantly, no click pulses ---- */
.arf-stage.playing.no-motion .arf-svg #arfBeamRevealRect,
.arf-stage.playing.no-motion .arf-svg #focalZone,
.arf-stage.playing.no-motion .arf-svg #dot,
.arf-stage.playing.no-motion .arf-label-group.arf-label-beam,
.arf-stage.playing.no-motion .arf-label-group.arf-label-focal {
  animation: none;
}

.arf-stage.playing.no-motion .arf-svg #arfBeamRevealRect {
  height: 1080.83px;
}

.arf-stage.playing.no-motion .arf-svg #focalZone {
  opacity: 1;
  transform: scale(1);
}

.arf-stage.playing.no-motion .arf-svg #dot {
  opacity: 1;
  transform: scale(1);
}

.arf-stage.playing.no-motion .arf-label-group.arf-label-beam,
.arf-stage.playing.no-motion .arf-label-group.arf-label-focal {
  opacity: 1;
}

@keyframes arfBeamReveal {
  to {
    height: 1080.83px;
  }
}

@keyframes arfFadeScaleIn {
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes arfDotPop {
  0% {
    opacity: 0;
    transform: scale(0);
  }

  70% {
    opacity: 1;
    transform: scale(1.25);
  }

  100% {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes arfFadeIn {
  to {
    opacity: 1;
  }
}

/* ---- Controls row ---- */
.arf-controls {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.arf-play-btn,
.arf-reset-btn {
  flex-shrink: 0;
  font-size: var(--fs-body-sm);
}

.arf-hint {
  font-size: var(--fs-body-sm);
  color: var(--text-2);
}

@media (max-width: 520px) {
  .arf-controls {
    flex-wrap: wrap;
  }
}

/* =========================================================
   Module 3-2: Natural Shear Waves (valve closure) interactive
   Both valves are drawn in one combined SVG (source already lays
   them out side by side, with its own baked-in dashed labels — no
   HTML-authored annotations needed here). Each valve's wave path is
   driven procedurally, the same way #shearWave's shear-arm-path is
   for the ARF beam: "playing" it pops the dot at the valve annulus
   (same arfDotPop-style keyframe as the ARF panel), then a single
   concave-down hump is traced frame-by-frame translating outward
   along the myocardial wall — one wave shape actually traveling,
   not a static shape being revealed. Each valve's dot lives in its
   own small <g class="valve-dot-group"> so the "playing"/"no-motion"
   state can be toggled per valve without the other valve's dot
   reacting — clicking the left/right half of the diagram (or that
   valve's play button) only replays its own wave. */
.valve-interactive {
  padding-top: 0.875rem;
}

.valve-split-row {
  display: flex;
  gap: 1.5rem;
}

.valve-split-col {
  flex: 1 1 0;
  min-width: 0;
}

@media (max-width: 520px) {
  .valve-split-row {
    flex-direction: column;
    gap: 0.75rem;
  }
}

.valve-row-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.valve-row-label {
  font-size: var(--fs-body-sm);
  font-weight: 600;
}

.valve-stage {
  position: relative;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  overflow: hidden;
  margin-bottom: 1.25rem;
}

.valve-svg {
  display: block;
  width: 100%;
  height: auto;
}

.valve-hit-area {
  cursor: pointer;
}

/* ---- Base (hidden) state for the animated layers ---- */
.valve-svg .valve-dot {
  opacity: 0;
  transform: scale(0);
  transform-box: fill-box;
  transform-origin: center;
}

/* ---- Playing state (scoped to the valve's own dot group) ---- */
.valve-dot-group.playing .valve-dot {
  animation: valveDotPop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

/* The traveling wave's 'd' is rewritten every animation frame by
   script.js (replayable per click), so there's no fixed CSS
   animation for it — same reasoning as the ARF panel's
   shear-arm-path. */

/* ---- Reduced motion: show the end state instantly ---- */
.valve-dot-group.playing.no-motion .valve-dot {
  animation: none;
  opacity: 1;
  transform: scale(1);
}

@keyframes valveDotPop {
  0% {
    opacity: 0;
    transform: scale(0);
  }

  70% {
    opacity: 1;
    transform: scale(1.25);
  }

  100% {
    opacity: 1;
    transform: scale(1);
  }
}

.valve-controls {
  display: flex;
  align-items: center;
  gap: 0.875rem;
}

.valve-play-btn,
.valve-reset-btn {
  flex-shrink: 0;
  font-size: var(--fs-body-sm);
}

.valve-hint {
  font-size: var(--fs-body-sm);
  color: var(--text-2);
}

@media (max-width: 520px) {
  .valve-controls {
    flex-wrap: wrap;
  }
}

/* ---- Section nav (back to top / next section) ---- */
.section-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 1.5rem;
}

/* ---- Right reference panel ---- */
.ref-panel {
  position: sticky;
  top: var(--nav-height);
  height: calc(100vh - var(--nav-height));
  width: var(--ref-panel-width);
  flex-shrink: 0;
  border-left: 1px solid var(--border);
  background: var(--bg-mid);
  overflow-y: auto;
  transition: width var(--transition);
}

.ref-panel.closed {
  width: 0;
  overflow: hidden;
  border-left: none;
}

/* Toggle button lives outside the panel so it is never clipped by
   the panel's overflow:hidden when collapsed. */
.ref-toggle {
  position: fixed;
  top: calc(var(--nav-height) + 1rem);
  right: calc(var(--ref-panel-width) - 18px);
  z-index: 296;
  transition: right var(--transition);
}

.ref-toggle.collapsed {
  right: 6px;
}

.ref-tabs {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  background: var(--bg-mid);
  border-bottom: 1px solid var(--border);
}

.ref-tab {
  flex: 1;
  padding: 0.75rem 0.5rem;
  font-size: var(--fs-label);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-3);
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: color var(--transition), border-color var(--transition);
}

.ref-tab:hover {
  color: var(--text-1);
}

.ref-tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.ref-panel-content {
  padding: 1.25rem;
}

/* =========================================================
   Ultrafast vs. Conventional Imaging Interactive (module 3-3)
   Two independent panels (conventional / ultrafast) share one
   play/pause/reset toolbar, driven from script.js
   (initUltrafastVsConventionalInteractive). Layout mirrors the
   Wave Types Interactive above it — reuses .wave-slider for both
   range inputs rather than redefining thumb/track styling.
   ========================================================= */
.uvc-toolbar-hint {
  font-size: var(--fs-caption);
  color: var(--text-2);
  margin: 0 0 1.25rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--border);
}

/* Each panel gets its own play/pause + reset pair, so the two systems
   can be started, paused, and inspected independently rather than
   only ever moving in lockstep. */
.uvc-row-toolbar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.85rem;
}

.uvc-play-toggle,
.uvc-reset-btn {
  flex-shrink: 0;
  font-size: var(--fs-body-sm);
}

/* Side-by-side by default so the two systems can be compared directly
   at a glance; .uvc-divider's align-self: stretch fills whichever axis
   is the cross axis, so the same rule draws a vertical rule here and
   (once the breakpoint below flips .uvc-scene to a column) a
   horizontal rule on narrow screens, with no separate mobile override
   needed for the divider's stretched dimension. */
.uvc-scene {
  display: flex;
  align-items: flex-start;
  gap: 1.75rem;
}

.uvc-divider {
  align-self: stretch;
  width: 1px;
  flex-shrink: 0;
  background: var(--border);
}

.uvc-row {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.uvc-row-label {
  display: flex;
  align-items: baseline;
}

.uvc-row-label span {
  font-size: var(--fs-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
  white-space: nowrap;
}

.uvc-row-main {
  min-width: 0;
}

.uvc-row-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  margin-bottom: 0.85rem;
}

.uvc-row-title {
  font-size: var(--fs-body-sm);
  font-weight: 600;
}

.uvc-legend {
  display: flex;
  gap: 0.85rem;
  font-size: var(--fs-caption);
  color: var(--text-2);
}

.uvc-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.uvc-legend-swatch {
  display: inline-block;
  width: 9px;
  height: 9px;
  flex-shrink: 0;
}

.uvc-legend-wave {
  border-radius: 50%;
  border: 1.5px solid var(--text-2);
}

.uvc-legend-scanline {
  width: 2px;
  height: 11px;
  background: var(--text-1);
}

.uvc-legend-sample {
  border-radius: 50%;
  background: var(--text-1);
}

/* Stacked, not side-by-side — each column is already half the panel's
   width now that the two systems sit next to each other, too narrow
   for a "Wave velocity"/"Imaging depth" pair to share a row without
   wrapping. */
.uvc-sliders {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.6rem;
  margin-bottom: 0.85rem;
}

.uvc-slider-group {
  min-width: 0;
}

.uvc-slider-labels {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-size: var(--fs-body-sm);
  color: var(--text-3);
  margin-bottom: 0.35rem;
}

.uvc-slider-labels span:last-child {
  font-weight: 600;
  font-family: var(--font-equation);
  color: var(--accent);
}

.uvc-stage-tag {
  font-size: var(--fs-caption);
  color: var(--text-2);
  margin-bottom: 0.35rem;
}

.uvc-stage-canvas {
  width: 100%;
  height: 110px;
  display: block;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  background: var(--bg-mid);
}

.uvc-readout {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.25rem;
  font-size: var(--fs-body-sm);
  color: var(--text-2);
  margin-top: 0.6rem;
}

.uvc-readout strong {
  color: var(--text-1);
  font-family: var(--font-equation);
  font-weight: 600;
}

.uvc-plot-canvas {
  width: 100%;
  height: 96px;
  display: block;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  background: var(--card);
  margin-top: 0.6rem;
}

.uvc-plot-readout {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.35rem 1.25rem;
  font-size: var(--fs-body-sm);
  color: var(--text-2);
  margin-top: 0.35rem;
}

.uvc-plot-readout strong {
  font-family: var(--font-equation);
  font-weight: 600;
}

/* Estimated-velocity readout is color-coded by how far it lands
   from the true velocity, so the accuracy gap reads at a glance and
   not just from the scatter of points on the mini plot above it. */
.uvc-est-good {
  color: var(--accent);
}

.uvc-est-warn {
  color: var(--accent-amber);
}

.uvc-est-bad {
  color: var(--accent-rose);
}

.uvc-notes {
  margin-top: 1.25rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.uvc-notes p {
  font-size: var(--fs-caption);
  color: var(--text-2);
  margin: 0;
}

/* Below this, two columns get too narrow to hold a slider pair, a
   stage canvas, and a scatter plot each — stack the panels instead. */
@media (max-width: 820px) {
  .uvc-scene {
    flex-direction: column;
    gap: 0.5rem;
  }

  /* Stacked panels are full-width again, so the slider pair fits
     comfortably side by side as it did before the layout change. */
  .uvc-sliders {
    grid-template-columns: 1fr 1fr;
  }
}

/* =========================================================
   Time-of-Flight Velocity Interactive (module 3-3)
   Wave speed (c) and shear modulus (μ) each get one consistent
   color — applied here to any element carrying the matching
   data-var attribute — so the slider, wave front, plot fit line,
   formula, and readout boxes all read as the same physical
   quantity wherever they appear. Young's modulus is derived from μ
   but isn't itself color-coded, so it keeps the page's plain text
   color throughout, matching how it's presented everywhere else on
   this page.
   Sections stack top to bottom (wave speed slider, then
   chart+readouts, then formula), matching the sketch this panel was
   built from.
   ========================================================= */
.tofv-interactive [data-var="velocity"] {
  color: var(--accent);
}

.tofv-interactive [data-var="shear"] {
  color: var(--accent-amber);
}

.tofv-toolbar {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-bottom: 1.125rem;
}

.tofv-slider-group {
  flex: 1;
  min-width: 0;
}

.tofv-slider-labels {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-size: var(--fs-body-sm);
  color: var(--text-3);
  margin-bottom: 0.35rem;
}

.tofv-slider-labels span:last-child {
  font-weight: 600;
  font-family: var(--font-equation);
}

.tofv-data-row {
  display: flex;
  align-items: flex-start;
  gap: 1.25rem;
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

.tofv-chart-col {
  flex: 1;
  min-width: 0;
}

.tofv-plot-canvas {
  width: 100%;
  height: 150px;
  display: block;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  background: var(--card);
}

.tofv-plot-caption {
  font-size: var(--fs-caption);
  color: var(--text-2);
  margin-top: 0.5rem;
}

.tofv-readouts {
  flex-shrink: 0;
  width: 168px;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.tofv-readout-box {
  border: 1px solid var(--border);
  border-left: 3px solid currentColor;
  border-radius: var(--radius-md);
  padding: 0.5rem 0.65rem;
}

.tofv-readout-label {
  display: block;
  font-size: var(--fs-label);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: 0.25rem;
  white-space: nowrap;
}

.tofv-readout-value {
  display: block;
  font-family: var(--font-equation);
  font-size: var(--fs-body-sm);
  font-weight: 600;
}

.tofv-formula {
  margin-top: 1.5rem;
  padding-top: 1.125rem;
  border-top: 1px solid var(--border);
  text-align: center;
}

/* Each formula is one continuous chain of equalities — symbol form,
   then numeric substitution, then result — read left to right on a
   single line (wrapping as a whole on narrow screens) rather than a
   symbolic line stacked over a separate numeric line. That sidesteps
   having two rows whose differently-sized tokens need to line up. */
.tofv-formula-chain {
  display: flex;
  align-items: baseline;
  justify-content: center;
  flex-wrap: wrap;
  row-gap: 0.5rem;
  column-gap: 0.55rem;
  margin: 0;
  font-family: var(--font-equation);
  font-size: 24px;
  font-weight: 700;
  color: var(--text-1);
}

.tofv-formula-chain-secondary {
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px dashed var(--border);
}

.tofv-formula-symbol,
.tofv-formula-value {
  font-weight: 700;
}

.tofv-formula-result {
  font-size: 1.05em;
}

.tofv-formula-chain sup {
  font-size: 0.65em;
  font-weight: 700;
  margin-left: 1px;
}

.tofv-formula-op {
  font-weight: 600;
  color: var(--text-3);
}

.tofv-legend {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-top: 0.85rem;
  font-size: var(--fs-caption);
  color: var(--text-2);
}

.tofv-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.tofv-legend-swatch {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
}

@media (max-width: 480px) {
  .tofv-data-row {
    flex-direction: column;
  }

  .tofv-readouts {
    width: auto;
    flex-direction: row;
  }

  .tofv-readout-box {
    flex: 1;
  }
}

/* =========================================================
   Viscoelastic Dispersion Interactive (module 3-4)
   Reuses .tofv-toolbar / .wave-slider* / .wave-canvas-caption for
   the toolbar row and captions, so this panel matches the toolbar
   and slider conventions already established elsewhere. Only the
   pieces unique to this panel (play/reset button group, the two
   canvases, and the ARF/natural legend) get their own rules here.
   ========================================================= */
.disp-toolbar-controls {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

.disp-play-btn,
.disp-reset-btn {
  flex-shrink: 0;
  font-size: var(--fs-body-sm);
}

.disp-slider-group {
  flex: 1;
  min-width: 0;
}

/* Color-codes the level readout the same way #stiffnessValueLabel
   does in module 3-1 — severity ramps from neutral to rose so the
   label itself hints at how much dispersion is currently active. */
#dispLevelLabel[data-level="none"] {
  color: var(--text-3);
}

#dispLevelLabel[data-level="mild"] {
  color: var(--accent-light);
}

#dispLevelLabel[data-level="moderate"] {
  color: var(--accent);
}

#dispLevelLabel[data-level="strong"] {
  color: var(--accent-rose);
}

/* Each wave gets its own row/canvas rather than sharing one baseline —
   overlapping colored traces were hard to read once they separated and
   distorted, so ARF and natural are drawn independently, stacked like
   the compressional/shear split in module 3-1. */
.disp-wave-row {
  margin-top: 1.125rem;
}

.disp-wave-row-label {
  font-size: var(--fs-body-sm);
  font-weight: 600;
  margin-bottom: 0.4rem;
}

.disp-wave-row[data-wave="arf"] .disp-wave-row-label {
  color: var(--accent-blue);
}

.disp-wave-row[data-wave="natural"] .disp-wave-row-label {
  color: var(--text-2);
}

.disp-wave-canvas {
  width: 100%;
  height: 110px;
  display: block;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  background: var(--card);
}

.disp-chart-section {
  margin-top: 1.25rem;
  padding-top: 1.125rem;
  border-top: 1px solid var(--border);
}

.disp-chart-canvas {
  width: 100%;
  height: 230px;
  display: block;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  background: var(--card);
}

@media (max-width: 480px) {
  .disp-interactive .tofv-toolbar {
    flex-wrap: wrap;
  }

  .disp-slider-group {
    flex-basis: 100%;
  }
}

/* =========================================================
   Guided Wave / Wall Thickness Interactive (module 3-4)
   Reuses .tofv-toolbar / .wave-slider* / .disp-slider-group /
   .disp-chart-section / .wave-canvas-caption / .wave-note from the
   dispersion panel above it, so the two panels read as one family.
   Only the wall cross-section row (with its rotated "Wall" label)
   and the chart canvas sizing are unique to this panel.
   ========================================================= */
.gw-wall-row {
  display: flex;
  align-items: stretch;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

.gw-wall-label {
  flex-shrink: 0;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-body-sm);
  font-weight: 600;
  color: var(--text-3);
}

.gw-wall-canvas {
  flex: 1;
  min-width: 0;
  height: 200px;
  display: block;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  background: var(--card);
}

.gw-chart-canvas {
  width: 100%;
  height: 230px;
  display: block;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  background: var(--card);
}

/* =========================================================
   Anisotropic Wave Speed Interactive (module 3-4)
   Two panels side by side, sharing the controls below them.
   Reuses .wave-slider* / .wave-note from the panels above it on
   this page, and the .uvc-scene / .uvc-divider side-by-side +
   stretch-divider trick from module 3-3 for the panel layout.
   ========================================================= */
.aniso-scene {
  display: flex;
  align-items: stretch;
  gap: 1.5rem;
}

.aniso-divider {
  align-self: stretch;
  width: 1px;
  flex-shrink: 0;
  background: var(--border);
}

.aniso-panel {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.aniso-panel-header {
  margin-bottom: 0.5rem;
}

.aniso-panel-title {
  font-size: var(--fs-body-sm);
  font-weight: 600;
  color: var(--text-3);
}

.aniso-panel-canvas {
  width: 100%;
  height: 210px;
  display: block;
  border-radius: var(--radius-md);
  border: 0.5px solid var(--border);
  background: var(--card);
}

/* Border-left color + a Fixed/Live badge carry the "one is frozen,
   one moves" teaching point even before either number is read. */
.aniso-readout-box {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  margin-top: 0.65rem;
  border: 1px solid var(--border);
  border-left: 3px solid currentColor;
  border-radius: var(--radius-md);
  padding: 0.5rem 0.7rem;
}

.aniso-readout-frozen {
  color: var(--text-2);
}

.aniso-readout-live {
  color: var(--accent);
}

.aniso-readout-label {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-label);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-3);
}

.aniso-readout-value {
  display: inline-block;
  font-family: var(--font-equation);
  font-size: 22px;
  font-weight: 700;
  color: currentColor;
}

.aniso-readout-badge {
  flex-shrink: 0;
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: currentColor;
  background: var(--bg-mid);
  border: 1px solid currentColor;
  border-radius: 3px;
  padding: 1px 6px;
}

@keyframes aniso-pulse {
  0% {
    transform: scale(1.15);
  }

  100% {
    transform: scale(1);
  }
}

.aniso-pulse {
  animation: aniso-pulse 0.28s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .aniso-pulse {
    animation: none;
  }
}

.aniso-controls {
  margin-top: 1.25rem;
  padding-top: 1.125rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.aniso-slider-group {
  min-width: 0;
}

.aniso-depth-row {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
}

.aniso-depth-row .aniso-slider-group {
  flex: 1;
}

.aniso-thumb-wrap {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
}

.aniso-thumb-canvas {
  width: 64px;
  height: 64px;
  display: block;
  border-radius: 50%;
  border: 0.5px solid var(--border);
  background: var(--card);
}

.aniso-thumb-caption {
  font-size: var(--fs-caption);
  color: var(--text-2);
  white-space: nowrap;
}

@media (max-width: 720px) {
  .aniso-scene {
    flex-direction: column;
    gap: 1.25rem;
  }
}

@media (max-width: 480px) {
  .aniso-depth-row {
    flex-direction: column;
    align-items: stretch;
  }

  .aniso-thumb-wrap {
    flex-direction: row;
    align-self: flex-start;
  }
}

/* =========================================================
   Callout boxes
   ========================================================= */
.callout {
  border-left: 2px solid var(--accent);
  background: var(--accent-bg);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  padding: 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin: 1.5rem 0;
}

.callout-label {
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}

.callout-text {
  font-size: var(--fs-body);
  color: var(--text-3);
}

.callout-list {
  margin: 0;
  padding-left: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.callout-list li {
  list-style: circle;
  font-size: var(--fs-body);
  color: var(--text-3);
}

.callout-highlight {
  border-left-color: var(--accent-violet);
  background: var(--accent-violet-bg);
}

.callout-highlight .callout-label {
  color: var(--accent-violet);
}

.callout-notes {
  border-left-color: var(--accent-violet);
  background: var(--accent-violet-bg);
}

.callout-notes .callout-label {
  color: var(--accent-violet);
}

/* =========================================================
   Module content: subsection headings
   ========================================================= */
.module-section .intro-text {
  padding-bottom: 1.5rem;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
}

.module-section h3 {
  font-size: 18px;
  margin: 2.25rem 0 0.75rem;
}

/* =========================================================
   Figure / image placeholders
   ========================================================= */
.figure-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 160px;
  margin: 1.5rem 0;
  padding: 1.25rem;
  border: 1px dashed var(--border-hover);
  border-radius: var(--radius-md);
  background: var(--bg-mid);
  text-align: center;
}

.figure-placeholder.has-image {
  border-style: solid;
}

.figure-placeholder.has-image .figure-caption {
  align-self: stretch;
  max-width: none;
  text-align: left;
}

.figure-caption-label {
  color: var(--text-1);
}

.figure-image {
  width: 100%;
  max-width: 640px;
  height: auto;
  display: block;
}

.figure-label {
  font-size: var(--fs-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}

.figure-caption {
  font-size: var(--fs-body-sm);
  color: var(--text-2);
  max-width: 52ch;
}

.figure-image-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.4rem;
  width: 100%;
  max-width: 900px;
}

.figure-image-wrap .figure-image {
  max-width: none;
}

.figure-row-tag {
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-1);
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid var(--border-hover);
  border-radius: 4px;
  padding: 0.1rem 0.45rem;
  white-space: nowrap;
}

/* =========================================================
   Formula display block
   ========================================================= */
.formula-block {
  margin: 1.25rem 0;
  padding: 0.75rem 0;
  text-align: center;
}

/* =========================================================
   Comparison table (summary callouts)
   ========================================================= */
.compare-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 0.5rem;
}

.compare-table th,
.compare-table td {
  text-align: left;
  font-size: var(--fs-body-sm);
  color: var(--text-3);
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.compare-table th {
  color: var(--text-1);
  font-weight: 600;
  white-space: nowrap;
}

/* =========================================================
   Reference panel: equations & glossary
   ========================================================= */
.eq-item {
  margin-bottom: 1.25rem;
}

.eq-item:last-child {
  margin-bottom: 0;
}

.eq-name {
  font-size: var(--fs-body-sm);
  font-weight: 600;
  color: var(--text-1);
  margin-bottom: 0.35rem;
}

.eq-formula {
  font-family: var(--font-equation);
  font-size: var(--fs-formula);
  color: var(--accent-dim);
  margin-bottom: 0.35rem;
}

.eq-note {
  font-size: var(--fs-caption);
  color: var(--text-2);
}

.glossary-item {
  margin-bottom: 1rem;
}

.glossary-item:last-child {
  margin-bottom: 0;
}

.glossary-term {
  font-size: var(--fs-body-sm);
  font-weight: 600;
  color: var(--text-1);
  margin-bottom: 0.25rem;
}

.glossary-def {
  font-size: var(--fs-body-sm);
  color: var(--text-2);
}

.source-item {
  margin-bottom: 1.25rem;
}

.source-item:last-child {
  margin-bottom: 0;
}

.source-citation {
  font-size: var(--fs-body-sm);
  color: var(--text-1);
  margin-bottom: 0.35rem;
  line-height: 1.4;
}

.source-title-link {
  font-weight: 600;
  color: var(--accent);
}

.source-title-link:hover,
.source-title-link:focus-visible,
.source-doi-link:hover,
.source-doi-link:focus-visible {
  text-decoration: underline;
}

.source-note {
  font-size: var(--fs-caption);
  color: var(--text-2);
}

/* =========================================================
   Equations & Glossary page (equationsGlossary.html)
   Same visual language as the reference-panel styles above
   (.eq-name/.eq-formula/.eq-note, .glossary-term/.glossary-def, .tag),
   laid out as a normal stacked list (divider between entries) rather
   than a card grid, plus a live search box that filters .eqg-item
   entries (see initEquationsGlossarySearch in script.js).
   Without a right-hand reference panel to narrow it, .module-main runs
   much wider here than on the module pages, which let every line of
   body copy (and the search status line) stretch edge-to-edge on wide
   screens. #module-3 caps the whole section back to the site's normal
   reading width so long lines wrap instead.
   ========================================================= */
#module-3 {
  max-width: var(--content-max-width);
}

.eqg-search-wrap {
  margin: 1.5rem 0;
}

.eqg-search-box {
  position: relative;
  max-width: 420px;
}

.eqg-search-icon {
  position: absolute;
  left: 0.8rem;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  color: var(--text-2);
  pointer-events: none;
  transition: color var(--transition);
}

.eqg-search-box:focus-within .eqg-search-icon {
  color: var(--accent);
}

.eqg-search-input {
  display: block;
  width: 100%;
  padding: 0.65rem 2.25rem 0.65rem 2.35rem;
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--text-1);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color var(--transition), box-shadow var(--transition);
}

/* Native type="search" clear icon is redundant with our own button below. */
.eqg-search-input::-webkit-search-cancel-button {
  display: none;
}

.eqg-search-input::placeholder {
  color: var(--text-2);
}

.eqg-search-input:focus {
  outline: none;
  border-color: var(--accent-light);
  box-shadow: 0 0 0 3px rgba(12, 122, 112, 0.15);
}

.eqg-search-clear {
  position: absolute;
  right: 0.45rem;
  top: 50%;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  line-height: 1;
  color: var(--text-2);
  transition: background var(--transition), color var(--transition);
}

.eqg-search-clear:hover {
  background: var(--card-hover);
  color: var(--text-1);
}

.eqg-search-status {
  margin-top: 0.65rem;
  font-size: var(--fs-caption);
  color: var(--text-2);
}

.eqg-search-status strong {
  color: var(--text-1);
  font-weight: 600;
}

.eqg-item mark {
  background: var(--accent-bg);
  color: var(--accent-dim);
  font-weight: 600;
  border-radius: 2px;
  padding: 0 1px;
}

.eqg-group {
  margin-bottom: 1rem;
}

.eqg-list {
  display: flex;
  flex-direction: column;
}

.eqg-item {
  padding: 1.25rem 0;
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.eqg-list .eqg-item:last-child {
  border-bottom: none;
}

.eqg-item[hidden] {
  display: none;
}

.eqg-item-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.eqg-item .eq-name,
.eqg-item .glossary-term {
  flex: 1;
  min-width: 0;
}

.eqg-jump-link {
  align-self: flex-start;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--accent);
  transition: color var(--transition);
}

.eqg-jump-link:hover {
  color: var(--accent-dim);
}

.eqg-no-results {
  font-size: var(--fs-body-sm);
  color: var(--text-2);
  padding: 1rem 0;
}

/* =========================================================
   Drawer/panel backdrops (mobile overlay only)
   ========================================================= */
.nav-backdrop,
.ref-backdrop {
  display: none;
}

/* =========================================================
   Guided tour overlay (home page tutorial)
   ========================================================= */
.tour-overlay {
  position: fixed;
  inset: 0;
  background: rgba(6, 14, 13, 0.72);
  z-index: 400;
  cursor: pointer;
}

.tour-overlay[hidden] {
  display: none;
}

body.tour-active [data-tour] {
  position: relative;
  z-index: 401;
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.85), 0 0 24px rgba(255, 255, 255, 0.35);
}

/* Elements that are normally transparent (the ghost button, the plain
   nav links) get lifted above the overlay along with everything else
   in [data-tour], but with nothing opaque of their own, their
   "transparent" background reveals the dark overlay sitting right
   behind them instead of the light page underneath — the framed area
   ends up looking just as masked as everywhere else. Giving them an
   opaque backer while spotlighted makes the whole framed box read as
   truly unmasked, not just the text drawn on top of it. */
body.tour-active .btn-ghost[data-tour],
body.tour-active .navbar [data-tour] {
  background: var(--card);
}

/* .navbar's `position: sticky` makes it a stacking context in
   practice (browsers do this even at z-index: auto, despite the
   spec's on-paper wording), which traps its children below the
   overlay even when they're given z-index: 401 — a child's z-index
   only competes within its own stacking context, so the spotlighted
   nav-logo/nav-link would render *through* the overlay (dim, muted)
   instead of above it. Lifting the whole navbar's z-index above the
   overlay "fixes" that but un-dims the entire header bar along with
   it, not just the two spotlighted items. Dropping position to static
   removes the stacking context outright (z-index only affects
   positioned elements), so the navbar stays under the overlay by
   normal DOM order like the rest of the page, while each spotlighted
   child's own z-index: 401 now competes directly against the overlay
   at the root level and wins. Sticky scrolling is paused for the
   handful of seconds the tour is open, which is an acceptable trade. */
body.tour-active .navbar {
  position: static;
}

/* The hero button row and the module grid can wrap their items onto a
   new line at narrow widths. Reserve room below each item for its
   label there so a wrapped item doesn't land on top of the previous
   item's label. (Not applied navbar-wide: the navbar never wraps, and
   its items are vertically centered, where a bottom margin would just
   push them upward out of view.) */
body.tour-active .button-row [data-tour],
body.tour-active .module-grid [data-tour] {
  margin-bottom: 4.5rem;
}

body.tour-active [data-tour]::after {
  content: attr(data-tour);
  position: absolute;
  /* Sized to the spotlighted element's own box (not a fixed max-content
     width) so neighboring labels wrap within their own column instead
     of spilling into each other — elements never overlap, so their
     same-width labels can't either. */
  left: 0;
  right: 0;
  top: calc(100% + 12px);
  color: #ffffff;
  font-size: var(--fs-body-sm);
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
  text-transform: lowercase;
  text-shadow: 0 1px 5px rgba(0, 0, 0, 0.7);
  pointer-events: none;
  z-index: 402;
}

/* The logo is much narrower than its label — anchor the label to the
   logo's left edge and let it grow rightward (into the empty space
   before the next nav item) instead of being squeezed to ~30px wide. */
body.tour-active .nav-logo[data-tour]::after {
  left: 0;
  right: auto;
  width: max-content;
  max-width: 220px;
  text-align: left;
}

/* A solid pill (not just shadowed text) so the hint stays legible no
   matter what page content happens to scroll underneath it — plain
   text here would sit on top of and overlap whatever's behind it. */
.tour-hint {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);
  z-index: 403;
  padding: 10px 20px;
  border-radius: 999px;
  background: rgba(15, 31, 30, 0.9);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  color: #ffffff;
  font-size: var(--fs-body-sm);
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  pointer-events: none;
}

/* =========================================================
   Responsive
   ========================================================= */
@media (max-width: 1100px) {
  .module-main {
    padding: 2rem 2.5rem;
  }
}

@media (max-width: 900px) {
  .hero-grid {
    flex-direction: column;
    align-items: stretch;
  }

  /* Let body-text, the hero image, and the button row reorder as
     siblings (paragraph, then image, then buttons) instead of the
     desktop grouping where the image is a separate column. */
  .hero-copy {
    display: contents;
  }

  .hero-copy .body-text {
    max-width: none;
    order: 1;
  }

  .hero-media {
    order: 2;
    max-width: 40%;
    margin: 0 auto;
  }

  .button-row {
    order: 3;
  }

  .nav-drawer {
    position: fixed;
    top: var(--nav-height);
    left: 0;
    bottom: 0;
    height: auto;
    z-index: 295;
    width: min(var(--drawer-width), 85vw);
    box-shadow: 12px 0 32px rgba(15, 31, 30, 0.18);
  }

  .nav-drawer.closed {
    width: 0;
    box-shadow: none;
  }

  .drawer-toggle {
    left: calc(min(var(--drawer-width), 85vw) - 18px);
  }

  .ref-panel {
    position: fixed;
    top: var(--nav-height);
    right: 0;
    bottom: 0;
    height: auto;
    z-index: 295;
    width: min(var(--ref-panel-width), 85vw);
    box-shadow: -12px 0 32px rgba(15, 31, 30, 0.18);
  }

  .ref-panel.closed {
    width: 0;
    box-shadow: none;
  }

  .ref-toggle {
    right: calc(min(var(--ref-panel-width), 85vw) - 18px);
  }

  .nav-backdrop,
  .ref-backdrop {
    display: block;
    position: fixed;
    top: var(--nav-height);
    right: 0;
    bottom: 0;
    left: 0;
    background: rgba(15, 31, 30, 0.45);
    z-index: 292;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--transition);
  }

  .nav-backdrop.visible,
  .ref-backdrop.visible {
    opacity: 1;
    visibility: visible;
  }
}

@media (max-width: 800px) {
  .module-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  :root {
    --fs-h1: 32px;
  }
}

@media (max-width: 520px) {
  .module-grid {
    grid-template-columns: 1fr;
  }

  .nav-links {
    display: none;
  }
}