/* ─── Base ─── */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    scroll-behavior: smooth;
}

/* Champs de saisie : 16px plancher partout (zoom mobile), cible tactile
   44px. :where() garde une specificite nulle : les composants peuvent
   preciser, jamais descendre sous le plancher sans le dire. */
:where(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="hidden"]):not([type="color"]), textarea, select) {
    font-size: var(--text-md);
    min-height: 44px;
}

/* Chiffres qui changent ou s'alignent en colonne : figures tabulaires,
   sinon le nombre tressaute a chaque mise a jour (DESIGN.md, Ecriture). */
time,
.room-timer,
.call-pill__timer,
.pomo-time,
.slot-time,
.wgms-time,
.wgmd-count,
.schedule-row-times,
.metric-value,
.counter-number,
.streak-value,
.review-hero-count,
.rev-done-count,
.rev-counter,
.progress-level-num,
.planner-group-count,
.exam-reality-num,
.wc-score,
.date-chip,
.sp-val,
.cm-day,
.stat-value,
.room-chat-time,
.exam-dash-card-num,
.fci-count,
.card-form-ue-val,
.lb-rank {
    font-variant-numeric: tabular-nums;
}

/* Bandeau hors connexion (chantier 10) : toujours visible tant que le
   reseau manque, sans animation. Il ne recouvre rien : sa hauteur est
   publiee dans --offline-bar-h (Sync._renderOfflineBar) et la sidebar, la
   topbar collante et le contenu descendent d'autant. Le menu reste ouvrable
   hors ligne. */
.offline-bar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: calc(var(--z-barre) + 20);
    padding: 8px 16px;
    padding-top: max(8px, var(--safe-top));
    background: var(--orange);
    color: var(--on-primary);
    font-size: var(--text-sm);
    font-weight: var(--weight-emphasis);
    text-align: center;
}

.offline-bar[hidden] {
    display: none;
}

/* Optimisation perf scroll sur mobile bas de gamme : on indique au
   navigateur qu'il peut sauter le rendu des sections hors viewport.
   `contain-intrinsic-size` reserve une hauteur estimee pour eviter
   les sauts de scrollbar. Les cartes du dashboard et les sections
   eligibles portent la classe .perf-section. */
.perf-section {
    content-visibility: auto;
    contain-intrinsic-size: auto 320px;
}

/* Respect du reglage utilisateur pour les animations reduites :
   coupe les transitions/animations couteuses, garde l'app fonctionnelle
   sans bouger sur les appareils sensibles ou les preferences accessibilite. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

/* Profil bas debit / appareil modeste : la classe est posee par main.js
   selon Network Information API et navigator.deviceMemory.
   On masque les Lottie (gros consommateurs CPU/reseau) et on accelere
   les transitions pour ne pas penaliser le ressenti.
   L'app reste 100 % fonctionnelle, juste plus sobre visuellement. */
html.low-bandwidth dotlottie-wc,
html.low-bandwidth lottie-player {
    display: none !important;
}

html.low-bandwidth *,
html.low-bandwidth *::before,
html.low-bandwidth *::after {
    transition-duration: .08s !important;
    animation-duration: .12s !important;
}

body {
    font-family: var(--font-body);
    background: var(--bg);
    color: var(--text);
    min-height: 100vh;
    overflow-x: hidden;
    font-size: 14px;
    /* Lot 2 typo fine. Le body ne declarait NI interlignage NI graisse.
       Consequence mesuree : le defaut navigateur (~1.2) s'appliquait a tout
       texte sans regle propre, alors que les reglages Lisible et Confort
       declarent 1.6 et 1.72 sur ce meme selecteur. Le reglage par DEFAUT,
       celui que presque tout le monde utilise, etait donc le plus serre des
       trois. Sur un Android d'entree de gamme, c'est la difference entre un
       cours lisible et un cours penible.
       font-weight est un no-op visuel (400 = defaut herite) : il ancre le
       token pour que l'echelle des graisses ne mente pas sur sa base. */
    font-weight: var(--weight-body);
    line-height: var(--leading-normal);
}

/* ─── Scrollbar ─── */
::-webkit-scrollbar {
    width: 5px;
}

::-webkit-scrollbar-track {
    background: var(--bg2);
}

::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: var(--radius-pill);
}

::-webkit-scrollbar-thumb:hover {
    background: var(--green3);
}

/* ─── App shell ─── */
.app {
    display: flex;
    min-height: 100vh;
    min-width: 0;
    overflow-x: hidden;
}

/* ─── Sidebar ─── */
.sidebar {
    width: 240px;
    min-height: 100vh;
    /* (Refonte shell) Sidebar fondue dans le meme fond que le contenu : un
       canvas plat unifie facon app Claude. Seul le filet de droite (--border)
       structure la separation ; les cartes (--surface) ressortent. */
    background: var(--bg);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 0;
    top: var(--offline-bar-h);
    bottom: 0;
    height: auto;
    z-index: calc(var(--z-barre) + 11);
    /* Tiroir plein ecran sur mobile : son en-tete reste sous la barre d'etat
       et son bas au-dessus de la barre de navigation gestuelle. */
    padding-top: var(--safe-top-shell);
    padding-bottom: var(--safe-bottom);
    /* Le tiroir defile d'un seul bloc. Avant, seule la liste de navigation
       pouvait defiler et c'est elle qui absorbait tout le manque de hauteur :
       a 320x568 et en paysage, aucun lien n'etait visible. */
    overflow-y: auto;
    overscroll-behavior: contain;
    transition: transform var(--dur-slow) var(--ease-out);
}

.sidebar-logo {
    padding: 24px 20px 16px;
    border-bottom: 1px solid var(--border);
}

/* Marque (DESIGN.md, section Marque) : monogramme carre a coins arrondis
   (le favicon 1:1, --brand-mark) et le mot UpTe en police de marque. La
   ligne .logo-sub en dessous est celle d'origine. */
.brand {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}

.brand-mark {
    flex-shrink: 0;
    width: var(--brand-mark);
    height: var(--brand-mark);
    border-radius: var(--radius-sm);
    background: var(--green3);
    /* Glyphe clair dans tous les themes : le monogramme est le favicon, il ne
       suit pas la doctrine des boutons (sombre sur accent). */
    color: var(--white);
}

.brand-mark svg {
    display: block;
    width: 100%;
    height: 100%;
}

.brand-name {
    font-family: var(--font-brand), sans-serif;
    font-weight: 800;
    font-size: var(--text-lg);
    letter-spacing: var(--tracking-tight);
    line-height: 1;
    color: var(--text);
}

/* Crenage manuel de la paire « pT » du mot-symbole (2.11, passage a Onest).
   La jambe descendante du p et le bras gauche du T ouvrent un trou optique
   en graisse 800 qu'Onest ne corrige pas par une paire de kerning. Classe
   partagee : barre laterale (.brand-name) et splash (.upte-loader__brand),
   toutes deux en graisse 800. */
.brand-word__t {
    margin-left: var(--brand-kern-pt);
}

.logo-sub {
    margin-top: 10px;
    font-size: 11px;
    color: var(--muted);
}

/* ─── Nav ─── */
.nav-section {
    padding: 12px 12px 4px;
    /* Grandit pour pousser le bas du tiroir, ne retrecit jamais sous sa
       hauteur naturelle : c'est la sidebar qui defile quand l'ecran est court. */
    flex: 1 0 auto;
    overflow-y: auto;
    overflow-x: hidden;
    min-height: 0;
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
    transition: scrollbar-color var(--dur-slow);
}

.nav-section:hover {
    scrollbar-color: var(--border) transparent;
}

.nav-section::-webkit-scrollbar {
    width: 3px;
}

.nav-section::-webkit-scrollbar-track {
    background: transparent;
}

.nav-section::-webkit-scrollbar-thumb {
    background: transparent;
    border-radius: var(--radius-pill);
    transition: background var(--dur-slow);
}

.nav-section:hover::-webkit-scrollbar-thumb {
    background: var(--border);
}

.nav-label {
    font-size: 11px;
    color: var(--muted2);
    padding: 0 8px 6px;
    font-weight: 600;
}

/* DESIGN.md, section Navigation : un lien (<a>) ou un bouton, jamais un div.
   L'etat actif se lit par la surface, la couleur du texte et la graisse ;
   aucun filet lateral, aucun carre teinte derriere l'icone. */
.nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: var(--tap-min);
    padding: 9px 12px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    cursor: pointer;
    /* Transitions ciblees (pas `all`) + easing physique : reactivite
       sans cout de rendu inutile sur les survols/clics rapides. */
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
    color: var(--muted);
    font: inherit;
    font-size: 13.5px;
    font-weight: 400;
    text-align: left;
    text-decoration: none;
    position: relative;
    user-select: none;
}

.nav-item:hover {
    background: var(--surface);
    color: var(--text);
}

.nav-item:active {
    transform: scale(.985);
}

.nav-item.active {
    background: var(--green-dim);
    color: var(--green-text);
    font-weight: 600;
}

.nav-item.active svg {
    color: var(--green-text);
}

/* Actif : wash plein et point de la marque (regles .nav-dot dans
   utilities.css, DESIGN.md section Icones). */
.nav-item.active .upte-icon {
    --icon-tint: var(--icon-tint-active);
}

.nav-item svg {
    flex-shrink: 0;
    position: relative;
    z-index: calc(var(--z-base) + 1);
    transition: color var(--dur-fast) var(--ease-out);
}

/* (T29) Separateur subtil entre nav principale et Partage / Parametres.
   Opacite 0.1 = visible mais sans bruit visuel. */
.sidebar .nav-divider {
    border: 0;
    border-top: 1px solid var(--text);
    opacity: 0.1;
    margin: 8px 8px;
}

.nav-badge {
    margin-left: auto;
    background: var(--green3);
    color: var(--on-primary);
    font-size: 10px;
    font-weight: 700;
    padding: 2px 7px;
    border-radius: var(--radius-pill);
    min-width: 20px;
    text-align: center;
}

/* ─── Sidebar bottom ─── */
.sidebar-bottom {
    margin-top: auto;
    padding: 16px;
    border-top: 1px solid var(--border);
}

/* Bureau : la place d'Apte, juste au-dessus de la navigation. Au
   telephone, l'orbe vit dans la barre d'onglets : la ligne est masquee
   dans le tiroir pour ne pas doubler l'entree. */
.sidebar-apte {
    min-height: var(--tap-min);
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    margin: var(--space-3) var(--space-3) 0;
    background: var(--green-dim);
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--green-text);
    font-family: var(--font-body);
    font-size: var(--text-sm);
    font-weight: var(--weight-emphasis);
    text-align: left;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out);
}

.sidebar-apte:hover {
    background: color-mix(in srgb, var(--green) 22%, transparent);
}

.sidebar-apte:focus-visible {
    outline: 2px solid var(--green);
    outline-offset: 2px;
}

.sidebar-apte__orbe {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-pill);
    background: var(--green2);
    color: var(--on-primary);
}

.sidebar-apte__orbe svg {
    width: 16px;
    height: 16px;
}

@media (max-width: 900px) {
    .sidebar-apte {
        display: none;
    }
}

.semester-progress {
    background: var(--surface);
    border-radius: var(--radius-sm);
    padding: 12px;
}

/* Version embarquee : visible en bas de sidebar, cliquable pour forcer
   une verification de mise a jour. Permet de savoir d'un coup d'oeil
   quelle build tourne sans ouvrir la console. */
.sidebar-version {
    display: block;
    width: 100%;
    margin-top: 10px;
    padding: 6px 8px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    color: var(--muted2);
    font-family: var(--font-body);
    font-size: 10.5px;
    text-align: center;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}

.sidebar-version:hover {
    color: var(--muted);
    border-color: var(--border);
}

.sp-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 6px;
}

.sp-title {
    font-size: 12px;
    color: var(--muted);
}

.sp-val {
    font-size: 12px;
    color: var(--green-text);
    font-weight: 600;
}

.bar-track {
    height: 4px;
    background: var(--border);
    border-radius: var(--radius-pill);
    overflow: hidden;
}

.bar-fill {
    height: 100%;
    background: var(--green);
    border-radius: var(--radius-pill);
    transition: width var(--dur-screen) var(--ease-out);
}

/* ─── Sidebar overlay (mobile) ─── */
.sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: var(--overlay-scrim);
    z-index: calc(var(--z-barre) + 10);
}

.sidebar-overlay.open {
    display: block;
}

/* ─── Main ─── */
.main {
    margin-left: 240px;
    flex: 1;
    min-height: 100vh;
    min-width: 0;
    padding-top: var(--offline-bar-h);
}

/* ─── Topbar ─── */
.topbar {
    position: sticky;
    top: var(--offline-bar-h);
    z-index: var(--z-barre);
    /* (Refonte shell) Barre du haut sans bande ni filet : meme fond que le
       contenu pour un rendu plein ecran sans couture (facon Claude). Le fond
       reste opaque (--bg) afin d'occulter proprement le contenu qui defile
       dessous. */
    background: var(--bg);
    /* Retraits propres a la barre, surcharges par les points de rupture
       ci-dessous : eux ne touchent qu'a ces deux variables, la composition
       avec les zones systeme reste ecrite une seule fois. Sans ce retrait,
       le titre et le bouton du menu passent sous l'heure et la batterie
       (edge-to-edge impose par Android 15 en targetSdk 35+, encoche iPhone,
       web mobile en viewport-fit=cover). */
    --topbar-pad-y: 12px;
    --topbar-pad-x: 28px;
    padding:
        calc(var(--topbar-pad-y) + var(--safe-top-shell))
        calc(var(--topbar-pad-x) + var(--safe-right))
        var(--topbar-pad-y)
        calc(var(--topbar-pad-x) + var(--safe-left));
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    min-height: 54px;
}

.topbar .menu-toggle {
    flex-shrink: 0;
}

.topbar>div:first-of-type {
    flex: 1;
    min-width: 0;
}

.topbar-title {
    font-family: var(--font-display), sans-serif;
    font-weight: 700;
    font-size: 18px;
    color: var(--text);
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.topbar-subtitle {
    font-size: 12px;
    color: var(--muted);
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Compte non personnalise : le sous-titre devient un indicateur cliquable
   "Personnalise ton espace" -> Reglages (au lieu des placeholders verbeux). */
.topbar-subtitle--cta {
    /* A11Y-01 : petit texte -> variante accent 4.5:1 (3.1:1 avant en light). */
    color: var(--green-text);
    font-weight: 600;
}

.topbar-subtitle--cta:hover {
    text-decoration: underline;
}

.topbar-right {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
}

.date-chip {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 6px 12px;
    font-size: 12px;
    color: var(--muted);
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.date-chip .day {
    font-weight: 600;
    color: var(--text);
}

/* ─── Bouton de connexion de l'en-tete (invite uniquement) ───
   Le seul bouton Se connecter vivait dans la sidebar, hors ecran sous 900px :
   sur telephone, un nouveau venu n'en voyait aucun. Celui-ci reste dans la
   barre du haut tant que personne n'est connecte, et il est retire du DOM a la
   connexion (UI.hideGuestAuthButtons). Action d'en-tete, pas bouton de
   contenu : il ne dispute pas le focal de la page. */
.topbar-auth-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    border: 1px solid var(--green3);
    background: var(--green-dim);
    color: var(--green-text);
    font-family: var(--font-display);
    font-size: var(--text-xs);
    font-weight: var(--weight-heading);
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out),
        color var(--dur-fast) var(--ease-out);
}

.topbar-auth-btn:hover {
    background: var(--green3);
    color: var(--on-primary);
}

.topbar-auth-btn:active {
    background: var(--green3);
    color: var(--on-primary);
}

/* Variante sidebar : pleine largeur, rayon de la carte. */
.sidebar-auth {
    padding: 0 var(--space-4) var(--space-3);
}

.topbar-auth-btn--block {
    width: 100%;
    border-radius: var(--radius);
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-sm);
}

/* ─── Content ─── */
.content {
    padding: 28px;
    min-width: 0;
    width: 100%;
    max-width: 100%;
}

/* ─── Page grids ─── */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 16px;
    margin-bottom: 24px;
}

.planner-grid {
    display: grid;
    grid-template-columns: 1fr 320px;
    gap: 20px;
}

/* ─── Week grid ─── */
.wg-header {
    background: var(--bg3);
    padding: 12px 8px;
    text-align: center;
    font-family: var(--font-display), sans-serif;
    font-size: 12px;
    font-weight: 600;
    color: var(--muted);
    border-bottom: 1px solid var(--border);
}

.wg-header.today-col {
    color: var(--green-text);
    background: var(--green-dim);
}

.wg-event {
    position: absolute;
    left: 2px;
    right: 2px;
    border-radius: var(--radius-sm);
    padding: 5px 7px;
    font-size: 10px;
    font-weight: 500;
    overflow: hidden;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out);
    z-index: calc(var(--z-base) + 2);
    /* Le bloc EST la couleur du cours (fond teinte, code dans la teinte) :
       ni filet gauche ni filet haut, DESIGN.md lot 3. */
}

.wg-event:hover {
    z-index: calc(var(--z-base) + 5);
}

.wg-event.type-cours {
    background: color-mix(in srgb, var(--event-accent, var(--green)) 16%, var(--surface));
    color: var(--text);
}

.wg-event.type-cours:hover {
    background: color-mix(in srgb, var(--event-accent, var(--green)) 24%, var(--surface));
}

.wg-event.type-study {
    background: color-mix(in srgb, var(--orange) 14%, var(--surface));
    color: var(--text);
}

.wg-event .ev-name {
    font-size: 10px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: color-mix(in srgb, var(--event-accent, var(--green-text)) 50%, var(--text));
}

.wg-event .ev-time {
    font-size: 9px;
    color: color-mix(in srgb, var(--muted) 60%, var(--text));
    margin-top: 1px;
}

/* ─── Mobile toggle (hamburger) — meme langage container que la topbar ─── */
.menu-toggle {
    display: none;
    width: var(--tap-min);
    height: var(--tap-min);
    background: var(--surface2);
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--muted);
    align-items: center;
    justify-content: center;
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                transform var(--dur-base) var(--ease-out);
}

.menu-toggle:hover {
    background: var(--green-dim);
    color: var(--green-text);
    transform: scale(1.06);
}

/* Menu ouvert : les barres passent au vert et le point qui ferme la
   troisieme barre s'allume (aria-expanded pilote par UI.toggleSidebar). */
.menu-toggle[aria-expanded="true"] {
    color: var(--green-text);
}

/* ─── Responsive ─── */
@media (max-width: 900px) {
    .sidebar {
        transform: translateX(-100%);
        /* Tiroir ouvert en paysage : le contenu sort de l'encoche. */
        padding-left: var(--safe-left);
    }

    .sidebar.open {
        transform: translateX(0);
    }

    .main {
        margin-left: 0;
    }

    .menu-toggle {
        display: flex;
    }

    .topbar {
        --topbar-pad-x: 16px;
        gap: 12px;
    }

    .topbar-title {
        font-size: 16px;
    }

    .topbar-subtitle {
        font-size: 11px;
    }

    .date-chip {
        padding: 5px 10px;
        font-size: 11px;
    }

    .topbar .topbar-planifier {
        padding: 6px 12px;
        font-size: 12px;
    }

    .stats-grid {
        grid-template-columns: 1fr 1fr;
    }

    .planner-grid {
        grid-template-columns: 1fr;
    }

    .section-header {
        flex-wrap: wrap;
        gap: 8px;
    }
}

@media (max-width: 600px) {
    .topbar {
        --topbar-pad-y: 10px;
        --topbar-pad-x: 12px;
        gap: 10px;
        min-height: 52px;
    }

    .topbar-title {
        font-size: 14px;
    }

    .topbar-subtitle {
        font-size: 11px;
    }

    .topbar-right {
        gap: 6px;
    }

    #pomoTopbarIndicator {
        display: none !important;
    }

    /* Telephone : l'app installee est deja plein ecran et le navigateur
       mobile le gere mal ; le bouton prenait la place du sous-titre. Planifier
       passe en icone seule (libelle accessible porte par aria-label). La barre
       garde menu, titre, cloche et Planifier. */
    .topbar #btnFullscreen {
        display: none;
    }

    .topbar .topbar-planifier .topbar-btn-label {
        display: none;
    }

    .topbar .topbar-planifier {
        min-width: var(--tap-min);
        min-height: var(--tap-min);
        justify-content: center;
    }

    /* Icone seule DANS L'EN-TETE : le libelle mangerait le titre de page. La
       variante sidebar (--block) garde son libelle et sa pleine largeur, d'ou
       la portee .topbar. Le bouton reste une cible tactile pleine (--tap-min). */
    .topbar .topbar-auth-btn__label {
        display: none;
    }

    .topbar .topbar-auth-btn {
        min-width: var(--tap-min);
        min-height: var(--tap-min);
        justify-content: center;
        padding: var(--space-2);
    }

    .date-chip {
        display: none;
    }

    .date-chip {
        padding: 4px 8px;
        font-size: 11px;
        gap: 4px;
    }

    .date-chip svg {
        width: 10px;
        height: 10px;
    }

    .topbar .topbar-planifier {
        padding: var(--space-2);
        font-size: 12px;
    }

    .stats-grid {
        grid-template-columns: 1fr 1fr;
    }

    .content {
        padding: 16px;
    }
}

@media (max-width: 480px) {
    .stats-grid {
        grid-template-columns: 1fr 1fr;
        gap: 10px;
    }

    .content {
        padding: 12px;
    }
}

@media (max-width: 380px) {
    .topbar {
        --topbar-pad-y: 8px;
        --topbar-pad-x: 10px;
        gap: 8px;
    }

    .topbar-title {
        font-size: 14px;
        /* Reste sur UNE ligne, tronque par "..." (jamais sur 2 lignes, meme
           sur les ecrans les plus etroits). Le conteneur a min-width:0. */
        white-space: nowrap;
        line-height: 1.25;
    }

    .date-chip {
        font-size: 10px;
        padding: 4px 6px;
    }

    .topbar .topbar-planifier .topbar-btn-label {
        display: none;
    }

    .topbar .topbar-planifier {
        padding: 8px 10px;
    }

    .content {
        padding: 10px;
    }
}

@media (max-width: 320px) {
    /* Retraits via les variables de la barre : un padding en shorthand
       ecrasait la composition avec la zone systeme (menu sous la barre
       d'etat a 320 px). */
    .topbar {
        --topbar-pad-y: 6px;
        --topbar-pad-x: 8px;
        gap: 6px;
        min-height: 48px;
    }

    .topbar-title {
        font-size: 13px;
    }

    /* Sous 320 px seulement : le titre garde toute la place. Au-dessus, le
       retrait du bouton plein ecran laisse la ligne au sous-titre. */
    .topbar-subtitle {
        display: none;
    }

    .content {
        padding: 8px;
    }

    .sidebar {
        width: 100%;
        max-width: 280px;
    }
}

/* 
   WEEK GRID — VUE MOBILE (≤ 768px)
 */

.wg-mobile-wrap {
    display: flex;
    flex-direction: column;
    gap: 0;
    min-height: 300px;
}

/* ─── Tabs jours ─── */
.wg-mobile-tabs {
    display: flex;
    gap: 0;
    overflow-x: auto;
    padding: 0;
    scrollbar-width: none;
    -ms-overflow-style: none;
    border-bottom: 1px solid var(--border);
    background: var(--bg2);
}

.wg-mobile-tabs::-webkit-scrollbar {
    display: none;
}

.wg-mobile-tab {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    padding: 12px 4px 10px;
    border: none;
    background: transparent;
    color: var(--muted);
    font-family: var(--font-display), sans-serif;
    font-size: 11px;
    font-weight: 700;
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    white-space: nowrap;
    transition: color var(--dur-fast), border-color var(--dur-fast), background var(--dur-fast);
    /* Les 7 jours se partagent la largeur a parts egales -> tout tient sur un
       ecran etroit, plus de scroll horizontal ni de SAM/DIM coupes. */
    flex: 1 1 0;
    min-width: 0;
}

.wg-mobile-tab:hover {
    background: var(--surface);
    color: var(--text);
}

.wg-mobile-tab.today .wgt-label {
    color: var(--green-text);
}

.wg-mobile-tab.active {
    color: var(--green-text);
    border-bottom-color: var(--green);
    background: var(--green-dim);
}

.wgt-label {
    font-size: 11px;
    font-weight: 600;
}

.wgt-dot {
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--green3);
    transition: background var(--dur-fast);
}

.wg-mobile-tab.active .wgt-dot {
    background: var(--green);
}

/* ─── Label jour actif ─── */
.wg-mobile-day-label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 16px 8px;
}

.wgmd-name {
    font-family: var(--font-display), sans-serif;
    font-weight: 800;
    font-size: 20px;
    color: var(--text);
    letter-spacing: var(--tracking-tight);
}

.wgmd-name.today {
    color: var(--green-text);
}

.wgmd-count {
    font-size: 11px;
    color: var(--muted);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 3px 10px;
    font-weight: 500;
}

/* ─── Liste des slots ─── */
.wg-mobile-slots {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 4px 12px 24px;
    animation: fadeIn var(--dur-base) var(--ease-out);
}

/* ─── Slot individuel — structure flexbox propre ─── */
.wg-mobile-slot {
    display: flex;
    align-items: stretch;
    background: var(--surface);
    border-radius: var(--radius-sm);
    overflow: hidden;
    cursor: pointer;
    transition: border-color var(--dur-base), box-shadow var(--dur-base), transform var(--dur-fast);
    min-height: 72px;
}

.wg-mobile-slot:hover {
    border-color: var(--slot-color, var(--green3));
}

.wg-mobile-slot.active {
    border-color: var(--slot-color, var(--green));
    background: color-mix(in srgb, var(--slot-color, var(--green)) 8%, var(--surface));
}

.wg-mobile-slot.past {
    color: var(--muted2);
}

/* La couleur du cours vit dans une pastille devant le code (DESIGN.md,
   lot 3) : plus de bande gauche, plus de separateurs verticaux, la case se
   lit par ses colonnes d'espace. */
.wgms-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    margin-right: 6px;
    vertical-align: 1px;
}

/* ─── Colonne heure ─── */
.wgms-time {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 3px;
    padding: 14px 4px 14px 16px;
    min-width: 78px;
    flex-shrink: 0;
}

.wgms-start {
    font-family: var(--font-display), sans-serif;
    font-size: 15px;
    font-weight: 800;
    color: var(--text);
    line-height: 1;
}

.wgms-arrow {
    font-size: 10px;
    color: var(--muted);
    line-height: 1;
}

.wgms-end {
    font-family: var(--font-display), sans-serif;
    font-size: 12px;
    font-weight: 600;
    color: var(--muted);
    line-height: 1;
}

.wgms-dur {
    display: inline-block;
    font-size: 10px;
    color: var(--muted2);
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    padding: 1px 5px;
    margin-top: 5px;
    line-height: 1.5;
}

/* ─── Corps central ─── */
.wgms-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    padding: 12px 14px;
}

.wgms-name {
    font-weight: 600;
    font-size: 13.5px;
    color: var(--text);
    line-height: 1.3;
    /* Deux lignes avant l'ellipse : sur une seule, le nom du cours se
       reduisait a deux mots a 320 px ou avec une police systeme agrandie. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
}

.wgms-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 6px;
    min-width: 0;
}

.wgms-code {
    font-family: var(--font-display), sans-serif;
    font-size: 10px;
    font-weight: 700;
    padding: 2px 7px;
    border-radius: var(--radius-xs);
    flex-shrink: 0;
    white-space: nowrap;
}

.wgms-prof {
    font-size: 11px;
    color: var(--muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.wgms-salle {
    font-size: 11px;
    color: var(--muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ─── Barre de progression EN COURS ─── */
.wgms-progress {
    height: 3px;
    background: var(--border);
    border-radius: var(--radius-pill);
    overflow: hidden;
    margin-top: 2px;
}

.wgms-progress-fill {
    height: 100%;
    border-radius: var(--radius-pill);
    transition: width 1s linear;
}

.wgms-live {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-display), sans-serif;
    font-size: 10px;
    font-weight: 600;
    color: var(--green-text);
    background: var(--green-dim);
    padding: 2px 8px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--green3);
    align-self: flex-start;
    margin-top: 2px;
}

/* ─── Crédits (droite) ─── */
.wgms-credits {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    padding: 0 16px 0 8px;
    flex-shrink: 0;
    min-width: 52px;
}

.wgms-credits span:first-child {
    font-family: var(--font-display), sans-serif;
    font-size: 18px;
    font-weight: 800;
    line-height: 1;
}

.wgms-credits span:last-child {
    font-size: 10px;
    color: var(--muted);
}

/* ─── État vide ─── */
.wg-mobile-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    padding: 56px 20px;
    color: var(--muted);
    font-size: 13px;
    text-align: center;
}

/* ─── Desktop : masquer la vue mobile ───
   Un telephone en paysage (large mais court) garde la vue jour : la grille
   de 7 colonnes n'y tenait que sur une centaine de pixels de haut. Meme
   requete que App.renderWeekGrid (WEEK_GRID_MOBILE_QUERY). */
@media (min-width: 901px), (min-width: 769px) and (min-height: 501px) {
    .wg-mobile-wrap {
        display: none;
    }
}

/* ─── Mobile — masquer la grille desktop ─── */
@media (max-width: 768px), (max-width: 900px) and (max-height: 500px) {
    #weekGridContainer>div:not(.wg-mobile-wrap) {
        display: none !important;
    }
}

/* ─── Ecran tactile : cibles de l'en-tete a 44 px ───
   Les autres boutons de la barre (menu, cloche, plein ecran) font deja
   --tap-min ; Planifier et Se connecter restaient a 34-35 px. A la souris,
   rien ne change. */
@media (pointer: coarse) {
    .topbar .topbar-planifier,
    .topbar .topbar-auth-btn,
    .sidebar-version {
        min-height: var(--tap-min);
    }

    /* Planifier sans libelle (<= 380 px) : icone seule, carre plein. */
    .topbar .topbar-planifier {
        min-width: var(--tap-min);
        justify-content: center;
    }
}
