/* style.css – Georgia SKYWARN shared stylesheet. Mobile-first, light/dark mode. */

/* ---------- Cascade Layer Order (T3.10, 2026-05-16) ----------
   Establishes the cascade priority for future authored rules. Rules placed
   in a layer have LOWER priority than any unlayered rule (later in this
   file), so the existing un-layered CSS keeps its current cascade behavior.
   New components SHOULD be added inside @layer components { ... } so future
   utilities and overrides have a clean place to live.
   Order matters: later layers in this list win over earlier ones.
   --------------------------------------------------------------------- */
@layer reset, tokens, base, components, utilities;

/* ---------- CSS Custom Properties (Light / Dark via light-dark()) ----------
   PATCH 2 (2026-05-16): Collapsed four duplicated theme blocks into one set of
   tokens using the native light-dark() function (Baseline 2024). The manual
   override selectors at the bottom just flip color-scheme; all tokens
   re-evaluate automatically. Reduced ~135 lines of duplicate token defs.
   --------------------------------------------------------------------- */
:root {
  /* Enable native theme switching — follows OS preference by default */
  color-scheme: light dark;

  /* ---------- Brand core (2026-10-22) ----------
     Shared with the WX4PTC net control dashboard, which keeps the same
     names and values, so the two read as one Georgia SKYWARN brand; change
     both together. All three come from the logo: --brand-ink is its own
     background (the header, so the logo has no visible edge), --brand-blue
     its ring hue darkened to carry white text (5.8:1) on fills, and --link
     that blue as text, lifted in dark to clear 4.5:1 on the navy page.
     --------------------------------------------------------------------- */
  --brand-ink:  light-dark(#0a2234, #0a2234);
  --brand-blue: light-dark(#0b5ed7, #0b5ed7);
  --link:       light-dark(#0b5ed7, #60a5fa);
  /* Site-only for now: the dashboard's nav-bar navy, used here for the bars
     that sit on or under the header (.page-nav, mobile menu links). */
  --brand-navy: light-dark(#1a3a5c, #1a3a5c);

  /* Theme-agnostic base colors */
  --white: #ffffff;
  --black: #000000;
  --slate-400: #94a3b8;
  --blue-400: #60a5fa;
  --green-500: #4caf50;

  /* Theme-aware tokens — first arg = light, second = dark */
  --bg-body:        light-dark(#f8f9fa,               #0b1522);
  --text-primary:   light-dark(#1a1a1a,               #e2e8f0);
  --text-secondary: light-dark(#444,                  #94a3b8);
  --card-bg:        light-dark(rgba(255,255,255,0.9), rgba(11,21,34,0.85));
  --card-border:    light-dark(rgba(0,0,0,0.1),       rgba(255,255,255,0.1));

  /* T3.12 (2026-05-16) + cleanup follow-up:
     -dark variants are derived from their base via OKLCH relative-color
     syntax wherever the pair represents a genuine "tonal shift toward
     darker." Lightness shifted -6% (perceptually uniform in OKLCH).
     Means: change the base accent once and the dark variant updates
     automatically. Baseline 2024; visual delta from prior hand-picked
     hex is <2% lightness.

     EXCEPTIONS — kept hand-tuned because the "-dark" suffix is misleading
     for these (they're actually LIGHTER variants, designed as hover or
     dark-mode-contrast pairs, not algorithmic darkenings):
       --accent-indigo-dark (#90CAF9) is a different hue entirely (light blue)
       --accent-orange-dark and --accent-pink-dark are both lighter than
       their base. Algorithmic darkening would change them visibly. */
  /* 2026-10-22: the brand's --link (light was #0066cc). Text and accents
     only; a fill behind white text uses --brand-blue, which stays dark in
     both themes (white on the dark-mode #60a5fa is 2.5:1). */
  --accent-blue:        var(--link);
  --accent-blue-dark:   oklch(from var(--accent-blue)      calc(l - 0.06) c h);
  --brand-blue-dark:    oklch(from var(--brand-blue)       calc(l - 0.06) c h);
  --accent-red:         light-dark(#e63946, #f87171);
  --accent-red-dark:    oklch(from var(--accent-red)       calc(l - 0.06) c h);
  --accent-green:       #4ad38e;
  --accent-green-dark:  oklch(from var(--accent-green)     calc(l - 0.06) c h);
  --accent-indigo:      #5C6BC0;
  --accent-indigo-dark: #90CAF9; /* hand-tuned: actually lighter, not darker */
  --accent-lightblue:   light-dark(#039BE5, #0288D1);
  --accent-lightblue-dark: oklch(from var(--accent-lightblue) calc(l - 0.04) c h);
  --accent-orange:      #FF9800;
  --accent-orange-dark: #FFB74D; /* hand-tuned: actually lighter, not darker */
  --accent-peach:       light-dark(#FF8A65, #FFAB91);
  --accent-yellow:      #FFC107;
  --accent-yellow-dark: oklch(from var(--accent-yellow)    calc(l - 0.06) c h);
  --accent-pink:        light-dark(#ec4899, #f9a8d4);
  --accent-pink-dark:   light-dark(#f472b6, #fbcfe8);

  /* Shadow scale — color portion shifts per theme so depth reads correctly */
  --shadow-sm: 0 2px 4px   light-dark(rgba(0,0,0,0.05), rgba(0,0,0,0.20));
  --shadow-md: 0 4px 8px   light-dark(rgba(0,0,0,0.10), rgba(0,0,0,0.30));
  --shadow-lg: 0 8px 16px  light-dark(rgba(0,0,0,0.15), rgba(0,0,0,0.40));
  --shadow-xl: 0 12px 24px light-dark(rgba(0,0,0,0.20), rgba(0,0,0,0.50));
  --shadow: var(--shadow-md); /* Backward-compat alias */

  /* Layering backgrounds */
  --bg-primary:   light-dark(#f8f9fa,        #0b1522);
  --bg-secondary: light-dark(var(--white),   #132133);
  --bg-tertiary:  light-dark(#f3f4f6,        #1a2b40);

  /* Borders */
  --border-primary:   light-dark(#e2e8f0, rgba(255,255,255,0.10));
  --border-secondary: light-dark(#d1d5db, rgba(255,255,255,0.08));

  /* Text variations */
  --text-muted: #6b7280;
  --glow: 0 0 18px light-dark(color-mix(in oklch, var(--brand-blue) 20%, transparent), rgba(96,165,250,0.30));

  /* ---- NWS WaWA Canonical Color Codes (theme-constant) ----
     Source: https://www.weather.gov/help-map  (NWS Watch/Warning/Advisory color table)
     These map the site's severity tiers to the colors NWS uses for the same
     event types, so trained spotters see the same signal here as on NWS products.
       --nws-warning-red    = Tornado Warning             (#FF0000)
       --nws-severe-orange  = Severe Thunderstorm Warning (#FFA500)
       --nws-watch-yellow   = Tornado Watch               (#FFFF00)
       --nws-advisory-cyan  = Severe Weather Statement    (#00FFFF)
       --nws-flood-green    = Flood Warning               (#00FF00)
  -------------------------------------------- */
  --nws-warning-red:   #FF0000;
  --nws-severe-orange: #FFA500;
  --nws-watch-yellow:  #FFFF00;
  --nws-advisory-cyan: #00FFFF;
  --nws-flood-green:   #00FF00;

  /* Header-gradient stops — composed into linear-gradient() below */
  --header-grad-start:    light-dark(#001f3f, #0a3d62);
  --header-grad-end:      light-dark(#003366, #1e40af);
  /* Table header (.repeater-table th) — flat/solid, a touch lighter than the
     main card header so the two don't compete visually. A gradient here read
     poorly (banding against the table's hard cell borders), so this is a
     single solid color, not a linear-gradient — derived from the card
     header's end-stop token via color-mix() so it stays theme-aware
     automatically. */
  --header-bg-th: color-mix(in oklch, var(--header-grad-end) 80%, white 20%);

  --header-gradient:        linear-gradient(135deg, var(--header-grad-start) 0%, var(--header-grad-end) 100%);
  --header-gradient-red:    linear-gradient(135deg, #8B0000 0%, var(--nws-warning-red) 100%);
  --header-gradient-green:  linear-gradient(135deg, #116045 0%, #075F5A 100%);
  --header-gradient-blue:   linear-gradient(135deg, var(--brand-blue) 0%, var(--brand-blue-dark) 100%);
  --header-gradient-yellow: linear-gradient(135deg, #B38F00 0%, var(--nws-watch-yellow) 100%);
  --header-gradient-orange: linear-gradient(135deg, #B36F00 0%, var(--nws-severe-orange) 100%);

  --title-grad-start: #99ACAF;
  --title-grad-end:   #99A1AF;
  --title-solid:      light-dark(#381aff, #7a5fff);

  --radius: 12px;
  /* T1.1 (2026-05-16): property-specific transition alias — replaces the old
     `all 0.3s ease` token. Covers the common animatable properties without
     recomputing layout for unrelated changes (e.g. dark-mode toggle, font swaps).
     Round-2 cleanup (2026-05-16): legacy --transition token deleted; nothing
     referenced it after Tier 1 migration was complete. */
  --t-fast: background-color 160ms ease, color 160ms ease, border-color 160ms ease,
            transform 160ms ease, box-shadow 160ms ease, opacity 160ms ease;

  /* T2.9 (2026-05-16): focus-ring tokens.
     Single source of truth for keyboard focus outlines. Apply via
     :focus-visible on any interactive element. The 70%-opacity color-mix
     keeps the ring readable against both light and dark backgrounds without
     a separate dark-mode definition. */
  --focus-ring:        2px solid color-mix(in oklch, var(--accent-blue) 75%, transparent);
  --focus-ring-offset: 3px;

  /* T3.14 (2026-05-16): fluid type scale.
     Each token scales smoothly between mobile-min and desktop-max via clamp().
     Eliminates the need for media-query font-size overrides. The 1.25× modular
     scale matches the existing visual rhythm. Adopt incrementally — currently
     applied to h2, h3, .card-header h2. */
  --text-xs:   clamp(0.75rem,  0.70rem + 0.25vw, 0.85rem);
  --text-sm:   clamp(0.875rem, 0.80rem + 0.40vw, 1rem);
  --text-base: clamp(1rem,     0.95rem + 0.25vw, 1.125rem);
  --text-lg:   clamp(1.125rem, 1.00rem + 0.60vw, 1.375rem);
  --text-xl:   clamp(1.25rem,  1.10rem + 0.75vw, 1.5rem);
  --text-2xl:  clamp(1.5rem,   1.30rem + 1.00vw, 1.875rem);
  --text-3xl:  clamp(2rem,     1.60rem + 2.00vw, 3rem);

  --alert-warning-bg:   light-dark(#ffe6e6, rgba(230, 57, 70, 0.25));
  --alert-watch-bg:     light-dark(#fff4e6, rgba(244, 162, 97, 0.25));
  --alert-other-bg:     light-dark(#e3f2fd, rgba(96, 165, 250, 0.25));
  --alert-header-color: light-dark(#333,    var(--white));
  --alert-desc-color:   var(--text-secondary);

  --nav-btn-bg:          light-dark(var(--white),            rgba(11, 21, 34, 0.9));
  /* T1.4 (2026-05-16): dark-mode value was var(--border-primary) which is
     rgba(255,255,255,0.10) — invisible as text color. Latent bug from the
     original four-block theme structure. Both modes now use --text-primary. */
  --nav-btn-color:       light-dark(var(--text-primary),     var(--text-primary));
  --nav-btn-border:      light-dark(rgba(0, 0, 0, 0.1),      rgba(255,255,255,0.1));
  --nav-btn-bg-hover:    light-dark(color-mix(in oklch, var(--brand-blue) 15%, transparent), rgba(96, 165, 250, 0.20));
  --nav-btn-color-hover: light-dark(var(--accent-blue-dark), var(--blue-400));
}

/* ---------- Manual Theme Override (wins over system preference) ----------
   These selectors only flip color-scheme; every light-dark() token above
   re-evaluates automatically. No token duplication needed.
   --------------------------------------------------------------------- */
html[data-theme="light"] { color-scheme: light; }
html[data-theme="dark"]  { color-scheme: dark;  }

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

html {
  scroll-behavior: smooth;
  font-size: 100%;
  scroll-padding-top: 200px; /* Offset for sticky header + page-nav on desktop */
}

@media (max-width: 768px) {
  html {
    scroll-padding-top: 100px; /* Offset for sticky header on mobile */
  }
}

/* ---------- View Transitions for Theme Toggle (2026-05-16) ----------
   When the user flips the theme toggle, components.js wraps the data-theme
   change in document.startViewTransition(). Supporting browsers (Baseline
   2024) snapshot the page before + after and crossfade between them, so
   colors shift smoothly instead of snapping. Tuned to 220ms ease to match
   the site's --t-fast duration. Reduced-motion users bypass the transition
   in JS, but ALSO get 0.01ms below via the Patch 1 guard — belt + braces. */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 220ms;
  animation-timing-function: ease;
}

/* ---------- Global Focus Ring (T2.9, 2026-05-16) ----------
   :focus-visible only triggers for keyboard navigation, so mouse-clicks
   no longer leave lingering outlines. The ring uses tokens defined in
   :root so dark-mode and brand changes propagate automatically.
   Component-specific overrides (e.g. .btn already had no outline) may
   still set their own; this is the base layer.
   --------------------------------------------------------------------- */
:where(a, button, [role="button"], input, select, textarea, summary,
       [tabindex]:not([tabindex="-1"])):focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
  border-radius: 4px;
}

/* ---------- Reduced-Motion Accessibility (PATCH 1, 2026-05-16) ----------
   Honors the user's prefers-reduced-motion: reduce setting by collapsing all
   animations, transitions, and smooth scrolling. Critical for users with
   vestibular disorders. Override individual rules with explicit
   @media (prefers-reduced-motion: no-preference) blocks if a specific
   transition is essential for comprehension.
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, sans-serif;
  line-height: 1.6;
  color: var(--text-primary);
  background: var(--bg-body);
  padding: 1rem;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ---------- Typography ---------- */
h1, h2, h3, h4, h5, h6 {
  margin: 1.5rem 0 1rem;
  color: var(--text-primary);
  font-weight: 700;
  line-height: 1.3;
  /* PATCH 5 (2026-05-16): balance produces visually-even multi-line headings
     instead of one long line + one short orphan. Baseline 2024. */
  text-wrap: balance;
}

/* T3.14 (2026-05-16): all headings on the fluid type scale */
h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl);  }

p, li, td, th {
  margin-bottom: 0.8rem;
  color: var(--text-secondary);
  font-size: 1rem;
  /* T1.3 (2026-05-16): pretty avoids single-word orphans on the last line.
     Baseline 2024 in Chromium; Safari fallback gracefully to default wrap. */
  text-wrap: pretty;
}

a {
  color: var(--accent-blue);
  text-decoration: underline;
  word-break: break-word;
  transition: var(--t-fast);
}

a:hover, a:focus-visible {
  color: var(--accent-blue-dark);
  outline: none;
}

.center { text-align: center; }

/* --- HEADER COMPONENT --- */

.header {
  /* Opaque --brand-ink (2026-10-22; was translucent slate): the logo's own
     background, so the logo sits on the bar with no visible square. */
  background: var(--brand-ink);
  border-bottom: 2px solid rgba(255, 255, 255, 0.1);
  position: sticky;
  top: 0;
  z-index: 1000;
  /* T4.16 (2026-05-16): subtler default shadow; deeper shadow added by
     .is-stuck class once the page has scrolled past the top sentinel.
     Gives the sticky header more "lift" once it's actively floating
     over content. JS toggles .is-stuck via IntersectionObserver. */
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
  transition: box-shadow 200ms ease, background-color 200ms ease, border-color 200ms ease;
}

.header.is-stuck {
  border-bottom-color: rgba(255, 255, 255, 0.15);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}

.header-content {
  max-width: 1280px;
  margin: 0 auto;
  padding: 1rem 1.5rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
}

/* Logo - left side */
.logo {
  display: flex;
  align-items: center;
  gap: 1rem;
  text-decoration: none;
  color: inherit;
  transition: var(--t-fast);
}

.logo:hover { opacity: 0.85; }

.logo-image {
  width: 64px;
  height: 64px;
  object-fit: contain;
  border-radius: 0.5rem;
  /* No tile, padding or shadow (2026-10-22): on --brand-ink the logo's own
     background meets the header with no edge. */
}

.logo-text h1 {
  font-size: 1.75rem;
  font-weight: 800;
  margin: 0;
  line-height: 1.2;
  color: #e2e8f0;
}

.logo-text p {
  font-size: 0.875rem;
  color: var(--slate-400);
  margin: 0.25rem 0 0;
  line-height: 1.3;
}

/* Desktop navigation - right side */
#desktopNav {
  display: flex;
  gap: 1.5rem;
  align-items: center;
}

#desktopNav a {
  color: #e2e8f0;
  text-decoration: none;
  font-weight: 600;
  transition: var(--t-fast);
  font-size: 1rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.5rem;
}

#desktopNav a:hover,
#desktopNav a:focus-visible {
  color: var(--blue-400);
  background: rgba(96, 165, 250, 0.1);
}

/* Active-page indicator (2026-08-27) — mirrors the .page-nav .is-active
   filled-pill treatment so the current page reads the same way the
   current in-page section does. */
#desktopNav a.is-active {
  color: var(--white);
  background: var(--brand-blue);
}

#desktopNav a.is-active:hover,
#desktopNav a.is-active:focus-visible {
  color: var(--white);
  background: var(--brand-blue-dark);
}

/* Theme toggle in nav */
#themeToggle,
#themeToggleMobile {
  transition: var(--t-fast);
  color: #e2e8f0;
}

#themeToggle svg,
#themeToggleMobile svg { stroke: #e2e8f0; }

#themeToggle:hover {
  color: var(--blue-400);
  transform: scale(1.1);
}

#themeToggle:hover svg { stroke: var(--blue-400); }

/* Theme control icons (2026-10-22): Auto / Dark / Light, the same three as
   the net control dashboard. CSS picks the icon from data-theme on <html>
   (no attribute = Auto), so it is right as soon as the header is drawn. */
#themeToggleMobile { gap: 0.5rem; }
.theme-toggle .theme-icon { display: none; }
.theme-toggle .icon-auto { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-auto,
:root[data-theme="light"] .theme-toggle .icon-auto { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-dark,
:root[data-theme="light"] .theme-toggle .icon-light { display: block; }

/* Mobile hamburger button */
.mobile-menu-btn {
  display: none;
  background: none;
  border: none;
  color: #e2e8f0;
  cursor: pointer;
  padding: 0.75rem;
  min-width: 44px;
  min-height: 44px;
  align-items: center;
  justify-content: center;
}

/* Mobile navigation overlay */
.mobile-nav {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: color-mix(in oklch, var(--brand-ink) 98%, transparent); /* More opaque dark background */
  backdrop-filter: blur(12px);
  z-index: 999;
  padding-top: 80px;
  opacity: 0;
  transform: translateY(-100%);
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.mobile-nav.active {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 0;
  opacity: 1;
  transform: translateY(0);
}

.mobile-nav a,
.mobile-nav button {
  color: var(--white); /* Always white text on dark mobile menu */
  text-decoration: none;
  font-size: 1.5rem;
  font-weight: 600;
  padding: 1.25rem 2rem;
  width: 100%;
  text-align: center;
  background: color-mix(in oklch, var(--brand-navy) 60%, transparent); /* Semi-transparent dark background */
  border-bottom: 1px solid rgba(71, 85, 105, 0.5);
  transition: all 0.3s;
  min-height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.mobile-nav a:hover,
.mobile-nav a:active,
.mobile-nav button:hover,
.mobile-nav button:active {
  background: rgba(59, 130, 246, 0.25);
  color: var(--blue-400);
}

/* Active-page indicator (2026-08-27) — left accent border (same idiom as
   .section-subheader) plus a tinted fill so the current page stands out
   from the rest of the full-width mobile menu rows. */
.mobile-nav a.is-active {
  background: rgba(59, 130, 246, 0.2);
  color: var(--blue-400);
  border-left: 4px solid var(--accent-blue);
}

.mobile-nav a.is-active:hover,
.mobile-nav a.is-active:active {
  background: rgba(59, 130, 246, 0.3);
}

/* Ensure theme toggle icons in mobile menu are white */
.mobile-nav button svg { stroke: var(--white); }

.mobile-nav button:hover svg { stroke: var(--blue-400); }

/* Responsive behavior */
@media (max-width: 768px) {
  #desktopNav {
    display: none;
  }

  .mobile-menu-btn {
    display: flex;
  }

  .logo-image {
    width: 48px;
    height: 48px;
  }

  .logo-text {
    max-width: calc(100vw - 180px); /* Leave room for logo image + hamburger */
  }

  .logo-text h1 {
    font-size: 1.25rem;
    line-height: 1.1;
  }

  .logo-text p {
    font-size: 0.7rem;
    line-height: 1.2;
    white-space: normal; /* Allow wrapping */
    word-wrap: break-word;
  }

  .header-content {
    padding: 0.75rem 1rem;
    gap: 0.75rem; /* Reduce gap on mobile */
  }
}

@media (min-width: 769px) {
  .mobile-menu-btn {
    display: none !important;
  }
}

/* --- MODAL SYSTEM --- */

/* T3.13 (2026-05-16): modern modal enter transition with @starting-style.
   Replaces the prior `animation: fadeIn` pattern. The browser interpolates
   from the @starting-style state to the .active state on the FIRST style
   computation after .active is added. `transition-behavior: allow-discrete`
   (Baseline 2024) lets `display` participate in the transition so it can
   animate from `display: none`. Same visual outcome, cleaner mechanism,
   plays nicely with prefers-reduced-motion (Patch 1) automatically. */
.modal-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(10px);
  z-index: 1000;
  display: none;
  opacity: 0;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  transition: opacity 200ms ease, display 200ms ease allow-discrete;
}

.modal-backdrop.active {
  display: flex;
  opacity: 1;
}

@starting-style {
  .modal-backdrop.active { opacity: 0; }
  .modal-backdrop.active .modal-content {
    opacity: 0;
    transform: translateY(12px);
  }
}

.modal-content {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  max-width: 42rem;
  width: 100%;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-xl);
  opacity: 1;
  transform: translateY(0);
  transition: opacity 220ms ease, transform 220ms ease;
}

.modal-header {
  padding: 1.5rem;
  background: var(--header-gradient-red);
  color: white;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-radius: var(--radius) var(--radius) 0 0;
}

/* Ensure all text elements in modal headers are white with consistent styling */
.modal-header h1,
.modal-header h2,
.modal-header h3,
.modal-header h4,
.modal-header h5,
.modal-header h6 {
  margin: 0;
  font-size: var(--text-xl); /* T3.14: fluid 1.25rem → 1.5rem (was fixed 1.4rem) */
  font-weight: 700;
  color: white;
  line-height: 1.3;
}

/* Standardized modal header color variants */
/* Used by both alert modals and HWO modal */
.modal-header--red { background: var(--header-gradient-red); }

.modal-header--yellow { background: var(--header-gradient-yellow); }

/* NWS canonical yellow (#FFFF00) requires dark text + dark close button for contrast */
.modal-header--yellow .modal-title,
.modal-header--yellow h1,
.modal-header--yellow h2,
.modal-header--yellow h3,
.modal-header--yellow h4,
.modal-header--yellow h5,
.modal-header--yellow h6 { color: var(--black); text-shadow: 0 1px 1px rgba(255,255,255,0.4); }
.modal-header--yellow .modal-close { color: var(--black); background: rgba(0, 0, 0, 0.15); }

.modal-header--blue { background: var(--header-gradient-blue); }

.modal-header--green { background: var(--header-gradient-green); }

.modal-title {
  margin: 0;
  font-size: var(--text-xl); /* T3.14: fluid scale (was fixed 1.4rem) */
  font-weight: 700;
  color: white;
}

.modal-close {
  /* Tonal tint via color-mix matches the Patch-3 badge / T2.7 pill vocabulary.
     OKLCH-space mixing keeps the tint perceptually consistent regardless of
     which header gradient sits behind it. */
  background: color-mix(in oklch, white 20%, transparent);
  border: none;
  color: white;
  font-size: 1.5rem;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: var(--t-fast);
}

.modal-close:hover {
  background: color-mix(in oklch, white 30%, transparent);
  transform: rotate(90deg);
}

.modal-body {
  padding: 1.5rem;
  color: var(--text-secondary);
}

/* fadeIn is still used by .alert-item (line ~1671) for initial render.
   slideUp was orphaned by T3.13 (modal animations replaced by
   @starting-style transitions) and has been removed. */
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(-10px); }
  to { opacity: 1; transform: translateY(0); }
}

/* --- PAGE NAVIGATION - Sticky horizontal bar (below header) --- */

.page-nav {
  position: sticky;
  top: 90px; /* Stick below the header (header height ~90px) */
  z-index: 998;
  background: color-mix(in oklch, var(--brand-navy) 92%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
  padding: 0.75rem 0;
}

/* Page-nav list - horizontal layout */
.page-nav .nav-list {
  list-style: none;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
  align-items: center;
  margin: 0;
  padding: 0 1rem;
  max-width: 1400px;
  margin-left: auto;
  margin-right: auto;
}

/* Page-nav links — true pill shape, tokenized accent colors
   T2.7 (2026-05-16): border-radius 8px → 999px (fully pill, scales w/ content).
                      Hard-coded rgba(96,165,250,*) replaced with color-mix() so
                      pills auto-theme with --accent-blue.
   T2.6 (2026-05-16): hover replaces translateY(-2px) lift with deeper tonal fill.
   T2.8 (2026-05-16): .is-active styled distinctly for scroll-spy.
   FIX  (2026-05-16): pill text is light-on-dark in BOTH themes because the
                      .page-nav background is dark navy (--brand-navy since 2026-10-22)
                      regardless of theme. Previously used var(--text-primary)
                      which is near-black in light mode → unreadable. */
.page-nav .nav-list a {
  display: inline-block;
  padding: 0.5rem 1.1rem;
  font-weight: 600;
  text-decoration: none;
  border-radius: 999px;
  background: color-mix(in oklch, var(--accent-blue) 15%, transparent);
  color: #e2e8f0;
  border: 1px solid color-mix(in oklch, var(--accent-blue) 30%, transparent);
  transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
  text-align: center;
  font-size: 0.9rem;
  white-space: nowrap;
}

.page-nav .nav-list a:hover,
.page-nav .nav-list a:focus-visible {
  background: color-mix(in oklch, var(--accent-blue) 28%, transparent);
  border-color: color-mix(in oklch, var(--accent-blue) 50%, transparent);
  color: var(--white);
}

/* Active section indicator — fills in fully when section is in view */
.page-nav .nav-list a.is-active {
  background: var(--brand-blue);
  border-color: var(--brand-blue);
  color: var(--white);
}

/* Hide toggle button on desktop - only visible on mobile */
.page-nav-toggle { display: none; }

/* Mobile adjustments for page-nav - hamburger menu */
@media (max-width: 768px) {
  .page-nav {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 997;
    background: transparent;
    backdrop-filter: none;
    border: none;
    box-shadow: none;
    padding: 0;
  }

  /* Page-nav toggle button (real button for accessibility) */
  .page-nav-toggle {
    position: fixed;
    bottom: 1rem;
    left: 1rem;
    background: linear-gradient(135deg, #16a34a 0%, #15803d 100%);
    color: white;
    padding: 0.85rem 1.25rem;
    border-radius: 50px;
    font-weight: 800;
    font-size: 0.9rem;
    cursor: pointer;
    box-shadow: 0 6px 20px rgba(22, 163, 74, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.2);
    z-index: 998;
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    border: 2px solid rgba(255, 255, 255, 0.15);
    letter-spacing: 0.05em;
    font-family: inherit;
  }

  .page-nav-toggle:active,
  .page-nav-toggle:focus-visible {
    outline: 3px solid rgba(255, 255, 255, 0.6);
    outline-offset: 2px;
    transform: scale(0.95);
    box-shadow: 0 3px 12px rgba(22, 163, 74, 0.3), inset 0 2px 4px rgba(0, 0, 0, 0.2);
  }

  /* Page-nav list - hidden by default on mobile */
  .page-nav .nav-list {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: color-mix(in oklch, var(--brand-ink) 85%, transparent); /* More transparent */
    backdrop-filter: blur(16px);
    flex-direction: column;
    justify-content: center; /* Center vertically */
    align-items: center;
    gap: 1.5rem;
    padding: 2rem;
    transform: translateX(-100%);
    transition: transform 0.3s ease;
    z-index: 997;
    overflow-y: auto; /* Allow scrolling if needed */
  }

  /* Menu header - "Jump to Section" - same size as buttons */
  .page-nav .nav-list::before {
    content: "Jump to Section";
    display: block;
    width: 100%;
    max-width: 320px;
    text-align: center;
    font-size: 1.15rem; /* Same as buttons */
    font-weight: 700;
    color: var(--white);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    padding: 1.25rem 2.5rem 1.25rem 3.5rem; /* Extra left padding for icon */
    border-radius: 16px;
    background: linear-gradient(135deg, rgba(96, 165, 250, 0.25) 0%, rgba(59, 130, 246, 0.35) 100%);
    border: 2px solid rgba(96, 165, 250, 0.5);
    box-shadow: 0 8px 24px rgba(59, 130, 246, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(12px);
    margin-bottom: 1rem; /* Extra space below header */
    position: relative;
  }

  /* Heroicon bolt icon as background */
  .page-nav .nav-list::after {
    content: "";
    position: absolute;
    left: 2rem;
    top: 50%;
    transform: translateY(-50%);
    width: 20px;
    height: 20px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='2' stroke='%23ffffff'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z' /%3E%3C/svg%3E");
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
  }

  /* Show menu when active */
  .page-nav.active .nav-list {
    transform: translateX(0);
  }

  .page-nav .nav-list a {
    font-size: 1.15rem;
    font-weight: 600;
    padding: 1.25rem 2.5rem;
    width: 100%;
    max-width: 320px;
    text-align: center;
    background: linear-gradient(135deg, rgba(59, 130, 246, 0.15) 0%, rgba(96, 165, 250, 0.1) 100%);
    border: 2px solid rgba(96, 165, 250, 0.35);
    border-radius: 16px;
    color: var(--white);
    text-decoration: none;
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.08);
    position: relative;
    overflow: hidden;
  }

  /* Shimmer effect on buttons */
  .page-nav .nav-list a::before {
    content: "";
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
    transition: left 0.5s ease;
  }

  .page-nav .nav-list a:active::before {
    left: 100%;
  }

  .page-nav .nav-list a:active {
    transform: scale(0.97);
    background: linear-gradient(135deg, rgba(96, 165, 250, 0.3) 0%, rgba(59, 130, 246, 0.25) 100%);
    border-color: rgba(96, 165, 250, 0.6);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2), inset 0 2px 4px rgba(0, 0, 0, 0.1);
  }
}

/* --- CALLOUT – <aside class="callout"> for tips, warnings, notes --- */

.callout {
  background: var(--card-bg);
  backdrop-filter: blur(12px);
  border-left: 6px solid var(--accent-blue);
  border-right: 6px solid var(--accent-blue);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  margin: 1.5rem 0;
  box-shadow: var(--shadow);
  position: relative;
  overflow: hidden;
  transition: var(--t-fast);
}

.callout:hover {
  /* T2.6 residual (2026-05-16): replaced translateY(-2px) lift with tonal
     shadow shift. Matches .btn / .sub-card / .page-nav vocabulary. */
  box-shadow: var(--shadow-md);
}

.callout::before {
  content: '';
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background: linear-gradient(135deg, transparent, color-mix(in oklch, var(--brand-blue) 5%, transparent));
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
}

.callout:hover::before { opacity: 1; }

.callout::after {
  content: "";
  position: absolute;
  top: 0.75rem; right: 1rem;
  font-size: 1.8rem;
  opacity: 0.2;
  pointer-events: none;
}

/* Variants – only change left border */
.callout.warning   { border-left-color: var(--accent-red); border-right-color: var(--accent-red);   }
.callout.note      { border-left-color: #9c27b0; border-right-color: #9c27b0;  }

/* ---------- Card Component (Reusable) ---------- */
.card {
  background: var(--card-bg);
  backdrop-filter: blur(14px);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-md), var(--glow);
  transition: var(--t-fast);
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-xl);
}

/* Shared header core – applies to every card header */
.card-header {
  padding: 1rem 1.5rem;
  text-align: center;
  background: var(--header-gradient);   /* default */
}

/* Header title – keep one rule */
.card-header h2 {
  margin: 0;
  font-size: var(--text-xl); /* T3.14: fluid scale */
  color: var(--white);
  font-weight: 700;
  text-wrap: balance; /* PATCH 5: even line breaks on long card titles */
}

/* Card header color modifiers */
.card-header--red    { background: var(--header-gradient-red); }
.card-header--green  { background: var(--header-gradient-green); }
.card-header--blue   { background: var(--header-gradient-blue); }
.card-header--yellow { background: var(--header-gradient-yellow); }

/* NWS canonical yellow (#FFFF00) requires dark text for WCAG contrast */
.card-header--yellow h2 { color: var(--black); text-shadow: 0 1px 1px rgba(255,255,255,0.4); }

.card-img {
  width: 100%;
  height: auto;
  max-height: 70vh;           /* prevent huge images on tall screens */
  display: block;
  margin: 0 auto;             /* horizontal center */
  border-radius: var(--radius) var(--radius) 0 0;
  object-fit: contain;        /* show full image, no cropping */
  background: var(--card-bg); /* fallback bg if image has transparency */
}

/* ---------- Photo Archive Grid ---------- */
.photo-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  margin: 1.5rem auto;
  max-width: 1200px;
  padding: 0 1rem;
}

@media (min-width: 640px) {
  .photo-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Uniform tile height for consistent grid appearance */
.photo-grid .card-img {
  height: 240px;
  object-fit: cover;
}

/* T3.11 (2026-05-16): establish .card-body as a query container so its
   children (notably .sub-cards) can size based on the card's actual width
   instead of the viewport. Means sub-cards collapse correctly even when
   placed in a narrower column (sidebar, modal, half-width layout). */
.card-body {
  padding: 1.5rem;
  container-type: inline-size;
  container-name: cardbody;
}

/* ---------- Sub-Cards (nested cards inside card-body) ---------- */
.sub-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

/* T3.11: container queries — sub-cards respond to .card-body's width,
   not the viewport. 600px threshold accounts for card-body's 1.5rem padding
   on each side (~530px effective at 768px viewport). */
@container cardbody (min-width: 600px) {
  .sub-cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

@container cardbody (min-width: 900px) {
  .sub-cards--3col {
    grid-template-columns: repeat(3, 1fr);
  }
}

.sub-card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  padding: 1.25rem;
  /* T2.6: dropped `transform` from transition list since hover no longer lifts */
  transition: border-color 160ms ease, background-color 160ms ease, box-shadow 160ms ease;
}

.sub-card:hover {
  /* T2.6 (2026-05-16): tonal-shift hover — accent border + faint accent tint
     in background. No translateY lift. Less motion = also helps reduced-motion. */
  border-color: var(--accent-blue);
  background: color-mix(in oklch, var(--accent-blue) 4%, var(--card-bg));
  box-shadow: var(--shadow-md);
}

.sub-card h4 {
  margin: 0 0 0.5rem 0;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-primary);
}

.sub-card p {
  margin: 0;
  font-size: 0.95rem;
  color: var(--text-secondary);
  line-height: 1.5;
}

.sub-card .btn {
  margin-top: 0.75rem;
}

.sub-card .link-url {
  display: inline-block;
  margin-top: 0.5rem;
}

/* Full-width sub-card spanning all columns */
.sub-card--full { grid-column: 1 / -1; }

/* Ordered list inside a sub-card */
.sub-card ol {
  margin: 0;
  padding-left: 1.5rem;
  line-height: 1.8;
}

/* Action buttons inside a sub-card need top breathing room */
.sub-card .action-buttons {
  margin-top: 1rem;
}

/* ---------- Activation Flow (numbered infographic step sequence) ----------
   Added 2026-08-16: reusable component for presenting a strict, ordered
   sequence (as opposed to .sub-cards, which implies an unordered set).
   Horizontal icon-circle + numbered-badge row on wide viewports, collapses
   to a left-rail vertical timeline on narrow ones. Container-query driven
   (matches the .sub-cards / T3.11 convention) so it responds to the card's
   actual width, not the viewport. Default accent is flat --accent-blue;
   the --escalate modifier shifts each step's color from blue → indigo →
   orange → red to visually imply rising urgency (used by Spotter Activation
   on wx4ptc.html). First used on wx4ptc.html's Spotter Activation and
   Activation Flow sections — see CLAUDE.md changelog 2026-08-16. */
.activation-flow {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.75rem;
  position: relative;
  counter-reset: activation-step;
}

/* Narrow (default): vertical rail running down the left side, behind the
   icon column. 34px = half the 68px icon width, centering the line on it. */
.activation-flow::before {
  content: "";
  position: absolute;
  top: 34px;
  bottom: 34px;
  left: 34px;
  width: 3px;
  background: var(--flow-line, var(--accent-blue));
  opacity: 0.35;
  z-index: 0;
}

@container cardbody (min-width: 640px) {
  .activation-flow {
    grid-template-columns: repeat(var(--flow-count, 4), 1fr);
    gap: 0;
  }

  .activation-flow::before {
    top: 34px;
    bottom: auto;
    left: 12%;
    right: 12%;
    width: auto;
    height: 3px;
  }

  .activation-step {
    flex-direction: column;
    text-align: center;
    padding: 0 0.75rem;
  }

  .activation-step .step-icon {
    margin-bottom: 0.9rem;
  }
}

/* Escalating color variant — step color shifts blue -> indigo -> orange -> red */
.activation-flow--escalate {
  --flow-line: linear-gradient(90deg, var(--accent-blue), var(--accent-indigo), var(--accent-orange), var(--accent-red));
}
.activation-flow--escalate .activation-step:nth-child(1) { --step-color: var(--accent-blue); }
.activation-flow--escalate .activation-step:nth-child(2) { --step-color: var(--accent-indigo); }
.activation-flow--escalate .activation-step:nth-child(3) { --step-color: var(--accent-orange); }
.activation-flow--escalate .activation-step:nth-child(4) { --step-color: var(--accent-red); }

.activation-step {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 1rem;
  counter-increment: activation-step;
}

.step-icon {
  width: 68px;
  height: 68px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.7rem;
  flex-shrink: 0;
  background: var(--card-bg);
  border: 3px solid var(--step-color, var(--accent-blue));
  box-shadow: var(--shadow-sm);
  position: relative;
}

.step-icon::after {
  content: counter(activation-step);
  position: absolute;
  top: -6px;
  right: -6px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--step-color, var(--brand-blue));
  color: var(--white);
  font-size: 0.7rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-sm);
}

.activation-step h4 {
  margin: 0 0 0.4rem;
  font-size: var(--text-base);
  color: var(--text-primary);
}

.activation-step p {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

@container cardbody (min-width: 640px) {
  .activation-step h4 { text-align: center; }
  .activation-step p  { text-align: center; }
}

/* Section subheaders for visual separation */
.section-subheader {
  margin: 1.5rem 0 1rem 0;
  padding: 0.75rem 1.5rem;
  background: linear-gradient(135deg, color-mix(in oklch, var(--accent-blue) 8%, transparent), color-mix(in oklch, var(--accent-blue) 12%, transparent));
  border-left: 4px solid var(--accent-blue);
  border-radius: 8px;
  text-align: center;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: 0.025em;
}

.section-subheader:first-of-type { margin-top: 0; }

.link-url {
  display: inline-block;
  margin-top: 0.25rem;
  font-size: 0.9rem;
  color: var(--accent-blue);
  text-decoration: none;
  font-weight: 500;
  transition: var(--t-fast);
  word-break: break-word;
}

.link-url:hover,
.link-url:focus-visible {
  color: var(--accent-blue-dark);
  text-decoration: underline;
}

/* ---------- Content Buttons (refined to match nav buttons) ---------- */
.btn {
  --btn-bg: var(--brand-blue);
  --btn-bg-hover: var(--brand-blue-dark);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: 0.5rem 1rem;
  font-weight: 600;
  font-size: var(--text-sm); /* T3.14 follow-up: fluid scale (0.875rem → 1rem) */
  text-decoration: none;
  color: var(--white);
  background: var(--btn-bg);
  border: none;
  border-radius: 8px;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  /* PATCH 4: property-specific transitions (avoids `all` perf footgun) */
  transition: background-color 160ms ease, color 160ms ease,
              transform 160ms ease, box-shadow 160ms ease;
  text-align: center;
}

.btn:hover,
.btn:focus-visible {
  /* T2.6 (2026-05-16): tonal-shift hover. Replaces translateY(-2px) lift,
     which was 2020-era SaaS visual language. Modern pattern (Linear, Vercel,
     Stripe Press) keeps the button planted and just darkens the fill. */
  background: var(--btn-bg-hover);
  color: var(--white);
  box-shadow: var(--shadow-md);
}

.btn:active {
  /* Tactile press feedback — small scale instead of position shift */
  scale: 0.98;
  box-shadow: var(--shadow-sm);
}

/* Color modifiers with hover variants */
.btn-blue    { --btn-bg: var(--brand-blue); --btn-bg-hover: var(--brand-blue-dark); }
.btn-red     { --btn-bg: var(--accent-red); --btn-bg-hover: var(--accent-red-dark); }
.btn-green   { --btn-bg: var(--accent-green); --btn-bg-hover: var(--accent-green-dark); }
.btn-orange  { --btn-bg: var(--accent-orange); --btn-bg-hover: var(--accent-orange-dark); }
.btn-yellow  { --btn-bg: var(--accent-yellow); --btn-bg-hover: var(--accent-yellow-dark); color: var(--black);}
.btn-yellow:hover,
.btn-yellow:focus-visible { color: var(--black); }

/* ---------- Contact Grid Component ---------- */
.contact-grid {
  display: grid;
  gap: 1.5rem;
  margin-top: 1rem;
}

.contact-item {
  display: flex;
  gap: 1rem;
  padding: 1.5rem;
  background: light-dark(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.03));
  border: 1px solid var(--card-border);
  border-radius: 12px;
  transition: var(--t-fast);
}

.contact-item:hover {
  /* T2.6 residual (2026-05-16): dropped translateY lift; accent-tint
     background + accent border + soft shadow do the work. */
  background: light-dark(color-mix(in oklch, var(--accent-blue) 6%, var(--card-bg)), rgba(255, 255, 255, 0.08));
  border-color: var(--accent-blue);
  box-shadow: var(--shadow-md), 0 4px 12px light-dark(transparent, color-mix(in oklch, var(--brand-blue) 20%, transparent));
}

.contact-icon {
  font-size: 2.5rem;
  flex-shrink: 0;
  width: 60px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg,
    light-dark(color-mix(in oklch, var(--brand-blue) 10%, transparent), color-mix(in oklch, var(--brand-blue) 20%, transparent)),
    light-dark(color-mix(in oklch, var(--brand-blue) 5%, transparent), color-mix(in oklch, var(--brand-blue) 10%, transparent)));
  border-radius: 50%;
}

.contact-details {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.contact-name {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text-primary);
  margin: 0;
  line-height: 1.3;
}

.contact-role {
  font-size: 0.95rem;
  color: var(--text-secondary);
  margin: 0;
  line-height: 1.4;
}

.contact-link {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-top: 0.25rem;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--accent-blue);
  text-decoration: none;
  transition: var(--t-fast);
}

.contact-link:hover,
.contact-link:focus-visible {
  color: var(--accent-blue-dark);
  text-decoration: underline;
  transform: translateX(3px);
}

/* ---------- Forecast Maps Component (About Page) ---------- */
/* Single-column layout: map on top, office cards grid below */
.forecast-maps {
  display: flex;
  flex-direction: column;
  gap: 2rem;
  margin: 1.5rem 0;
}

/* NWS Offices List — sits below the map, no panel background needed */
.nws-offices-list {
  padding: 0;
}

.nws-office-links {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}

@media (min-width: 540px) {
  .nws-office-links { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 900px) {
  .nws-office-links { grid-template-columns: repeat(3, 1fr); }
}

/* Base card — left-border color swatch; full radius now that cards are in a grid */
.nws-office-item {
  border-radius: 10px;
  transition: var(--t-fast);
  border: 1px solid transparent;
  border-left: 4px solid transparent;
  overflow: hidden;
}

/* .nws-office-btn — button replacing the original <a> tag; inherits same layout */
.nws-office-btn {
  /* Reset browser button defaults */
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
  /* Match original <a> layout */
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.75rem 1rem;
  color: var(--text-primary);
  border-radius: 8px;
  transition: var(--t-fast);
}

.nws-office-btn strong {
  font-size: 1rem;
  display: block;
  margin-bottom: 0.25rem;
}

.office-coverage {
  font-size: 0.85rem;
  color: var(--text-secondary);
  font-style: italic;
}

/* Color-coded offices – colors match CWA_CONFIG in cwa-map.js exactly.
 * FFC #5c6bc0 indigo | GSP #039be5 sky blue | CAE #f4511e tomato
 * CHS #43a047 green  | JAX #fdd835 yellow   | TAE #d81b60 magenta
 *
 * Each card uses: thick left border (full opacity) + gradient bg + colored strong text.
 * The left border creates an immediate visual link to the matching map region. */
[class^="nws-office-"]:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }

.nws-office-ffc {
  border-left-color: #5c6bc0;
  border-color: rgba(92,107,192,0.25);
  background: linear-gradient(135deg, rgba(92,107,192,0.12), rgba(92,107,192,0.04));
}
.nws-office-ffc:hover { background: linear-gradient(135deg, rgba(92,107,192,0.22), rgba(92,107,192,0.09)); }
.nws-office-ffc a strong { color: #5c6bc0; }

.nws-office-gsp {
  border-left-color: #039be5;
  border-color: rgba(3,155,229,0.25);
  background: linear-gradient(135deg, rgba(3,155,229,0.12), rgba(3,155,229,0.04));
}
.nws-office-gsp:hover { background: linear-gradient(135deg, rgba(3,155,229,0.22), rgba(3,155,229,0.09)); }
.nws-office-gsp a strong { color: #039be5; }

.nws-office-cae {
  border-left-color: #f4511e;
  border-color: rgba(244,81,30,0.25);
  background: linear-gradient(135deg, rgba(244,81,30,0.12), rgba(244,81,30,0.04));
}
.nws-office-cae:hover { background: linear-gradient(135deg, rgba(244,81,30,0.22), rgba(244,81,30,0.09)); }
.nws-office-cae a strong { color: #f4511e; }

.nws-office-chs {
  border-left-color: #43a047;
  border-color: rgba(67,160,71,0.25);
  background: linear-gradient(135deg, rgba(67,160,71,0.12), rgba(67,160,71,0.04));
}
.nws-office-chs:hover { background: linear-gradient(135deg, rgba(67,160,71,0.22), rgba(67,160,71,0.09)); }
.nws-office-chs a strong { color: #43a047; }

.nws-office-jax {
  border-left-color: #f9a825;
  border-color: rgba(249,168,37,0.35);
  background: linear-gradient(135deg, rgba(253,216,53,0.16), rgba(253,216,53,0.06));
}
.nws-office-jax:hover { background: linear-gradient(135deg, rgba(253,216,53,0.26), rgba(253,216,53,0.12)); }
.nws-office-jax a strong { color: #f57f17; }

.nws-office-tae {
  border-left-color: #d81b60;
  border-color: rgba(216,27,96,0.25);
  background: linear-gradient(135deg, rgba(216,27,96,0.12), rgba(216,27,96,0.04));
}
.nws-office-tae:hover { background: linear-gradient(135deg, rgba(216,27,96,0.22), rgba(216,27,96,0.09)); }
.nws-office-tae a strong { color: #d81b60; }

/* ---------- Georgia CWA Interactive Map (about.html) ---------- */
.ga-cwa-map-wrap {
  position: relative;
  width: 100%;          /* flex item won't stretch when margin:auto is set — explicit width fixes it */
  max-width: 850px;
  margin-inline: auto;
}

#ga-cwa-map {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 12px;
  border: 1px solid var(--card-border);
  background: color-mix(in oklch, var(--card-bg) 80%, transparent);
}

/* County paths — fill set via JS; stroke creates visible county borders */
.ga-cwa-path {
  stroke: white;
  stroke-opacity: 0.4;
  stroke-width: 0.5;
  cursor: pointer;
  transition: stroke-opacity 100ms ease, stroke-width 100ms ease, fill-opacity 100ms ease;
  fill-opacity: 0.72;
}
.ga-cwa-path:hover,
.ga-cwa-path:focus {
  fill-opacity: 0.92;
  stroke-opacity: 0.9;
  stroke-width: 1.5;
  outline: none;
}
.ga-cwa-path:focus-visible {
  outline: 2px solid var(--accent-blue);
  outline-offset: 1px;
}

/* Floating tooltip */
.ga-cwa-tooltip {
  position: fixed;
  pointer-events: none;
  z-index: 2000;
  background: var(--card-bg);
  color: var(--text-primary);
  border: 1px solid var(--card-border);
  border-radius: 8px;
  padding: 0.4rem 0.8rem;
  font-size: 0.8rem;
  line-height: 1.3;
  white-space: nowrap;
  box-shadow: var(--shadow-md);
  opacity: 0;
  transition: opacity 80ms ease;
}
.ga-cwa-tooltip.visible { opacity: 1; }

/* Loading / error states */
.ga-cwa-loading {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1.5rem 0.5rem;
  color: var(--text-secondary);
  font-size: 0.9rem;
}
.ga-cwa-loading[hidden] { display: none; }
.ga-cwa-error { padding: 1rem 0.5rem; color: var(--text-secondary); font-size: 0.9rem; }
.ga-cwa-error[hidden] { display: none; }

/* Spinner animation */
@keyframes ga-cwa-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
.ga-cwa-spinner {
  animation: ga-cwa-spin 1s linear infinite;
  transform-origin: center;
  flex-shrink: 0;
}
@media (prefers-reduced-motion: reduce) {
  .ga-cwa-spinner { animation: none; }
}

/* Map hint text */
.ga-cwa-hint {
  font-size: 0.8rem;
  color: var(--text-secondary);
  margin: 0.5rem 0 0;
  text-align: center;
}


/* ---------- CWA County Modal (about.html — populated by cwa-map.js) ---------- */

/* Filter row: search input + "show all" clear chip */
.cwa-county-filter {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 1rem;
}

.cwa-county-filter-input {
  flex: 1;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--card-border);
  border-radius: 8px;
  background: var(--bg-tertiary);
  color: var(--text-primary);
  font-size: 0.9rem;
  font-family: inherit;
}
.cwa-county-filter-input:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
  border-color: var(--accent-blue);
}

/* Pill chip to clear the filter — hidden until a filter is active */
.cwa-county-filter-clear {
  white-space: nowrap;
  font-size: 0.8rem;
  padding: 0.35rem 0.8rem;
  border-radius: 999px;
  background: color-mix(in oklch, var(--accent-blue) 15%, transparent);
  color: var(--accent-blue);
  border: 1px solid color-mix(in oklch, var(--accent-blue) 30%, transparent);
  cursor: pointer;
  font-family: inherit;
  transition: var(--t-fast);
}
.cwa-county-filter-clear:hover {
  background: color-mix(in oklch, var(--accent-blue) 25%, transparent);
}
.cwa-county-filter-clear[hidden] { display: none; }

/* County list — 3-column grid; collapses to 2 on narrow screens */
.cwa-county-list {
  list-style: none;
  padding: 0;
  margin: 0 0 1.5rem;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.2rem 0.5rem;
}
@media (max-width: 480px) {
  .cwa-county-list { grid-template-columns: repeat(2, 1fr); }
}

.cwa-county-list li {
  padding: 0.2rem 0.4rem;
  border-radius: 4px;
  font-size: 0.85rem;
  color: var(--text-primary);
}
/* Exact-match highlight — shown when user clicked a county on the map */
.cwa-county-list li.cwa-county-highlighted {
  background: color-mix(in oklch, var(--accent-blue) 15%, transparent);
  color: var(--accent-blue);
  font-weight: 600;
}
.cwa-county-list li[hidden] { display: none; }

/* Bottom action row */
.cwa-modal-actions {
  border-top: 1px solid var(--card-border);
  padding-top: 1rem;
  display: flex;
  justify-content: flex-end;
}

/* ---------- Georgia County Search Bar (about.html forecastarea) ---------- */

/* Outer row: [label] [box-wrap] */
.county-search-wrap {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.5rem auto;
  padding: 1rem 1.25rem;
  max-width: 850px;
  flex-wrap: wrap;  /* stacks on narrow screens */
}

.county-search-label {
  white-space: nowrap;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-secondary);
  flex-shrink: 0;
}

/* Box-wrap: positions the dropdown relative to the input pill */
.county-search-box-wrap {
  position: relative;
  flex: 1;
  min-width: 220px;
}

/* Input pill — now the shared .search-pill component (see "Standard
   Search Pill" above). */

/* Dropdown results list — positioned under the pill */
.county-search-results {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 10px;
  list-style: none;
  padding: 0.35rem 0;
  margin: 0;
  z-index: 300;
  max-height: 280px;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
}
.county-search-results[hidden] { display: none; }

.county-search-result {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.45rem 1rem;
  cursor: pointer;
  font-size: 0.9rem;
  transition: background-color 120ms ease;
}
.county-search-result:hover,
.county-search-result[aria-selected="true"] {
  background: color-mix(in oklch, var(--accent-blue) 10%, transparent);
}

.county-search-result-name { color: var(--text-primary); }

/* CWA badge on each result */
.county-search-result-cwa {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: color-mix(in oklch, var(--accent-blue) 14%, transparent);
  color: var(--accent-blue);
  flex-shrink: 0;
}

/* ---------- SKYWARN Information Styles ---------- */
.skywarn-intro { margin-bottom: 2rem; }

.skywarn-intro h3 {
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 0.75rem;
}

.skywarn-intro p {
  font-size: 1rem;
  line-height: 1.7;
  color: var(--text-secondary);
}

.callout h4 {
  font-size: var(--text-base); /* T3.14 follow-up: fluid scale (1rem → 1.125rem) */
  font-weight: 700;
  color: var(--text-primary);
  margin: 0 0 0.5rem 0;
  text-align: center;
}

/* ---------- Standard Search Pill (2026-06-30) ----------
   Shared "search bar" component used by every search/filter box on the
   site: the repeater search (repeaters.html), the county alert filter
   (index.html), and the county coverage search (about.html). One pill:
   a magnifying-glass icon on the left when idle, which swaps to a red
   circular clear (×) button once there's a value, and swaps to an amber
   warning-triangle when the page reports a validation error (only the
   county alert filter currently uses the error state — the icon/clear
   behavior is identical everywhere, .has-error is simply never toggled
   on the other two). Established as the site standard so all search
   boxes look and behave identically instead of drifting independently.
   --------------------------------------------------------------- */
.search-pill {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: color-mix(in oklch, var(--accent-blue) 6%, var(--card-bg));
  border: 1px solid color-mix(in oklch, var(--accent-blue) 25%, var(--card-border));
  border-radius: 999px;
  padding: 0.6rem 0.9rem;
  transition: var(--t-fast);
}

.search-pill:focus-within {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent-blue) 18%, transparent);
}

.search-pill.has-error {
  border-color: var(--accent-yellow);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent-yellow) 18%, transparent);
}

.search-pill-icon {
  color: var(--text-secondary);
  flex-shrink: 0;
  display: flex;
  align-items: center;
}

/* Hide the idle search icon once a clear button or error triangle is
   showing — only one indicator occupies the leading slot at a time. */
.search-pill:has(.search-pill-clear:not([hidden])) .search-pill-icon,
.search-pill.has-error .search-pill-icon {
  display: none;
}

.search-pill-input {
  flex: 1;
  min-width: 0;
  background: none;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-size: var(--text-sm);
  padding: 0;
  font-family: inherit;
}

.search-pill-input::placeholder {
  color: var(--text-secondary);
  opacity: 0.8;
}

/* Hide the native browser clear-x on type="search" inputs — the pill's
   own .search-pill-clear button is the only clear control we want shown. */
.search-pill-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}

.search-pill-error-icon {
  display: none;
  align-items: center;
  justify-content: center;
  color: var(--accent-yellow);
  background: color-mix(in oklch, var(--accent-yellow) 15%, var(--card-bg));
  border: 1px solid color-mix(in oklch, var(--accent-yellow) 35%, transparent);
  border-radius: 50%;
  width: 1.6rem;
  height: 1.6rem;
  flex-shrink: 0;
  cursor: default;
  position: relative;
}

.search-pill.has-error .search-pill-error-icon {
  display: flex;
}

/* CSS tooltip — fires instantly, no OS delay */
.search-pill-error-icon::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  background: var(--card-bg);
  color: var(--text-primary);
  font-size: 0.75rem;
  white-space: nowrap;
  padding: 0.35rem 0.6rem;
  border-radius: 6px;
  border: 1px solid var(--card-border);
  box-shadow: var(--shadow);
  opacity: 0;
  pointer-events: none;
  transition: opacity 80ms ease;
  z-index: 300;
}

.search-pill-error-icon:hover::after {
  opacity: 1;
}

.search-pill-clear {
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in oklch, var(--accent-red) 15%, var(--card-bg));
  border: 1px solid color-mix(in oklch, var(--accent-red) 35%, transparent);
  cursor: pointer;
  color: var(--accent-red);
  padding: 0.3rem;
  flex-shrink: 0;
  border-radius: 50%;
  transition: var(--t-fast);
  width: 1.6rem;
  height: 1.6rem;
}

.search-pill-clear:hover {
  background: color-mix(in oklch, var(--accent-red) 30%, var(--card-bg));
  border-color: var(--accent-red);
}

.search-pill-clear:focus-visible {
  outline: 3px solid var(--accent-red);
  outline-offset: 2px;
}

.search-pill-clear[hidden] {
  display: none;
}

/* --- REPEATER SEARCH – FILTER AND SEARCH FUNCTIONALITY --- */

.search-container {
  margin-bottom: 1rem;
}

/* Faint fill in dark only (moved here from the old dark-mode block). */
.search-container input {
  background: light-dark(transparent, rgba(255, 255, 255, 0.05));
}

#search-results {
  font-size: var(--text-sm);
  margin-top: 0.5rem;
  padding-left: 0.25rem;
}

/* Hidden row when filtered out */
.repeater-table tbody tr.hidden {
  display: none;
}

/* --- REPEATER BADGE SYSTEM - Multi-tag support --- */

/* Tags container for repeater table - max 2 per row */
.tags-container {
  display: grid;
  grid-template-columns: repeat(2, auto);
  gap: 0.25rem;
  justify-content: center;
}

/* Base badge styling - shared properties */
/* ---------- Network / Tag Badges (PATCH 3, 2026-05-16) ----------
   Tonal style: light tint background + accent-colored text + faint border.
   Replaces the previous solid-fill + white-text "sticker" look, which read
   as Bootstrap 4 / 2018-era. Tonal badges recede until scanned, so they
   stop competing with card content for attention.
   Uses color-mix(in oklch, ...) (Baseline 2024) so derived shades stay
   perceptually consistent across hues. Border-radius bumped 4 → 999px
   (fully pill) to match modern conventions (Linear, Notion, Vercel).
   --------------------------------------------------------------------- */
.badge-wx4ema,
.badge-wx4ptc,
.badge-peach-state,
.badge-cherry-blossom,
.badge-se-linked,
.badge-county-only,
.badge-iplink,
.badge-desktop-only {
  --badge-hue: var(--accent-blue); /* per-class override below */
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  background: color-mix(in oklch, var(--badge-hue) 14%, transparent);
  color: var(--badge-hue);
  border: 1px solid color-mix(in oklch, var(--badge-hue) 32%, transparent);
  /* Property-specific transitions (no `all`) */
  transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}

.badge-wx4ema         { --badge-hue: var(--accent-green); }
.badge-wx4ptc         { --badge-hue: var(--accent-blue); }
.badge-peach-state    { --badge-hue: var(--accent-peach); }
.badge-cherry-blossom { --badge-hue: var(--accent-pink); }
.badge-se-linked      { --badge-hue: var(--accent-lightblue); }
.badge-county-only    { --badge-hue: var(--accent-orange); }
.badge-iplink         { --badge-hue: var(--accent-indigo); }
.badge-desktop-only   {
  --badge-hue: var(--text-secondary);
  font-size: 0.65rem;
  vertical-align: middle;
}

/* Progressive enhancement: if a browser doesn't support color-mix(), the
   transparent backgrounds above would still render but the border + text
   color stay readable. No explicit fallback needed for Baseline-2024 targets. */

/* --- SITE BANNER --- */
.site-banner {
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding: 0.9rem 1.5rem;
  background: var(--brand-blue);
  border-radius: var(--radius);
  margin: 1rem 0 1.25rem;
  border-left: 5px solid var(--brand-blue-dark);
}


.site-banner-link {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--white);
  text-decoration: none;
  white-space: nowrap;
  border: 1.5px solid rgba(255, 255, 255, 0.7);
  border-radius: 4px;
  padding: 0.3rem 0.85rem;
  transition: var(--t-fast);
}

.site-banner-link:hover {
  background: rgba(255, 255, 255, 0.15);
  border-color: var(--white);
}

.scanner-pulse-icon {
  display: inline-block;
  animation: scannerPulse 2s ease-in-out infinite;
}

@keyframes scannerPulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.25); opacity: 0.65; }
}

/* --- REPEATER TABLES --- */

.table-wrapper {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: 1rem 0;
  border-radius: var(--radius);
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

.repeater-table {
  width: 100%;
  min-width: 600px;
  border-collapse: collapse;
  font-size: 0.95rem;
  background: var(--card-bg);
}

.repeater-table th {
  background: var(--header-bg-th);
  color: white;
  font-weight: 700;
  text-align: left;
  padding: 0.9rem 1rem;
  position: sticky;
  top: 0;
  z-index: 10;
  font-size: var(--text-sm); /* T3.14 follow-up: fluid scale */
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.repeater-table th.center {
  text-align: center;
}

/* Validation Sources (repeater-validation.html): four separate <table>
   elements (Not Validated / Partially / Validated / Fully) share identical
   headers but were auto-sizing columns independently per table, so they
   drifted out of alignment with each other. A shared colgroup + fixed
   layout locks every table to the same column widths so they line up
   exactly regardless of each tier's content. */
.sources-table {
  table-layout: fixed;
}

.sources-table .col-location { width: 22%; }
.sources-table .col-freq     { width: 14%; }
.sources-table .col-county   { width: 12%; }
.sources-table .col-tags     { width: 22%; }
.sources-table .col-source   { width: 10%; }

/* Directory tables (repeaters.html): the Linked and Non-Linked tables are
   separate <table> elements, so with auto layout each sized its columns from
   its own content and the two drifted apart. Shared colgroup + fixed layout
   locks both to identical widths. Same technique as .sources-table above;
   min-width keeps columns readable and lets .table-wrapper scroll on mobile. */
.directory-table {
  table-layout: fixed;
  min-width: 900px;
}

.directory-table .col-location { width: 20%; }
.directory-table .col-freq     { width: 13%; }
.directory-table .col-county   { width: 10%; }
.directory-table .col-tags     { width: 23%; }
.directory-table .col-ilinks   { width: 14%; }
.directory-table .col-rlinks   { width: 20%; }

.repeater-table td {
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--card-border);
  vertical-align: top;
  transition: background 0.2s ease;
}

/* Zebra striping */
.repeater-table tbody tr:nth-child(even) {
  background: light-dark(rgba(0, 0, 0, 0.02), rgba(255, 255, 255, 0.03));
}

/* Frequency column */
.repeater-table .freq {
  font-family: 'Courier New', monospace;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
}

/* Links */
.repeater-table a {
  color: var(--accent-blue);
  text-decoration: underline;
  font-weight: 600;
}
.repeater-table a:hover { color: var(--accent-blue-dark); }
.repeater-table tbody tr td:first-child strong {
    /* example: change colour or weight */
    color: var(--accent-blue);
    font-weight: 700;
}

/* Scroll hint */
.scroll-hint {
  font-size: 0.8rem;
  color: var(--text-primary);
  text-align: center;
  margin: 0.5rem 0 0;
  font-style: italic;
}

/* Color coding: Red = Warning, Orange = Watch */

/* Base enhancements for the alert card */
.alert-card {
  box-shadow: var(--shadow), 0 0 20px rgba(0, 0, 0, 0.15); /* Stronger shadow for more pop */
  border: 1px solid var(--card-border); /* Subtle outline */
  transition: var(--t-fast);
}

.alert-card:hover {
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.2), var(--glow); /* Enhanced hover for visibility */
}

/* Alert container - ensure proper layout */
#alerts-container {
  display: block;
  width: 100%;
  overflow: visible;
  position: relative;
}

.alert-item {
  display: block;
  width: 100%;
  margin: 1rem 0;
  padding: 1rem;
  border-radius: 6px;
  border-left: 6px solid;
  background: var(--card-bg);
  backdrop-filter: blur(10px);
  font-size: 0.95rem;
  animation: fadeIn 0.5s ease-in;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  cursor: pointer;
  clear: both;
  position: relative;
}

.alert-item:hover {
  transform: translateX(4px);
  box-shadow: var(--shadow-md);
}

.alert-more {
  margin-top: 0.75rem;
  font-size: 0.875rem;
  color: var(--accent-blue);
  font-weight: 600;
  text-align: right;
  opacity: 0.8;
  transition: opacity 0.2s ease;
}

.alert-item:hover .alert-more {
  opacity: 1;
}

.alert-warning {
  border-left-color: var(--accent-red); /* Red */
  background-color: var(--alert-warning-bg);
}

.alert-watch {
  border-left-color: #f4a261; /* Orange */
  background-color: var(--alert-watch-bg);
}

.alert-other {
  border-left-color: #42a5f5; /* Blue */
  background-color: var(--alert-other-bg);
}

.alert-header {
  color: var(--alert-header-color);
  font-weight: bold;
  margin-bottom: 0.5rem;
}

.alert-description {
  color: var(--alert-desc-color);
  margin: 0.5rem 0;
  line-height: 1.5;
  /* T3.14 follow-up: alert body text on fluid scale so long descriptions
     read comfortably on small phones and don't shrink on big monitors. */
  font-size: var(--text-base);
  /* T1.3 follow-up (2026-05-16): explicit pretty wrap on alert text.
     Long NWS alert descriptions otherwise leave a single-word orphan on
     the last line which looks unprofessional in the dashboard cards. */
  text-wrap: pretty;
}

/* No alerts message – ensure visibility */
.no-alerts {
  color: var(--text-primary);
  font-weight: 600;
}

/* Alert status bar - combined info line */
.alert-status-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
  margin-bottom: 1rem;
  padding: 0.75rem 1rem;
  background: color-mix(in oklch, var(--brand-blue) 5%, transparent);
  border-radius: 8px;
  border-left: 4px solid var(--accent-red);
  border-right: 4px solid var(--accent-red);
}

/* ─────────────────────────────────────────
   County Filter Bar — outer padding wrapper only; the pill itself is the
   shared .search-pill component (see "Standard Search Pill" above).
   ───────────────────────────────────────── */
.county-filter-bar {
  padding: 0 1rem 0.75rem;
  position: relative;
}

/* Autocomplete dropdown — position:fixed so it escapes overflow:hidden on .card */
.county-autocomplete {
  position: fixed;
  /* top / left / width set dynamically by JS via positionAutocomplete() */
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  list-style: none;
  margin: 0;
  padding: 0.3rem 0;
  z-index: 1000;
  box-shadow: var(--shadow-md);
  backdrop-filter: blur(8px);
}

.county-autocomplete[hidden] {
  display: none;
}

.county-autocomplete li {
  padding: 0.5rem 1rem;
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--text-primary);
  transition: background-color 120ms ease, color 120ms ease;
}

.county-autocomplete li:hover,
.county-autocomplete li:focus {
  background: color-mix(in oklch, var(--accent-blue) 12%, var(--card-bg));
  color: var(--accent-blue);
  outline: none;
}

.alert-status-text {
  font-weight: 600;
  color: var(--text-primary);
}

/* ---- Alert type filter (Warnings / Watches / Advisories toggles) ----
   The count row doubles as the filter. "On" = solid tinted pill; "off" is
   signalled by a dashed border, struck-through label and dimmed marker, so
   state never depends on color alone. */
.alert-type-filter {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
}
.alert-type-toggle {
  font: inherit;
  font-weight: 600;
  line-height: 1.4;
  color: var(--text-primary);
  background: color-mix(in oklch, var(--accent-blue) 10%, var(--card-bg));
  border: 1px solid color-mix(in oklch, var(--accent-blue) 45%, var(--card-border));
  border-radius: 999px;
  padding: 0.3rem 0.8rem;
  cursor: pointer;
  transition: var(--t-fast);
}
.alert-type-toggle:hover {
  border-color: var(--accent-blue);
  background: color-mix(in oklch, var(--accent-blue) 18%, var(--card-bg));
}
.alert-type-toggle[aria-pressed="false"] {
  background: transparent;
  border-style: dashed;
  border-color: var(--card-border);
  color: var(--text-secondary);
}
.alert-type-toggle[aria-pressed="false"] .alert-type-label {
  text-decoration: line-through;
}
.alert-type-toggle[aria-pressed="false"] .legend-shape {
  opacity: 0.45;
}

/* "Hiding Watches, showing 2 of 7 alerts, plus a Show all link" note */
.alert-filter-note {
  margin: 0 0 1rem;
  padding: 0.5rem 0.9rem;
  font-size: var(--text-sm);
  font-weight: 600;
  text-align: center;
  color: var(--text-primary);
  background: color-mix(in oklch, var(--accent-yellow) 14%, transparent);
  border-left: 4px solid var(--accent-yellow);
  border-radius: 8px;
}
.alert-filter-reset {
  font: inherit;
  font-weight: 700;
  color: var(--accent-blue);
  background: none;
  border: 0;
  padding: 0;
  text-decoration: underline;
  cursor: pointer;
}

/* ---- Legend shape markers ----
   Two shape families distinguish the two legends in the spotter dashboard:
     • Triangles = alert TYPES   (Warnings / Watches / Advisories)  — warning-sign metaphor
     • Circles   = activation LEVEL (Activated / Monitor / Standby) — neutral status dot
   Same colour can mean different things across the two rows; the shape difference
   prevents a glanced-at red dot from being misread between activation severity
   and active warning count.  Pure CSS, no emoji rendering. */
.legend-shape {
  display: inline-block;
  vertical-align: -0.05em;
  margin-right: 0.25em;
  flex-shrink: 0;
}
.legend-shape--circle {
  width: 0.75em;
  height: 0.75em;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.3);
}
.legend-shape--triangle {
  width: 0;
  height: 0;
  border-left: 0.45em solid transparent;
  border-right: 0.45em solid transparent;
  border-bottom: 0.75em solid currentColor;
  margin-right: 0.3em;
  border-top: 0;
  /* Upward triangle is visually bottom-heavy (mass at base, point at apex),
     so its optical centre sits below the geometric one. Nudge it up so the
     centroid aligns with the same x-height position as the circles
     (which use the base .legend-shape value of -0.05em). */
  vertical-align: 0.05em;
}
/* Triangles use currentColor; set via color: on the element */
.legend-shape--triangle.legend-red    { color: var(--nws-warning-red); }
.legend-shape--triangle.legend-yellow { color: var(--nws-watch-yellow); }
.legend-shape--triangle.legend-blue   { color: var(--accent-blue); }
.legend-shape--triangle.legend-green  { color: var(--accent-green); }
/* Circles use background-color */
.legend-shape--circle.legend-red    { background: var(--nws-warning-red); }
.legend-shape--circle.legend-yellow { background: var(--nws-watch-yellow); }
.legend-shape--circle.legend-blue   { background: var(--accent-blue); }
.legend-shape--circle.legend-green  { background: var(--accent-green); }

.alert-status-text a {
  color: var(--accent-blue);
  text-decoration: underline;
  font-weight: 600;
}

.alert-status-divider {
  color: var(--text-secondary);
  opacity: 0.5;
  font-weight: 300;
}

.alert-status-refresh {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.alert-status-time {
  font-style: italic;
  font-weight: 500;
}

.pulse {
  display: inline-block;
  width: 6px;
  height: 6px;
  background: var(--accent-green);
  border-radius: 50%;
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.5; transform: scale(1.2); }
}

/* --- REPORTING REQUIREMENTS – IMPROVED LAYOUT --- */

/* Report checklist inside sub-card */
.report-checklist {
  list-style: none;
  margin: 0;
  padding: 0;
}

.report-checklist li {
  position: relative;
  padding-left: 1.75rem;
  margin-bottom: 0.5rem;
  font-size: 0.95rem;
  color: var(--text-secondary);
  line-height: 1.5;
}

.report-checklist li::before {
  content: "\2714";
  position: absolute;
  left: 0;
  top: 0;
  font-weight: bold;
  color: var(--accent-green);
  font-size: 1.1rem;
  line-height: 1.4;
}

/* Color-coded top border for report sub-cards */
.report-card-tornado  { border-top: 4px solid #d32f2f; }
.report-card-hail     { border-top: 4px solid #f57c00; }
.report-card-wind     { border-top: 4px solid #fbc02d; }
.report-card-flood    { border-top: 4px solid #1976d2; }
.report-card-winter   { border-top: 4px solid #5e35b1; }
.report-card-lightning { border-top: 4px solid #7cb342; }

/* Clickable report cards */
.report-card-clickable {
  cursor: pointer;
  user-select: none;
}

.report-card-clickable:hover { transform: translateY(-3px); }

.report-card-clickable:focus-visible {
  outline: 3px solid var(--accent-blue);
  outline-offset: 2px;
}

.report-card-hint {
  display: block;
  margin-top: 0.5rem;
  font-size: 0.8rem;
  color: var(--text-muted);
  font-style: italic;
}

/* Modal header color variants for report modals */
.modal-header--tornado  { background: linear-gradient(135deg, #b71c1c 0%, #d32f2f 100%); }
.modal-header--hail     { background: linear-gradient(135deg, #e65100 0%, #f57c00 100%); }
.modal-header--wind     { background: linear-gradient(135deg, #f9a825 0%, #fbc02d 100%); color: #1a1a1a; }
.modal-header--flood    { background: linear-gradient(135deg, #0d47a1 0%, #1976d2 100%); }
.modal-header--winter   { background: linear-gradient(135deg, #4527a0 0%, #5e35b1 100%); }
.modal-header--lightning { background: linear-gradient(135deg, #558b2f 0%, #7cb342 100%); }

.modal-header--wind h1,
.modal-header--wind h2,
.modal-header--wind .modal-title,
.modal-header--wind .modal-close { color: #1a1a1a; }

/* More Resources Section */
.nws-more-resources {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--card-border);
  text-align: center;
}

.nws-more-text {
  font-size: 0.95rem;
  color: var(--text-secondary);
  margin: 0 0 1rem 0;
  font-weight: 500;
}

/* --- ACTION BUTTONS – HORIZONTAL GROUP (Responsive) --- */

.action-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
  margin: 1.5rem 0;
  padding: 0 1rem;
}

.action-buttons .btn {
  flex: 1;
  min-width: 200px;
  max-width: 300px;
  padding: 0.9rem 1.2rem;
  font-size: clamp(0.95rem, 2.5vw, 1.1rem);
  text-align: center;
  white-space: normal;
}

.action-buttons small {
  display: block;
  font-size: 0.75rem;
  opacity: 0.9;
  margin-top: 0.25rem;
}

/* --- MAP GRID - Dashboard Quick Maps --- */

.map-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin: 1rem 0;
}

@media (min-width: 768px) {
  .map-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1.5rem;
  }
}

.map-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.25rem;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--text-primary);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: var(--shadow-sm);
}

.map-card:hover,
.map-card:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: var(--accent-blue);
  background: linear-gradient(135deg, var(--card-bg) 0%, rgba(92, 107, 192, 0.05) 100%);
}

.map-card-icon {
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--brand-blue) 0%, var(--accent-indigo) 100%);
  border-radius: 12px;
  color: white;
  box-shadow: var(--shadow-md);
  transition: transform 0.3s ease;
}

.map-card:hover .map-card-icon,
.map-card:focus-visible .map-card-icon {
  transform: scale(1.1) rotate(5deg);
}

.map-card-content {
  flex: 1;
  min-width: 0;
}

.map-card-title {
  font-size: 1.1rem;
  font-weight: 600;
  margin: 0 0 0.25rem 0;
  color: var(--text-primary);
}

.map-card-desc {
  font-size: 0.875rem;
  margin: 0;
  color: var(--text-secondary);
  opacity: 0.8;
}

/* --- CHANGELOG GRID - Recent Updates Display --- */

.changelog-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  margin-top: 1rem;
}

@media (min-width: 768px) {
  .changelog-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.changelog-month-card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  padding: 1.25rem;
  transition: all 0.3s ease;
  box-shadow: var(--shadow-sm);
}

.changelog-month-card:hover {
  /* T2.6 residual (2026-05-16): dropped translateY lift; shadow upgrade
     alone signals interactivity. */
  box-shadow: var(--shadow-lg);
  border-color: var(--accent-blue);
}

.changelog-month-header {
  font-size: 1.25rem;
  font-weight: 600;
  margin: 0 0 1rem 0;
  padding-bottom: 0.75rem;
  border-bottom: 2px solid;
}

.changelog-month-header--green  { color: var(--accent-green);  border-bottom-color: var(--accent-green); }
.changelog-month-header--blue   { color: var(--accent-blue);   border-bottom-color: var(--accent-blue); }
.changelog-month-header--indigo { color: var(--accent-indigo); border-bottom-color: var(--accent-indigo); }
.changelog-month-header--orange { color: var(--accent-orange); border-bottom-color: var(--accent-orange); }
.changelog-month-header--red    { color: var(--accent-red);    border-bottom-color: var(--accent-red); }
.changelog-month-header--yellow { color: var(--accent-yellow); border-bottom-color: var(--accent-yellow); }

.changelog-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.changelog-list li {
  margin-bottom: 0.75rem;
  padding-left: 1.25rem;
  position: relative;
  line-height: 1.6;
}

.changelog-list li::before {
  content: "•";
  position: absolute;
  left: 0;
  color: var(--accent-blue);
  font-weight: bold;
  font-size: 1.2rem;
}

.changelog-list li:last-child { margin-bottom: 0; }

.site-footer {
  display: flex;
  flex-direction: column;      /* stack the <p> elements vertically */
  align-items: center;         /* <-- horizontally centre everything */
  justify-content: center;     /* optional – centre vertically within its own height */
  padding: 1rem 0;
}

/* --- MOBILE RESPONSIVE (<768px) - Consolidated --- */
@media (max-width: 767px) {
  /* Layout */
  .site-main { padding: 0 .5rem; }

  /* Page nav - always visible, smaller on mobile */
  .page-nav .nav-list {
    gap: 0.5rem;
  }
  .page-nav .nav-list a {
    font-size: 0.85rem;
    padding: 0.4rem 0.75rem;
  }

  /* Buttons - consistent sizing on mobile */
  .btn {
    width: 100%;
    max-width: 280px;
    font-size: 0.85rem;
    padding: 0.4rem 0.75rem;
  }
  /* Action buttons */
  .action-buttons {
    flex-direction: column;
    align-items: center;
  }
  .action-buttons .btn {
    width: 100%;
    max-width: 340px;
  }

  /* Alert status bar */
  .alert-status-bar {
    flex-direction: column;
    gap: 0.25rem;
    text-align: center;
  }
  .alert-status-divider {
    display: none;
  }

}

@media (max-width: 480px) {
  body { padding: .5rem; }
  /* T3.14: heading font-size overrides removed — handled by fluid clamp() scale */
}

/* --- DESKTOP/TABLET RESPONSIVE (≥768px) - Consolidated --- */
@media (min-width: 768px) {
  /* Grid layouts - 2 columns */
  .contact-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 2rem;
  }

  .nws-more-resources {
    margin-top: 1rem;
    padding-top: 1rem;
  }

}

/* --- LOADING SKELETON – Modern loading animation --- */
.skeleton-loader {
  background: linear-gradient(
    90deg,
    light-dark(rgba(200, 200, 200, 0.2), rgba(100, 100, 100, 0.2)) 25%,
    light-dark(rgba(200, 200, 200, 0.4), rgba(100, 100, 100, 0.4)) 50%,
    light-dark(rgba(200, 200, 200, 0.2), rgba(100, 100, 100, 0.2)) 75%
  );
  background-size: 200% 100%;
  animation: loading 1.5s ease-in-out infinite;
  border-radius: 8px;
  height: 100px;
  margin: 1rem 0;
}

@keyframes loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* --- DARK MODE ---
   Every dark value is a light-dark() colour on its own rule (2026-10-22).
   The old @media (prefers-color-scheme: dark) block here applied only when
   the device was dark, so choosing Dark on a light-mode device left these
   parts light. */

/* --- BACK TO TOP BUTTON – Floating button to scroll to top --- */
.back-to-top {
  position: fixed;
  bottom: 2rem;
  right: 2rem;
  width: 50px;
  height: 50px;
  background: color-mix(in oklch, var(--brand-blue) 85%, transparent);
  backdrop-filter: blur(10px);
  color: var(--white);
  border: none;
  border-radius: 50%;
  font-size: 1.5rem;
  cursor: pointer;
  box-shadow: var(--shadow-lg);
  transition: var(--t-fast);
  opacity: 0;
  visibility: hidden;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
}

.back-to-top.visible {
  opacity: 1;
  visibility: visible;
}

.back-to-top:hover,
.back-to-top:focus-visible {
  background: var(--brand-blue-dark);
  transform: translateY(-4px);
  box-shadow: var(--shadow-xl);
}

.back-to-top:active { transform: translateY(-2px); }

/* --- ENHANCED FOOTER --- */
.site-footer {
  background: var(--bg-secondary);
  border-top: 1px solid var(--border-primary);
  margin-top: 3rem;
}

.footer-content {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem 1rem;
  color: var(--text-muted);
}

.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 2rem;
  padding-bottom: 2rem;
  border-bottom: 1px solid var(--border-primary);
  margin-bottom: 1.5rem;
  text-align: left;
}

.footer-section h4 {
  color: var(--text-primary);
  font-size: 1rem;
  font-weight: 600;
  margin-bottom: 1rem;
}

.footer-section p {
  color: var(--text-muted);
  line-height: 1.6;
  font-size: 0.9rem;
}

.footer-nav {
  list-style: none;
  padding: 0;
  margin: 0;
}

.footer-nav li { margin-bottom: 0.5rem; }

.footer-nav a {
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.3s ease;
  font-size: 0.9rem;
}

.footer-nav a:hover { color: var(--accent-blue); }

.footer-bottom {
  text-align: center;
  font-size: 0.875rem;
}

.footer-bottom p {
  margin-bottom: 0.5rem;
  color: var(--text-muted);
}

@media (max-width: 768px) {
  .footer-grid {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }
}

/* --- REPEATER DETAIL MODAL --- */

/* Repeater detail modal — uses the same @starting-style + allow-discrete
   pattern as the primary .modal-backdrop stack (T3.13). The .open class
   participates in opacity + transform transitions even though the modal
   flips from display: none → flex. prefers-reduced-motion is handled by
   the Patch 1 global guard. Visually matches .modal-content: 1px border,
   --shadow-xl, backdrop blur. */
.repeater-detail-modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(10px);
  z-index: 1000;
  overflow-y: auto;
  display: none;
  opacity: 0;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  transition: opacity 200ms ease, display 200ms ease allow-discrete;
}

.repeater-detail-modal.open {
  display: flex;
  opacity: 1;
}

@starting-style {
  .repeater-detail-modal.open { opacity: 0; }
  .repeater-detail-modal.open .repeater-detail-content {
    opacity: 0;
    transform: translateY(12px);
  }
}

.repeater-detail-content {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  max-width: 800px;
  width: 100%;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-xl);
  opacity: 1;
  transform: translateY(0);
  transition: opacity 220ms ease, transform 220ms ease;
}

.repeater-detail-header {
  background: var(--accent-indigo);
  color: white;
  padding: 1.5rem;
  border-radius: var(--radius) var(--radius) 0 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.repeater-detail-header h2 {
  margin: 0;
  font-size: var(--text-xl); /* T3.14: fluid scale (was fixed 1.5rem) */
  color: white;
}

.repeater-detail-close {
  background: transparent;
  border: none;
  color: white;
  font-size: 2rem;
  cursor: pointer;
  padding: 0;
  width: 2rem;
  height: 2rem;
  line-height: 1;
  transition: var(--t-fast);
}

.repeater-detail-close:hover { transform: scale(1.2); }

.repeater-detail-body { padding: 2rem; }

.detail-section { margin-bottom: 2rem; }

.detail-section h3 {
  color: var(--accent-indigo);
  margin-bottom: 1rem;
  font-size: 1.2rem;
  border-bottom: 2px solid var(--accent-indigo);
  padding-bottom: 0.5rem;
}

.detail-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.detail-label {
  font-weight: 700;
  color: var(--text-secondary);
}

.detail-value { color: var(--text-primary); }

.detail-value a {
  color: var(--accent-indigo);
  text-decoration: underline;
}

.detail-value a:hover { color: var(--accent-blue); }

/* Repeater Audit Modal (repeater-validation.html only) — data-quality
   issue list and raw-JSON block, used alongside .detail-section/.detail-grid
   above for the Summary Status block. */
.audit-issues {
  margin: 0;
  padding-left: 1.25rem;
}

.audit-issue {
  color: var(--accent-red);
  font-weight: 600;
  margin-bottom: 0.5rem;
  line-height: 1.4;
}

.audit-json {
  background: var(--bg-body);
  border: 1px solid var(--card-border);
  border-radius: 8px;
  padding: 1rem;
  font-family: 'Courier New', monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 400px;
  overflow-y: auto;
  color: var(--text-primary);
}

/* Clickable table rows */
.repeater-table tbody tr {
  cursor: pointer;
  transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}

.repeater-table tbody tr:hover {
  background-color: rgba(92, 107, 192, 0.1);
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}

.repeater-table tbody tr:active { background-color: rgba(92, 107, 192, 0.2); }

/* External link icon for clickable IP links */
.external-link-icon {
  font-size: 0.75rem;
  color: var(--accent-indigo);
  margin-left: 0.25rem;
  opacity: 0.7;
}

.clickable-row:hover .external-link-icon {
  opacity: 1;
}

/* Clickable IP link rows in modal */
.iplink-row { cursor: pointer; }

.iplink-row:hover { background-color: rgba(92, 107, 192, 0.15); }

/* --- PRINT STYLESHEET – Optimized for printing --- */
@media print {
  /* Hide navigation and interactive elements */
  .page-nav-toggle,
  .nav-list,
  #footer-placeholder,
  .action-buttons,
  .back-to-top,
  .btn {
    display: none !important;
  }

  /* Optimize layout for print */
  body {
    background: white;
    color: black;
    font-size: 10pt;
    padding: 0;
  }

  /* Optimize cards for print */
  .card {
    page-break-inside: avoid;
    border: 1px solid #000;
    box-shadow: none;
    margin-bottom: 1rem;
  }

  .card-header {
    background: #f0f0f0 !important;
    color: black !important;
    border-bottom: 2px solid #000;
  }

  .card-body {
    padding: 0.5rem;
  }

  /* Optimize tables for print */
  .repeater-table {
    font-size: 8pt;
    page-break-inside: avoid;
  }

  .repeater-table th {
    background: #e0e0e0 !important;
    color: black !important;
  }

  .repeater-table td {
    border: 1px solid #ccc;
  }

  /* Hide scroll hints */
  .scroll-hint {
    display: none;
  }

  /* Ensure links show URLs */
  a[href]:after {
    content: " (" attr(href) ")";
    font-size: 8pt;
    color: #666;
  }

  /* Don't show URLs for internal links */
  a[href^="#"]:after,
  a[href^="/"]:after {
    content: "";
  }

  /* Alert items */
  .alert-item {
    border: 2px solid #000;
    page-break-inside: avoid;
  }

  /* Callouts */
  .callout {
    border: 1px solid #000;
    background: #f9f9f9;
    page-break-inside: avoid;
  }
}
/* ---------------------------------------------------------------------
   Repeater Network Map (repeater-validation.html)
   Generated live from data/repeaters.json's rflinks graph — see
   computeNetworkStatus()/renderNetworkMap() in scripts.js.
   Tier = sequential (ordered hop-distance from the hub), so it's one
   ramp light->dark rather than unrelated categorical hues. Role and
   link-type are encoded separately (shape/size, line style) so identity
   is never carried by color alone.
   --------------------------------------------------------------------- */
:root {
  --netmap-hub:       light-dark(#0d3d78, #1e4f9c);
  --netmap-primary:   var(--accent-blue);
  --netmap-secondary: light-dark(#8fc1f2, #7fa8d9);
}

.netmap-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin-bottom: 1rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.netmap-legend-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}
.netmap-legend-label {
  font-weight: 600;
  color: var(--text-primary);
}
.netmap-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  white-space: nowrap;
}
.netmap-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}
.netmap-swatch--relay {
  border: 2px solid var(--text-secondary);
  background: transparent;
  width: 12px;
  height: 12px;
}
.netmap-swatch--spoke {
  background: var(--text-secondary);
}

.netmap-container {
  width: 100%;
  overflow-x: auto;
  background: var(--bg-tertiary);
  border: 1px solid var(--card-border);
  border-radius: 8px;
  padding: 1rem;
}
.netmap-svg {
  width: 100%;
  height: auto;
  min-width: 700px;
  display: block;
  margin: 0 auto;
}
.netmap-node {
  cursor: pointer;
}
.netmap-node:focus {
  outline: none;
}
.netmap-node:hover circle:last-of-type,
.netmap-node:focus circle:last-of-type {
  stroke: var(--accent-yellow);
  stroke-width: 3;
}
.netmap-node:hover .netmap-label,
.netmap-node:focus .netmap-label {
  font-weight: 700;
}
.netmap-label {
  font-size: 12px;
  fill: var(--text-primary);
}
.netmap-label-freq {
  font-size: 10px;
  fill: var(--text-secondary);
}
.netmap-hub-label {
  font-size: 14px;
  font-weight: 700;
  fill: var(--text-primary);
}
