/* ═══════════════════════════════════════════════════════════
   ScoreMatrix — Premium Polish Pack
   Additive only: no existing selectors are overridden in a
   breaking way, this file only adds new visual behavior.
═══════════════════════════════════════════════════════════ */

/* ── 1. Smooth theme-switch transitions ──
   Because --bg/--surface/etc are CSS custom properties, adding
   transitions on the properties that consume them makes the
   light/dark toggle animate instead of snapping instantly. */
body,
header,
.tabs,
.card,
.most-likely-hero,
.team-card,
.outcome-tile,
.xg-pill,
input,
select,
.fm-input,
.fm-name-input,
.header-stats-strip,
.header-stats-strip-mobile,
.header-mode-badge {
  transition: background-color 0.35s ease, border-color 0.35s ease, color 0.35s ease, box-shadow 0.35s ease;
}

/* ── 2. Tabular numerals everywhere numbers show up ──
   Keeps digits fixed-width so scores/odds/percentages don't
   jiggle side to side as they animate or update. */
.mlh-score, .otile-pct, .otile-odds, .xg-pill-val, .hss-value, .hssm-value,
.ev-value, .ev-model-prob, .conf-meter-badge, .confidence-bar-label,
.ps-scoreline, .bar-pct, .pill-score, .pill-pct, .badge-mode,
#badge-rho, #badge-tau {
  font-variant-numeric: tabular-nums;
}

/* ── 3. Hero score: entrance pop + probability ring ── */
.mlh-score-row {
  position: relative;
}
.mlh-score.sm-score-animating {
  animation: sm-score-pop 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.3);
}
@keyframes sm-score-pop {
  0%   { transform: scale(0.85); opacity: 0.3; filter: blur(2px); }
  60%  { transform: scale(1.05); opacity: 1; filter: blur(0); }
  100% { transform: scale(1); }
}
/* The ring is positioned via JS (left/top set inline) relative to
   .mlh-score-row, glued to the right edge of the score's own
   rendered width. This keeps the score text itself perfectly
   centered in the card no matter how wide the ring is or isn't. */
.sm-prob-ring-wrap {
  position: absolute;
  width: 54px;
  height: 54px;
  display: none;
}
.sm-prob-ring-wrap.sm-visible { display: inline-flex; }
@media (max-width: 480px) {
  /* Not enough horizontal room next to the score on small phones —
     drop the ring below the score instead of crowding/clipping it. */
  .sm-prob-ring-wrap {
    width: 40px;
    height: 40px;
  }
}
.sm-prob-ring-wrap svg { transform: rotate(-90deg); }
.sm-prob-ring-track { fill: none; stroke: var(--border); stroke-width: 4; }
.sm-prob-ring-fill {
  fill: none;
  stroke: var(--accent);
  stroke-width: 4;
  stroke-linecap: round;
  transition: stroke-dashoffset 0.9s cubic-bezier(0.4,0,0.2,1), stroke 0.4s ease;
}
.sm-prob-ring-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.62rem;
  font-weight: 800;
  color: var(--text);
}

/* ── 4. xG pills: progressive disclosure ──
   Slightly recessive until the hero card is hovered/tapped, so
   the score stays the visual anchor and the pills read as
   secondary detail rather than equal-weight noise. */
.xg-pill {
  opacity: 0.72;
  filter: saturate(0.85);
  transition: opacity 0.2s ease, filter 0.2s ease, background 0.15s, border-color 0.15s;
}
.most-likely-hero:hover .xg-pill,
.most-likely-hero:focus-within .xg-pill,
.xg-pill:hover {
  opacity: 1;
  filter: saturate(1);
}

/* ── 5. Staggered results reveal ──
   Applied via JS by toggling .sm-stagger-in on tiles/pills the
   moment the Results tab actually renders data. */
.sm-stagger-item {
  opacity: 0;
  transform: translateY(8px);
}
.sm-stagger-item.sm-stagger-in {
  animation: sm-stagger-fade 0.45s cubic-bezier(0.2,0.8,0.3,1) forwards;
  animation-delay: var(--sm-delay, 0ms);
}
@keyframes sm-stagger-fade {
  to { opacity: 1; transform: translateY(0); }
}

/* ── 6. Team input rows: ambient home/away tinting ──
   Very low-opacity wash behind each row so the form reads as
   structured even before anything is typed. */
.fm-row-home {
  position: relative;
  background: linear-gradient(90deg, rgba(29,177,255,0.055), transparent 55%);
}
.fm-row-away {
  position: relative;
  background: linear-gradient(90deg, rgba(255,107,53,0.055), transparent 55%);
}
body.light .fm-row-home { background: linear-gradient(90deg, rgba(0,116,232,0.045), transparent 55%); }
body.light .fm-row-away { background: linear-gradient(90deg, rgba(224,85,0,0.045), transparent 55%); }

/* ── 7. Micro-validation checkmarks on team inputs ──
   A small fade-in check to the right of a field once it looks
   plausibly filled in — cheap, but reads as "considered". */
.fm-team-cell, .fm-input-cell { position: relative; }
.sm-field-check {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%) scale(0.6);
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: rgba(0,212,139,0.15);
  color: var(--green, #00d48b);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 800;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.sm-field-check.sm-visible { opacity: 1; transform: translateY(-50%) scale(1); }
.fm-team-cell .sm-field-check { right: 10px; }

/* ── 7b. MP (matches played): tick if the sample looks sufficient,
   caution badge + tooltip if it's thin ──
   Same fade-in treatment as the checkmark above, but this one reflects
   sample SIZE rather than just "filled in": ≥8 venue-specific matches
   gets a plain tick, fewer gets an amber caution badge that nudges
   toward the existing "Last Season Data" blend toggle further down
   this tab. Purely a real-time hint — doesn't touch validation or
   the calculation itself. */
.sm-mp-indicator {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%) scale(0.6);
  width: 15px;
  height: 15px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 800;
  opacity: 0;
  pointer-events: none;
  cursor: default;
  background: rgba(0,212,139,0.15);
  color: var(--green, #00d48b);
  transition: opacity 0.2s ease, transform 0.2s ease, background 0.15s, border-color 0.15s;
}
.sm-mp-indicator.sm-visible { opacity: 1; transform: translateY(-50%) scale(1); }

.sm-mp-indicator.sm-mp-low {
  background: rgba(245,200,66,0.16);
  border: 1px solid rgba(245,200,66,0.35);
  color: var(--yellow, #f5c842);
  pointer-events: auto;
  cursor: pointer;
}
.sm-mp-indicator.sm-mp-low:hover,
.sm-mp-indicator.sm-mp-low:focus {
  background: rgba(245,200,66,0.26);
  border-color: rgba(245,200,66,0.55);
  outline: none;
}

.sm-mp-warn-popup {
  display: none;
  position: absolute;
  z-index: 9999;
  bottom: calc(100% + 7px);
  right: 0;
  background: var(--surface2, #1a2540);
  border: 1px solid rgba(245,200,66,0.35);
  border-radius: 8px;
  padding: 8px 11px;
  width: 190px;
  font-size: 11px;
  line-height: 1.6;
  color: var(--text-dim, #a0b0cc);
  box-shadow: 0 6px 24px rgba(0,0,0,0.35);
  pointer-events: none;
  white-space: normal;
  text-align: left;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
}
.sm-mp-warn-popup strong { color: var(--text, #e8f0ff); }
.sm-mp-indicator.sm-mp-low:hover .sm-mp-warn-popup,
.sm-mp-indicator.sm-mp-low:focus .sm-mp-warn-popup { display: block; }

@media (max-width: 480px) {
  .sm-mp-warn-popup { width: 160px; font-size: 10px; right: -10px; }
}

/* ── 8. Empty state: quiet "waiting for data" pulse ── */
.sm-empty-pulse {
  display: flex;
  justify-content: center;
  gap: 5px;
  margin-top: 10px;
}
.sm-empty-pulse span {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.35;
  animation: sm-empty-dot 1.4s ease-in-out infinite;
}
.sm-empty-pulse span:nth-child(2) { animation-delay: 0.18s; }
.sm-empty-pulse span:nth-child(3) { animation-delay: 0.36s; }
@keyframes sm-empty-dot {
  0%, 100% { opacity: 0.25; transform: scale(0.85); }
  50% { opacity: 0.9; transform: scale(1.15); }
}

/* ── 9. Theme toggle: rotate + cross-fade instead of an instant swap ── */
#theme-icon {
  display: inline-block;
  transition: transform 0.35s cubic-bezier(0.4,0,0.2,1), opacity 0.2s ease;
}
#theme-icon.sm-spin {
  animation: sm-theme-spin 0.4s ease;
}
@keyframes sm-theme-spin {
  0%   { transform: rotate(0deg) scale(1); opacity: 1; }
  45%  { transform: rotate(150deg) scale(0.7); opacity: 0.25; }
  55%  { transform: rotate(190deg) scale(0.7); opacity: 0.25; }
  100% { transform: rotate(360deg) scale(1); opacity: 1; }
}

/* ── 10. Confidence dots — shared visual language for signal
   strength, reused across outcome tiles (and reusable anywhere
   else .sm-conf-dots is dropped in). ── */
.sm-conf-dots {
  display: inline-flex;
  gap: 3px;
  margin-top: 5px;
  justify-content: center;
}
.sm-conf-dots span {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--border);
  transition: background 0.3s ease, transform 0.3s ease;
}
.sm-conf-dots.sm-dots-1 span:nth-child(1),
.sm-conf-dots.sm-dots-2 span:nth-child(1),
.sm-conf-dots.sm-dots-2 span:nth-child(2),
.sm-conf-dots.sm-dots-3 span:nth-child(1),
.sm-conf-dots.sm-dots-3 span:nth-child(2),
.sm-conf-dots.sm-dots-3 span:nth-child(3) {
  background: var(--sm-dot-color, var(--accent));
  transform: scale(1.15);
}

/* ── 11. Custom "Install ScoreMatrix" banner ──
   Deliberately mirrors #sw-update-banner's shape/placement/tone so
   it reads as part of the same system rather than a bolted-on
   widget. Sits a little higher so it can't collide with the update
   banner if both were ever visible at once. */
#sm-install-banner {
  position: fixed;
  bottom: 80px;
  left: 50%;
  transform: translateX(-50%) translateY(12px);
  background: var(--surface);
  border: 1px solid var(--accent);
  color: var(--text);
  font-family: 'Inter', sans-serif;
  font-size: 0.78rem;
  padding: 10px 12px 10px 14px;
  border-radius: 10px;
  z-index: 9997;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(92vw, 420px);
  box-shadow: 0 8px 24px rgba(0,0,0,0.35);
  opacity: 0;
  transition: opacity 0.25s ease, transform 0.25s ease;
}
#sm-install-banner.sm-visible { opacity: 1; transform: translateX(-50%) translateY(0); }
@media (max-width: 640px) { #sm-install-banner { bottom: 96px; } }
.sm-install-icon { font-size: 1.1rem; flex: none; }
.sm-install-text { flex: 1; line-height: 1.35; }
#sm-install-banner button {
  font-family: 'Inter', sans-serif;
  cursor: pointer;
  border-radius: 6px;
  flex: none;
}
#sm-install-yes {
  background: rgba(29,177,255,0.12);
  border: 1px solid rgba(29,177,255,0.35);
  color: var(--accent);
  font-size: 0.74rem;
  font-weight: 700;
  padding: 5px 12px;
}
#sm-install-yes:hover { background: rgba(29,177,255,0.2); }
#sm-install-no {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 0.8rem;
  padding: 4px 6px;
  line-height: 1;
}
#sm-install-no:hover { color: var(--text); }
body.light #sm-install-banner { background: var(--surface); border-color: rgba(0,116,232,0.4); }
body.light #sm-install-yes { background: rgba(0,116,232,0.08); border-color: rgba(0,116,232,0.25); color: var(--accent); }

/* ── 12. Tab switch: brief exit fade before the swap ──
   .section.active already fades/slides in via section-fade-in in
   app-styles.css; this adds a short matching exit so the outgoing
   tab dissolves instead of vanishing mid-frame the instant the
   incoming one starts. Toggled by initTabTransition() below —
   .sm-tab-leaving is only ever applied briefly, right before the
   section's 'active' class (and thus display:block) is removed. */
.section.sm-tab-leaving {
  animation: none !important;
  opacity: 0 !important;
  transform: translateY(-4px) !important;
  transition: opacity 0.11s ease, transform 0.11s ease;
}

/* ── 13. Count-up numbers ──
   Purely a transition treatment for the number-swap itself; the
   final text set by initCountUp() below always matches exactly
   what predictor.js wrote, so nothing here changes any figure. */
.sm-counting {
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .mlh-score.sm-score-animating,
  .sm-stagger-item.sm-stagger-in,
  #theme-icon.sm-spin,
  .sm-empty-pulse span {
    animation: none !important;
  }
  .sm-stagger-item { opacity: 1; transform: none; }
  .section.sm-tab-leaving { transition: none !important; opacity: 1 !important; transform: none !important; }
}

/* ── 14. Per-tab ribbon borders ──
   Each tab gets its own accent, reusing colors already established
   elsewhere in the app (team blue, market purple/teal, green, yellow)
   plus one new --pink for FAQ, so every card visibly belongs to the
   section it's in and separates cleanly from the page background.
   --sm-ribbon is a custom property, so it cascades from the section
   wrapper down to every .card inside it without needing per-tab
   duplicate selectors on .card itself. */
#tab-inputs      { --sm-ribbon: var(--accent); }
#tab-results     { --sm-ribbon: var(--market-under); }
#tab-betsummary  { --sm-ribbon: var(--green); }
#tab-evfinder    { --sm-ribbon: var(--yellow); }
#tab-help        { --sm-ribbon: var(--market-over); }
#tab-faqtab      { --sm-ribbon: var(--pink); }

.card {
  border: 1.5px solid var(--sm-ribbon, var(--accent));
  box-shadow:
    0 16px 38px -14px color-mix(in srgb, var(--sm-ribbon, var(--accent)) 50%, transparent),
    var(--card-shadow);
}
/* NOTE: body.light .card (app-styles.css) sets border-color at a
   higher specificity (2 classes vs 1) than the bare .card rule
   above, so it would silently win over the ribbon color in light
   mode regardless of file order. Matching its specificity here
   restores the ribbon. */
body.light .card {
  border-color: var(--sm-ribbon, var(--accent));
}
.card:hover {
  border-color: var(--sm-ribbon, var(--accent));
  box-shadow:
    0 22px 48px -12px color-mix(in srgb, var(--sm-ribbon, var(--accent)) 62%, transparent),
    0 16px 36px rgba(0,0,0,0.4),
    inset 0 1px 0 rgba(255,255,255,0.06);
}
body.light .card:hover {
  box-shadow:
    0 20px 40px -12px color-mix(in srgb, var(--sm-ribbon, var(--accent)) 45%, transparent),
    0 4px 6px rgba(20,30,60,0.07),
    inset 0 1px 0 rgba(255,255,255,0.7);
}
/* Fallback for browsers without color-mix() support: still get a
   colored border and the app's original elevation shadow, just
   without the tinted glow layered on top. */
@supports not (box-shadow: 0 0 0 color-mix(in srgb, red 50%, transparent)) {
  .card { box-shadow: var(--card-shadow); }
  .card:hover { box-shadow: 0 16px 36px rgba(0,0,0,0.4); }
}

/* ── 15. Recency Adjustments — unified ribbon group ──
   Recent Form + Last Season Data are two separate .toggle-card
   elements but conceptually one decision ("how do we weight
   recency?"), so they're wrapped in a single ribboned container
   instead of each getting its own competing border. Uses the same
   --sm-ribbon variable as .card, so it automatically matches
   whichever tab it's placed in. */
.sm-recency-group {
  border: 1.5px solid var(--sm-ribbon, var(--accent));
  border-radius: 16px;
  padding: 12px;
  margin-bottom: 16px;
  background: rgba(29,177,255,0.02);
  box-shadow:
    0 16px 38px -14px color-mix(in srgb, var(--sm-ribbon, var(--accent)) 50%, transparent),
    var(--card-shadow);
  transition: box-shadow 0.2s, border-color 0.2s;
}
body.light .sm-recency-group { background: rgba(0,116,232,0.015); }
.sm-recency-group:hover {
  box-shadow:
    0 20px 44px -12px color-mix(in srgb, var(--sm-ribbon, var(--accent)) 60%, transparent),
    0 16px 36px rgba(0,0,0,0.35);
}
/* The two toggle-cards inside keep only their left-accent striping —
   drop their own full border/shadow so the group ribbon reads as
   the single outer edge, not a border-within-a-border. */
.sm-recency-group .toggle-card {
  border-top-color: transparent;
  border-right-color: transparent;
  border-bottom-color: transparent;
  box-shadow: none;
  margin-bottom: 8px;
}
body.light .sm-recency-group .toggle-card {
  border-top-color: transparent;
  border-right-color: transparent;
  border-bottom-color: transparent;
}
.sm-recency-group .toggle-card:last-child { margin-bottom: 0; }
.sm-recency-group .toggle-card:hover {
  border-top-color: transparent;
  border-right-color: transparent;
  border-bottom-color: transparent;
  box-shadow: none;
}
body.light .sm-recency-group .toggle-card:hover {
  border-top-color: transparent;
  border-right-color: transparent;
  border-bottom-color: transparent;
}
@supports not (box-shadow: 0 0 0 color-mix(in srgb, red 50%, transparent)) {
  .sm-recency-group { box-shadow: var(--card-shadow); }
  .sm-recency-group:hover { box-shadow: 0 16px 36px rgba(0,0,0,0.35); }
}

/* ── 16. Hero cards — same ribbon language as the cards ──
   Two hero-style cards exist: .most-likely-hero (in #tab-results)
   and .ps-hero (the inline "quick preview" shown on the Inputs tab
   right after Calculate — easy to miss since it isn't named like
   the other one). Both just consume var(--sm-ribbon) from whichever
   tab they're actually sitting in, so .ps-hero picks up the Inputs
   tab's blue and .most-likely-hero picks up the Results tab's
   violet automatically. */
.most-likely-hero,
.ps-hero {
  border: 2px solid var(--sm-ribbon, var(--accent));
  box-shadow:
    0 16px 40px -14px color-mix(in srgb, var(--sm-ribbon, var(--accent)) 55%, transparent),
    0 0 0 1px color-mix(in srgb, var(--sm-ribbon, var(--accent)) 10%, transparent);
}
body.light .most-likely-hero,
body.light .ps-hero { border-color: var(--sm-ribbon, var(--accent)); }
.most-likely-hero:hover,
.ps-hero:hover {
  border-color: var(--sm-ribbon, var(--accent));
  box-shadow:
    0 22px 50px -12px color-mix(in srgb, var(--sm-ribbon, var(--accent)) 65%, transparent),
    0 4px 16px rgba(0,0,0,0.25);
}
body.light .most-likely-hero:hover,
body.light .ps-hero:hover { border-color: var(--sm-ribbon, var(--accent)); }
@supports not (box-shadow: 0 0 0 color-mix(in srgb, red 50%, transparent)) {
  .most-likely-hero, .ps-hero { box-shadow: 0 0 0 1px rgba(29,177,255,0.1); }
  .most-likely-hero:hover, .ps-hero:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.25); }
}

/* ── 17. Quick Stats card values — swap JetBrains Mono for Inter ──
   Applied via .sm-qstat-val (added alongside .pick-num in the JS
   template for the Bet Summary "quick stats" row: Exp. Goals, Clean
   Sheet Chance, Match Volatility, Goal Distribution). Inter reads
   cleaner and more premium here than a monospace/code font, while
   tabular-nums keeps digits from jittering in width as they update. */
.sm-qstat-val {
  font-family: 'Inter', sans-serif;
  font-variant-numeric: tabular-nums;
}

/* ── 18. Match List entry — kickoff row & outcome row parity on mobile ──
   Both rows are conceptually the same shape (a small field + a Save
   button), but the kickoff time field is a native <input type="time">
   while the outcome field is a plain text input — different browsers
   size native time controls very differently from text inputs, so on
   narrow screens the two rows ended up wildly mismatched in width
   (one cramped, one stretched), breaking the tidy column alignment
   the desktop table view has. Locking both to the same flex
   proportions and height makes them read as a matched pair again. */
@media (max-width: 640px) {
  .ml-kickoff-row,
  .ml-outcome-row {
    align-items: stretch;
    gap: 8px;
  }
  .ml-kickoff-input,
  .ml-outcome-input {
    flex: 1 1 auto;
    width: auto;
    max-width: 150px;
    min-width: 0;
    height: 34px;
    box-sizing: border-box;
  }
  .ml-outcome-save-btn {
    flex: 0 0 auto;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
  }

  /* The pinned-score card in the middle (.ml-entry-pred) already has
     its own tinted box — background, border, rounded corners — but
     the kickoff row above it and the outcome row below it were bare
     input+button pairs with no box at all. That boxed/unboxed/boxed
     mismatch is what still read as cluttered even with the row
     proportions fixed. Giving all three the same box treatment turns
     the card into one consistent stack instead of mixed styles. */
  .ml-kickoff-row {
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.07);
    border-radius: 8px;
    padding: 8px 10px;
    margin-top: 6px;
  }
  body.light .ml-kickoff-row { background: rgba(0,0,0,0.025); border-color: rgba(0,0,0,0.07); }

  .ml-entry-outcome {
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.07);
    border-radius: 8px;
    padding: 8px 10px;
    border-top: 1px solid rgba(255,255,255,0.07);
  }
  body.light .ml-entry-outcome { background: rgba(0,0,0,0.025); border-color: rgba(0,0,0,0.07); }
  .ml-outcome-row { width: 100%; }
}

/* ── 19. Sticky header/tabs/context-bar — fix scroll "ghosting" ──
   header, .tabs, and .match-ctx-bar are all position:sticky, each
   with a gradient background and/or box-shadow. Browsers (mobile
   Safari especially, some Android Chrome too) don't always promote
   sticky elements with shadows/gradients to their own compositor
   layer automatically, so as the page scrolls underneath them the
   bar gets redrawn against the moving content instead of composited
   cleanly on top — that's the flicker/"ghost" trail during scroll.
   Forcing each onto its own GPU layer (a standard, purely technical
   fix — no visual change) makes the browser composite it once and
   just move it, instead of repainting it every scroll frame. */
header,
.tabs,
.match-ctx-bar {
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

/* ── 20. Match context bar — subtle home/away gradient edge ──
   Not a full ribbon (border on all sides + floating glow) like the
   .card treatment — this bar is edge-to-edge and pinned to the
   viewport, so framing it like a floating card would fight its
   "anchored furniture" role. Instead: turn the existing flat blue
   bottom border into a home→away gradient line (it already shows
   Home in accent-blue and Away in accent2-orange, so the line now
   echoes that), plus a soft glow underneath — a light identity touch
   rather than a boxed frame. */
.match-ctx-bar {
  border-bottom: none;
  background:
    linear-gradient(90deg, var(--accent), var(--accent2)) bottom / 100% 2px no-repeat,
    linear-gradient(90deg, rgba(29,177,255,0.08), rgba(255,107,53,0.06));
  box-shadow: 0 4px 16px -8px rgba(29,177,255,0.3);
}
body.light .match-ctx-bar {
  background:
    linear-gradient(90deg, var(--accent), var(--accent2)) bottom / 100% 2px no-repeat,
    linear-gradient(90deg, rgba(0,116,232,0.06), rgba(224,85,0,0.04));
  box-shadow: 0 4px 16px -8px rgba(0,116,232,0.2);
}

/* ── 21. Match context bar — mobile only ──
   On desktop the calculated result is already visible on the page
   itself, so this sticky summary strip is redundant clutter. It
   earns its keep on mobile, where scrolling moves the result out of
   view. JS sets its display via inline style (element.style.display
   = "flex"), which beats any non-!important CSS, so !important is
   required here to actually override it above the mobile breakpoint. */
@media (min-width: 641px) {
  .match-ctx-bar { display: none !important; }
}
