/* ============================================================
   hub.css — page d'accueil (hub) : hero, stats, aujourd'hui, listing, palette.
   ============================================================ */

/* ---------- Hero ---------- */
.hero { position: relative; padding-block: clamp(var(--space-8), 9vw, var(--space-10)) var(--space-7); }
.hero-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-6); align-items: center; }
.hero-inner { display: flex; flex-direction: column; gap: var(--space-5); min-width: 0; }
.hero h1 { font-size: var(--fs-hero); }
.hero h1 .accent { color: var(--brand); text-shadow: 0 0 40px var(--brand-glow); }
.hero .lede { font-size: var(--fs-lead); color: var(--ink-soft); max-width: 46rem; }
/* a partir de 960px : texte a gauche, constellation des marches a droite */
@media (min-width: 960px) {
  .hero-grid { grid-template-columns: minmax(0, 1.04fr) minmax(0, 0.96fr); gap: var(--space-7); }
}

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-4); margin-block-start: var(--space-4); }
.stat {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  background: linear-gradient(180deg, var(--surface), var(--surface) 55%, var(--bg-2));
  border: 1px solid var(--line); border-radius: var(--radius);
  transition: transform var(--t) var(--ease-out), border-color var(--t) var(--ease-out), box-shadow var(--t) var(--ease-out);
}
.stat::before {
  content: ""; position: absolute; inset-block-start: 0; inset-inline: 0; block-size: 2px;
  background: linear-gradient(90deg, transparent, var(--brand), transparent);
  opacity: 0.45; transition: opacity var(--t) var(--ease-out);
}
.stat:hover { transform: translateY(-3px); border-color: var(--brand-dim); box-shadow: var(--shadow-2), var(--glow-soft); }
.stat:hover::before { opacity: 1; }
.stat-ico {
  flex: none; display: grid; place-items: center; inline-size: 38px; block-size: 38px;
  border-radius: 11px; background: var(--brand-mist); border: 1px solid var(--brand-dim); color: var(--brand-soft);
}
.stat-ico svg { inline-size: 20px; block-size: 20px; }
.stat-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.stat .v { font-family: var(--font-display); font-size: clamp(1.8rem, 1.2rem + 2vw, 2.6rem); color: var(--ink); line-height: 1; font-variant-numeric: tabular-nums; }
.stat .k { font-size: var(--fs-small); color: var(--ink-mute); }

/* ---------- Hero : constellation des marches suivis (DXY pivot + 5 actifs) ----------
   Tout SVG inline + anime en CSS compositor-only (transform/opacity). Le pulse des noeuds
   anime le HALO (enfant), jamais le <g> porteur du transform=translate (sinon la CSS ecraserait
   la position). Coupe par prefers-reduced-motion ET le motion-toggle. --gold reste exclusif a XAU. */
/* masquee sous 960px : empilee sous le texte, elle repousserait le contenu et tomberait au
   fold (void noir au 1er paint avant reveal). Elle n'a de valeur que dans la compo 2-col desktop.
   Mobile = texte + stats prioritaires. (re-affichee ci-dessous a >=960px, APRES la base = bon ordre source.) */
.hero-visual { position: relative; display: none; justify-content: center; align-items: center; }
@media (min-width: 960px) { .hero-visual { display: flex; } }
.hv { inline-size: min(100%, 30rem); block-size: auto; aspect-ratio: 1 / 1; overflow: visible; animation: hv-float 9s var(--ease-out) infinite alternate; }

.hv-rings circle { fill: none; stroke: var(--line-strong); stroke-width: 1; }
.hv-rings circle:nth-of-type(3) {
  stroke: var(--brand); stroke-opacity: 0.28; stroke-dasharray: 3 9;
  transform-box: view-box; transform-origin: 230px 244px; animation: hv-spin 60s linear infinite;
}
.hv-links line { stroke: var(--brand); stroke-opacity: 0.2; stroke-width: 1.25; }

.hv-halo { fill: var(--brand-glow); opacity: 0.5; transform-box: fill-box; transform-origin: center; animation: hv-pulse 4.4s var(--ease-out) infinite; }
.hv-core { fill: var(--brand-soft); }
.hv-node:nth-of-type(1) .hv-halo { animation-delay: 0s; }
.hv-node:nth-of-type(2) .hv-halo { animation-delay: 0.9s; }
.hv-node:nth-of-type(3) .hv-halo { animation-delay: 1.8s; }
.hv-node:nth-of-type(4) .hv-halo { animation-delay: 2.6s; }
.hv-node:nth-of-type(5) .hv-halo { animation-delay: 3.4s; }
/* XAU = seul noeud dore (invariant --gold) */
.hv-node.hv-gold .hv-core { fill: var(--gold); }
.hv-node.hv-gold .hv-halo { fill: var(--gold-dim); }
.hv-node.hv-gold .hv-sym { fill: var(--gold); }

.hv-pivot-halo { fill: var(--brand-glow); opacity: 0.42; transform-box: fill-box; transform-origin: center; animation: hv-pulse 5.4s var(--ease-out) infinite; }
.hv-pivot-ring { fill: none; stroke: var(--brand); stroke-width: 1.5; stroke-opacity: 0.6; }
.hv-pivot-core { fill: var(--brand-bright); }

.hv-sym { fill: var(--ink-soft); font-family: var(--font-mono); font-size: 14px; font-weight: 600; letter-spacing: 0.03em; }
.hv-name { fill: var(--ink-mute); font-family: var(--font-body); font-size: 12.5px; }
.hv-pivot .hv-sym { fill: var(--brand-bright); font-size: 16px; }
.hv-pivot-name { fill: var(--brand-soft); }

@keyframes hv-float { from { transform: translateY(-6px); } to { transform: translateY(6px); } }
@keyframes hv-spin { to { transform: rotate(360deg); } }
@keyframes hv-pulse { 0%, 100% { opacity: 0.28; transform: scale(0.9); } 50% { opacity: 0.6; transform: scale(1.12); } }

/* hero visual masque < 960px (cf. .hero-visual display:none) : sous le breakpoint 2-col il
   repousse le contenu et risque le void-au-fold. Visible desktop, ou 0 overflow est verifie. */
/* !important : l'anneau pointille .hv-rings circle:nth-of-type(3) (spin) a une specificite
   (0,2,1) superieure a `.hv-rings circle` (0,1,1) -> sans !important il continuerait a tourner
   sous reduced-motion. Aligne sur le pattern data-motion ci-dessous et animations.css. */
@media (prefers-reduced-motion: reduce) {
  .hv, .hv-rings circle, .hv-halo, .hv-pivot-halo { animation: none !important; }
}
html[data-motion="reduce"] .hv,
html[data-motion="reduce"] .hv-rings circle,
html[data-motion="reduce"] .hv-halo,
html[data-motion="reduce"] .hv-pivot-halo { animation: none !important; }

/* ---------- Aujourd'hui ---------- */
.today { position: relative; }
.today-card {
  position: relative; overflow: hidden;
  display: grid; gap: var(--space-4);
  padding: var(--space-6); border-radius: var(--radius-xl);
  background: linear-gradient(135deg, var(--surface), var(--surface-2));
  border: 1px solid var(--brand-dim);
}
.today-card::before {
  content: ""; position: absolute; inset: -2px; border-radius: inherit; padding: 1px;
  background: conic-gradient(from var(--ang, 0deg), transparent, var(--brand-glow), transparent 40%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: spin-border 8s linear infinite; opacity: 0.7;
}
@property --ang { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
/* NB : animer --ang repeint le conic-gradient (paint-bound, pas compositor-only) — exception
   assumee, limitee a CE seul element decoratif, coupee par reduced-motion ET le motion-toggle. */
@keyframes spin-border { to { --ang: 360deg; } }
/* (pause manuelle via motion-toggle : couverte par animations.css html[data-motion="reduce"]) */
@media (prefers-reduced-motion: reduce) { .today-card::before { animation: none; } }

.today-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.today-slots { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
/* journee vedette a seance unique : eviter l'etirement plein-large d'une carte solo
   (le cas courant 2 cartes garde l'auto-fit 1fr). :has() = baseline 2026. */
.today-slots:has(> :only-child) { grid-template-columns: minmax(0, 36rem); justify-content: center; }

/* ---------- Toolbar (filtres + vue) ---------- */
.toolbar {
  position: sticky; inset-block-start: var(--nav-h); z-index: var(--z-sticky);
  display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; justify-content: space-between;
  padding-block: var(--space-3); margin-block-end: var(--space-5);
  background: var(--surface-glass-film); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border-block-end: 1px solid var(--line);
}
.filters { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.filter-pill {
  padding-block: 0.4rem; padding-inline: var(--space-3); font-size: var(--fs-small); font-weight: 600;
  border-radius: var(--radius-pill); border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-soft);
  cursor: pointer; transition: all var(--t-fast) var(--ease-out);
}
.filter-pill:hover { border-color: var(--line-strong); color: var(--ink); }
.filter-pill[aria-pressed="true"] { background: var(--brand-dim); border-color: var(--brand); color: var(--brand-bright); }

.view-toggle { display: inline-flex; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 3px; }
.view-toggle button { min-block-size: 36px; padding-inline: var(--space-3); border-radius: var(--radius-pill); border: 0; background: transparent; color: var(--ink-soft); cursor: pointer; display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--fs-small); transition: color var(--t-fast) var(--ease-out), background-color var(--t-fast) var(--ease-out); }
.view-toggle button[aria-pressed="true"] { background: var(--surface-3); color: var(--ink); }
/* survol coherent avec les .filter-pill voisines (transition explicite -> pas de snap) ;
   color-only sur l'etat inactif pour rester >= AA (un fond clair sous --ink-soft tiendrait
   aussi, mais le simple eclaircissement du libelle suffit et evite tout risque de contraste). */
.view-toggle button:not([aria-pressed="true"]):hover { color: var(--ink); }
.view-toggle svg { inline-size: 16px; block-size: 16px; }

/* forced-colors : les fonds tokenises (--brand-dim / --surface-3) sont neutralises par le
   mode contraste systeme -> l'etat actif d'un toggle deviendrait indistinguable. On rebascule
   sur les couleurs systeme Highlight pour garder filtre/vue actifs lisibles. */
@media (forced-colors: active) {
  .filter-pill[aria-pressed="true"],
  .view-toggle button[aria-pressed="true"] { background: Highlight; color: HighlightText; border-color: Highlight; forced-color-adjust: none; }
}

/* ---------- Listing : timeline ---------- */
/* Vue grille : `.day` en display:contents fait remonter `.day-label` et `.day-sessions`
   comme items directs de la grille parente. Le bandeau date doit traverser toute la rangee
   (grid-column 1/-1) et le bloc de cartes s'aplatir (display:contents) pour que chaque `.ses`
   devienne un item direct -> grille uniforme + date pleine largeur (sinon label coince sur 1
   colonne et cartes empilees dans une grille imbriquee). `minmax(min(300px,100%),1fr)` empeche
   la piste de 300px de deborder sous 320px (le viewport l'emporte). */
.sessions[data-view="grid"] { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: var(--space-4); }
.sessions[data-view="grid"] .day { display: contents; }
.sessions[data-view="grid"] .day-label { grid-column: 1 / -1; margin-block-end: 0; }
.sessions[data-view="grid"] .day-sessions { display: contents; }
/* un jour dont TOUTES les seances sont filtrees est masque par hub.js via [hidden] ; mais en
   vue grille la regle auteur `.day{display:contents}` l'emporte sur la regle UA [hidden]{display:none}
   -> on re-affirme le masquage, sinon un bandeau date orphelin (sans carte) reste affiche. */
.sessions[data-view="grid"] .day[hidden] { display: none; }

.day { margin-block-end: var(--space-6); }
.day-label { display: flex; align-items: center; gap: var(--space-3); margin-block-end: var(--space-4); }
.day-label .date { font-family: var(--font-display); font-size: 1.1rem; color: var(--ink); }
.day-label .rule { flex: 1; block-size: 1px; background: linear-gradient(90deg, var(--line-strong), transparent); }
.day-sessions { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }

.ses { position: relative; display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-5); }
/* une carte du listing ramenee par Tab ne doit pas atterrir SOUS la toolbar collante
   (nav-h + hauteur toolbar ~66px). scroll-margin-top scope ICI (pas scroll-padding-top global)
   pour ne pas sur-padder les ancres des pages sans toolbar (seance/admin). Cible le listing
   uniquement : la vedette "a la une" est au-dessus de la toolbar, donc exclue. */
.sessions .ses { scroll-margin-top: calc(var(--nav-h) + var(--space-8) + var(--space-4)); }
.ses-top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.ses-type { display: inline-flex; align-items: center; gap: var(--space-2); font-family: var(--font-mono); font-size: var(--fs-micro); letter-spacing: 0.12em; text-transform: uppercase; }
.ses[data-type="analyse"] .ses-type { color: var(--brand-soft); }
.ses[data-type="debrief"] .ses-type { color: var(--violet); }

/* accent lateral par slot : revele au hover des cartes cliquables, permanent sur la vedette.
   compositor-only (opacity + scaleY), origine centree pour ne pas heurter les coins arrondis. */
.ses-accent {
  position: absolute; inset-block: var(--space-5); inset-inline-start: 0; inline-size: 3px;
  border-radius: 0 3px 3px 0; background: var(--brand);
  opacity: 0; transform: scaleY(0.35); transform-origin: center;
  transition: opacity var(--t) var(--ease-out), transform var(--t) var(--ease-out);
}
.ses[data-type="debrief"] .ses-accent { background: var(--violet); }
.card-link:hover .ses-accent, .ses-featured .ses-accent { opacity: 0.9; transform: scaleY(1); }
.ses[data-status="cancelled"] .ses-accent { background: var(--bear); opacity: 0.55; transform: scaleY(1); }
@media (prefers-reduced-motion: reduce) { .ses-accent { transition: none; } }
.ses-title { font-family: var(--font-display); font-size: 1.15rem; line-height: 1.15; }
/* .ses-desc vit dans components.css (feuille globale) : reutilise par la page seance annulee
   (motif d'annulation) qui ne charge pas hub.css. Hub et page annulee la partagent. */
.ses-foot { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-block-start: auto; }
.ses-thumb { aspect-ratio: 16 / 9; border-radius: var(--radius); overflow: hidden; background: var(--bg-2); border: 1px solid var(--line); position: relative; }
.ses-thumb .play { position: absolute; inset: 50% auto auto 50%; transform: translate(-50%, -50%); inline-size: 44px; block-size: 44px; border-radius: 50%; background: var(--surface-glass); display: grid; place-items: center; color: var(--ink); }
/* Carte annulee : de-emphase visuelle SANS dimmer le texte porteur d'info.
   L'opacity sur la carte ferait tomber le badge d'etat sous AA (1.4.3) ; on ne
   dim que le titre (reste >=4.5:1) et le badge "Seance annulee" garde son plein contraste. */
.ses[data-status="cancelled"] .ses-title { opacity: 0.6; }
.ses-cancel { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--bear); font-size: var(--fs-micro); font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }

/* vedette « a la une » : <a> sans .card-link -> affordance de hover coherente (lift + glow) */
.ses-featured { transition: transform var(--t) var(--ease-out), border-color var(--t) var(--ease-out), box-shadow var(--t) var(--ease-out); }
.ses-featured:hover { transform: translateY(-2px); border-color: var(--brand-dim); box-shadow: var(--shadow-2), var(--glow-soft); }
@media (prefers-reduced-motion: reduce) { .ses-featured:hover { transform: none; } }

.empty-state { text-align: center; padding: var(--space-8) var(--space-4); color: var(--ink-mute); display: none; }
.sessions:empty + .empty-state { display: block; }
/* etat vide rendu cote serveur quand AUCUNE seance n'est publiee (toujours visible, contrairement
   a .empty-state pilote par le JS de filtrage) — repond au cas reel du tout 1er demarrage. */
.sessions-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  text-align: center;
  padding: var(--space-8) var(--space-5);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(120% 100% at 50% 0%, var(--brand-mist), transparent 70%),
    var(--surface);
}
.sessions-empty-icon {
  display: grid;
  place-items: center;
  width: 60px;
  height: 60px;
  color: var(--brand);
  border: 1px solid var(--brand-dim);
  border-radius: var(--radius);
  background: var(--brand-mist);
  box-shadow: var(--glow-soft);
}
.sessions-empty-icon svg { width: 30px; height: 30px; }
.sessions-empty h3 { margin: 0; font-size: var(--fs-h3); color: var(--ink); }
.sessions-empty p { margin: 0; max-width: 52ch; color: var(--ink-soft); line-height: var(--lh-body); }

/* ---------- Command palette ---------- */
.cmdk-backdrop { position: fixed; inset: 0; z-index: var(--z-overlay); background: rgba(3,4,8,0.6); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); display: none; }
.cmdk-backdrop[data-open="true"] { display: block; }
.cmdk {
  position: fixed; inset-block-start: 12vh; inset-inline: 0; z-index: var(--z-modal);
  inline-size: min(100% - 2rem, 600px); margin-inline: auto;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-3); overflow: hidden; display: none;
}
.cmdk[data-open="true"] { display: block; animation: cmdk-in var(--t) var(--ease-out); }
@keyframes cmdk-in { from { opacity: 0; transform: translateY(-8px) scale(0.99); } to { opacity: 1; transform: none; } }
.cmdk-input { inline-size: 100%; padding: var(--space-4) var(--space-5); background: transparent; border: 0; border-block-end: 1px solid var(--line); color: var(--ink); font-size: 1.05rem; font-family: var(--font-body); }
.cmdk-input::placeholder { color: var(--ink-mute); }
.cmdk-list { max-block-size: 50vh; overflow-y: auto; padding: var(--space-2); }
.cmdk-item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); color: var(--ink-soft); cursor: pointer; }
.cmdk-item[aria-selected="true"] { background: var(--surface-2); color: var(--ink); }
/* survol souris : meme surlignage, MAIS supprime pendant la navigation clavier (data-nav=kbd pose
   par hub.js move()) — sinon une fleche alors que le curseur repose sur une AUTRE ligne afficherait
   DEUX lignes "actives". aria-selected reste la source de verite unique de l'option active. */
.cmdk-list:not([data-nav="kbd"]) .cmdk-item:hover { background: var(--surface-2); color: var(--ink); }
.cmdk-item .meta { margin-inline-start: auto; font-size: var(--fs-micro); color: var(--ink-mute); }
.cmdk-empty { padding: var(--space-5); text-align: center; color: var(--ink-mute); }
/* rappel clavier (decouvrabilite) — purement visuel (aria-hidden), l'a11y passe par la live region */
.cmdk-foot { display: flex; flex-wrap: wrap; gap: var(--space-4); padding: var(--space-3) var(--space-4); border-block-start: 1px solid var(--line); color: var(--ink-mute); font-size: var(--fs-micro); }
.cmdk-foot span { display: inline-flex; align-items: center; gap: var(--space-2); }
.cmdk-foot kbd { font-size: 0.72rem; padding: 0.1rem 0.34rem; }

/* prefers-reduced-transparency (+ prefers-contrast:more) : ces preferences rendent
   --surface-glass-film transparent (tokens.css) mais NE coupent PAS le backdrop-filter -> la
   toolbar collante et le scrim de la palette resteraient transparents ET flouteraient le contenu
   defilant dessous (l'inverse de l'intention). On neutralise le flou et on pose des fonds OPAQUES
   de repli. Place EN FIN de fichier : a specificite egale, l'override doit suivre en source-order
   les definitions de base de .toolbar (l.132) ET .cmdk-backdrop (l.225) pour les emporter.
   (.site-header a deja un fond opaque -> non concernee.) */
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
  .toolbar { background: var(--bg-2); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .cmdk-backdrop { background: rgba(3, 4, 8, 0.92); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
