/* ArcVision visual system. Every color below was checked against WCAG AA
   (4.5:1 for normal text) against the specific surfaces it's used on before
   being picked -- see the contrast math in the implementation notes, not
   just eyeballed. Structure/class names are unchanged from the prior
   iteration on purpose: this pass is tokens + component styling, not a
   markup rewrite (the dashboard's tab structure is untouched this stage). */

:root {
  color-scheme: light;
  /* Landing-page-only wide canvas (the hero already used this exact
     clamp() inline; hoisted here as a token so "How It Works", "Explore a
     Real Shooting Session", and the proof strip can share it -- keeps the
     page from visibly contracting right after the hero. Never applied to
     .container itself, so the dashboard/other app views are unaffected. */
  --landing-max-width: clamp(1180px, 86vw, 1560px);
  --surface-0: #f4f6f9;
  --surface-1: #ffffff;
  --surface-2: #fbfcfd;
  --text-primary: #10151f;
  --text-secondary: #45505f;
  --text-muted: #5b6675;
  --gridline: #e4e8ee;
  --baseline: #c7ceda;
  --border: rgba(16, 21, 31, 0.10);

  /* Ember orange (primary) and teal (secondary) -- both re-picked from the
     brand direction to clear 4.5:1 as text/button color against
     surface-0/1, not just as a decorative swatch. */
  --brand: #b23d14;
  --brand-dark: #8a2d0d;
  --accent-2: #0b7a7a;
  --accent-2-dark: #096363;
  --series-2: #0b7a7a;
  --series-3: #128f63;
  --series-4: #8c5f00;
  --series-7: #4a3fb8;

  --good: #14803f;
  --critical: #c62828;
  --warning: #f5a623;
  --warning-text: #8c5f00;
  --unknown: #5b6675;

  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-5: 24px; --space-6: 32px; --space-7: 48px;

  --radius-sm: 6px;
  --radius-md: 9px;
  --radius-lg: 12px;
  --shadow-1: 0 1px 2px rgba(16,21,31,0.05), 0 6px 16px rgba(16,21,31,0.06);
  --shadow-card-hover: 0 2px 4px rgba(16,21,31,0.06), 0 10px 24px rgba(16,21,31,0.09);
  --font: system-ui, -apple-system, "Segoe UI", sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface-0: #080b11;
    --surface-1: #111826;
    --surface-2: #1c2536;
    --text-primary: #f3f5f9;
    --text-secondary: #a8b3c4;
    --text-muted: #8a95a6;
    --gridline: #232c3f;
    --baseline: #34405a;
    --border: rgba(255,255,255,0.14);
    --brand: #ff7a45;
    --brand-dark: #ff9466;
    --accent-2: #2dd4d4;
    --accent-2-dark: #57dede;
    --series-2: #2dd4d4;
    --series-3: #3fd692;
    --series-4: #f0b429;
    --series-7: #9085e9;
    --good: #34d67a;
    --critical: #f16565;
    --warning: #fbbf24;
    --warning-text: #fbbf24;
    --unknown: #8a95a6;
    --shadow-1: 0 1px 2px rgba(0,0,0,0.3), 0 8px 20px rgba(0,0,0,0.35);
    --shadow-card-hover: 0 2px 4px rgba(0,0,0,0.35), 0 12px 28px rgba(0,0,0,0.4);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface-0: #080b11;
  --surface-1: #111826;
  --surface-2: #1c2536;
  --text-primary: #f3f5f9;
  --text-secondary: #a8b3c4;
  --text-muted: #8a95a6;
  --gridline: #232c3f;
  --baseline: #34405a;
  --border: rgba(255,255,255,0.14);
  --brand: #ff7a45;
  --brand-dark: #ff9466;
  --accent-2: #2dd4d4;
  --accent-2-dark: #57dede;
  --series-2: #2dd4d4;
  --series-3: #3fd692;
  --series-4: #f0b429;
  --series-7: #9085e9;
  --good: #34d67a;
  --critical: #f16565;
  --warning: #fbbf24;
  --warning-text: #fbbf24;
  --unknown: #8a95a6;
  --shadow-1: 0 1px 2px rgba(0,0,0,0.3), 0 8px 20px rgba(0,0,0,0.35);
  --shadow-card-hover: 0 2px 4px rgba(0,0,0,0.35), 0 12px 28px rgba(0,0,0,0.4);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--surface-0);
  color: var(--text-primary);
  font-family: var(--font);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent-2); }
a:hover { color: var(--accent-2-dark); }

.app-shell { min-height: 100vh; display: flex; flex-direction: column; }

/* ---------- Top bar ---------- */
.topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 13px 24px; border-bottom: 1px solid var(--border);
  background: var(--surface-1);
  position: sticky; top: 0; z-index: 20;
  flex-wrap: wrap; row-gap: 8px; column-gap: 16px;
}
.topbar-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.brand { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 18.5px; letter-spacing: -0.01em; flex-wrap: wrap; }
@media (max-width: 640px) {
  /* Keep the topbar to brand + History on narrow screens rather than
     letting the tagline/privacy pill crowd or wrap awkwardly next to it. */
  .tagline, .privacy-pill { display: none; }
}
.brand-mark { display: flex; flex: none; }
.brand-mark svg { display: block; }
.tagline { color: var(--text-secondary); font-size: 13px; margin-left: 8px; font-weight: 400; }
.privacy-pill {
  font-size: 12px; color: var(--text-secondary); border: 1px solid var(--border);
  border-radius: 999px; padding: 5px 12px; display: flex; align-items: center; gap: 6px;
}
.privacy-pill::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--good); display: inline-block; }

.active-job-indicator {
  display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 700;
  color: var(--text-primary); background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 999px; padding: 6px 12px 6px 10px; cursor: pointer; font-variant-numeric: tabular-nums;
}
.active-job-indicator:hover { border-color: var(--baseline); }
.active-job-indicator.ready { color: var(--good); }
.active-job-indicator.errored { color: var(--critical); }
.active-job-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--brand); flex: none;
  animation: aji-pulse 1.4s ease-in-out infinite;
}
.active-job-indicator.ready .active-job-dot { background: var(--good); animation: none; }
.active-job-indicator.errored .active-job-dot { background: var(--critical); animation: none; }
@keyframes aji-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .active-job-dot { animation: none; } }

/* ---------- Layout ---------- */
.view { display: none; flex: 1; }
.view.active { display: block; }
.container { max-width: 1180px; margin: 0 auto; padding: 32px 24px 80px; }

/* ---------- Landing / upload ---------- */
/* Bottom padding trimmed well below .container's default 80px (meant for
   space above a page footer, not a mid-page section transition) -- the
   fake trajectory decoration is gone, so the next section can start close
   to the fold without a large empty gap after the hero.

   max-width overrides .container's shared 1180px for the hero ONLY (every
   other section stays at 1180px) -- at large desktop widths 1180px reads as
   a small, isolated composition floating in a lot of unused viewport. The
   clamp() floor equals .container's 1180px (so nothing below ~1372px
   viewport width changes at all -- moderate desktop sizes stay exactly as
   already verified), scales with the viewport above that, and caps at
   1560px so the hero widens with the screen without stretching the photo
   column tall enough to push "How It Works" far below the fold. */
.hero-container { position: relative; max-width: var(--landing-max-width); padding: 28px 24px 20px; }
/* Extremely faint structural grid behind the hero only -- technical depth,
   not decoration: opacity is low enough that it reads as texture, never as
   a pattern competing with the real photo/analysis frame. */
.hero-container::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    linear-gradient(90deg, var(--gridline) 1px, transparent 1px),
    linear-gradient(180deg, var(--gridline) 1px, transparent 1px);
  background-size: 64px 64px; opacity: .05;
}
.hero {
  position: relative; z-index: 1; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center; gap: clamp(40px, 3vw, 56px); overflow: hidden; margin-bottom: 8px;
}
@media (max-width: 860px) { .hero { grid-template-columns: 1fr; gap: 28px; } }
/* Nudged above the row's true vertical center (align-items: center on
   .hero) rather than sitting dead-center against the much taller photo
   column -- reads as intentionally composed, not just mathematically
   balanced. */
.hero-copy { position: relative; z-index: 1; text-align: left; margin-top: -50px; }
@media (max-width: 860px) {
  /* The -50px nudge above only makes sense against the much-taller photo
     column in the 2-column desktop layout -- in this single-column stacked
     layout it has nothing to center against and just pulls the headline up
     into the navbar. Must come AFTER the base .hero-copy rule above (same
     specificity -- source order decides the winner). */
  .hero-copy { margin-top: 0; }
}
.hero-eyebrow {
  font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent-2); margin-bottom: 14px;
}
/* The real annotated basketball photo is boxed inside a bordered
   "analysis frame" (label bar + photo/metric-panel row + a footer that
   opens the real demo) rather than floating as a plain photo -- see
   .analysis-frame below. An earlier pass had a decorative dual-color
   radial glow behind the photo (ember + teal blobs); that stays removed,
   the frame's own restrained border/label is the only structure added. */
.hero-visual { position: relative; z-index: 1; }
.analysis-frame {
  position: relative; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface-1); box-shadow: var(--shadow-1); overflow: hidden;
}
.analysis-frame-label {
  display: flex; align-items: center; gap: 8px; padding: 10px 16px;
  font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent-2); border-bottom: 1px solid var(--gridline);
}
.analysis-frame-label::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--accent-2); flex: none; }
.analysis-frame-body { display: grid; grid-template-columns: minmax(0, 1fr) 148px; }
/* frontend/static/images/hero-basketball.png (pose keypoints, ball
   trajectory, hoop detection over a real shot). "center 20%" keeps the
   hoop + trajectory apex (the image's upper band) in view under
   background-size:cover instead of center-cropping into the shooter's feet.
   Path is RELATIVE to this file (never /static/...) so this resolves
   correctly both locally and on the GitHub Pages project-subpath demo --
   see index.html's head comment on the same constraint. */
.hero-photo {
  position: relative; aspect-ratio: 4 / 5;
  /* Inset shadow, not a mask -- only darkens a thin band at the perimeter
     without removing any pixel data, so it never hides the real
     trajectory dots/pose skeleton baked into the photo itself. No edge
     feather here (unlike an earlier pass) -- inside a bordered frame, a
     clean rectangular photo reads as a UI panel, not a floating cutout. */
  box-shadow: inset 0 0 40px 0 rgba(4, 6, 10, 0.22);
  background-image:
    linear-gradient(180deg,
      color-mix(in srgb, var(--surface-0) 4%, transparent) 0%,
      color-mix(in srgb, var(--surface-0) 30%, transparent) 100%),
    url('../images/hero-basketball.png');
  background-size: cover, cover;
  background-position: center, center 20%;
  background-repeat: no-repeat, no-repeat;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hero-photo {
    background-image:
      linear-gradient(180deg, rgba(8,11,17,0.05) 0%, rgba(8,11,17,0.35) 100%),
      url('../images/hero-basketball.png');
  }
}
:root[data-theme="dark"] .hero-photo {
  background-image:
    linear-gradient(180deg, rgba(8,11,17,0.05) 0%, rgba(8,11,17,0.35) 100%),
    url('../images/hero-basketball.png');
}
/* Real per-shot output from the featured demo session (shot 7 of
   fee1c3bcd7b84322 -- elbow angle at release, ball tracking quality, and
   the frozen verified outcome, straight from that session's stored
   analysis; see scripts/demo_ground_truth.py for the outcome mapping).
   Deliberately NOT captioned as describing the adjacent photo -- that
   photo is a different recording (confirmed by comparing frames) used
   purely as hero art, so this panel is framed as its own real data
   excerpt from the actual interactive demo instead. */
.analysis-panel { display: flex; flex-direction: column; border-left: 1px solid var(--gridline); background: var(--surface-2); }
.analysis-metric { flex: 1; padding: 10px 12px; display: flex; flex-direction: column; justify-content: center; border-bottom: 1px solid var(--gridline); }
.analysis-metric:last-child { border-bottom: none; }
.analysis-metric-value { font-size: 19px; font-weight: 800; color: var(--text-primary); font-variant-numeric: tabular-nums; line-height: 1.1; }
.analysis-metric-label { font-size: 10px; color: var(--text-muted); margin-top: 4px; line-height: 1.3; }
.analysis-metric-value.analysis-outcome-made { color: var(--good); }
.analysis-metric-value.analysis-outcome-missed { color: var(--critical); }
.analysis-frame-footer {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
  padding: 12px 16px; background: none; border: none; border-top: 1px solid var(--gridline);
  font: inherit; font-size: 12.5px; color: var(--text-secondary); cursor: pointer; text-align: left;
  transition: background .15s, color .15s;
}
.analysis-frame-footer:hover, .analysis-frame-footer:focus-visible { background: var(--surface-2); color: var(--text-primary); }
.analysis-frame-footer-arrow { color: var(--brand); font-weight: 700; transition: transform .15s; }
.analysis-frame-footer:hover .analysis-frame-footer-arrow { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  .analysis-frame-footer, .analysis-frame-footer-arrow { transition: none; }
  .analysis-frame-footer:hover .analysis-frame-footer-arrow { transform: none; }
}
@media (max-width: 860px) {
  .hero-photo { aspect-ratio: 16 / 10; }
  .analysis-frame-body { grid-template-columns: 1fr; }
  .analysis-panel { flex-direction: row; border-left: none; border-top: 1px solid var(--gridline); }
  .analysis-metric { border-bottom: none; border-right: 1px solid var(--gridline); }
  .analysis-metric:last-child { border-right: none; }
}
/* Real ArcVision pipeline stages (RF-DETR ball+hoop detector, MediaPipe
   pose, the shot/mechanics analytics) as a capability row -- scaled up to
   read as three real product capabilities, not footer metadata, while
   staying an integrated row (no cards/backgrounds per item). Grid with
   three equal columns (not flex + justify-content: space-between) so each
   capability owns a genuine third of the row's width -- content is
   centered within that third rather than clustering tight around the
   icon+text with huge gaps between three narrow clusters. */
.hero-capabilities {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; margin: 36px 0 0; padding-top: 28px;
  border-top: 1px solid var(--gridline); list-style: none;
}
.hero-capabilities li { display: flex; align-items: center; gap: 20px; }
.hero-capabilities svg { width: clamp(28px, 1.1vw + 16px, 40px); height: clamp(28px, 1.1vw + 16px, 40px); color: var(--accent-2); flex: none; }
.hero-capabilities span { display: flex; flex-direction: column; font-size: clamp(14.5px, 0.3vw + 13px, 17px); font-weight: 600; color: var(--text-muted); line-height: 1.3; }
.hero-capabilities span strong { font-size: clamp(18px, 0.45vw + 15px, 24px); font-weight: 800; color: var(--text-primary); margin-bottom: 3px; }
@media (max-width: 720px) {
  /* Below the hero's own stacked-layout breakpoint, three equal grid
     columns get too narrow for "Ball + hoop" to read comfortably -- revert
     to the flex-wrap row (2-up then 1) used before this pass. */
  .hero-capabilities { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 24px; }
  .hero-capabilities svg { width: 26px; height: 26px; }
  .hero-capabilities span { font-size: 13px; }
  .hero-capabilities span strong { font-size: 15px; }
}
@media (max-width: 480px) { .hero-capabilities { gap: 18px; } }
.hero h1 { position: relative; font-size: clamp(38px, 2.6vw + 20px, 56px); font-weight: 800; margin: 0 0 16px; letter-spacing: -0.025em; line-height: 1.08; }
.hero-emph { color: var(--brand); }
/* max-width widened from 480px (well under the hero-copy column's ~700px+
   at desktop) so the sentence uses the column and wraps to roughly two
   natural lines instead of four short ones. */
.hero p.lede { position: relative; font-size: 19px; line-height: 1.5; color: var(--text-secondary); max-width: 700px; margin: 0 0 26px; }

.dropzone {
  position: relative; z-index: 1;
  max-width: 640px; margin: 0 auto; border: 2px dashed var(--baseline);
  border-radius: var(--radius-lg); padding: 48px 24px; text-align: center;
  background: var(--surface-1); cursor: pointer;
  transition: border-color .15s, background .15s, box-shadow .15s;
}
.dropzone:hover { border-color: var(--accent-2); box-shadow: var(--shadow-1); }
.dropzone.dragover { border-color: var(--brand); background: var(--surface-2); box-shadow: var(--shadow-card-hover); }
.dropzone .icon { font-size: 36px; margin-bottom: 10px; }
.dropzone .hint { color: var(--text-muted); font-size: 13px; margin-top: 8px; }
.dropzone input[type=file] { display: none; }
.btn {
  display: inline-block; background: var(--brand); color: white; border: none;
  padding: 11px 22px; border-radius: var(--radius-sm); font-size: 14px; font-weight: 700;
  cursor: pointer; margin-top: 14px; transition: background .15s, transform .1s, box-shadow .15s;
}
.btn:hover { background: var(--brand-dark); box-shadow: 0 4px 12px color-mix(in srgb, var(--brand) 35%, transparent); transform: translateY(-1px); }
.btn:active { transform: translateY(1px); }
@media (prefers-reduced-motion: reduce) { .btn { transition: background .15s, box-shadow .15s; } .btn:hover, .btn:active { transform: none; } }
.btn.secondary { background: transparent; color: var(--accent-2); border: 1px solid var(--border); box-shadow: none; }
.btn.secondary:hover { background: var(--surface-2); color: var(--accent-2-dark); box-shadow: none; }

.hero-actions { position: relative; margin-top: 4px; }
.btn-demo { font-size: 15px; padding: 13px 28px; }
.demo-cta-caption { margin-top: 10px; font-size: 12px; color: var(--text-muted); }
/* Was a large standalone "Analyze your own footage" card directly under the
   hero -- interrupted the page flow on its own. Its two links now live here
   instead, as a small secondary/tertiary action row right under the primary
   CTA (still the exact same #demo-upload-notice/#demo-upload-actions
   elements app.js already populates -- see initDemoMode() -- only the
   surrounding markup/position/styling changed). */
.hero-secondary-actions { margin-top: 14px; }
.demo-upload-actions { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; }
.demo-upload-actions .btn {
  background: none; color: var(--text-secondary); border: none; padding: 0; margin: 0;
  font-size: 13px; font-weight: 600; box-shadow: none; text-decoration: underline;
  text-decoration-color: var(--border); text-underline-offset: 3px;
}
.demo-upload-actions .btn:hover { color: var(--accent-2); text-decoration-color: currentColor; box-shadow: none; }
.demo-upload-actions-pending { font-size: 12.5px; color: var(--text-muted); }

@media (max-width: 480px) {
  .hero h1 { font-size: 30px; }
  .hero p.lede { font-size: 15px; }
}

.upload-error {
  position: relative; z-index: 1;
  max-width: 640px; margin: 16px auto 0; background: color-mix(in srgb, var(--critical) 8%, var(--surface-1));
  border: 1px solid color-mix(in srgb, var(--critical) 35%, transparent); color: var(--critical);
  padding: 12px 16px; border-radius: var(--radius-md); font-size: 14px; display: none;
}

/* ---------- Topbar demo-only nav ---------- */
/* "Demo"/"How It Works"/"GitHub"/"Run Locally" only make sense on the
   public demo -- there's nothing to point at in local mode, which never
   reaches initDemoMode() and so never gains .is-demo-mode. */
.topbar-links, .btn-nav-cta { display: none; }
body.is-demo-mode .topbar-links { display: flex; align-items: center; gap: 18px; margin-right: 4px; }
body.is-demo-mode .btn-nav-cta { display: inline-block; padding: 7px 14px; font-size: 13px; margin: 0; }
.topbar-link { font-size: 13.5px; font-weight: 600; color: var(--text-secondary); text-decoration: none; }
.topbar-link:hover { color: var(--text-primary); }
@media (max-width: 760px) { .topbar-links { display: none !important; } }

/* ---------- How it works (3-step) ---------- */
/* These three sections each provide their own vertical rhythm via their
   own padding -- the shared .container's default vertical padding (tuned
   for a single full-height view like the dashboard) would otherwise stack
   on top of it and leave a large empty gap before the footer. */
.steps .container, .product-preview .container, .proof-strip .container {
  padding-top: 0; padding-bottom: 0;
  /* Same wide canvas as the hero, applied only to these landing sections'
     own .container instances -- the dashboard's .container (and every
     other view) is untouched, so this never affects the actual app. */
  max-width: var(--landing-max-width);
}
.steps { padding: 72px 0 8px; }
.section-eyebrow {
  font-size: clamp(14px, 0.9vw + 10px, 16px); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent-2); margin-bottom: 28px;
}
.steps-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 48px; }
@media (max-width: 720px) { .steps-grid { grid-template-columns: 1fr; gap: 28px; } }
.step { position: relative; padding-top: 12px; border-top: 2px solid var(--gridline); transition: border-color .18s; }
.step:hover { border-top-color: var(--brand); }
.step-num {
  font-size: clamp(17px, 0.6vw + 15px, 19px); font-weight: 800; letter-spacing: .04em; color: var(--brand);
  font-variant-numeric: tabular-nums; margin-bottom: 20px;
}
.step-icon { color: var(--accent-2); margin-bottom: 16px; }
.step-icon svg { width: 32px; height: 32px; }
.step h3 { margin: 0 0 10px; font-size: clamp(21px, 1vw + 17px, 24px); }
.step p { margin: 0; font-size: clamp(15px, 0.5vw + 14px, 16.5px); color: var(--text-secondary); line-height: 1.58; }
.steps-details { margin: 32px 0 0; }
.steps-details summary {
  cursor: pointer; font-size: 15px; font-weight: 700; color: var(--accent-2);
  list-style: none; display: inline-flex; align-items: center; gap: 6px; user-select: none;
}
.steps-details summary::-webkit-details-marker { display: none; }
.steps-details summary::before { content: "▸"; font-size: 11px; transition: transform .12s; }
.steps-details[open] summary::before { transform: rotate(90deg); }
.steps-details ul { margin: 14px 0 0; padding: 0 0 0 18px; font-size: 15px; color: var(--text-secondary); line-height: 1.7; }

/* ---------- Product preview ---------- */
/* Bottom padding trimmed to match .steps' own convention (8px, not a
   symmetric 64px) -- .product-preview-media's margin-bottom below already
   reserves ample clearance for the inset screenshot's overhang and the
   caption beneath it, so a full 64px of section padding on top of that
   double-counts the same space and reads as one large empty gap before the
   proof strip. */
.product-preview { display: none; padding: 64px 0 8px; }
body.is-demo-mode .product-preview { display: block; }
.product-preview-inner { display: grid; grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); gap: 48px; align-items: center; }
@media (max-width: 860px) { .product-preview-inner { grid-template-columns: 1fr; gap: 28px; } }
.product-preview-copy h2 { margin: 0 0 14px; font-size: 30px; font-weight: 800; letter-spacing: -0.015em; }
.product-preview-copy p { margin: 0 0 24px; font-size: 16.5px; color: var(--text-secondary); line-height: 1.6; max-width: 540px; }
/* margin (not padding) on purpose -- the inset screenshot's `bottom: -9%`
   below resolves against this element's own height, so padding here would
   grow that height and push the inset down further, chasing the caption's
   clearance in a feedback loop. Margin reserves flow space for the caption
   without feeding back into that percentage. */
.product-preview-media { position: relative; margin-bottom: 150px; }
.product-preview-main {
  display: block; width: 100%; border-radius: var(--radius-lg); border: 1px solid var(--border);
  box-shadow: var(--shadow-1); transition: box-shadow .2s, transform .2s;
}
.product-preview-inset {
  position: absolute; width: 46%; left: -6%; bottom: -9%; border-radius: var(--radius-md);
  border: 1px solid var(--border); box-shadow: var(--shadow-card-hover); background: var(--surface-1);
  transition: transform .2s;
}
.product-preview-media:hover .product-preview-main { box-shadow: var(--shadow-card-hover); transform: translateY(-2px); }
.product-preview-media:hover .product-preview-inset { transform: translateY(-3px); }
@media (prefers-reduced-motion: reduce) {
  .product-preview-main, .product-preview-inset { transition: none; }
}
@media (max-width: 860px) { .product-preview-inset { left: auto; right: -2%; } }
@media (max-width: 480px) { .product-preview-inset { width: 52%; bottom: -7%; } }
/* Fixed px, positioned within the margin-bottom reserved above -- clears
   the inset screenshot's ~80-110px overhang (measured across breakpoints)
   with room to spare. States plainly these are static screenshots, not a
   clickable mini-demo. */
.product-preview-caption {
  position: absolute; left: 0; right: 0; bottom: -140px;
  font-size: 12px; color: var(--text-muted); text-align: center;
}

/* ---------- Proof strip ---------- */
.proof-strip { display: none; padding: 44px 0 64px; border-top: 1px solid var(--gridline); }
body.is-demo-mode .proof-strip { display: block; }
.proof-strip-head { font-size: clamp(17px, 0.8vw + 14px, 19.5px); font-weight: 700; margin-bottom: 30px; }
.proof-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; }
@media (max-width: 720px) { .proof-grid { grid-template-columns: repeat(2, 1fr); } }
.proof-num { font-size: clamp(34px, 2vw + 26px, 40px); font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; color: var(--accent-2); }
.proof-label { font-size: 14.5px; color: var(--text-muted); margin-top: 6px; }
/* max-width widened from a flat 680px, which was well under the proof
   strip's now-wide container (1560px at 1920px viewport) and forced a
   2-line wrap with plenty of room to spare -- clamp() lets it use much
   more of that width on large desktop while still capping at a sensible
   line length on smaller screens. */
.proof-strip-note { margin-top: 28px; font-size: 15px; color: var(--text-muted); max-width: clamp(480px, 56vw, 1040px); }
.proof-strip-note a { color: var(--text-secondary); }

/* ---------- Processing ---------- */
.processing-wrap { max-width: 560px; margin: 80px auto; text-align: center; }
.processing-live {
  display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600;
  color: var(--text-secondary); font-variant-numeric: tabular-nums; margin-bottom: 4px;
}
.processing-live-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--accent-2);
  animation: aji-pulse 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .processing-live-dot { animation: none; } }
.processing-visual { position: relative; width: 130px; height: 96px; margin: 0 auto 8px; }
.processing-visual svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.pv-ball {
  position: absolute; top: 0; left: 0; width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px;
  border-radius: 50%; background: var(--accent-2);
  offset-path: path("M10,84 C 26,20 70,2 96,36");
  animation: pv-move 1.7s cubic-bezier(.4,.05,.4,.95) infinite;
}
@keyframes pv-move {
  0%   { offset-distance: 0%;   opacity: 0; transform: scale(.6); }
  10%  { opacity: 1; transform: scale(1); }
  85%  { opacity: 1; transform: scale(1); }
  100% { offset-distance: 100%; opacity: 0; transform: scale(.6); }
}
@media (prefers-reduced-motion: reduce) {
  .pv-ball { animation: none; offset-distance: 55%; opacity: 1; }
}
.stage-list { text-align: left; max-width: 380px; margin: 24px auto 0; }
.stage-item { display: flex; align-items: center; gap: 10px; padding: 6px 0; font-size: 13px; color: var(--text-muted); }
.stage-item.current { color: var(--text-primary); font-weight: 700; }
.stage-item.done { color: var(--good); }
.stage-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gridline); flex: none; }
.stage-item.current .stage-dot { background: var(--brand); }
.stage-item.done .stage-dot { background: var(--good); }
.progress-track { height: 6px; background: var(--gridline); border-radius: 4px; overflow: hidden; margin-top: 20px; }
.progress-fill { height: 100%; background: linear-gradient(90deg, var(--brand), var(--accent-2)); transition: width .3s ease; }

/* ---------- Dashboard ---------- */
.demo-banner {
  background: color-mix(in srgb, var(--accent-2) 8%, var(--surface-1));
  border: 1px solid color-mix(in srgb, var(--accent-2) 30%, var(--border));
  border-radius: var(--radius-md); padding: 14px 18px; margin-bottom: 20px;
}
.demo-banner-eyebrow {
  font-size: 11.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--accent-2); margin-bottom: 6px;
}
.demo-banner p { margin: 0; font-size: 13px; color: var(--text-secondary); line-height: 1.5; }
.demo-banner a { color: var(--accent-2); font-weight: 600; }

.dash-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 22px; flex-wrap: wrap; gap: 12px; }
.dash-title { font-size: 23px; font-weight: 800; letter-spacing: -0.01em; }
.dash-sub { color: var(--text-secondary); font-size: 13px; margin-top: 4px; }

.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 24px; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tab-btn {
  background: none; border: none; padding: 11px 14px; font-size: 13.5px; font-weight: 700;
  color: var(--text-muted); cursor: pointer; border-bottom: 2px solid transparent; white-space: nowrap;
  transition: color .12s;
}
.tab-btn:hover { color: var(--text-primary); }
.tab-btn.active { color: var(--brand); border-bottom-color: var(--brand); }
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* ---------- Details sub-navigation (secondary, quieter than the primary tabs) ---------- */
.details-subtabs { display: flex; gap: 4px; margin: 4px 0 20px; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.subtab-btn {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 14px; font-size: 12.5px; font-weight: 600; color: var(--text-muted); cursor: pointer; white-space: nowrap;
  transition: color .12s, background .12s, border-color .12s;
}
.subtab-btn:hover { color: var(--text-primary); }
.subtab-btn.active { color: var(--surface-1); background: var(--accent-2); border-color: var(--accent-2); }
.subtab-panel { display: none; }
.subtab-panel.active { display: block; }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 26px; }
.stat-tile {
  background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: 14px 16px; transition: box-shadow .15s, border-color .15s;
}
.stat-tile:hover { box-shadow: var(--shadow-1); }
.stat-tile .label { font-size: 12px; color: var(--text-muted); margin-bottom: 6px; font-weight: 600; }
.stat-tile .value { font-size: 25px; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.stat-tile .sub { font-size: 11.5px; color: var(--text-muted); margin-top: 4px; }

/* ---------- Coach ---------- */
.coach-stats { display: flex; flex-wrap: wrap; align-items: center; gap: 22px; padding: 4px 0 18px; }
.coach-stat { display: flex; flex-direction: column; gap: 2px; }
.coach-stat-value { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.coach-stat-label { font-size: 11px; color: var(--text-muted); font-weight: 600; }
.coach-confidence-pill {
  margin-left: auto; font-size: 12px; font-weight: 700; color: var(--text-secondary);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; padding: 5px 12px;
}

.coach-note-block { font-size: 17px; line-height: 1.5; color: var(--text-primary); font-weight: 600; padding: 2px 0 22px; max-width: 880px; }
.coach-note-block p { margin: 0; }

/* The visual centerpiece -- deliberately larger and more elaborate than
   every other Coach card, so "#1 focus" reads as the one thing to look at
   first, not one card among equals. */
.coach-focus {
  position: relative;
  background: var(--surface-1); border: 1px solid var(--border); border-left: 3px solid var(--brand);
  border-radius: var(--radius-md);
  padding: 26px 30px; margin-bottom: 22px;
}
.coach-focus-eyebrow {
  position: relative; font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--brand); margin-bottom: 10px;
}
.coach-focus h2 { position: relative; margin: 0 0 10px; font-size: 30px; font-weight: 800; letter-spacing: -0.02em; }
.coach-focus-evidence { position: relative; font-size: 15px; color: var(--text-secondary); max-width: 880px; margin: 0 0 20px; }
.coach-focus.coach-focus-empty { padding: 26px 28px; }
.coach-focus.coach-focus-empty p { position: relative; color: var(--text-secondary); margin: 0; }
@media (max-width: 480px) {
  .coach-focus { padding: 24px 20px; }
  .coach-focus h2 { font-size: 24px; }
  .coach-note-block { font-size: 15.5px; }
}

/* "What this means" -- connects the measurement to the physical shooting
   motion, between the noticed-sentence and the actionable cue. Teal (not
   the cue's orange) to read as explanatory, not actionable, matching the
   same accent-2-for-explanation convention used elsewhere (e.g. the hero
   eyebrow, section labels). */
.coach-focus-meaning { position: relative; margin: 0 0 18px; }
.coach-focus-meaning-label { font-size: 11.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--accent-2); margin-bottom: 6px; }
.coach-focus-meaning p { font-size: 14px; color: var(--text-secondary); margin: 0; max-width: 880px; }

.coach-card-strength-detail { font-size: 13px; color: var(--text-muted); margin: 6px 0 0; }

.coach-cues { position: relative; margin-bottom: 16px; }
.coach-cues-label { font-size: 11.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 8px; }
.coach-cue-primary {
  font-size: 16px; font-weight: 700; color: var(--text-primary); line-height: 1.4;
  padding: 12px 16px; background: var(--surface-2); border-left: 3px solid var(--brand); border-radius: var(--radius-sm);
  margin-bottom: 8px;
}
.coach-cue-secondary { font-size: 13px; color: var(--text-muted); padding: 0 16px; line-height: 1.4; }

.coach-evidence { position: relative; margin-top: 4px; }
.coach-evidence summary {
  cursor: pointer; font-size: 12.5px; font-weight: 700; color: var(--accent-2);
  list-style: none; display: inline-flex; align-items: center; gap: 5px; user-select: none;
}
.coach-evidence summary::-webkit-details-marker { display: none; }
.coach-evidence summary::before { content: "▸"; font-size: 10px; transition: transform .12s; }
.coach-evidence[open] summary::before { transform: rotate(90deg); }
.coach-evidence p { font-size: 13px; color: var(--text-secondary); margin: 10px 0; }
.coach-evidence .metric-table { font-size: 12.5px; }

.coach-secondary-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 720px) { .coach-secondary-grid { grid-template-columns: 1fr; } }

.coach-card-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 6px; }
.coach-card-strength { border-left: 3px solid var(--accent-2); }
.coach-card-strength .coach-card-eyebrow { color: var(--accent-2); }
.coach-card-strength h3 { margin: 0 0 6px; font-size: 17px; }
.coach-card-mvm h3 { margin: 0 0 6px; font-size: 16px; }
.coach-card-mvm p { margin: 0; font-size: 13.5px; color: var(--text-secondary); }
.coach-card-empty { color: var(--text-muted); }
.coach-card-empty p { font-size: 13px; margin: 0; }

/* ---------- Details tab: short section explainers + session takeaway ---------- */
/* Reuses .card's box (background/border/padding/shadow) with the same
   colored-left-border accent already used for .coach-card-strength above,
   not a new visual language. */
.details-explainer { border-left: 3px solid var(--accent-2); }
.details-explainer p { margin: 0; font-size: 13px; color: var(--text-secondary); line-height: 1.55; }
.details-explainer p + p { margin-top: 8px; }
.session-takeaway { border-left: 3px solid var(--brand); }
.session-takeaway-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--brand); margin-bottom: 6px; }
.session-takeaway h3 { margin: 0 0 6px; font-size: 17px; }
.session-takeaway p { margin: 0; font-size: 13.5px; color: var(--text-secondary); }

.coach-abstain { text-align: center; max-width: 480px; margin: 40px auto; padding: 12px; }
.coach-abstain-icon {
  width: 56px; height: 56px; border-radius: 50%; margin: 0 auto 16px;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent-2) 12%, transparent); color: var(--accent-2);
}
.coach-abstain h2 { font-size: 19px; margin: 0 0 8px; }
.coach-abstain p { font-size: 14px; color: var(--text-secondary); margin: 0; }

.card {
  background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 20px 22px; margin-bottom: 20px; box-shadow: var(--shadow-1);
  /* Wide tables (e.g. the makes-vs-misses comparison) scroll within the
     card on narrow screens instead of overflowing and forcing the whole
     page to scroll horizontally. */
  max-width: 100%; overflow-x: auto;
}
.card h3 { margin: 0 0 4px; font-size: 15.5px; font-weight: 700; }
.card .card-sub { color: var(--text-muted); font-size: 12.5px; margin-bottom: 14px; }

/* Chart footnote: a shot genuinely missing from a per-shot chart (no
   reliable measurement, never fabricated) can read as a rendering bug
   without context. Deliberately plain -- no icon, no colored box, no
   pill -- so it reads as an ordinary footnote, not a warning. */
.chart-note { color: var(--text-muted); font-size: 11.5px; margin-top: 8px; }

/* Mechanics sub-tab: five per-shot charts used to stack as five full-width
   cards, each mostly empty space around one narrow chart -- a two-up grid
   keeps each card close to the chart's own natural size instead of
   stretching it across the whole page. */
.metric-chart-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr)); gap: 16px; }
.metric-chart-grid .card { margin-bottom: 0; }

.warning-banner {
  background: color-mix(in srgb, var(--warning) 10%, var(--surface-1)); border: 1px solid color-mix(in srgb, var(--warning) 35%, transparent);
  color: var(--text-primary); border-radius: var(--radius-md); padding: 10px 14px;
  font-size: 13px; margin-bottom: 10px; display: flex; gap: 8px;
}
.warning-banner::before { content: "\26A0"; color: var(--warning); }

/* Per-shot quality notices -- informational tracking/measurement caveats,
   deliberately calmer than .warning-banner (reserved for genuine session-
   level issues) so they read as "here's a quality note," not an error. One
   panel combines every note for a shot rather than repeating a banner per
   code. */
.quality-panel {
  background: color-mix(in srgb, var(--accent-2) 6%, var(--surface-1));
  border: 1px solid color-mix(in srgb, var(--accent-2) 25%, var(--border));
  border-radius: var(--radius-md); padding: 12px 14px; margin-bottom: 16px;
}
.quality-panel-header {
  display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 700;
  color: var(--text-primary); margin-bottom: 8px;
}
.quality-panel-icon { color: var(--accent-2); flex: none; display: flex; }
.quality-panel-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.quality-panel-list li { font-size: 12.5px; }
.quality-panel-list li strong { display: block; color: var(--text-primary); font-weight: 700; }
.quality-panel-list li span { display: block; color: var(--text-secondary); font-size: 12px; margin-top: 1px; }
.quality-panel-tech { margin-top: 10px; }
.quality-panel-tech summary { font-size: 11.5px; }
.quality-panel-tech p { font-size: 11.5px; word-break: break-word; }

/* Verified-demo-mode collapsed disclosure wrapping .quality-panel (see
   renderQualityPanel's collapseIfOrdinary in app.js) -- reuses the same
   restrained .coach-evidence summary treatment as "Automatic analysis
   details"/"Why ArcVision thinks this" elsewhere, so a collapsed
   measurement-notes disclosure looks like the rest of the app's existing
   disclosure pattern rather than a new component. */
.quality-panel-collapsed { margin: 14px 0; }
.quality-panel-collapsed > .quality-panel { margin-top: 10px; margin-bottom: 0; }

.finding-card { border-left: 3px solid var(--brand); padding-left: 14px; margin-bottom: 16px; }
.finding-card .finding-title { font-weight: 700; font-size: 14.5px; margin-bottom: 4px; }
.finding-card .finding-evidence { font-size: 12.5px; color: var(--text-secondary); margin-bottom: 4px; }
.finding-card .finding-focus { font-size: 13px; color: var(--text-primary); }
.confidence-badge {
  display: inline-block; font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 999px; margin-left: 8px; font-weight: 700;
}
.confidence-badge.high { background: color-mix(in srgb, var(--good) 15%, transparent); color: var(--good); }
.confidence-badge.medium { background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning-text); }
.confidence-badge.low { background: color-mix(in srgb, var(--unknown) 18%, transparent); color: var(--text-muted); }
.confidence-badge.critical { background: color-mix(in srgb, var(--critical) 15%, transparent); color: var(--critical); }

/* Manually verified demo outcome -- deliberately distinct from
   .confidence-badge (which reflects an AI-computed 0-1 score): this label
   never represents a model confidence tier, only that the outcome shown was
   manually confirmed against the source video before ArcVision ran. */
.verified-badge {
  display: inline-block; font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 999px; margin-left: 8px; font-weight: 700;
  background: color-mix(in srgb, var(--brand) 15%, transparent); color: var(--brand);
}

.history-row-main { display: flex; flex-direction: column; gap: 2px; }
.history-row-error { font-size: 11.5px; color: var(--text-muted); }

/* Shot list */
.shot-list { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.shot-chip {
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 12px;
  font-size: 12.5px; cursor: pointer; background: var(--surface-1); text-align: center; min-width: 64px;
  transition: border-color .12s, box-shadow .12s;
}
.shot-chip:hover { border-color: var(--baseline); }
.shot-chip.active { outline: 2px solid var(--brand); outline-offset: -1px; }
.shot-chip .n { font-weight: 700; display: block; }
.shot-chip .outcome-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-top: 4px; }
.outcome-made { background: var(--good); color: var(--good); }
.outcome-missed { background: var(--critical); color: var(--critical); }
.outcome-unknown { background: var(--unknown); color: var(--unknown); }

.metric-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.metric-table th, .metric-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--gridline); }
.metric-table th { color: var(--text-muted); font-weight: 700; font-size: 11.5px; text-transform: uppercase; letter-spacing: .03em; }
.metric-table td.num { font-variant-numeric: tabular-nums; text-align: right; }

.legend-row { display: flex; gap: 16px; font-size: 12px; color: var(--text-secondary); margin-bottom: 10px; flex-wrap: wrap; }
.legend-item { display: flex; align-items: center; gap: 6px; }
.legend-swatch { width: 10px; height: 10px; border-radius: 3px; }

.replay-wrap { display: flex; gap: 24px; flex-wrap: wrap; }
.replay-video { flex: 2; min-width: 320px; }
.replay-video video { width: 100%; border-radius: var(--radius-md); background: black; }
.replay-side { flex: 1; min-width: 220px; }
.mode-toggle { display: flex; gap: 6px; margin-bottom: 10px; }

.methodology-body { font-size: 14px; color: var(--text-secondary); }
.methodology-body h1, .methodology-body h2, .methodology-body h3 { color: var(--text-primary); }
.methodology-body pre { background: var(--surface-2); padding: 12px; border-radius: var(--radius-sm); overflow-x: auto; }
.methodology-body code { background: var(--surface-2); padding: 1px 5px; border-radius: 4px; font-size: 12.5px; }

.empty-state { text-align: center; padding: 40px 20px; color: var(--text-muted); font-size: 13.5px; }

.history-row {
  display: flex; justify-content: space-between; align-items: center; padding: 12px 14px;
  border-bottom: 1px solid var(--gridline); font-size: 13.5px; cursor: pointer;
}
.history-row:hover { background: var(--surface-2); }

.svg-tooltip {
  position: absolute; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 6px; padding: 6px 9px; font-size: 12px; pointer-events: none; box-shadow: var(--shadow-1);
  display: none; z-index: 30; white-space: nowrap;
}

footer.app-footer { text-align: center; color: var(--text-muted); font-size: 12px; padding: 24px; }
.footer-link {
  background: none; border: none; color: var(--text-muted); font-size: 12px; text-decoration: underline;
  cursor: pointer; padding: 0; font-family: inherit;
}
.footer-link:hover { color: var(--text-secondary); }
