/*
 * tool-shell.css — shared chrome tokens + accessibility floor for the
 * three public tool pages (seasonal-spiral, timing-grid, serp-seasonality)
 * and the SERP result page.
 *
 * Created 2026-05-19 as part of the "Tool-page consistency pass" PR
 * driven by /impeccable critique findings on each tool. This file is
 * ADDITIVE — each tool's inline <style> block still owns its component
 * styles. This file owns:
 *   1. Canonical token block (light + dark) aligned with DESIGN.md
 *   2. Two new semantic tokens (--pro-accent, --pro-accent-dim) that
 *      replace the raw #a3424e burgundy literal used across all 3 tools
 *      for "Pro feature" badges
 *   3. Global :focus-visible ring rule on every interactive control
 *      (WCAG 2.4.7) — was missing on form controls across all 3 tools
 *
 * Why CSS not a renderToolShell() helper: the tool pages are static
 * HTML served from /public via env.ASSETS.fetch (Cloudflare Workers
 * Sites binding), not server-rendered. The shared chrome lives in a
 * stylesheet linked from each page rather than a function call.
 *
 * Drift policy: when a tool needs a token or a new global utility,
 * add it here first. Component-specific styles (canvas, heatmap cell
 * gradients, spiral palette) stay in the tool's inline <style>.
 */

/* ============================================================
 * Canonical tokens
 * ============================================================
 * The full set of design tokens used across every tool page. As of
 * 2026-05-19 (PR I) this file is the source of truth; the inline
 * :root{} blocks in each tool page were removed so we can never drift.
 *
 * Tool-specific tokens (e.g. --canvas-bg for SERP) stay inline in the
 * tool that owns them — they're not site-wide.
 *
 * Aligned with DESIGN.md § Color (2026-05-08 contrast bumps,
 * 2026-05-18 --accent-text addition).
 *
 * Note: --positive was previously missing from Grid + Spiral. PR #64
 * used var(--positive) in timing-grid's delta-mode stat cards without
 * defining it — promoting it to this file fixes that latent bug.
 */
:root {
  --bg: #f5f6f8;
  --bg-surface: #ffffff;
  --bg-elevated: #f0f1f3;
  --text-primary: #1a1d26;
  --text-secondary: #5a5e68;
  --text-muted: #6e727b;
  --accent: #0e9e96;
  --accent-text: #087671;
  --accent-hover: #0cb3aa;
  --accent-dim: rgba(14, 158, 150, 0.08);
  --border: rgba(0, 0, 0, 0.08);
  --border-hover: rgba(0, 0, 0, 0.15);
  --danger: #d1344b;
  --positive: #1d9e75;
  --pro-accent: #a3424e;
  --pro-accent-dim: rgba(163, 66, 78, 0.1);
  /* AI ecosystem tokens — owned by the AI Mention Monitor tool.
     Three entity classes in the ChatGPT response space:
       brand  = companies/products visible in answers (anchors on --accent,
                so the "you" arc IS your identity);
       fanout = internal related queries the model expands the seed into;
       source = cited URLs the model surfaces as evidence.
     Each role has a fill + a darker text variant for WCAG AA contrast.
     Added 2026-05-24 — see DESIGN.md Decisions Log. */
  --ai-brand: var(--accent);
  --ai-brand-text: var(--accent-text);
  --ai-fanout: #e5b04a;
  --ai-fanout-text: #8a6a14;
  --ai-source: #b07cc6;
  --ai-source-text: #6f3f88;
}

body.dark {
  --bg: #0c0e13;
  --bg-surface: #13161d;
  --bg-elevated: #1a1d26;
  --text-primary: #e8eaed;
  --text-secondary: #a8acb5;
  --text-muted: #7a7e88;
  --accent: #4ecdc4;
  --accent-text: #4ecdc4;
  --accent-hover: #5fe0d7;
  --accent-dim: rgba(78, 205, 196, 0.12);
  --border: rgba(255, 255, 255, 0.06);
  --border-hover: rgba(255, 255, 255, 0.12);
  --danger: #e74c5e;
  --positive: #4cd99a;
  /* Lift the terracotta toward a pink on dark surfaces so it stays
     readable. Matches the inline override that previously existed on
     the SERP entry + result pages. */
  --pro-accent: #e8a4a4;
  --pro-accent-dim: rgba(232, 164, 164, 0.12);
  /* AI ecosystem dark-mode overrides — fills stay the same hue (they
     read well on --canvas-bg); text variants lift for AA on dark
     surfaces. --ai-brand / --ai-brand-text inherit via --accent so no
     dark-mode override is needed for those. */
  --ai-fanout: #e5b04a;
  --ai-fanout-text: #f5b41e;
  --ai-source: #b07cc6;
  --ai-source-text: #c89cd8;
}

/* ============================================================
 * Global :focus-visible — WCAG 2.4.7
 * ============================================================
 * Each tool's inline CSS sets `select:focus,input:focus{outline:none}`
 * to suppress the browser default ring; that removes the keyboard
 * affordance for users who never use the mouse. This rule restores it
 * with a 2px accent-dim halo that matches the app-shell.ts treatment
 * on /app/welcome and /app/settings — consistent vocabulary across
 * the whole site.
 *
 * `:focus-visible` only triggers for keyboard focus, so mouse users
 * never see it. We over-specify on `outline:none` so this beats the
 * inline `outline:none` rules without !important.
 */
:where(button, select, input, textarea, a, [role="button"], [tabindex]):focus-visible {
  outline: 2px solid transparent;
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--accent-dim, rgba(14, 158, 150, 0.25)),
              0 0 0 4px var(--accent, #0e9e96);
  border-radius: 6px;
}

/* Inputs with an existing focused border (--accent-text) keep that
   visual; the ring adds keyboard-only redundancy. The compound shadow
   stays subtle on dark mode because --accent-dim picks up the brighter
   teal automatically. */

/* ============================================================
 * Pro upsell card
 * ============================================================
 * Free-tier replacement for the visibly-disabled Pro filter controls
 * (DESIGN.md anti-pattern #14). Used by Spiral + Grid Filter (Pro)
 * fieldsets. Each tool's JS toggles between .controls-group-inner
 * (Pro users see live controls) and .pro-upsell (free users see this
 * card) based on the .is-free body class set by applyProTierLocks().
 *
 * Hidden by default; tool JS flips display:flex when needed.
 */
.pro-upsell {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.65rem 0.9rem;
  background: var(--accent-dim);
  border-radius: 8px;
  text-decoration: none;
  color: var(--text-primary);
  font-size: 0.85rem;
  line-height: 1.4;
  transition: background 0.15s, box-shadow 0.15s;
}
.pro-upsell:hover {
  background: rgba(14, 158, 150, 0.14);
  color: var(--text-primary);
}
body.dark .pro-upsell:hover {
  background: rgba(78, 205, 196, 0.18);
}
.pro-upsell:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent);
}
.pro-upsell .pro-upsell-text {
  flex: 1;
  min-width: 0;
}
.pro-upsell .pro-upsell-text strong {
  font-weight: 500;
  color: var(--text-primary);
}
.pro-upsell .pro-upsell-cta {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent-text);
  white-space: nowrap;
}

/* ============================================================
 * Topbar shared chrome
 * ============================================================
 * Each tool's <header> still owns its own HTML (the Help link target
 * + Plausible event tool prop differ per page) but the class-level
 * styles live here so every topbar gets the same focus rings, hover
 * states, and Pro-chip treatment without inline duplication.
 *
 * Classes used across tools:
 *   .logo-link        — the wrapping <a> around the DATA HIT logo
 *   .topbar-back-link — Help, "All tools", and any other top-nav links
 *   .topbar-pro-chip  — the "Pro · See plans" upsell chip (hidden for
 *                       Pro users via body.is-pro #proChip{display:none}
 *                       which the tools each scope locally)
 *
 * Inline style attributes on individual elements (e.g. height on the
 * logo img, gap on the nav row) stay inline — those vary per tool.
 */
.logo-link img {
  height: 28px;
  display: block;
}
.topbar-back-link {
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.15s;
  outline: none;
}
.topbar-back-link:hover,
.topbar-back-link:focus-visible {
  color: var(--accent-text);
}
.topbar-back-link:focus-visible {
  box-shadow: 0 0 0 2px var(--accent-dim);
  border-radius: 3px;
}
.topbar-pro-chip {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.62rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent-text);
  background: var(--accent-dim);
  padding: 0.3rem 0.65rem;
  border-radius: 3px;
  text-decoration: none;
  transition: background 0.15s;
  outline: none;
}
.topbar-pro-chip:hover,
.topbar-pro-chip:focus-visible {
  background: rgba(14, 158, 150, 0.14);
}
body.dark .topbar-pro-chip:hover,
body.dark .topbar-pro-chip:focus-visible {
  background: rgba(78, 205, 196, 0.18);
}
.topbar-pro-chip:focus-visible {
  box-shadow: 0 0 0 2px var(--accent-dim);
}

/* ============================================================
 * Themed dialog overlay (dh-modal.js)
 * ============================================================
 * Shared overlay treatment used by both dhConfirm + dhPrompt helpers
 * in /static/dh-modal.js. Each tool page loads dh-modal.js + this CSS;
 * the script injects the modal DOM on first use.
 *
 * Tools also reuse this overlay container for their own modals
 * (e.g. Spiral's #ann-modal already uses .ann-card via its own CSS).
 * Class names match the existing pattern (.ann-card, .ann-actions,
 * .ann-primary, .ann-secondary, .ann-danger) so the same buttons +
 * card styling carry through.
 */
.dh-modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: none; align-items: flex-start; justify-content: center;
  padding: 3rem 1rem 1rem; z-index: 200;
}
.dh-modal-overlay.visible { display: flex; }
.dh-modal-overlay .ann-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1.5rem;
  width: 100%; max-width: 440px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
}
body.dark .dh-modal-overlay .ann-card { box-shadow: none; }
.dh-modal-overlay h2 {
  font-size: 1.05rem; font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 0.5rem;
}
.dh-modal-overlay .dh-modal-msg {
  font-size: 0.88rem;
  color: var(--text-secondary);
  margin-bottom: 1rem;
  line-height: 1.5;
}
.dh-modal-overlay label {
  display: block;
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.6rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-muted);
  margin-bottom: 0.3rem;
}
.dh-modal-overlay input[type="text"] {
  width: 100%;
  font-family: 'DM Sans', sans-serif;
  font-size: 0.88rem;
  padding: 0.55rem 0.75rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-primary);
  transition: border-color 0.15s;
  box-sizing: border-box;
}
.dh-modal-overlay input[type="text"]:focus {
  outline: none;
  border-color: var(--accent-text);
}
.dh-modal-overlay .ann-actions {
  display: flex; gap: 0.5rem; align-items: center;
  margin-top: 1.1rem; flex-wrap: wrap;
}
.dh-modal-overlay .ann-actions .spacer { flex: 1; }
.dh-modal-overlay button {
  font-family: 'DM Sans', sans-serif;
  font-size: 0.85rem; font-weight: 500;
  border: none; border-radius: 6px;
  padding: 0.55rem 1rem;
  cursor: pointer;
  transition: all 0.15s;
}
.dh-modal-overlay .ann-primary {
  background: var(--accent); color: var(--bg);
}
.dh-modal-overlay .ann-primary:hover { background: var(--accent-hover); }
.dh-modal-overlay .ann-secondary {
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border);
}
.dh-modal-overlay .ann-secondary:hover {
  border-color: var(--accent-text);
  color: var(--accent-text);
}
.dh-modal-overlay .ann-danger {
  background: transparent;
  color: var(--danger);
  border: 1px solid rgba(209, 52, 75, 0.3);
}
.dh-modal-overlay .ann-danger:hover {
  background: rgba(209, 52, 75, 0.06);
  border-color: var(--danger);
}

/* ============================================================
 * Reduced-motion floor
 * ============================================================
 * Mirrors the rule in each tool's inline CSS so users who land here
 * via a shared link or cache miss still get the protection even if
 * the inline block fails to parse. Safe to be duplicated — the
 * `prefers-reduced-motion` media query is idempotent.
 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
  }
}

/* ============================================================
 * Sitewide footer (standardized 2026-05-20 per beta feedback)
 * ============================================================
 * Canonical pattern from /public/index.html. Promoted to the shared
 * stylesheet so every public page can drop the same `<footer>` block
 * and get identical layout. Excludes /app/welcome and /app/settings
 * which use the renderAppShell sidebar instead.
 *
 * The class hooks `.site-footer` + `.site-footer-inner` are namespaced
 * to avoid colliding with any page-local `<footer>` styles that may
 * still exist during the migration. After every page has been
 * converted, we can prune those local copies.
 */
.site-footer {
  border-top: 1px solid var(--border);
  margin-top: 3rem;
  padding: 1.5rem 1rem 2rem;
}
.site-footer-inner {
  max-width: 900px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  font-size: 0.72rem;
  color: var(--text-muted);
}
.site-footer-inner nav {
  display: flex;
  gap: 1.25rem;
  flex-wrap: wrap;
}
.site-footer-inner a {
  color: var(--accent-text);
  text-decoration: none;
}
.site-footer-inner a:hover,
.site-footer-inner a:focus-visible {
  text-decoration: underline;
}
.site-footer .theme-toggle {
  cursor: pointer;
  background: none;
  border: none;
  color: var(--text-muted);
  font-family: 'DM Sans', sans-serif;
  font-size: 0.72rem;
  padding: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  transition: color 0.15s;
}
.site-footer .theme-toggle:hover,
.site-footer .theme-toggle:focus-visible {
  color: var(--text-secondary);
}
.site-footer .theme-toggle svg {
  width: 14px;
  height: 14px;
  vertical-align: middle;
}

/* ============================================================
 * Canonical action-button vocabulary (2026-05-27)
 * ============================================================
 * Two classes own every chart-adjacent action button across the four
 * tools. Introduced by the "button consistency" pass after the AI
 * Monitor weekly-diff build surfaced drift between Spiral / Grid /
 * SERP / AI's ad-hoc button styles.
 *
 *   .tool-action-primary
 *     Persistent-monitor action — "Track this keyword" (SERP),
 *     "★ Watch" (AI Monitor). Filled accent so it reads as the
 *     primary action in the row. State (tracked / watching) is
 *     conveyed by icon swap + label swap, NOT a different visual
 *     treatment — keeps the button stable in place.
 *
 *   .tool-action-export
 *     One-off file export — PNG / CSV. Bordered outline so it reads
 *     as a secondary action. No active state (one-off).
 *
 * Both share radius + DM Sans 0.78rem + icon-left layout so the row
 * has visual rhythm. Primary is slightly larger padding (denotes
 * primacy without changing font scale).
 *
 * Drift policy: NEVER add an inline-styled action button on a tool
 * page. If a tool needs a new action shape, add a third semantic
 * class here.
 */
.tool-action-primary,
.tool-action-export {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  font-family: 'DM Sans', sans-serif;
  font-size: 0.78rem;
  font-weight: 500;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s;
  white-space: nowrap;
  text-decoration: none;
}
.tool-action-primary svg,
.tool-action-export svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

.tool-action-primary {
  background: var(--accent);
  color: var(--bg);
  border: 1px solid var(--accent);
  padding: 0.5rem 1rem;
}
.tool-action-primary:hover,
.tool-action-primary:focus-visible {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}
body.dark .tool-action-primary {
  color: #0c0e13;
}
/* Active state (already tracking / watching). Same shape, swapped
 * icon + label is what the user sees. We keep the visual identical
 * so the button doesn't jump on toggle. */
.tool-action-primary[aria-pressed="true"] {
  background: var(--accent);
}
.tool-action-primary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.tool-action-export {
  background: var(--bg-surface);
  color: var(--text-muted);
  border: 1px solid var(--border);
  padding: 0.4rem 0.85rem;
}
.tool-action-export:hover,
.tool-action-export:focus-visible {
  color: var(--accent-text);
  border-color: var(--accent);
}
.tool-action-export:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ============================================================
 * Pro badge (2026-05-27 design consolidation)
 * ============================================================
 * The small uppercase "Pro" pill that flags Pro-only controls and
 * sections. Pre-consolidation, this was inlined as a 12-property style
 * block in ~8 places across Spiral and Grid (and inconsistently named
 * .pro-badge / .badge). Now one canonical class.
 *
 * Default size suits inline label decorators ("Event name [Pro]").
 * .pro-badge--lg suits section headers ("Anomalies [Pro]").
 * Teal accent — the dominant pre-consolidation pattern. The burgundy
 * --pro-accent token stays available for any future use that needs
 * to visually distinguish a Pro region; it's not the default.
 */
.pro-badge {
  display: inline-flex;
  align-items: center;
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.55rem;
  font-weight: 500;
  color: var(--accent-text);
  background: var(--accent-dim);
  padding: 0.1rem 0.35rem;
  border-radius: 3px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.pro-badge--lg {
  font-size: 0.6rem;
  padding: 0.2rem 0.55rem;
  letter-spacing: 0.08em;
}

/* ============================================================
 * Canonical form controls (2026-05-27 design consolidation)
 * ============================================================
 * select + input[type=text] + input[type=email] were byte-identical
 * duplicated blocks in Spiral and Grid pre-consolidation. SERP and
 * AI Monitor didn't declare equivalents, so a select in either of
 * those tools would have rendered with browser defaults. Promoting
 * to shared gives all four tools the same form-control treatment by
 * default, and future tools inherit clean.
 *
 * The arrow SVG is a data: URL with the dropdown chevron in the
 * --text-muted hex (#6e727b). Light + dark themes share the same
 * arrow because --text-muted is similar enough in both.
 */
select,
input[type="text"],
input[type="email"] {
  appearance: none;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  color: var(--text-primary);
  font-family: 'DM Sans', sans-serif;
  font-size: 0.82rem;
  padding: 0.5rem 0.7rem;
  border-radius: 6px;
  transition: border-color 0.15s;
  min-width: 0;
}
select {
  padding-right: 2rem;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%238b8f98' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
}
input[type="text"]::placeholder,
input[type="email"]::placeholder {
  color: var(--text-muted);
}
select:hover,
input[type="text"]:hover,
input[type="email"]:hover {
  border-color: var(--border-hover);
}
select:focus,
input[type="text"]:focus,
input[type="email"]:focus {
  outline: none;
  border-color: var(--accent-text);
}
select:disabled,
input[type="text"]:disabled,
input[type="email"]:disabled,
input:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ============================================================
 * Panel card utility (2026-05-27 design consolidation)
 * ============================================================
 * Common chrome for content panels that sit alongside the chart —
 * stats summaries, insight blocks, Pro action regions, annotation
 * panels. Pre-consolidation, the same five declarations were
 * inlined ~6 times across SERP-result and twice on AI Monitor.
 *
 * Apply directly to a panel container; layout (flex / grid /
 * padding overrides) belongs in the tool's own component classes
 * — this is just the surface treatment.
 */
.panel-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1.1rem 1.35rem;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
}
body.dark .panel-card {
  box-shadow: none;
}

/* ============================================================
 * AI overview panel family (2026-05-27 design consolidation)
 * ============================================================
 * The Pro "Generate AI overview" feature — header + Generate button
 * + result body. Spiral and Grid had byte-identical 16-line CSS
 * blocks. SERP-result had a near-duplicate (missing a couple of
 * sub-styles). AI Monitor had its own variant with outline button
 * and a wrapper-div for the action.
 *
 * Canonical: Spiral/Grid's button-with-class pattern (no wrapper
 * div) + AI Monitor's useful additions (.is-empty italic body state)
 * + .is-above modifier for placement above the chart (AI Monitor's
 * use case) vs default below.
 *
 * Each tool still controls visibility — the body:not(.is-pro)
 * hide rule is NOT here because AI Monitor renders the panel for
 * free users too with an upgrade-prompt body.
 */
.ai-overview-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1.1rem 1.35rem;
  margin-top: 1.5rem;
}
/* Modifier — panel sits ABOVE the chart instead of below. */
.ai-overview-panel.is-above {
  margin-top: 0;
  margin-bottom: 1.25rem;
}
.ai-overview-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.6rem;
  flex-wrap: wrap;
}
.ai-overview-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}
.ai-overview-action {
  font-family: 'DM Sans', sans-serif;
  font-size: 0.78rem;
  font-weight: 500;
  background: var(--accent);
  color: var(--bg);
  border: none;
  border-radius: 6px;
  padding: 0.42rem 0.9rem;
  cursor: pointer;
  transition: background 0.15s;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.ai-overview-action:hover,
.ai-overview-action:focus-visible {
  background: var(--accent-hover);
}
.ai-overview-action:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
.ai-overview-action.is-hidden {
  display: none;
}
/* Regenerate variant — quieter than the primary Generate. */
.ai-overview-action.is-regenerate {
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  font-weight: 400;
}
.ai-overview-action.is-regenerate:hover,
.ai-overview-action.is-regenerate:focus-visible {
  border-color: var(--accent-text);
  color: var(--accent-text);
  background: transparent;
}
body.dark .ai-overview-action {
  color: #0c0e13;
}
.ai-overview-spinner {
  width: 12px;
  height: 12px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}
.ai-overview-body {
  font-size: 0.88rem;
  color: var(--text-secondary);
  line-height: 1.6;
  min-height: 1rem;
}
.ai-overview-body:empty {
  display: none;
}
.ai-overview-body.is-empty {
  color: var(--text-muted);
  font-style: italic;
}
.ai-overview-body.is-error {
  color: var(--danger);
}
.ai-overview-body strong {
  color: var(--text-primary);
  font-weight: 600;
}
.ai-overview-hint {
  font-size: 0.72rem;
  color: var(--text-muted);
  margin: 0.65rem 0 0;
  line-height: 1.5;
}
.ai-overview-hint:empty {
  display: none;
}

/* ============================================================
 * Saved Views bar (2026-05-27 design consolidation)
 * ============================================================
 * Pro feature: per-tool saved filter/view sets. Spiral had this since
 * day one; Grid acquired the same pattern via copy-paste (the audit
 * sub-agent missed Grid's copy, the user caught it). Six inline-style
 * attributes across each tool — ~1200 chars of identical inline CSS
 * duplicated between Spiral and Grid pre-consolidation.
 *
 * Default state is hidden — the per-tool JS bootstraps the bar by
 * setting bar.style.display='inline-flex' once it detects a signed-in
 * Pro user, which overrides the CSS default cleanly.
 *
 * Inner select inherits canonical select styles (no need to re-declare
 * appearance/SVG arrow), only the min-width override stays in scope.
 * Save / Delete buttons compose .tool-action-primary / .tool-action-export
 * for visual consistency with the rest of the tool action vocabulary.
 */
.saved-views-bar {
  display: none;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
  font-size: 0.82rem;
}
.saved-views-bar select {
  min-width: 160px;
}
.saved-views-bar .sv-msg {
  font-size: 0.78rem;
  color: var(--text-muted);
  min-height: 1rem;
}
.saved-views-bar .sv-meter {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.62rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-left: auto;
}
/* Pro-Agency client filter + per-view assign selects (Spiral + Grid saved
   views). Compact + muted so they read as secondary to the views dropdown. */
.saved-views-bar .sv-client {
  min-width: 0;
  max-width: 150px;
  font-size: 0.78rem;
  color: var(--text-muted);
}

/* ============================================================
 * Universal [hidden] attribute floor (2026-05-27)
 * ============================================================
 * Several canonical classes here (.tool-action-export, .tool-action-primary,
 * .pro-badge) set explicit `display: inline-flex` which clobbers the
 * browser's default `display: none` rule for [hidden]. Same bug class
 * as PR #110's quota-strip fix on the AI Monitor page; promote the
 * fix to shared CSS so any element on any tool page using the HTML
 * `hidden` attribute hides correctly without per-component overrides.
 */
[hidden] {
  display: none !important;
}

/* ============================================================
 * Tooltip vocabulary (PR 11, 2026-05-28)
 * ============================================================
 * Outer `.tooltip` container plus six semantic children for the
 * tooltips that hover-over chart cells on Spiral / SERP-result /
 * AI Monitor. Grid uses `.hm-tip` (heatmap-specific affordance,
 * different positioning) — left alone, different concept.
 *
 * Pre-consolidation, every tool invented its own `.tt-*` children
 * (Spiral: .tt-date/.tt-value/.tt-day/.tt-changes/.tt-anomaly; SERP:
 * .tt-month/.tt-domain/.tt-swatch/.tt-position/.tt-url; AI Monitor:
 * .tt-tag/.tt-title/.tt-meta). Zero overlap, three vocabularies for
 * the same visual roles. Canonical mapping documented in DESIGN.md
 * decisions log.
 *
 * The `.tooltip` base only sets the common chrome (positioning,
 * background, border, opacity transition, font-size). Each tool keeps
 * the tool-specific bits as local overrides:
 *   - white-space (Spiral + AI Monitor want nowrap; SERP wants wrap)
 *   - max-width (SERP only)
 *   - padding (varies slightly per tool)
 *   - mobile hide rules (SERP has @media (max-width: 720px))
 */
.tooltip {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.1s;
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(0, 0, 0, 0.1);
  border-radius: 8px;
  z-index: 10;
  font-size: 0.78rem;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}
.tooltip.visible {
  opacity: 1;
}

/* .tt-eyebrow — small uppercase mono context label (date / month /
   category line above the tooltip title). Matches SERP's pre-
   consolidation .tt-month and the structural role of AI Monitor's
   .tt-tag (which is a coloured pill variant — kept separate, see below). */
.tt-eyebrow {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 0.25rem;
}

/* .tt-tag — coloured inline pill used when the tooltip needs to encode
   the cell's category by colour. AI Monitor uses this with inline-style
   bg + colour set from the spiral arc's class (brand=teal, fan-out=yellow,
   source=purple). Distinct from .tt-eyebrow because the colour treatment
   is the semantic; muted-uppercase wouldn't convey arc identity. */
.tt-tag {
  display: inline-block;
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.58rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.12rem 0.4rem;
  border-radius: 3px;
  margin-bottom: 0.25rem;
}

/* .tt-title — primary line of the tooltip. Brand / domain / date /
   page title. Always text-primary at default weight. */
.tt-title {
  font-weight: 500;
  color: var(--text-primary);
  margin-bottom: 0.1rem;
}

/* .tt-value — numeric value line. Mono so column-aligned digits read
   cleanly when the user hovers across cells. Accent-text for visual
   prominence (the value is usually what the user came to see). */
.tt-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--accent-text);
  margin-bottom: 0.25rem;
}

/* .tt-meta — secondary line. Tabular-nums so any numbers in the
   string (counts, percentages) align. Non-mono — keeps the tooltip
   from feeling like a terminal panel. */
.tt-meta {
  font-size: 0.7rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  margin-top: 0.1rem;
}

/* .tt-swatch — small colour-coded dot, paired with .tt-title in
   layouts where the title needs a visual colour anchor (e.g. SERP
   domain rows). Caller sets background-color via inline style. */
.tt-swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  flex-shrink: 0;
}
