/* Revontulet — design-system components (ported 1:1 from the Claude Design
 * export's React components; token-driven, same values, static-HTML friendly). */

/* ============== Button ============== */
.rv-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--rv-btn-gap, 8px);
  height: var(--rv-btn-h, 40px);
  padding: var(--rv-btn-pad, 0 18px);
  font-size: var(--rv-btn-fs, 14px);
  font-family: var(--font-sans);
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1;
  border-radius: var(--rv-btn-radius, var(--radius-md));
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.rv-btn:active:not(:disabled) { transform: translateY(1px); }
.rv-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.rv-btn--full { display: flex; width: 100%; }

.rv-btn--sm { --rv-btn-h: 32px; --rv-btn-pad: 0 12px; --rv-btn-fs: 13px; --rv-btn-gap: 6px; --rv-btn-radius: var(--radius-sm); }
.rv-btn--lg { --rv-btn-h: 48px; --rv-btn-pad: 0 24px; --rv-btn-fs: 15px; --rv-btn-gap: 10px; }

.rv-btn--primary { background: var(--aurora-green); color: var(--text-on-accent); }
.rv-btn--primary:hover:not(:disabled) { background: var(--accent-hover); box-shadow: var(--glow-green); }

.rv-btn--secondary { background: var(--surface-hover); color: var(--text-primary); border-color: var(--border-default); }
.rv-btn--secondary:hover:not(:disabled) { background: var(--night-500); }

.rv-btn--ghost { background: transparent; color: var(--text-secondary); }
.rv-btn--ghost:hover:not(:disabled) { background: var(--surface-hover); }

.rv-btn--outline { background: transparent; color: var(--aurora-green); border-color: var(--aurora-green); }
.rv-btn--outline:hover:not(:disabled) { background: var(--sport-bike-tint); }

.rv-btn--danger { background: var(--status-danger); color: var(--night-950); }
.rv-btn--danger:hover:not(:disabled) { background: #F47D95; }

/* ============== Card ============== */
.rv-card {
  position: relative;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  padding: var(--rv-card-pad, 20px);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  flex-shrink: 0;
  font-family: var(--font-sans);
  color: var(--text-primary);
  transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.rv-card--interactive { cursor: pointer; }
a.rv-card--interactive { display: block; text-decoration: none; color: inherit; }
.rv-card--interactive:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--border-strong); }
.rv-card--glow { box-shadow: var(--glow-green); }
.rv-card--accent::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: var(--rv-card-accent, var(--aurora-green));
}
.rv-card--accent-aurora::before { background: var(--grad-aurora); }

/* ============== Input ============== */
.rv-field-wrap { display: flex; flex-direction: column; gap: 6px; width: 100%; font-family: var(--font-sans); }
.rv-label { font-size: 12px; font-weight: 500; letter-spacing: 0.02em; color: var(--text-secondary); transition: color var(--dur-fast) var(--ease-out); }
.rv-field-wrap:focus-within .rv-label { color: var(--aurora-green); }
.rv-field-wrap--error .rv-label { color: var(--status-danger); }

.rv-field {
  display: flex; align-items: center; gap: 10px; height: 44px; padding: 0 14px;
  background: var(--night-850); border: 1px solid var(--border-default); border-radius: var(--radius-md);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.rv-field-wrap:focus-within .rv-field { border-color: var(--aurora-green); box-shadow: var(--glow-green); }
.rv-field-wrap--error .rv-field { border-color: var(--status-danger); box-shadow: none; }
.rv-field-wrap--disabled .rv-field { opacity: 0.5; }

.rv-field .icon { color: var(--text-muted); display: flex; flex: 0 0 auto; }
.rv-input { flex: 1; background: transparent; border: none; outline: none; color: var(--text-primary); font-size: 15px; font-family: var(--font-sans); min-width: 0; }
.rv-input::placeholder { color: var(--text-muted); }

/* select[color-scheme:dark] on <html> (base.css) isn't enough on its own in
   every browser to theme the native OPTION popup — explicit background/color
   on <option> is the reliable cross-browser fix (affects every <select
   class="rv-input"> in the app, not just one form). */
select.rv-input { color-scheme: dark; }
select.rv-input option { background: var(--surface-raised); color: var(--text-primary); }

.rv-hint { font-size: 12px; color: var(--text-muted); }
.rv-hint--error { color: var(--status-danger); }

.rv-field--area { height: auto; padding: 10px 14px; align-items: flex-start; }
textarea.rv-input { resize: vertical; min-height: 72px; line-height: 1.5; padding: 0; }

.rv-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 560px) { .rv-form-grid { grid-template-columns: 1fr; } }

/* ============== Details (accordion) ==============
   Native <details>/<summary>, pas de JS dedie (SPOR-503, demande explicite
   de l'utilisateur) : replie par defaut un bloc de contenu secondaire dans
   un formulaire long — premier usage : sous-categories par sport de la
   modale gear, une par sport coche, qui s'empilaient toutes deployees. */
.rv-details > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 6px; }
.rv-details > summary::-webkit-details-marker { display: none; }
.rv-details > summary::before {
  content: '▸'; display: inline-block; font-size: 10px; color: var(--text-muted);
  transition: transform var(--dur-fast) var(--ease-out);
}
.rv-details[open] > summary::before { transform: rotate(90deg); }
.rv-details > summary:hover::before { color: var(--text-secondary); }
.rv-details[open] > summary { margin-bottom: 8px; }

/* ============== Tabs ============== */
.rv-tabs { display: flex; flex-wrap: wrap; gap: 2px; row-gap: 6px; font-family: var(--font-sans); border-bottom: 1px solid var(--border-subtle); }
.rv-tabs--pill { gap: 6px; border-bottom: none; }

.rv-tab {
  appearance: none; background: transparent; border: none; cursor: pointer; font-family: inherit;
  font-size: 14px; font-weight: 500; padding: 9px 16px; color: var(--text-muted);
  display: inline-flex; align-items: center; gap: 7px; line-height: 1; margin-bottom: -1px;
  transition: color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.rv-tabs--pill .rv-tab { margin-bottom: 0; border-radius: var(--radius-pill); border: 1px solid transparent; }
.rv-tab.is-active { font-weight: 600; color: var(--text-primary); box-shadow: inset 0 -2px 0 var(--aurora-green); }
.rv-tabs--pill .rv-tab.is-active { box-shadow: none; background: var(--surface-hover); border-color: var(--border-strong); }
.rv-tab[aria-selected="false"]:hover { color: var(--text-secondary); }

.rv-tab__badge {
  font-family: var(--font-mono); font-size: 10.5px; padding: 2px 7px; border-radius: var(--radius-pill);
  background: var(--surface-hover); color: var(--text-secondary); font-weight: 700;
}
.rv-tab.is-active .rv-tab__badge { background: var(--aurora-green); color: var(--text-on-accent); }

/* ============== Sport badge ============== */
.rv-badge {
  display: inline-flex; align-items: center; gap: var(--rv-badge-gap, 6px);
  height: var(--rv-badge-h, 28px); padding: 0 var(--rv-badge-px, 10px);
  border-radius: var(--radius-pill); font-family: var(--font-sans);
  font-size: var(--rv-badge-fs, 12px); font-weight: 600; letter-spacing: 0.02em;
  line-height: 1; white-space: nowrap;
  background: var(--rv-badge-color); color: var(--text-on-accent); border: none;
}
.rv-badge .icon { flex: 0 0 auto; }
.rv-badge--tint { background: var(--rv-badge-tint); color: var(--rv-badge-color); border: 1px solid var(--rv-badge-color); }

.rv-badge--sm { --rv-badge-h: 22px; --rv-badge-fs: 11px; --rv-badge-px: 8px; --rv-badge-gap: 5px; }
.rv-badge--lg { --rv-badge-h: 36px; --rv-badge-fs: 14px; --rv-badge-px: 14px; --rv-badge-gap: 8px; }

.rv-badge--run  { --rv-badge-color: var(--sport-run);  --rv-badge-tint: var(--sport-run-tint); }
.rv-badge--bike { --rv-badge-color: var(--sport-bike); --rv-badge-tint: var(--sport-bike-tint); }
.rv-badge--swim { --rv-badge-color: var(--sport-swim); --rv-badge-tint: var(--sport-swim-tint); }
.rv-badge--golf   { --rv-badge-color: var(--sport-golf);   --rv-badge-tint: var(--sport-golf-tint); }
.rv-badge--ppg    { --rv-badge-color: var(--sport-ppg);    --rv-badge-tint: var(--sport-ppg-tint); }
.rv-badge--yoga   { --rv-badge-color: var(--sport-yoga);   --rv-badge-tint: var(--sport-yoga-tint); }
.rv-badge--cardio { --rv-badge-color: var(--sport-cardio); --rv-badge-tint: var(--sport-cardio-tint); }
.rv-badge--triathlon { --rv-badge-color: var(--sport-triathlon); --rv-badge-tint: var(--sport-triathlon-tint); }

/* Timeline globale (epic Jira SPOR-527, demande explicite de l'utilisateur :
   "des badge comme cela" — reference visuelle fournie, pastille pleine +
   icone + libelle court) : memes variantes de couleur que les marqueurs
   .rv-timeline-row--* (components.css, section Timeline globale), portees
   ici sur le badge plutot que dupliquees en style inline. "race" reprend
   EXACTEMENT --fox-500 (deja utilise pour le badge "Race" de sessions.php,
   meme code couleur que la carte "Next race" du dashboard). */
.rv-badge--session  { --rv-badge-color: var(--text-muted); --rv-badge-tint: rgba(148,163,184,0.14); }
.rv-badge--race     { --rv-badge-color: var(--fox-500); --rv-badge-tint: rgba(249,115,22,0.14); }
.rv-badge--gear      { --rv-badge-color: var(--aurora-blue); --rv-badge-tint: rgba(97,114,243,0.14); }
.rv-badge--injury    { --rv-badge-color: var(--status-danger); --rv-badge-tint: rgba(240,97,127,0.14); }
.rv-badge--recovery  { --rv-badge-color: var(--aurora-teal); --rv-badge-tint: rgba(45,212,191,0.14); }
.rv-badge--health    { --rv-badge-color: var(--aurora-violet); --rv-badge-tint: rgba(166,107,240,0.14); }
.rv-badge--document  { --rv-badge-color: var(--aurora-cyan); --rv-badge-tint: rgba(56,189,248,0.14); }
/* Course virtuelle (SPOR-546) : badge sur la card /races, a cote du badge
   sport — meme teinte que --gear (tech/digital), classe dediee pour rester
   self-documenting plutot que de reutiliser rv-badge--gear hors contexte. */
.rv-badge--virtual   { --rv-badge-color: var(--aurora-blue); --rv-badge-tint: rgba(97,114,243,0.14); }

/* Materiel multi-sport (bug remonte par l'utilisateur, epic Materiel
   SPOR-175/SPOR-479) : pas de teinte de sport unique a lui donner (il en
   couvre plusieurs) — bordure en tirets + fond neutre, design volontairement
   DIFFERENT des badges pleins/teintes ci-dessus pour signaler au premier
   coup d'oeil "plusieurs sports" sans lister chacun d'eux (deborderait vite
   une carte compacte, voir templates/gear.php). */
.rv-badge--multisport {
  background: var(--surface-hover); color: var(--text-secondary);
  border: 1px dashed var(--border-strong);
}
.rv-badge--multisport .icon { color: var(--accent); }

/* ============== Tag badge tooltip (SPOR-322, epic Tags de seance SPOR-319) ==============
   Premier composant tooltip du projet : hover desktop + tap mobile (fermeture
   au tap/clic en dehors), gere par initTagTooltip() (interactions.js) — pas
   de librairie tierce (CLAUDE.md).
   Panneau UNIQUE ajoute a <body> et positionne en `fixed` via JS
   (getBoundingClientRect() du badge survole/tape), PAS imbrique dans la
   carte comme un premier essai le faisait : `.rv-card`/`.rv-session-card`
   ont `overflow: hidden` (arrondi des coins), qui rognait silencieusement
   tout panneau positionne en `absolute` a l'interieur (bug remonte par
   l'utilisateur : le badge s'affichait, le survol ne montrait jamais rien).
   `position: fixed` sur un panneau qui vit hors de la carte n'est plus
   soumis a cet overflow, quel que soit l'etat (:hover transform des cartes
   inclus). */
.rv-tag-tooltip__badge { cursor: pointer; }
.rv-tag-tooltip-panel {
  position: fixed; z-index: 45; transform: translate(-50%, -100%);
  display: none; flex-wrap: wrap; gap: 6px; width: max-content; max-width: 220px; padding: 10px;
  background: var(--surface-raised); border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md); pointer-events: none;
}
.rv-tag-tooltip-panel.is-open { display: flex; }
.rv-tag-tooltip-panel::after {
  content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 6px solid transparent; border-top-color: var(--border-strong);
}
.rv-tag-tooltip__chip {
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em; padding: 3px 8px;
  border-radius: var(--radius-pill); background: rgba(167,139,250,0.14); color: var(--aurora-violet);
  white-space: nowrap;
}

/* ============== Gear badge tooltip (SPOR-525) ==============
   Meme mecanique que le tooltip Tags ci-dessus (panneau unique ajoute a
   <body>, position: fixed via getBoundingClientRect(), initGearTooltip()
   dans interactions.js) mais panneau/couleur DEDIES (famille de tooltip
   independante) : ton neutre gris, coherent avec la couleur du badge
   Equipement lui-meme (--text-muted), pas le violet des Tags. Chaque chip
   = un materiel, complete de sa charge de batterie loggee pour cette
   seance si elle existe ("Nom · 62%"). */
.rv-gear-tooltip__badge { cursor: pointer; }
.rv-gear-tooltip-panel {
  position: fixed; z-index: 45; transform: translate(-50%, -100%);
  display: none; flex-direction: column; gap: 6px; width: max-content; max-width: 220px; padding: 10px;
  background: var(--surface-raised); border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md); pointer-events: none;
}
.rv-gear-tooltip-panel.is-open { display: flex; }
.rv-gear-tooltip-panel::after {
  content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 6px solid transparent; border-top-color: var(--border-strong);
}
.rv-gear-tooltip__chip {
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em; padding: 3px 8px;
  border-radius: var(--radius-pill); background: rgba(148,163,184,0.14); color: var(--text-secondary);
  white-space: nowrap;
}

/* ============== Partner badge tooltip (SPOR-561, epic Partenaires de seance SPOR-559) ==============
   Meme mecanique que le tooltip Tags/Gear ci-dessus (panneau unique ajoute a
   <body>, position: fixed via getBoundingClientRect(), initPartnerTooltip()
   dans interactions.js) mais panneau/couleur DEDIES (famille de tooltip
   independante) : accent magenta, coherent avec le badge Partenaires
   lui-meme. Chaque chip = un partenaire. */
.rv-partner-tooltip__badge { cursor: pointer; }
.rv-partner-tooltip-panel {
  position: fixed; z-index: 45; transform: translate(-50%, -100%);
  display: none; flex-wrap: wrap; gap: 6px; width: max-content; max-width: 220px; padding: 10px;
  background: var(--surface-raised); border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md); pointer-events: none;
}
.rv-partner-tooltip-panel.is-open { display: flex; }
.rv-partner-tooltip-panel::after {
  content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 6px solid transparent; border-top-color: var(--border-strong);
}
.rv-partner-tooltip__chip {
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em; padding: 3px 8px;
  border-radius: var(--radius-pill); background: rgba(236,91,156,0.14); color: var(--aurora-magenta);
  white-space: nowrap;
}

/* ============== Tag cloud (SPOR-526) ==============
   Nuage de tags des seances utilisant un materiel donne (fiche detail
   /gear/{id}) : mots simples (PAS des pilules/chips comme .rv-chip ou les
   tooltips ci-dessus) de taille variable (voir Domain\Training\TagCloud),
   convention "tag cloud" classique (WordPress/Flickr) — accent violet,
   meme famille de couleur que les Tags de seance ailleurs dans l'appli
   (badge+tooltip Tags sur /sessions, SPOR-322). */
.rv-tag-cloud { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 16px; }
.rv-tag-cloud__item { font-weight: 600; line-height: 1.2; color: var(--aurora-violet); white-space: nowrap; cursor: default; }

/* ============== Metric stat ============== */
.rv-metric { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; font-family: var(--font-mono); }
.rv-metric--center { align-items: center; }
.rv-metric__top { display: flex; align-items: baseline; gap: 5px; }
.rv-metric__value {
  font-size: var(--rv-metric-fs, 32px); font-weight: 500; letter-spacing: -0.02em;
  color: var(--text-primary); line-height: 1; font-variant-numeric: tabular-nums;
}
.rv-metric__unit { font-size: var(--rv-metric-unit-fs, 14px); font-weight: 500; color: var(--rv-metric-accent, var(--aurora-green)); }
.rv-metric__bottom { display: flex; align-items: center; gap: 8px; }
.rv-metric__label {
  font-family: var(--font-sans); font-size: var(--rv-metric-label-fs, 11px); text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--text-muted);
}
.rv-metric__delta { display: inline-flex; align-items: center; gap: 3px; font-size: var(--rv-metric-label-fs, 11px); font-weight: 500; color: var(--text-muted); }
.rv-metric__delta--up { color: var(--status-success); }
.rv-metric__delta--down { color: var(--status-danger); }

/* Variante avg + min/max (SPOR-332) : la tuile montre la MOYENNE en grand
   (bloc __main inchange) et le min/max du capteur empiles juste a cote,
   groupes a gauche de la tuile (pas espaces sur toute la largeur de la
   grille) — evite d'empiler deux tuiles quasi identiques (Avg HR / Max HR,
   Avg Power / Max Power). Modificateur plutot que de changer .rv-metric
   lui-meme : la classe de base reste column partout ailleurs (dashboard,
   health, profile, trends...). */
.rv-metric--minmax { flex-direction: row; align-items: center; justify-content: flex-start; gap: 10px; }
.rv-metric__main { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.rv-metric__minmax { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; font-family: var(--font-mono); font-size: 12px; font-weight: 700; line-height: 1; }
.rv-metric__minmax-max { color: var(--aurora-magenta); }
.rv-metric__minmax-min { color: var(--aurora-green); }

.rv-metric--sm { --rv-metric-fs: 22px; --rv-metric-unit-fs: 12px; --rv-metric-label-fs: 10px; }
.rv-metric--lg { --rv-metric-fs: 48px; --rv-metric-unit-fs: 18px; --rv-metric-label-fs: 12px; }

/* ============== Zone bar ============== */
.rv-zonebar { font-family: var(--font-mono); width: 100%; }
.rv-zonebar__track {
  display: flex; height: var(--rv-zonebar-h, 44px); border-radius: var(--radius-md);
  overflow: hidden; border: 1px solid var(--border-subtle);
}
.rv-zonebar__seg {
  display: flex; align-items: center; justify-content: center;
  color: rgba(5,7,11,0.82); font-size: 11px; font-weight: 700;
}
.rv-zonebar__seg--1 { background: var(--zone-1); }
.rv-zonebar__seg--2 { background: var(--zone-2); }
.rv-zonebar__seg--3 { background: var(--zone-3); }
.rv-zonebar__seg--4 { background: var(--zone-4); }
.rv-zonebar__seg--5 { background: var(--zone-5); }
.rv-zonebar__seg--6 { background: var(--zone-6); }
.rv-zonebar__seg--7 { background: var(--zone-7); }
.rv-zonebar__seg--8 { background: var(--zone-8); }
.rv-zonebar__legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; }
.rv-zonebar__legend-item { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text-secondary); }
.rv-zonebar__dot { width: 10px; height: 10px; border-radius: 3px; display: block; }
.rv-zonebar__legend-item .muted { color: var(--text-muted); }

/* ============== Session card ============== */
.rv-session-card {
  display: flex; align-items: stretch; background: var(--surface-card); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card); overflow: hidden; flex-shrink: 0; font-family: var(--font-sans);
  transition: transform var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
  width: 100%; text-align: left; cursor: default;
}
a.rv-session-card, button.rv-session-card { cursor: pointer; }
a.rv-session-card:hover, button.rv-session-card:hover { transform: translateY(-2px); border-color: var(--border-strong); box-shadow: var(--shadow-md); }
.rv-session-card__rail { width: 4px; flex: 0 0 4px; }
.rv-session-card__rail--run { background: var(--sport-run); }
.rv-session-card__rail--bike { background: var(--sport-bike); }
.rv-session-card__rail--swim { background: var(--sport-swim); }
/* Mini-trace GPS simplifiee (SPOR-572) : SVG inline, pas de tuiles/carte reelle
   (contrairement a .rv-route-thumb sur /routes) — cout nul sur une liste paginee. */
.rv-session-card__trace { flex: 0 0 64px; width: 64px; height: 64px; display: flex; align-items: center; justify-content: center; }
.rv-session-card__trace--empty { color: var(--text-muted); opacity: 0.35; }
.rv-session-card__body { padding: 14px 18px; display: flex; flex-direction: column; gap: 12px; flex: 1; min-width: 0; }
.rv-session-card__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.rv-session-card__titlewrap { min-width: 0; display: flex; flex-direction: column; }
.rv-session-card__badges { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: flex-start; gap: 6px 8px; flex-shrink: 0; }
.rv-session-card__sport-group { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.rv-session-card__title { display: block; font-size: 15px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rv-session-card__date { display: block; font-size: 12px; color: var(--text-muted); margin-top: 2px; font-family: var(--font-mono); }
.rv-session-card__metrics { display: flex; gap: 22px; flex-wrap: wrap; }
.rv-session-card__metric { display: flex; flex-direction: column; gap: 1px; }
.rv-session-card__metric-value { display: block; font-family: var(--font-mono); font-size: 16px; font-weight: 500; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.rv-session-card__metric-unit { font-size: 11px; color: var(--text-muted); margin-left: 2px; }
.rv-session-card__metric-label { display: block; font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); }

/* ============== Data table ============== */
.rv-table { width: 100%; border-collapse: collapse; font-size: 13px; color: var(--text-secondary); }
.rv-table--dense { font-size: 12.5px; }
.rv-table th {
  padding: 10px 12px; text-align: left; font-family: var(--font-sans); font-size: 10.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-muted); border-bottom: 1px solid var(--border-default);
}
.rv-table th.numeric, .rv-table td.numeric { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.rv-table td { padding: 10px 12px; border-bottom: 1px solid var(--border-subtle); white-space: nowrap; }
.rv-table--dense th, .rv-table--dense td { padding: 7px 12px; }
.rv-table tr:last-child td { border-bottom: none; }
.rv-table tr.is-highlight { background: rgba(61,220,132,0.07); box-shadow: inset 2px 0 0 var(--aurora-green); }
.rv-table tr.is-highlight td { font-weight: 600; }
.rv-table tr.is-highlight td.numeric { color: var(--text-primary); }
.rv-table tfoot td { border-top: 1px solid var(--border-default); border-bottom: none; color: var(--text-primary); font-weight: 600; }

/* ============== Trend chart ============== */
.rv-trendchart { --rv-trendchart-axis-w: 52px; display: flex; flex-direction: column; }
.rv-trendchart__row { position: relative; flex: 1; min-height: 0; display: flex; }
.rv-trendchart__plot { position: relative; flex: 1; min-width: 0; }
.rv-trendchart__plot svg, .rv-trendchart__plot canvas { width: 100%; height: 100%; display: block; }
/* Graduation uniforme (0/25/50/75/100 % de l'amplitude tracee, voir
   SessionPresenter::GRID_DIVISIONS) — meme opacite discrete que
   .rv-route-map__grid, sous la courbe/l'aire (ordre DOM). */
.rv-trendchart__grid { stroke: rgba(255,255,255,0.07); stroke-width: 1; }
.rv-trendchart__labels { display: flex; justify-content: space-between; margin-top: 6px; }
.rv-trendchart__labels--with-axis { padding-right: var(--rv-trendchart-axis-w); }
.rv-trendchart__labels span { font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); }
.rv-trendchart--bars .rv-trendchart__bars { display: flex; align-items: flex-end; gap: 6px; }
.rv-trendchart--bars .rv-trendchart__bar { flex: 1; min-height: 4px; background: var(--grad-aurora); border-radius: 4px; }
/* Colonne DEDIEE a droite du graphe pour la valeur de chaque ligne de
   graduation (le canvas Chart.js n'a pas d'axe natif) — PAS superposee sur la courbe :
   un retour utilisateur a signale que les chips precedents (position
   absolute par-dessus le SVG) masquaient la fin du trace. Largeur fixe
   (--rv-trendchart-axis-w, reprise par .rv-trendchart__labels--with-axis
   pour garder l'axe temporel aligne sur la LARGEUR DU GRAPHE, pas sur le
   composant entier). */
.rv-trendchart__axis { position: relative; width: var(--rv-trendchart-axis-w); flex: none; }
.rv-trendchart__tick {
  position: absolute; left: 4px; right: 0; transform: translateY(-50%); font-family: var(--font-mono); font-size: 9px; line-height: 1;
  color: var(--text-muted); white-space: nowrap;
}

/* ============== Calendar week ============== */
.rv-calendar-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; font-family: var(--font-sans); }
@media (max-width: 640px) { .rv-calendar-week { gap: 4px; } }
.rv-calendar-week__day {
  display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 10px 6px 8px; min-height: 96px;
  background: var(--surface-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
  cursor: default;
}
button.rv-calendar-week__day { cursor: pointer; }
.rv-calendar-week__day.is-today { border-color: var(--aurora-green); }
.rv-calendar-week__day.is-selected { background: var(--surface-hover); border-color: var(--border-strong); }
.rv-calendar-week__label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); }
.rv-calendar-week__date { font-family: var(--font-mono); font-size: 15px; font-weight: 600; color: var(--text-primary); }
.rv-calendar-week__day.is-today .rv-calendar-week__date { color: var(--aurora-green); }
.rv-calendar-week__dots { display: flex; gap: 4px; min-height: 8px; }
.rv-calendar-week__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--night-500); }
.rv-calendar-week__dot--run { background: var(--sport-run); }
.rv-calendar-week__dot--bike { background: var(--sport-bike); }
.rv-calendar-week__dot--swim { background: var(--sport-swim); }
.rv-calendar-week__load { margin-top: auto; width: 70%; height: 3px; border-radius: 2px; background: var(--night-600); overflow: hidden; display: block; }
.rv-calendar-week__load-fill { display: block; height: 100%; background: var(--grad-aurora); }

/* ============== Alert ============== */
.rv-alert {
  display: flex; gap: 12px; align-items: flex-start; padding: 12px 14px;
  background: var(--night-800); border-radius: var(--radius-md); border-left: 3px solid var(--rv-alert-color, var(--status-info));
  font-family: var(--font-sans);
}
.rv-alert--info { --rv-alert-color: var(--status-info); }
.rv-alert--success { --rv-alert-color: var(--status-success); }
.rv-alert--warn { --rv-alert-color: var(--status-warn); }
.rv-alert--error { --rv-alert-color: var(--status-danger); }
.rv-alert .icon { color: var(--rv-alert-color, var(--status-info)); flex: 0 0 auto; margin-top: 1px; display: flex; }
.rv-alert__body { flex: 1; min-width: 0; }
.rv-alert__title { font-size: 13px; font-weight: 600; color: var(--text-primary); margin-bottom: 3px; }
.rv-alert__text { font-size: 13px; line-height: 1.5; color: var(--text-secondary); }
.rv-alert__link { display: inline-block; margin-top: 6px; font-size: 13px; font-weight: 600; color: var(--rv-alert-color, var(--status-info)); text-decoration: none; }
.rv-alert__link:hover { text-decoration: underline; }
.rv-alert__close { background: none; border: none; color: var(--text-muted); cursor: pointer; padding: 0; display: flex; flex: 0 0 auto; }

/* ============== OTP input (2FA, epic SPOR-584) ============== */
.rv-otp { display: flex; gap: 8px; }
.rv-otp__box {
  width: 44px; height: 52px; text-align: center; font-family: var(--font-mono); font-size: 20px; font-weight: 600;
  background: var(--night-850); border: 1px solid var(--border-default); border-radius: var(--radius-md); color: var(--text-primary);
  outline: none;
}
.rv-otp__box:focus { border-color: var(--aurora-green); box-shadow: var(--glow-green); }
.rv-otp__box--error { border-color: var(--status-danger); box-shadow: none; }

/* ============== Backup codes grid (2FA, epic SPOR-584) ============== */
.rv-code-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 16px; }
.rv-code-grid__item {
  font-family: var(--font-mono); font-size: 15px; letter-spacing: 0.02em; color: var(--text-primary);
  background: var(--night-850); border: 1px solid var(--border-default); border-radius: var(--radius-sm); padding: 8px 12px;
}

/* ============== Empty state ============== */
.rv-empty {
  border: 1px dashed var(--border-default); border-radius: var(--radius-lg); padding: 56px 24px; text-align: center;
  font-family: var(--font-sans); color: var(--text-muted); display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.rv-empty--compact { padding: 28px 20px; }
.rv-empty .icon { color: var(--night-400); display: flex; }
.rv-empty__title { font-family: var(--font-display); font-size: 19px; font-weight: 600; color: var(--text-secondary); }
.rv-empty--compact .rv-empty__title { font-size: 16px; }
.rv-empty__text { margin: 0; font-size: 13px; line-height: 1.6; max-width: 340px; }
.rv-empty__action { margin-top: 8px; }

/* ============== Logo ============== */
.rv-logo { display: inline-flex; align-items: center; gap: var(--rv-logo-gap, 14px); font-family: var(--font-sans); }
.rv-logo__mark { height: var(--rv-logo-mark, 40px); display: block; }
.rv-logo__col { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.rv-logo--center .rv-logo__col { align-items: center; }
.rv-logo__word {
  font-family: var(--font-display); font-weight: 700; font-size: var(--rv-logo-word, 30px);
  letter-spacing: 0.16em; text-transform: uppercase; line-height: 1;
  background: var(--grad-aurora); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.rv-logo--mono .rv-logo__word { background: none; -webkit-background-clip: initial; background-clip: initial; color: var(--text-primary); }
.rv-logo__tagline { font-family: var(--font-mono); font-size: var(--rv-logo-tag, 11px); letter-spacing: 0.05em; color: var(--text-muted); }

.rv-logo--sm { --rv-logo-gap: 10px; --rv-logo-mark: 26px; --rv-logo-word: 20px; --rv-logo-tag: 9px; }
.rv-logo--lg { --rv-logo-gap: 18px; --rv-logo-mark: 60px; --rv-logo-word: 46px; --rv-logo-tag: 13px; }

/* ============== Aurora field ============== */
.rv-aurora { position: relative; width: 100%; overflow: hidden; background: var(--grad-night); }
.rv-aurora__stars {
  position: absolute; inset: 0; opacity: 0.5;
  background-image:
    radial-gradient(1px 1px at 20% 30%, #fff, transparent),
    radial-gradient(1px 1px at 70% 20%, #cfe, transparent),
    radial-gradient(1px 1px at 40% 60%, #fff, transparent),
    radial-gradient(1.5px 1.5px at 85% 45%, #fff, transparent),
    radial-gradient(1px 1px at 55% 15%, #dfe, transparent),
    radial-gradient(1px 1px at 10% 70%, #fff, transparent),
    radial-gradient(1px 1px at 90% 75%, #fff, transparent);
}
.rv-aurora__ribbon { position: absolute; left: -20%; right: -20%; height: 55%; filter: blur(46px); will-change: transform; }
.rv-aurora__ribbon--1 { top: -6%; opacity: 0.55; background: linear-gradient(90deg, transparent, #3DDC84 30%, #2DD4BF 55%, transparent); animation: rvDrift 16s var(--ease-in-out) infinite; }
.rv-aurora__ribbon--2 { top: 8%; opacity: 0.42; background: linear-gradient(90deg, transparent, #38BDF8 35%, #A66BF0 65%, transparent); animation: rvDrift2 22s var(--ease-in-out) infinite; }
.rv-aurora__ribbon--3 { top: 24%; opacity: 0.3; background: linear-gradient(90deg, transparent, #A66BF0 40%, #EC5B9C 70%, transparent); animation: rvDrift 28s var(--ease-in-out) infinite; }
.rv-aurora__content { position: relative; height: 100%; }

@keyframes rvDrift {
  0%   { transform: translate3d(-6%,0,0) rotate(-4deg) scale(1.2); }
  50%  { transform: translate3d(6%,-3%,0) rotate(3deg) scale(1.35); }
  100% { transform: translate3d(-6%,0,0) rotate(-4deg) scale(1.2); }
}
@keyframes rvDrift2 {
  0%   { transform: translate3d(4%,2%,0) rotate(2deg) scale(1.25); }
  50%  { transform: translate3d(-5%,-2%,0) rotate(-3deg) scale(1.4); }
  100% { transform: translate3d(4%,2%,0) rotate(2deg) scale(1.25); }
}

/* ============== Toggle switch (Settings) ============== */
.rv-toggle {
  width: 42px; height: 24px; border-radius: 12px; border: 1px solid var(--border-default);
  cursor: pointer; padding: 2px; background: var(--night-600); display: flex; justify-content: flex-start;
  transition: background var(--dur-fast) var(--ease-out);
}
.rv-toggle.is-on { background: var(--aurora-green); justify-content: flex-end; }
.rv-toggle__knob { width: 18px; height: 18px; border-radius: 50%; background: var(--frost-200); display: block; }
.rv-toggle.is-on .rv-toggle__knob { background: var(--night-950); }

/* ============== Filter chip (sessions list) ============== */
.rv-chip {
  padding: 7px 16px; border-radius: var(--radius-pill); cursor: pointer; font-family: var(--font-sans);
  font-size: 13px; font-weight: 500; border: 1px solid var(--border-default);
  background: transparent; color: var(--text-secondary); transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.rv-chip.is-active { border-color: transparent; background: var(--aurora-green); color: var(--night-950); }

/* Chip "Race" (SPOR-289) : couleur/icone dediees (--fox-500), meme accent que le
   badge "Race" des cartes de seance — volontairement distinct des chips sport
   neutres pour rester reconnaissable et separe visuellement des autres filtres. */
.rv-chip--race { display: inline-flex; align-items: center; gap: 6px; border-color: var(--fox-500); color: var(--fox-500); background: rgba(249, 115, 22, 0.14); }
.rv-chip--race.is-active { border-color: transparent; background: var(--fox-500); color: var(--text-on-accent); }

/* ============== Product spec grid (gear detail page) ============== */
.rv-spec-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; }
.rv-spec-item {
  background: var(--surface-hover); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  padding: 10px 12px;
}
.rv-spec-item__label { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.rv-spec-item__value { display: block; margin-top: 4px; font-size: 14px; font-weight: 600; color: var(--text-primary); }

/* ============== Timeline (gear detail page: linked sessions history) ============== */
.rv-timeline { position: relative; margin: 4px 0; padding-left: 28px; }
.rv-timeline::before {
  content: ''; position: absolute; left: 7px; top: 6px; bottom: 6px; width: 3px;
  border-radius: 2px; background: var(--grad-aurora-full);
}
.rv-timeline__item { position: relative; padding: 0 0 20px; }
.rv-timeline__item:last-child { padding-bottom: 0; }
.rv-timeline__dot {
  position: absolute; left: -28px; top: 3px; width: 14px; height: 14px; border-radius: 50%;
  background: var(--night-700); border: 3px solid var(--frost-300);
}
.rv-timeline__item--achievement .rv-timeline__dot { border-color: var(--aurora-green); background: var(--aurora-green); }
.rv-timeline__date { font-family: var(--font-mono); font-size: 12px; color: var(--text-muted); }
.rv-timeline__title { margin: 3px 0 2px; font-size: 14px; font-weight: 600; color: var(--text-primary); }
.rv-timeline__title .icon { vertical-align: -2px; margin-right: 4px; }

/* ============== Timeline globale (epic Jira SPOR-527) ==============
   Ligne centrale + cartes alternees gauche/droite (demande explicite de
   l'utilisateur, reference visuelle fournie) : DELIBEREMENT une structure
   separee de .rv-timeline ci-dessus (gear_detail/roadmap, liste alignee a
   gauche) plutot qu'une reutilisation — les deux pages existantes gardent
   leur mise en page inchangee. */
/* Le panneau de frise remplit la hauteur restante de .rv-screen (flex column)
   plutot qu'un plafond fixe (560px) qui laissait une grande bande vide sous la
   frise sur les ecrans hauts et une scrollbar interne minuscule. flex-basis 0
   + min-height 0 : la hauteur vient entierement de l'espace libre, et le
   contenu long defile DANS ce panneau (overflow-y) sans pousser .rv-screen.
   Selecteur enfant-direct pour l'emporter sur `.rv-screen > * { flex-shrink }`. */
.rv-screen > .rv-timeline-scroll { flex: 1 1 0; min-height: 0; }
.rv-timeline-scroll { overflow-y: auto; padding-right: 6px; }

.rv-timeline-central { position: relative; padding: 2px 0; font-size: 13px; }
.rv-timeline-central::before {
  content: ''; position: absolute; left: 50%; top: 0; bottom: 0; width: 2px;
  transform: translateX(-50%); border-radius: 2px; background: var(--grad-aurora-full);
}

/* En-tete de groupe (Today/Yesterday/date, semaine, mois, annee) : demande
   explicite de l'utilisateur — un bandeau PLEINE LARGEUR (au lieu d'un
   simple libelle qui casse la ligne au centre), jamais alterne (ce n'est
   pas un evenement, juste un repere temporel). z-index:1 pour peindre
   par-dessus .rv-timeline-central::before (positionne, donc peint APRES le
   contenu statique sans ce coup de pouce — la ligne semblerait sinon
   traverser le bandeau). */
.rv-timeline-central__heading {
  position: relative; z-index: 1; text-align: center; margin: 14px 0 10px; padding: 5px 12px;
  background: rgba(61,220,132,0.12); border: 1px solid rgba(61,220,132,0.4); border-radius: var(--radius-sm);
  font-size: 11.5px; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: .04em;
  cursor: pointer;
}
.rv-timeline-central__heading:first-child { margin-top: 0; }

.rv-timeline-row {
  position: relative; display: grid; grid-template-columns: 1fr 34px 1fr; align-items: start;
  margin-bottom: 4px;
}
.rv-timeline-row:last-child { margin-bottom: 0; }
.rv-timeline-row__marker {
  grid-column: 2; justify-self: center; z-index: 1; width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  /* --rv-badge-color, quand fourni en style inline (seances classiques, voir
     timeline.php/Html::sportBadgeStyle), colore le marqueur par sport ; sinon
     repli sur le gris neutre d'origine. Les types a couleur dediee (race,
     gear, injury...) ci-dessous restent plus specifiques et l'emportent. */
  background: var(--rv-badge-color, var(--night-800));
  border: 2px solid var(--rv-badge-color, var(--frost-300)); color: var(--text-primary);
}
/* Compact par defaut (demande explicite de l'utilisateur) : la carte ne
   s'affiche, avec une animation, que lorsqu'on CLIQUE l'icone du marqueur
   (toggle JS, classe .is-open sur .rv-timeline-row, voir le script en bas de
   templates/timeline.php) — la frise ne montre alors que la ligne centrale +
   les pastilles tant qu'aucune n'est ouverte. Volontairement pas un
   survol (:hover/:has()) : premiere version testee au clavier/souris, la
   carte qui se deploie grandit la ligne, ce qui peut deplacer l'icone sous
   le curseur (recalcul de layout, notamment si la frise defile dans
   .rv-timeline-scroll et que la nouvelle hauteur fait apparaitre/disparaitre
   sa scrollbar) — mouseleave declenche alors la fermeture, qui replace
   l'icone sous le curseur, qui redeclenche mouseenter : boucle infinie
   d'ouverture/fermeture. Un clic explicite n'a pas ce probleme (pas de
   dependance a la position du curseur apres coup). max-height (plutot que
   height:auto, non transitionnable) borne a une valeur large arbitraire pour
   permettre la transition tout en laissant le contenu reel determiner la
   hauteur finale. */
.rv-timeline-row__card {
  position: relative; background: var(--night-850); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm); padding: 0 10px; line-height: 1.3;
  max-height: 0; overflow: hidden; opacity: 0; transform: translateY(-4px); pointer-events: none;
  transition: max-height var(--dur-base) var(--ease-out), opacity var(--dur-fast) var(--ease-out),
    transform var(--dur-base) var(--ease-out), padding var(--dur-base) var(--ease-out);
}
.rv-timeline-row.is-open .rv-timeline-row__card {
  max-height: 240px; opacity: 1; transform: translateY(0); padding: 5px 10px; pointer-events: auto;
}
/* Petit connecteur horizontal carte -> pastille centrale. */
.rv-timeline-row__card::before { content: ''; position: absolute; top: 11px; height: 2px; width: 10px; background: var(--border-subtle); }
.rv-timeline-row--left .rv-timeline-row__card { grid-column: 1; }
.rv-timeline-row--left .rv-timeline-row__card::before { right: -10px; }
.rv-timeline-row--right .rv-timeline-row__card { grid-column: 3; }
.rv-timeline-row--right .rv-timeline-row__card::before { left: -10px; }
/* Vue reduite de seance identique partout (SPOR-572, addendum, CLAUDE.md
   "Maquette & UI") : le vrai .rv-session-card (rail + trace + badges +
   metriques) ne tient pas dans la largeur ~1/3/hauteur 240px d'une bulle
   timeline classique — pour une entree session/course, la bulle s'etend sur
   toute la largeur de la frise (grid-column: 1 / -1, casse volontairement
   l'alternance gauche/droite pour CETTE bulle) et perd son propre habillage
   (fond/bordure/padding — .rv-session-card a deja les siens, un double
   encadrement aurait ete redondant) ; connecteur horizontal supprime (n'a
   plus de sens en pleine largeur) ; marge du haut pour ne pas chevaucher le
   marqueur de la ligne centrale (toujours positionne en grid-column: 2). */
.rv-timeline-row__card--session { padding: 0; background: transparent; border: none; }
/* Specificite egale a .rv-timeline-row--left/--right .rv-timeline-row__card
   ci-dessus (deux classes) et placee APRES dans la feuille : necessaire pour
   gagner le conflit sur grid-column, sinon la regle alternee gauche/droite
   l'emporterait et la carte resterait coincee dans une seule moitie. */
.rv-timeline-row--left .rv-timeline-row__card--session,
.rv-timeline-row--right .rv-timeline-row__card--session { grid-column: 1 / -1; }
.rv-timeline-row__card--session::before { display: none; }
.rv-timeline-row.is-open .rv-timeline-row__card--session { max-height: 600px; padding: 0; }
.rv-timeline-row__card--session .rv-session-card { margin-top: 16px; }
.rv-timeline-row__badges { margin-bottom: 4px; }
.rv-timeline-row__date { font-family: var(--font-mono); font-size: 10.5px; color: var(--text-muted); }
.rv-timeline-row__title { margin: 1px 0; font-size: 12.5px; font-weight: 600; color: var(--text-primary); }
.rv-timeline-row__meta { font-size: 10.5px; color: var(--text-muted); }
/* Month/Year (SPOR-529) : carte resumee au lieu du detail evenement par evenement. */
.rv-timeline-row__card--summary { font-size: 12px; color: var(--text-secondary); }
.rv-timeline-row__card--summary .icon { vertical-align: -1px; margin-right: 4px; color: var(--text-muted); }

/* Couleur du marqueur par type d'evenement (voir Http\Timeline\TimelineController,
   typeClass) — "gear"/"injury" partagent respectivement l'ajout/le retrait et le
   debut/la resolution, differencies par icone (data-icon) plutot qu'une couleur de plus. */
.rv-timeline-row--race .rv-timeline-row__marker { border-color: var(--aurora-magenta); background: var(--aurora-magenta); }
.rv-timeline-row--gear .rv-timeline-row__marker { border-color: var(--aurora-blue); background: var(--aurora-blue); }
.rv-timeline-row--injury .rv-timeline-row__marker { border-color: var(--status-danger); background: var(--status-danger); }
.rv-timeline-row--recovery .rv-timeline-row__marker { border-color: var(--aurora-teal); background: var(--aurora-teal); }
.rv-timeline-row--health .rv-timeline-row__marker { border-color: var(--aurora-violet); background: var(--aurora-violet); }
.rv-timeline-row--document .rv-timeline-row__marker { border-color: var(--aurora-cyan); background: var(--aurora-cyan); }

/* Mobile (meme seuil que .rv-edit-grid, app.css) : ligne centrale illisible sur un
   ecran etroit une fois les cartes empilees — repli sur une seule colonne, ligne a
   gauche, toutes les cartes a droite (meme idiome que .rv-timeline d'origine). */
@media (max-width: 720px) {
  .rv-timeline-central::before { left: 12px; }
  .rv-timeline-row { grid-template-columns: 24px 1fr; }
  .rv-timeline-row__marker { grid-column: 1; }
  .rv-timeline-row--left .rv-timeline-row__card,
  .rv-timeline-row--right .rv-timeline-row__card { grid-column: 2; }
  .rv-timeline-row--left .rv-timeline-row__card::before,
  .rv-timeline-row--right .rv-timeline-row__card::before { left: -10px; right: auto; }
}

/* ============== Body-map (epic Pain & Injuries, SPOR-373/374) ==============
   Ported 1:1 from References/design/assets/css/components.css. Front/back
   silhouettes built from plain rects/ellipses (Domain\Health\BodyRegion +
   interactions.js RECTS/ELLIPSES), never a hand-authored outline path, so no
   bezier geometry that can self-intersect. viewBox 0 0 220 480 for every
   figure; front and back share the same primitives, only the side/label flip
   (handedness rule in interactions.js). */
.rv-bodymap2 { display: flex; flex-direction: column; gap: 12px; font-family: var(--font-sans); }
.rv-bodymap2__toolbar { display: flex; flex-wrap: wrap; gap: 8px; justify-content: space-between; align-items: center; }
.rv-bodymap2__toggle { display: flex; gap: 4px; padding: 3px; background: var(--night-850); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); }
.rv-bodymap2__toggle button {
  border: none; cursor: pointer; font-family: var(--font-sans); font-size: 12.5px; font-weight: 600;
  padding: 6px 13px; border-radius: 9px; background: transparent; color: var(--text-muted);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.rv-bodymap2__toggle button.is-active { background: var(--surface-hover); color: var(--text-primary); }

/* Scanner-HUD stage: topographic contour field + radial glow behind both
   figures, shared once per widget (not per-figure — corner chrome would
   duplicate awkwardly with two figures side by side). */
.rv-bodymap2__stage {
  position: relative; display: flex; justify-content: center; align-items: flex-start; gap: 28px;
  padding: 16px 34px 10px; border-radius: var(--radius-md); overflow: hidden;
  background:
    radial-gradient(ellipse at 50% 30%, rgba(56,189,248,0.13) 0%, transparent 62%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='70' viewBox='0 0 140 70'%3E%3Cpath d='M0 15 Q17.5 2 35 15 T70 15 T105 15 T140 15' fill='none' stroke='%2338BDF8' stroke-opacity='0.16' stroke-width='1'/%3E%3Cpath d='M0 38 Q17.5 25 35 38 T70 38 T105 38 T140 38' fill='none' stroke='%2338BDF8' stroke-opacity='0.10' stroke-width='1'/%3E%3Cpath d='M0 58 Q17.5 48 35 58 T70 58 T105 58 T140 58' fill='none' stroke='%2338BDF8' stroke-opacity='0.07' stroke-width='1'/%3E%3C/svg%3E"),
    var(--night-950);
  background-repeat: repeat; background-size: auto, 140px 70px, auto;
  border: 1px solid rgba(56,189,248,0.28);
  box-shadow: inset 0 0 30px rgba(56,189,248,0.08);
}
.rv-bodymap2__figure { position: relative; display: flex; flex-direction: column; align-items: center; gap: 8px; z-index: 1; }
.rv-bodymap2__figure svg { height: 340px; width: auto; display: block; overflow: visible; -webkit-user-select: none; user-select: none; }
.rv-bodymap2__figure-label { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted); }

/* The 15 region shapes live inside this group so the outer glow is one
   continuous drop-shadow around their combined silhouette, not one glow
   per shape (which would light up every internal seam individually). */
.rv-bodymap2__silhouette {
  filter: drop-shadow(0 0 2px rgba(56,189,248,0.9)) drop-shadow(0 0 7px rgba(56,189,248,0.45));
}
.rv-bodymap2__region {
  fill: rgba(56,189,248,0.06); stroke: rgba(56,189,248,0.5); stroke-width: 1;
  cursor: pointer; transition: fill 160ms cubic-bezier(.16,1,.3,1), stroke 160ms cubic-bezier(.16,1,.3,1);
}
.rv-bodymap2__region:hover { fill: rgba(56,189,248,0.16); stroke: rgba(56,189,248,0.85); }
.rv-bodymap2__region.is-marked { stroke: rgba(255,255,255,0.35); }
.rv-bodymap2__region.is-resolved { stroke: rgba(255,255,255,0.22); }
.rv-bodymap2__region.is-selected { stroke: var(--aurora-green) !important; stroke-width: 2.2; }
.rv-bodymap2--readonly .rv-bodymap2__region { cursor: default; pointer-events: none; }

/* Decorative-only variants of the same geometry (pain-injuries.php):
   --hero for the 1g poster constellation (no cyan HUD glow, near-invisible
   so the aurora flares read as the only light), --heatmap for the 1e
   report-count map (flat, no glow, fill set per-region in JS by count). */
.rv-bodymap2__silhouette--hero { filter: none; pointer-events: none; }
.rv-bodymap2__silhouette--hero .rv-bodymap2__region { fill: rgba(255,255,255,0.035) !important; stroke: rgba(255,255,255,0.1) !important; }
.rv-bodymap2__silhouette--heatmap { filter: none; pointer-events: none; }
.rv-bodymap2__silhouette--heatmap .rv-bodymap2__region { stroke: rgba(255,255,255,0.12) !important; }

/* Slow vertical scan sweep across the stage. */
.rv-bodymap2__stage::after {
  content: ''; position: absolute; left: 0; right: 0; height: 40%; top: -40%;
  background: linear-gradient(180deg, transparent, rgba(56,189,248,0.14), transparent);
  animation: rvScan 6s linear infinite; pointer-events: none; will-change: transform;
}
@keyframes rvScan { 0% { transform: translateY(0%); } 100% { transform: translateY(350%); } }

/* Decorative HUD chrome injected once per stage by initBodyMap2() — side
   nav chevrons + corner readout codes, purely stylistic (pointer-events:
   none), never bound to real data. */
.rv-bodymap2__hud { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.rv-bodymap2__hud-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); color: var(--fox-400);
  font-size: 14px; text-shadow: 0 0 6px rgba(251,146,60,0.7); opacity: 0.85;
}
.rv-bodymap2__hud-arrow--left { left: 8px; }
.rv-bodymap2__hud-arrow--right { right: 8px; }
.rv-bodymap2__hud-label {
  position: absolute; font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.06em;
  color: rgba(56,189,248,0.6); white-space: nowrap;
}
.rv-bodymap2__hud-label--tl { top: 8px; left: 10px; }
.rv-bodymap2__hud-label--br { bottom: 8px; right: 10px; }

/* HUD-style badge over a marked region's center: colored ring + intensity
   number + soft glow + a slowly spinning dashed outer ring — the graphic
   punch from the earlier reticle design, layered on top of the reliable
   region hit-targets instead of replacing them. Pure decoration: pointer
   events pass straight through to the region underneath. */
.rv-bodymap2__badge { pointer-events: none; }
.rv-bodymap2__badge-glow { fill: rgb(var(--badge-rgb)); opacity: 0.45; filter: blur(4px); }
.rv-bodymap2__badge-ring { fill: rgba(7,8,9,0.8); stroke: rgb(var(--badge-rgb)); stroke-width: 2.2; }
.rv-bodymap2__badge-dash {
  fill: none; stroke: rgb(var(--badge-rgb)); stroke-width: 1.3; stroke-dasharray: 2.6 3.4; opacity: 0.8;
  transform-box: fill-box; transform-origin: center; animation: rvBadgeSpin 6s linear infinite;
}
.rv-bodymap2__badge-text {
  fill: #fff; font-family: var(--font-mono); font-weight: 700; font-size: 15px;
  text-anchor: middle; dominant-baseline: central; paint-order: stroke; stroke: rgba(7,8,9,0.6); stroke-width: 2px;
}
@keyframes rvBadgeSpin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

.rv-bodymap2__hoverlabel { position: absolute; right: 4px; top: 0; font-size: 12px; color: var(--frost-200); min-height: 16px; }
.rv-bodymap2__viewlabel { position: absolute; left: 4px; top: 0; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-muted); }

.rv-bodymap2__legend { display: flex; align-items: center; gap: 8px; }
.rv-bodymap2__legend-swatches { display: flex; gap: 3px; }
.rv-bodymap2__legend-swatch { width: 15px; height: 8px; border-radius: 2px; }

/* ---- Selected-region editor ---- */
.rv-bodymap2__selected { display: flex; align-items: center; gap: 10px; }
.rv-bodymap2__selected-dot { width: 10px; height: 10px; border-radius: 50%; box-shadow: 0 0 12px currentColor; background: currentColor; flex: 0 0 auto; }

.rv-bodymap2__scale { display: flex; gap: 4px; }
.rv-bodymap2__scale button {
  flex: 1; height: 34px; border: none; border-radius: 6px; cursor: pointer;
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  background: rgba(255,255,255,0.05); color: var(--text-muted);
  transition: background 120ms cubic-bezier(.16,1,.3,1), color 120ms cubic-bezier(.16,1,.3,1);
}

/* ---- "Marked on this entry" list ---- */
.rv-bodymap2__marks { display: flex; flex-direction: column; gap: 8px; }
.rv-bodymap2__mark {
  display: flex; align-items: center; gap: 11px; padding: 11px 12px; border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle); background: rgba(255,255,255,0.02); cursor: pointer; text-align: left;
  width: 100%; font-family: var(--font-sans);
}
.rv-bodymap2__mark.is-selected { border-color: rgba(61,220,132,0.5); }
.rv-bodymap2__mark-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 8px; }
.rv-timeline__meta { font-size: 12px; color: var(--text-muted); }
