/* =========================================================
   THÈME DE PAGE — PAR ONGLET (DM orange / DS rouge / Colles teal)
   =========================================================
   Même mécanisme que le rose des pages d'import (imports.css) ou le vert du
   planning : on ne repeint pas chaque règle, on redéfinit --accent* sur les
   conteneurs de la page. Tout ce qui consomme --accent (boutons, focus,
   filtre actif, bordures de carte au survol) change de teinte sans une
   seule règle supplémentaire.
   Demande explicite (2026-08-26) : chaque onglet de /devoirs/ a son propre
   thème, comme /colles/ (bleu, colles.css) — DM garde l'orange historique,
   DS devient rouge (--kind-ds), Programmes de colles reprend le bleu de
   colles.css via body.colles-ctx-active (nav.css) qui prend le dessus quand
   kind=colle (cf. base.html), Colles (kind=colle_notee, 2026-08-27) est teal
   via body.colle-notee-ctx-active. D'où le `:not(...)` : sur ces deux
   onglets, #doc-devoirs-root ne doit PAS recevoir l'orange DM par défaut. */
.devoirs-ctx-bar,
body:not(.colles-ctx-active):not(.colle-notee-ctx-active) #doc-devoirs-root,
body:not(.colles-ctx-active):not(.colle-notee-ctx-active):has(#doc-devoirs-root) .doc-page-sidebar,
body:not(.colles-ctx-active):not(.colle-notee-ctx-active):has(#doc-devoirs-root) .doc-header {
    /* --kind-devoirs-text (pas --kind-devoirs) : demande explicite, la même
       teinte avivée que le nom "Devoirs" du nav menu (#ff9a4d) — l'orange
       brut (--kind-devoirs, #f97316) jugé trop terne pour l'accent de toute
       la page. */
    --accent:        var(--kind-devoirs-text);
    --accent-strong: color-mix(in srgb, var(--kind-devoirs-text) 82%, black);
    --accent-soft:   color-mix(in srgb, var(--kind-devoirs-text) 14%, transparent);
    --accent-dim:    color-mix(in srgb, var(--kind-devoirs-text) 8%, transparent);
    --shadow-glow:   0 4px 20px color-mix(in srgb, var(--kind-devoirs-text) 22%, transparent);
}

/* Rouge DS dédié : --danger (#d9534f) est trop proche de l'orange DM
   (#f97316, même famille "brique") pour distinguer les deux d'un coup
   d'œil dans la sidebar/les cartes — cf. demande. Un rouge plus franc,
   tirant vers le magenta plutôt que l'orange, sépare mieux les deux.
   Variables gardées pour compat (badges DS déjà écrits contre ces noms). */
.devoirs-ctx-bar,
#doc-devoirs-root {
    --devoirs-ds:    var(--kind-ds);
    --devoirs-ds-bg: rgba(224, 38, 77, 0.14);
}

/* Onglet DS actif (body.devoirs-ds-ctx-active, cf. base.html) : accent de
   toute la page en rouge --kind-ds au lieu de l'orange DM par défaut. */
body.devoirs-ds-ctx-active #doc-devoirs-root,
body.devoirs-ds-ctx-active:has(#doc-devoirs-root) .doc-page-sidebar,
body.devoirs-ds-ctx-active:has(#doc-devoirs-root) .doc-header {
    --accent:        var(--kind-ds);
    --accent-strong: color-mix(in srgb, var(--kind-ds) 82%, black);
    --accent-soft:   color-mix(in srgb, var(--kind-ds) 14%, transparent);
    --accent-dim:    color-mix(in srgb, var(--kind-ds) 8%, transparent);
    --shadow-glow:   0 4px 20px color-mix(in srgb, var(--kind-ds) 22%, transparent);
}

/* Onglet Colles notées actif (body.colle-notee-ctx-active, cf. base.html) :
   accent teal --kind-colle — badge de contenu Colle et page Colles notées
   partagent désormais la même teinte (cf. demande 2026-08-27, --colles-accent
   de colles.css étant lui passé au bleu de "Programme de colles"). */
body.colle-notee-ctx-active #doc-devoirs-root,
body.colle-notee-ctx-active:has(#doc-devoirs-root) .doc-page-sidebar,
body.colle-notee-ctx-active:has(#doc-devoirs-root) .doc-header {
    --accent:        var(--kind-colle);
    --accent-strong: color-mix(in srgb, var(--kind-colle) 82%, black);
    --accent-soft:   color-mix(in srgb, var(--kind-colle) 14%, transparent);
    --accent-dim:    color-mix(in srgb, var(--kind-colle) 8%, transparent);
    --shadow-glow:   0 4px 20px color-mix(in srgb, var(--kind-colle) 22%, transparent);
}

/* Σ, onglet nav et fil d'Ariane : pilotés par --page-accent, déclaré une
   seule fois pour cette page dans nav.css. */

/* =========================================================
   THOMATHS — PAGE DEVOIRS (DM / DS / Colles)
   Refonte DA 2026-08-17 : même langage que Cours & TD — context bar
   pleine largeur (fil d'Ariane + onglets Type + Niveau/Recherche) et
   liste de lignes `.doc-resource-row` (doc.css) plutôt qu'un tableau.
   `.devoirs-table` reste néanmoins utilisé tel quel ailleurs
   (exercises/orphans.html, admin_panel.html) : ne pas le supprimer.
   Consomme uniquement les tokens (tokens.css).
   ========================================================= */

/* ---------- Context bar : onglets Type, Niveau, recherche ---------- */
/* .devoirs-breadcrumb-current : supprimée, redondante avec --page-accent. */

.devoirs-kind-tabs { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
.devoirs-kind-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border-radius: 999px;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-muted);
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.12s, color 0.12s;
}
.devoirs-kind-tab:hover { background: var(--surface-2); color: var(--text); }
.devoirs-kind-tab.active { background: var(--accent); color: var(--on-accent); }
.devoirs-kind-tab-count { font-size: 0.72rem; opacity: 0.7; }
.devoirs-kind-tab.active .devoirs-kind-tab-count { opacity: 0.9; }

/* Pas de sidebar sur cette page (doc_sidebar_wrap vidé) : même raison que
   .courstd-ctx-bar .ctx-bar-extra, colle au bord du contenu (24px) plutôt
   qu'au rail générique de 49px pensé pour les pages avec sidebar. */
.devoirs-ctx-bar .ctx-bar-extra { right: 24px; }
.devoirs-niveau-toggle .filiere-btn { padding: 5px 12px; font-size: 0.82rem; }

@media (max-width: 980px) {
    .devoirs-ctx-bar .ctx-bar-center {
        position: static;
        transform: none;
        margin: 8px auto 0;
        max-width: 100%;
        overflow-x: auto;
    }
}

/* ---------- Liste des documents (lignes, réutilise .doc-resource-row) ----------
   L'en-tête Σ (.doc-sticky-top) est réaffiché : demande explicite pour ne
   pas tomber directement sur la liste sous la context bar.
   2026-08-21 : agrandi (900px → 1240px) avec le passage des filtres en
   sidebar, mais PAS "toute la largeur restante" (essayé, rejeté : les
   cartes s'étiraient bord à bord, boutons collés au bord de l'écran) —
   un plafond généreux mais fini, comme les autres pages "document". */
main:has(#doc-devoirs-root) {
    max-width: 1240px;
}

/* Marge droite fixe de 424px (= 400px de .exo-info-panel + 24px d'air) :
   réservée en PERMANENCE, panneau ouvert ou pas, pour que la position/largeur
   du bloc ne bouge jamais quand on ouvre le panneau (demande explicite
   2026-08-24, "c'est calculé à l'avance pour fit" — le panneau est en
   position:fixed par-dessus, donc rien ne le pousse dynamiquement, cf.
   .exo-info-panel dans exercise_card.css). margin-left:auto absorbe le reste
   de l'espace ; le bloc n'est donc plus centré mais collé contre la marge
   droite, ce qui est assumé ("pas grave si ça n'est plus tout à fait
   centré"). Scopé à #doc-devoirs-root (pas à .doc-page-scroll générique dans
   doc.css) : sheet_detail.html réutilise aussi .doc-page-scroll pour son
   propre centrage et n'a rien à voir avec ce panneau (constaté 2026-08-24 :
   décalait la zone PDF de l'éditeur de feuille).
   Essai 2026-08-24 de centrage symétrique (boîte 1240+424 centrée sur la
   fenêtre via margin:auto, gutter creusé en padding-right) : rejeté après
   test — l'effet dépend trop de la largeur d'écran (améliore sur petit
   écran, empire sur grand écran en poussant le contenu à gauche du centre).
   Retour à cette version. */
main:has(#doc-devoirs-root) {
    margin: 0 424px 0 auto;
}
@media (max-width: 860px) {
    /* Sous 860px, .exo-info-panel passe déjà en pleine largeur (width:
       min(400px, 100vw)) donc réserver 424px en continu écraserait le
       contenu pour rien : la position fixe cesse d'avoir un intérêt sur
       petit écran, retour au centrage classique. */
    main:has(#doc-devoirs-root) { margin: 0 auto; }
}

/* padding-top (pas margin-top) : le séparateur vertical juste en dessous
   (border-right) doit rester flush avec la context bar — un margin-top
   décalerait la boîte entière et laisserait un blanc au-dessus du trait. */
body:has(#doc-devoirs-root) .doc-page-sidebar {
    padding-top: 20px;
    /* Séparateur vertical unique sidebar/corps, au lieu du contour autour de
       chaque bloc (.doc-sidebar-card) — cf. demande. */
    border-right: 1px solid var(--border-soft);
    padding-right: 20px;
}
/* Contour retiré : les blocs (Type, Filtres) redeviennent de simples zones
   sans fond ni bordure propre, seul le séparateur ci-dessus délimite la
   colonne. */
body:has(#doc-devoirs-root) .doc-sidebar-card {
    border: none;
    background: none;
    padding: 0;
}

.devoirs-doc-row { cursor: pointer; gap: 14px; }
.devoirs-doc-kind { flex: 0 0 auto; }
.devoirs-doc-title {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.92rem;
    font-weight: 500;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: color 0.12s;
}
.devoirs-doc-row:hover .devoirs-doc-title { color: #fff; }

/* .exo-info-btn (exercise_card.css) est pensé pour flotter en position:
   absolute sur une carte/bloc (.exercise-card, .doc-block) et rester
   invisible hors survol — ici c'est un item de flex normal au fil de la
   ligne (comme .doc-resource-arrow) : on neutralise position/pointer-events
   (plutôt que de dupliquer un bouton) mais on GARDE le masquage hors survol
   (opacity 0 par défaut, révélé au survol/focus de la ligne), comme les
   autres icônes ⓘ/favori du site (demande explicite). */
.devoirs-doc-row .exo-info-btn {
    position: static;
    flex: 0 0 auto;
    opacity: 0;
    pointer-events: none;
}
.devoirs-doc-row:hover .exo-info-btn,
.devoirs-doc-row:focus-within .exo-info-btn {
    opacity: 1;
    pointer-events: auto;
}

/* =========================================================
   REFONTE 2026-08-21 (v2) — Filtres passés dans la sidebar gauche (à côté du
   menu Type), colonne verticale plutôt que barre horizontale au-dessus du
   corps : le corps (liste de cartes) doit rester la zone la plus large de
   la page, cf. demande.
   ========================================================= */
.devoirs-filters-side { display: flex; flex-direction: column; gap: 12px; }
.devoirs-filter-search {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 7px 14px;
}
.devoirs-filter-search svg { flex: 0 0 auto; width: 16px; height: 16px; color: var(--text-faint); }
.devoirs-filter-search input {
    flex: 1;
    min-width: 0;
    background: none;
    border: none;
    color: var(--text);
    font-size: 0.85rem;
}
.devoirs-filter-search input:focus { outline: none; }

.devoirs-filter-group { display: flex; flex-direction: column; gap: 5px; }
.devoirs-filter-group-label { font-size: 0.72rem; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.04em; }
.devoirs-filter-group select {
    width: 100%;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    padding: 7px 10px;
    font-size: 0.85rem;
}
.devoirs-filter-group select:focus { outline: none; border-color: var(--accent); }
.devoirs-filter-group .devoirs-niveau-toggle { width: 100%; }
.devoirs-filter-group .filiere-btn { flex: 1; text-align: center; }

.devoirs-table-wrap {
    overflow-x: auto;
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
}
.devoirs-table {
    width: 100%;
    min-width: 760px;
    border-collapse: collapse;
    background: var(--surface);
}
.devoirs-table th {
    text-align: left;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #fff;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-soft);
}
.devoirs-table td {
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-soft);
    font-size: 0.88rem;
    color: var(--text-muted);
    vertical-align: middle;
    transition: background 0.12s;
}
.devoirs-table tr:last-child td { border-bottom: none; }
.devoirs-table tr:hover td { background: var(--surface-2); }

/* Le champ date ne vit plus que dans le panneau info (ⓘ, admin-only) : plus
   besoin de bascule mode-admin, il est toujours affiché tel quel. */
.devoirs-date-input {
    display: inline-block;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    padding: 4px 6px;
    font-size: 0.8rem;
}

.devoirs-pagination { display: flex; gap: 4px; margin-top: 16px; justify-content: flex-end; }
.devoirs-pagination a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 30px; height: 30px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-soft);
    color: var(--text-muted);
    text-decoration: none;
    font-size: 0.82rem;
    padding: 0 8px;
}
.devoirs-pagination a:hover { border-color: var(--border); color: var(--text); }
.devoirs-pagination a.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }


/* =========================================================
   SIDEBAR — MENU DE TYPE (DM / DS / Colles)
   =========================================================
   2026-08-21 (v3) — style épuré demandé : plus de carte encadrée ni de fond
   coloré derrière le logo (le PNG a déjà sa propre couleur), juste une ligne
   de menu discrète qui ne se colore qu'au survol/sélection. Compteur de
   documents retiré (pas nécessaire ici, cf. demande).
   ========================================================= */
.devoirs-stats { display: flex; flex-direction: column; gap: 2px; margin-bottom: 0; }
.devoirs-stat {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    text-decoration: none;
    transition: background 0.12s;
}
.devoirs-stat:hover { background: var(--surface-2); }
/* Survol thématique : chaque type reprend la teinte de son état actif au
   survol plutôt que le gris générique ci-dessus (specificite egale, mais ces
   regles arrivent apres donc l'emportent). */
.devoirs-stat-dm:hover     { background: var(--content-exercise-bg); }
.devoirs-stat-ds:hover     { background: var(--devoirs-ds-bg); }
.devoirs-stat-colle:hover  { background: var(--kind-colle-bg); }
.devoirs-stat-programme-colle:hover { background: var(--kind-programme-colle-bg); }
.devoirs-stat-badge {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.devoirs-stat-icon { width: 34px; height: 34px; object-fit: contain; }
.devoirs-stat-label { font-size: 0.9rem; font-weight: 500; }

/* Couleur de type (DM orange, DS rouge, Colle teal) appliquée au libellé en
   permanence, pas seulement sélectionné (cf. demande) — mêmes tokens que les
   badges de carte (--content-exercise, --danger, --kind-colle), cohérent
   avec le reste du site (sh-kind-*, sheet_card.css). Le fond ne se colore
   lui qu'à la sélection, pour que l'entrée active reste repérable.
   DM et DS : le libellé est ICI recalé sur la couleur RÉELLE du PNG
   (icon_dm.png/icon_ds.png, mesurée au pixel) plutôt que sur le token
   générique --content-exercise/--devoirs-ds — les deux teintes divergeaient
   visiblement côte à côte (orange plus vif/rouge plus rose côté texte que
   côté logo). Colle non touché : --kind-colle colle déjà de très près à
   icon_colle.png. */
.devoirs-stat-dm    .devoirs-stat-label { color: #df8909; }
.devoirs-stat-ds    .devoirs-stat-label { color: #d22e2e; }
.devoirs-stat-colle .devoirs-stat-label { color: var(--kind-colle); }
/* Programmes de colles (page/nav) : bleu dedie, distinct du badge de contenu
   Colle (teal, --kind-colle) desormais reserve aux colles notees. */
.devoirs-stat-programme-colle .devoirs-stat-badge svg { width: 34px; height: 34px; color: var(--kind-programme-colle); }
.devoirs-stat-programme-colle .devoirs-stat-label { color: var(--kind-programme-colle); }
.devoirs-stat.active .devoirs-stat-label { font-weight: 700; }
.devoirs-stat-dm.active    { background: var(--content-exercise-bg); }
.devoirs-stat-ds.active    { background: var(--devoirs-ds-bg); }
.devoirs-stat-colle.active { background: var(--kind-colle-bg); }
.devoirs-stat-programme-colle.active { background: var(--kind-programme-colle-bg); }

.devoirs-new-btn { display: block; width: 100%; margin-top: 12px; text-align: center; }

/* "+ Créer un devoir" (.button-outline générique, cf. theme.css) recoloré
   selon le kind affiché — même principe que colles.css pour "+ Nouveau
   programme" (bouton partagé DM/DS, pas de variante par page dans theme.css). */
.devoirs-ctx-bar[data-kind="dm"] .button-outline { color: #df8909; border-color: #df8909; }
.devoirs-ctx-bar[data-kind="dm"] .button-outline:hover { background: var(--content-exercise-bg); }
.devoirs-ctx-bar[data-kind="ds"] .button-outline { color: #d22e2e; border-color: #d22e2e; }
.devoirs-ctx-bar[data-kind="ds"] .button-outline:hover { background: var(--devoirs-ds-bg); }
.devoirs-ctx-bar[data-kind="prog_colles"] .button-outline { color: var(--kind-programme-colle); border-color: var(--kind-programme-colle); }
.devoirs-ctx-bar[data-kind="prog_colles"] .button-outline:hover { background: var(--kind-programme-colle-bg); }
.devoirs-ctx-bar[data-kind="colles"] .button-outline { color: var(--kind-colle); border-color: var(--kind-colle); }
.devoirs-ctx-bar[data-kind="colles"] .button-outline:hover { background: var(--kind-colle-bg); }

/* =========================================================
   REFONTE 2026-08-21 — CARTES DM/DS
   Remplace .devoirs-doc-row (ligne compacte) pour l'onglet DM/DS : une carte
   par document, avec les actions Sujet/Correction toujours visibles (pas
   besoin du panneau info pour les consulter, cf. demande).
   ========================================================= */
.devoirs-section-title {
    font-family: var(--font-head);
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--text);
    margin: 4px 0 16px;
}

/* Groupe repliable (thème OU année selon le mode, cf. demande) — <details>
   natif, même mécanique que .doc-chapter (doc.css). */
.devoirs-group { margin: 22px 0 0; }
.devoirs-group:first-of-type { margin-top: 0; }
.devoirs-group-head {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    list-style: none;
    user-select: none;
    outline: none;
    padding: 4px 0;
    margin: 0 0 8px;
}
.devoirs-group-head::-webkit-details-marker { display: none; }
/* Chevron assez visible (cf. demande) : plus contrasté et plus épais que
   celui de .doc-chapter, qui reste discret dans une sidebar déjà chargée. */
.devoirs-group-chevron {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    transition: transform 0.15s, color 0.12s;
    flex: 0 0 auto;
}
.devoirs-group-chevron svg {
    width: 20px;
    height: 20px;
    display: block;
    /* Le path n'a pas de fill (juste un stroke) : sans ça, un clic dans le
       "creux" du chevron ne touche aucune zone peinte de l'icône et le
       navigateur peut le laisser filer au lieu de le faire remonter
       proprement vers <summary> (cf. demande — clic peu fiable près du
       titre/chevron). pointer-events: none force le clic à toujours
       atterrir sur son parent .devoirs-group-chevron/summary. */
    pointer-events: none;
}
.devoirs-group-head:hover .devoirs-group-chevron { color: var(--accent); }
.devoirs-group[open] > .devoirs-group-head .devoirs-group-chevron { transform: rotate(90deg); }

.devoirs-group-title {
    font-size: 0.82rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-primary);
    margin: 0;
}

/* Sous-titre listant les chapitres d'un ChapterGroup, sous le nom du groupe
   (mode Thématique uniquement, cf. group.chapter_titles). */
.devoirs-group-chapters {
    font-size: 0.78rem;
    color: var(--text-faint);
    margin: -4px 0 8px 28px;
}

/* Bascule Historique / Thématique dans la ctx-bar (prof only). */
.devoirs-view-toggle {
    display: flex;
    background: var(--surface-2);
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    padding: 2px;
    gap: 2px;
}
.devoirs-view-toggle-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border-radius: 6px;
    font-size: 0.82rem;
    font-weight: 600;
    /* text-muted plutôt que text-faint (trop peu contrasté, cf. capture
       fournie : "Thématique" à peine lisible au repos) — l'onglet inactif
       doit rester clairement identifiable comme cliquable. */
    color: var(--text-muted);
    white-space: nowrap;
    cursor: pointer;
    /* Ce sont des <a> (navigation par URL, ?view=...) : sans ce reset,
       l'UA garde son soulignement par défaut, jamais voulu ici — le
       fond/la couleur au survol suffisent déjà comme affordance. */
    text-decoration: none;
    transition: color .12s ease, background-color .12s ease;
}
.devoirs-view-toggle-btn svg { width: 15px; height: 15px; }
/* Fond discret au survol (pas juste un changement de couleur de texte) :
   affordance plus claire que c'est cliquable, cohérent avec le style
   segmented-control du groupe. */
.devoirs-view-toggle-btn:hover { color: var(--text); background: var(--surface-3); }
.devoirs-view-toggle-btn.active { background: var(--accent); color: var(--on-accent); }
.devoirs-view-toggle-btn.active:hover { background: var(--accent); color: var(--on-accent); }
/* Onglet actif teinté selon le type affiché (logo DM/DS) plutôt que
   l'orange générique de la page — demande explicite : DS doit se
   distinguer visuellement, pas juste hériter du thème DM. */
.devoirs-view-toggle[data-kind="ds"] .devoirs-view-toggle-btn.active,
.devoirs-view-toggle[data-kind="ds"] .devoirs-view-toggle-btn.active:hover {
    background: var(--devoirs-ds);
    color: #fff;
}

/* Liste plus épurée : simples lignes séparées par un filet, pas de carte
   encadrée individuelle (moins de bruit visuel, plus "pro"). */
.devoirs-card-list {
    display: flex;
    flex-direction: column;
    border-top: 1px solid var(--border-soft);
}
/* padding vertical retiré d'ici et déplacé sur chaque enfant (badge/body/
   actions) : un padding porté par .devoirs-card laisse un blanc de 14px en
   haut/bas AVANT la zone stretchée par les enfants (le stretch ne remplit
   que la content-box du flex container, pas son padding) — les bordures
   droites de body/actions s'arrêtaient donc avant le bord réel de la ligne
   et ne se raccordaient pas d'une ligne à l'autre. En portant le padding
   sur les enfants eux-mêmes, leur propre border-box (padding inclus) va
   bien de bord à bord de la ligne : les traits se touchent. */
.devoirs-card {
    position: relative;
    display: flex;
    align-items: stretch;
    gap: 16px;
    /* padding-right retiré (cf. demande "ça dépend toujours un chouia") :
       .devoirs-card-body porte déjà tout l'espacement droit dont il a
       besoin (34px pour le ⓘ flottant) — un padding ici en plus décalait
       son border-right de quelques px par rapport au bord réel de la
       carte, désaligné avec le border-bottom pleine largeur. */
    border-bottom: 1px solid var(--border-soft);
    border-left: 3px solid transparent;
    transition: background 0.12s;
    /* Carte cliquable (navigation, cf. [data-href] ci-dessous) : sans ça,
       un double-clic n'importe où dedans — même dans une zone vide comme un
       gap flex ou un padding — sélectionne le mot le plus proche (le
       navigateur ignore les zones visuellement vides, seul compte le texte
       le plus proche dans le flux). Demande 2026-08-24. */
    user-select: none;
}
.devoirs-card:hover { background: var(--surface-2); }
.devoirs-card[data-href] { cursor: pointer; }

/* Liseré de couleur de type (DM orange, DS rouge) EN PERMANENCE, pas
   seulement au survol — cohérent avec la couleur du logo, du libellé
   sidebar et du bouton Correction. Spécificité doublée (.devoirs-card en
   plus de .devoirs-card-dm/-ds) pour ne jamais pouvoir être repassé
   transparent par la règle de base ci-dessus, quel que soit l'ordre des
   règles dans le fichier. */
.devoirs-card.devoirs-card-dm { border-left-color: var(--content-exercise); }
.devoirs-card.devoirs-card-ds { border-left-color: var(--devoirs-ds); }

/* Pas de fond coloré derrière le logo (épuré, cf. demande) : le PNG porte
   déjà sa propre couleur, un carré de fond ne fait que l'alourdir. */
.devoirs-card-badge {
    flex: 0 0 auto;
    display: flex;
    /* flex-start (pas center) + padding-top fixe : le logo doit rester à un
       y FIXE, qu'il soit dans une carte repliée (~104px) ou dépliée (plus
       haute) — .devoirs-card est en align-items: stretch, donc cette boîte
       (elle) s'étire bien sur toute la hauteur de la ligne (colonne
       "orange" pleine hauteur, cf. demande 2026-08-24 "la zone orange
       devrait être étendue sur l'espace ajouté" — avec un align-self:
       flex-start ici à la place, la boîte s'arrêtait à la hauteur du logo
       et laissait un vide en dessous une fois la carte agrandie), mais SON
       CONTENU (le logo) ne doit pas se recentrer dynamiquement dedans à
       chaque agrandissement (cf. demande précédente, "ça ne doit pas être
       dynamique") — d'où align-items: flex-start + padding-top fixe
       plutôt qu'un centrage recalculé. */
    align-items: flex-start;
    justify-content: center;
    /* Padding élargi (+ icône agrandie ci-dessous) : la ligne a gagné en
       hauteur (séparateur + espacements, cf. demande "ça respire"), un
       logo resté à 38px avait l'air écrasé/riquiqui dedans (cf. demande).
       padding-top: 25px (mesuré) : reproduit la position qu'avait le logo
       quand il était centré dynamiquement dans une carte repliée
       (~104px de haut, logo de 52px → (104-52)/2 ≈ 26px du haut). */
    padding: 25px 14px 20px 22px;
    box-sizing: border-box;
}
.devoirs-card-badge .sh-kind-icon { width: 52px; height: 52px; }

.devoirs-card-body {
    /* position: relative : sert de containing block à .devoirs-card-chapters
       ::after (cf. devoirs.css plus bas, zone de hover étendue jusqu'au bas
       réel de la carte) — cette boîte-ci couvre déjà toute la hauteur de la
       carte via align-self: stretch ci-dessous, donc bottom: 0 dessus tombe
       pile sur le bord bas réel. */
    position: relative;
    /* z-index: 0 (pas juste position: relative) : force un VRAI contexte
       d'empilement local — position: relative seule, avec z-index: auto par
       défaut, n'en crée PAS un (règle CSS précise, oubliée au premier essai
       "tu as cassé tout le hover" du 2026-08-24). Sans lui, le z-index: -1
       de .devoirs-card-chapters::after (cf. plus bas) remonte chercher le
       contexte d'empilement le plus proche bien plus haut dans l'arbre —
       potentiellement sous TOUT le contenu de la page — au lieu de rester
       juste sous le contenu de CETTE carte. */
    z-index: 0;
    flex: 1 1 auto;
    /* stretch (pas center) : la carte respire sur toute sa hauteur plutôt
       que de tasser titre/séparateur/infos au milieu (cf. demande "ça ne
       respire pas assez"). */
    align-self: stretch;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 8px;
    /* padding-bottom: 33px (pas 10px comme en haut) : .devoirs-card-chapters
       est maintenant position: absolute (cf. plus bas, pour un centrage
       vertical automatique via flexbox) donc SORTI du flux — sans réserve
       explicite ici, .devoirs-card-body redevient plus courte que le badge
       (92px) et la carte entière rétrécit trop pour laisser la place à la
       ligne chapitres (constaté : chevauchement avec la ligne date). Ce
       padding-bottom recrée artificiellement l'espace qu'occupait avant la
       ligne chapitres dans le flux, pour retrouver la même hauteur de carte
       qu'avant (~111px) — asymétrique par nécessité, mais la ligne
       chapitres elle-même reste centrée DANS cet espace réservé grâce au
       flexbox de .devoirs-card-chapters. */
    /* 36px (pas 33px) : +3px pour laisser un peu d'air sous les pastilles
       repliées avant le bord bas réel de la carte (cf. demande 2026-08-24,
       "les tags sont trop collés au bas") — à ajuster de concert avec le
       padding-bottom de .devoirs-card-chapters.expanded ci-dessous (même
       espace visuel visé dans les deux modes). */
    padding: 10px 16px 36px 0;
}
.devoirs-card-title-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.devoirs-card-title { min-width: 0; font-size: 0.95rem; font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Filet horizontal sous le titre (cf. mockup fourni) : sépare le titre/badge
   année de la ligne date + de la ligne chapitres. */
.devoirs-card-sep { width: 100%; height: 0; margin: 0; border: none; border-top: 1px solid var(--border-soft); }

/* Non publié = invisible aux élèves : déjà signalé par l'atténuation de
   toute la ligne (.devoirs-card-unpublished ci-dessous) et le title du
   titre, pas besoin d'un badge texte en plus (cf. demande). */
/* Année scolaire (mode Thématique uniquement, cf. devoirs.py::_school_year_label
   + demande 2026-08-21) : bi-année déduite de la dernière date d'assignation,
   poussée à droite du titre via margin-left auto. */
.devoirs-card-year-badge {
    flex: 0 0 auto;
    margin-left: auto;
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px solid var(--border-soft);
    border-radius: 999px;
    padding: 3px 10px;
}
/* Repère discret sur une carte qui n'est PAS la première assignation d'un
   DM/DS (cf. demande 2026-08-22 : historique = une carte par assignation,
   dupliquant le devoir s'il a été redonné plusieurs fois). */
.devoirs-card-occurrence-badge {
    flex: 0 0 auto;
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--text-faint);
    background: var(--surface-2);
    border-radius: 999px;
    padding: 1px 7px;
}
/* Ligne "Classe cible" + bouton "+ Nouvelle classe" de la modale Assigner
   (cf. demande 2026-08-22) : même form-label, poussé à gauche, bouton
   discret aligné à droite plutôt qu'un gros bouton plein qui alourdirait
   une modale déjà dense. */
.form-label-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin: 12px 0 6px;
}
.form-label-row .form-label { margin: 0; }
.button-link {
    background: none;
    border: none;
    padding: 0;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--accent);
    cursor: pointer;
}
.button-link:hover { text-decoration: underline; }
.devoirs-new-classe-error {
    color: var(--danger);
    font-size: 0.82rem;
    margin: 8px 0 0;
}
.devoirs-card-unpublished { opacity: 0.55; }
.devoirs-card-unpublished:hover { opacity: 0.85; }
.devoirs-card-meta { display: flex; align-items: center; gap: 6px; font-size: 0.78rem; color: var(--text-muted); }
.devoirs-card-meta svg { width: 14px; height: 14px; flex: 0 0 auto; color: var(--text-faint); }
/* Petites icônes (calendrier/livre) teintées comme le logo principal —
   texte laissé neutre (cf. demande). */
.devoirs-card-dm .devoirs-card-meta svg { color: var(--content-exercise); }
.devoirs-card-ds .devoirs-card-meta svg { color: var(--devoirs-ds); }
/* Anciens DS (PDF d'origine, cf. services/legacy_ds.py) : liens Sujet/Corrigé. */
.devoirs-card-legacy .devoirs-card-meta a { color: var(--text-muted); text-decoration: none; }
.devoirs-card-legacy .devoirs-card-meta a:hover { color: var(--devoirs-ds); text-decoration: underline; }

/* Chapitres en pills sous la date (icône livre), commun aux deux modes
   d'affichage — remplace l'ancienne liste "· Chap1 · Chap2" en texte plat. */
/* Une seule ligne visible (demande 2026-08-23) : au-delà, un badge "+N"
   plutôt qu'un retour à la ligne — posé dynamiquement par
   updateChapterPillsClamp (cf. devoirs.js), qui masque les pastilles en
   trop après mesure réelle (largeurs de titres variables, pas de seuil
   fixe). max-height ci-dessous n'est qu'un filet de sécurité contre un
   flash 2 lignes avant l'exécution du JS. */
/* Zone de hover/clic étendue, ET boîte visible, ne font plus qu'UN seul
   élément (contrairement aux essais précédents avec un ::after séparé, ou
   un flex-grow qui regonflait la hauteur de la carte) : .devoirs-card-
   chapters est directement positionné en absolu, calé sur la zone voulue
   (juste sous la ligne date, jusqu'au bord bas réel de la carte, du logo à
   la colonne Corriger/chevron) — SANS toucher au flux normal de
   .devoirs-card-body (donc sans effet sur la hauteur de la carte). Comme
   c'est un vrai flex container (display:flex + align-items:center), son
   contenu (icône + pastilles) se centre AUTOMATIQUEMENT dans cette boîte,
   quelle que soit sa hauteur exacte — plus besoin de calculer un margin-top
   au pixel près pour équilibrer l'espace au-dessus/en-dessous. */
.devoirs-card-chapters {
    display: flex;
    /* flex-start (pas center) : la position de l'icône/1ʳᵉ ligne dépend
       ainsi UNIQUEMENT de padding-top (8px, identique en mode déplié
       ci-dessous), jamais de la hauteur totale de la boîte — sinon,
       repliée elle se centre sur toute la hauteur restante de la carte
       (top: 74 à bottom: 0, potentiellement énorme) tandis que dépliée
       (position: static, hauteur = contenu) elle se centre sur une hauteur
       toute différente : deux calculs de centrage distincts qui ne
       tombaient jamais pile au même pixel, d'où le sursaut au clic
       constaté 2026-08-24 ("ça descend un peu"). Avec flex-start partout
       et le même padding-top des deux côtés, la 1ʳᵉ ligne reste
       à un pixel identique, repliée ou dépliée. */
    align-items: flex-start;
    /* PAS de flex-wrap ici : l'icône livre et .devoirs-card-chapter-pills
       (cf. plus bas) sont les deux SEULS enfants directs, et ne doivent
       jamais eux-mêmes se retrouver sur deux lignes distinctes — c'est le
       groupe de pastilles, à l'intérieur, qui wrap (avec son propre
       flex-wrap). Avec un wrap ici en plus, icône+groupe finissaient
       parfois cote à cote SUR DEUX LIGNES dès que le groupe approchait la
       largeur du conteneur (bug constaté 2026-08-24 : l'icône se
       retrouvait au-dessus des pastilles au lieu d'à côté). */
    gap: 6px;
    position: absolute;
    /* top mesuré : juste sous la ligne date/meta — DOIT tomber exactement
       sur le même y que le calage du mode déplié ci-dessous (metaBottom,
       via son margin-top négatif), sans quoi la 1ʳᵉ ligne saute au clic
       (constaté 2026-08-24 : un ancien 74px, calé à une époque où le
       padding-top de .devoirs-card-body différait, retombait 4px plus bas
       que metaBottom réel). Revérifier ce chiffre si la ligne date/meta
       change de hauteur. */
    top: 70px;
    bottom: 0;
    /* 16px de chaque côté : traverse le gap du flex .devoirs-card jusqu'au
       logo (gauche) et jusqu'à .devoirs-card-side (droite) — mesuré,
       .devoirs-card-body en est le containing block direct (position:
       relative, cf. plus haut). */
    left: -16px;
    right: -16px;
    overflow: hidden;
    box-sizing: border-box;
    /* padding-left: 16px (= left: -16px ci-dessus, annulé) : remet l'icône
       livre à l'aplomb de l'icône calendrier de la ligne date au-dessus
       (.devoirs-card-meta, flush avec .devoirs-card-body) — le bleed à
       gauche ne sert qu'à agrandir la zone de hover jusqu'au logo, pas à
       décaler le contenu visible. padding-top: 8px (pas 0) : même valeur
       qu'en mode déplié ci-dessous, pour la raison ci-dessus (align-items). */
    padding: 8px 6px 0 16px;
    border-radius: 0;
    transition: background 0.12s;
}
/* cursor + hover de fond réservés à .has-more (cf. devoirs.js) : quand tous
   les chapitres tiennent déjà sur la ligne, il n'y a ni +N à déplier ni −
   à replier — rien à faire au clic, donc pas de curseur pointer ni de
   survol qui le laisserait croire (demande 2026-08-24). */
.devoirs-card-chapters.has-more { cursor: pointer; }
.devoirs-card-chapters.has-more:hover { background: var(--surface-3); }
/* Le badge +N/− prend son propre look ":hover" dès que c'est la LIGNE
   entière qui est survolée (zone de clic pleine largeur, cf. plus haut) —
   pas seulement quand la souris est pile dessus — pour rester cohérent avec
   le fait que cliquer n'importe où sur la ligne l'active (demande
   2026-08-24, "le +1 dans le même style que quand il est lui-même hover"). */
.devoirs-card-chapters.has-more:hover .devoirs-card-chapter-more {
    color: var(--text);
    border-color: var(--accent);
}
/* Cliqué sur le badge "+N" (cf. devoirs.js) : affiche tout, sur autant de
   lignes que nécessaire. Un second bouton "−" (même classe .devoirs-card-
   chapter-more, posé dynamiquement) permet de re-réduire.
   position: static (pas juste overflow: visible) : en absolute, la boîte
   garde la hauteur fixée par top/bottom (calée sur la carte d'origine,
   cf. plus haut) — les lignes en trop débordaient donc PAR-DESSUS le
   contenu suivant au lieu d'agrandir la carte (demande : "ça doit
   agrandir la card pour accueillir le reste"). Repasser dans le flux
   normal de .devoirs-card-body (flex column) fait grandir la carte
   comme n'importe quel autre contenu. margin négatif (au lieu de
   left/right) pour retrouver le même bleed visuel jusqu'aux bords. */
.devoirs-card-chapters.expanded {
    position: static;
    overflow: visible;
    /* margin-top négatif : .devoirs-card-body espace déjà ses enfants de
       8px (gap, cf. plus haut) — un margin-top positif ICI s'ajouterait à
       ce gap et créerait un vide EN PLUS de la zone cliquable/hoverable
       (le gap flex n'appartient à aucune boîte, donc jamais couvert par le
       :hover ci-dessous — constaté "zone non couverte + espacement en trop"
       2026-08-24). On annule le gap du parent puis on le recrée en
       padding-top (même valeur 8px que ci-dessus, pas de override
       align-items ici : les deux modes partagent la même règle de base
       désormais), À L'INTÉRIEUR de cette boîte cette fois, pour qu'il
       fasse partie de la zone survolable. */
    /* margin-right: -32px (pas -16px) : à droite, .devoirs-card-body porte
       un padding-right de 16px (contrairement à son padding-left: 0) — un
       -16px n'annule donc que ce padding et s'arrête pile au bord de
       .devoirs-card-body, SANS traverser le gap flex de 16px qui le sépare
       de .devoirs-card-side (le gap, comme tout gap flex, n'appartient à
       aucune boîte donc n'est jamais couvert par :hover). Il faut annuler
       le padding ET le gap, d'où -32px, pour retomber pile sur le bord
       gauche de .devoirs-card-side — exactement comme le mode replié
       (position: absolute + right: -16px) qui, lui, traverse ce même gap
       gratuitement car les offsets absolute se mesurent depuis le bord de
       padding du containing block, pas depuis le bord de marge (bug
       constaté 2026-08-24, "hover qui rétrécit et ne va pas jusqu'au bout"). */
    margin: -8px -32px 0 -16px;
    /* padding-bottom: 14px (pas 4px) : couvre les 10px auparavant réservés
       en padding-bottom de .devoirs-card-body (cf. règle :has() ci-dessous,
       désormais à 0) — un padding sur le PARENT après la boîte ne fait pas
       partie de la boîte elle-même, donc jamais couvert par son :hover
       (même bug que le -32px ci-dessus, cf. "pareil en bas, ça crée un
       espace non couvert par le hover" 2026-08-24). En le rapatriant DANS
       la boîte (comme padding-bottom), le vide bas devient partie de la
       zone survolable. */
    /* padding-bottom: 6px (compromis 2026-08-24, même espace visuel que le
       mode replié ci-dessus, cf. son commentaire "36px") — pas 14px, qui
       laissait trop d'air sous les pastilles une fois dépliées. */
    padding: 8px 6px 6px 16px;
}
/* padding-bottom: 0 (pas 10px) : cf. commentaire padding-bottom: 14px de
   .devoirs-card-chapters.expanded ci-dessus — l'espace bas est maintenant
   à l'intérieur de la boîte hoverable, plus la peine de le réserver ici
   (un padding ici EN PLUS créerait à nouveau un vide non couvert). */
.devoirs-card-body:has(.devoirs-card-chapters.expanded) { padding-bottom: 0; }
/* margin-top: 5px (dans les deux modes, cf. align-items: flex-start
   ci-dessus) : recentre l'icône (14px) sur la hauteur d'une pastille
   (24px) une fois calée en haut plutôt que sur toute la hauteur du bloc
   (qui, dépliée, engloberait plusieurs lignes et ferait flotter l'icône au
   milieu au lieu de la garder au niveau de la 1ʳᵉ ligne). Une seule règle
   pour les deux modes, comme align-items/padding-top ci-dessus : sinon
   l'icône saute d'1-2px au clic (constaté 2026-08-24). */
.devoirs-card-chapters svg { width: 14px; height: 14px; flex: 0 0 auto; color: var(--text-faint); margin-right: 2px; margin-top: 5px; }
.devoirs-card-dm .devoirs-card-chapters svg { color: var(--content-exercise); }
.devoirs-card-ds .devoirs-card-chapters svg { color: var(--devoirs-ds); }
/* Le groupe de pastilles (PAS l'icône) est le seul à wrapper : une ligne
   suivante repart ainsi au même x que la 1ʳᵉ pastille, jamais au x de
   l'icône (demande 2026-08-24, "aligne les tags sur la ligne d'en dessous
   par rapport au premier tag") — avec un seul flex-wrap sur tout le
   conteneur (icône incluse), chaque nouvelle ligne repart au bord gauche du
   conteneur, donc sous l'icône. */
.devoirs-card-chapter-pills {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    min-width: 0;
    flex: 1 1 auto;
    /* position: relative : fait de ce groupe l'offsetParent de ses pastilles
       (cf. devoirs.js, updateChapterPillsClamp compare leurs offsetTop pour
       détecter les retours à la ligne). Sans lui, offsetTop se mesure par
       rapport à .devoirs-card-chapters (l'ancêtre positionné le plus
       proche) — une boîte bien plus grande/centrée différemment depuis
       qu'elle contient aussi l'icône à côté (et non plus au-dessus) du
       groupe — ce qui redonnait des offsetTop aberrants et revidait la
       ligne (bug reconstaté 2026-08-24 lors de l'ajout de ce sous-groupe). */
    position: relative;
}
.devoirs-card-chapter-pill {
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--text-muted);
    background: var(--surface-2);
    border: 1px solid var(--border-soft);
    border-radius: 999px;
    padding: 2px 10px;
}
.devoirs-card-chapter-more {
    /* <button> plutôt qu'un <span> (cf. devoirs.js, guard "a, button" du
       clic carte) : reset des styles UA de bouton par-dessus le look pilule.
       Volontairement plus étroit que les pastilles de chapitre (padding
       réduit, pas de min-width) — demande explicite 2026-08-23 ("largeur
       beaucoup plus petite"), c'est juste un compteur, pas un chip de
       contenu. width:fit-content + white-space:nowrap en garde-fou contre
       tout étirement flex hérité. */
    font: inherit;
    appearance: none;
    display: inline-flex;
    width: fit-content;
    /* form.css pose `button { min-width: 120px }` en global : sans l'annuler
       ici, min-width gagne toujours sur width et le badge reste collé à
       120px quel que soit son contenu (bug constaté 2026-08-24, "+4" à
       120px de large). */
    min-width: 0;
    white-space: nowrap;
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--text-faint);
    background: var(--surface-2);
    border: 1px solid var(--border-soft);
    border-radius: 999px;
    padding: 2px 7px;
    line-height: 1.3;
    flex: 0 0 auto;
    cursor: pointer;
}
.devoirs-card-chapter-more:hover { color: var(--text); border-color: var(--accent); }

/* ---------- Progression de correction + chevron (cf. demande, mockup) ----------
   Colonne dédiée à l'extrême droite de la carte (le filet vertical est ICI,
   sur .devoirs-card-side, pas plus tôt) : contrairement au ⓘ, chevron ET
   progression restent visibles en permanence (pas juste au survol),
   l'avancement de correction étant une info utile au premier coup d'œil. */
.devoirs-card-side {
    flex: 0 0 auto;
    align-self: stretch;
    display: flex;
    align-items: center;
    /* Pas de gap ni de padding horizontal : .devoirs-card-progress-link et
       .devoirs-card-chevron débordent chacun jusqu'à leur bord respectif et
       se touchent directement (cf. leurs marges négatives plus bas) — sinon
       l'espace entre les deux zones de hover redevient une bande morte où
       survoler déclenche le hover de .devoirs-card-side entier (cf. demande
       "je veux ni l'espace ni le fait de tout hover"). */
    gap: 0;
    padding: 16px 0;
    border-right: 1px solid var(--border-soft);
    transition: background 0.12s;
}
/* Mode admin uniquement (demande 2026-08-23) : cette zone a son propre
   comportement de clic (ouvre le panneau info, cf. devoirs.js) alors que le
   reste de la carte ouvre la fiche — un hover distinct de celui de toute la
   carte (.devoirs-card:hover ci-dessous) lui donne un repère visuel propre.
   Absent en mode élève : là, toute la carte se comporte pareil (ouvre le
   PDF), pas de zone à distinguer. */
body.mode-admin .devoirs-card-side:hover { background: var(--surface-3); }
/* Survoler la zone "Corriger" (.devoirs-card-progress-link) OU le chevron
   ne doit PAS déclencher aussi le hover de .devoirs-card-side entier (cf.
   capture "les deux surbrillances se chevauchent") : ce sont des clics
   différents (corriger vs ouvrir le panneau info), donc des zones de hover
   visuellement distinctes, chacune avec sa propre boîte (cf. plus bas).
   :has() retire le fond du parent quand l'une de ces sous-zones est
   survolée. */
body.mode-admin .devoirs-card-side:has(.devoirs-card-progress-link:hover) { background: none; }
body.mode-admin .devoirs-card-side:has(.devoirs-card-chevron:hover) { background: none; }
.devoirs-card-progress {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
}
.devoirs-card-progress-count { font-size: 1rem; font-weight: 700; white-space: nowrap; }
.devoirs-card-progress-total { font-size: 0.8rem; font-weight: 500; color: var(--text-faint); }
.devoirs-card-progress-count-low { color: #e5484d; }
.devoirs-card-progress-count-mid { color: #f5a524; }
.devoirs-card-progress-row { display: flex; align-items: center; gap: 8px; }
.devoirs-card-progress-bar {
    width: 90px;
    height: 6px;
    border-radius: 999px;
    background: var(--surface-3);
    overflow: hidden;
    flex-shrink: 0;
}
.devoirs-card-progress-fill { display: block; height: 100%; border-radius: 999px; }
.devoirs-card-progress-fill-low { background: #e5484d; }
.devoirs-card-progress-fill-mid { background: #f5a524; }
.devoirs-card-progress-pct { font-size: 0.78rem; color: var(--text-faint); white-space: nowrap; }
.devoirs-card-progress-done {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    color: #46a758;
    white-space: nowrap;
}
.devoirs-card-progress-done-row {
    font-size: 1rem;
    font-weight: 700;
}
.devoirs-card-progress-done-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.95rem;
    font-weight: 600;
}
.devoirs-card-progress-done-label svg { width: 16px; height: 16px; flex-shrink: 0; }

/* Zone "pas encore corrigé" cliquable sur toute la hauteur de la carte :
   déborde du padding de .devoirs-card-side (marges négatives) pour occuper
   toute la bande verticale, hover propre pour se distinguer du reste de
   .devoirs-card-side (qui ouvre le panneau info). */
.devoirs-card-progress-link {
    align-self: stretch;
    justify-content: center;
    margin: -16px 0 -16px 0;
    padding: 16px 10px 16px 16px;
    /* Coins droits (demande) : cette boîte touche le bord haut/bas de la
       carte (marge négative ci-dessus), un arrondi y créait un angle bizarre
       contre le border-bottom/border-right droits de la carte. */
    border-radius: 0;
    text-decoration: none;
    color: inherit;
    transition: background 0.12s;
}
.devoirs-card-progress-link:hover { background: rgba(255, 255, 255, 0.07); }
/* .devoirs-card-progress-link force color: inherit (lien "pas encore
   corrigé", texte neutre) : quand ce même <a> est aussi .devoirs-card-
   progress-done (100%, cf. devoirs.html), le vert doit rester prioritaire. */
.devoirs-card-progress-link.devoirs-card-progress-done { color: #46a758; }

.devoirs-card-chevron {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-faint);
    transition: color 0.12s, background 0.12s;
    /* Boîte de hover dédiée (même principe que .devoirs-card-progress-link
       juste au-dessus) : déborde du padding vertical ET du padding droit de
       .devoirs-card-side (dernier élément de la zone) pour aller jusqu'au
       bord de la carte, coins droits pour la même raison. */
    align-self: stretch;
    margin: -16px 0 -16px 0;
    padding: 16px 16px 16px 10px;
    border-radius: 0;
}
.devoirs-card-chevron svg { width: 24px; height: 24px; }
.devoirs-card-chevron:hover { color: var(--accent); background: var(--surface-4, var(--surface-3)); }
/* DS : même teinte rouge que le reste de la carte (meta/chapitres, cf.
   .devoirs-card-ds .devoirs-card-meta/.devoirs-card-chapters svg
   ci-dessus), pas l'orange générique --accent. */
.devoirs-card-ds .devoirs-card-chevron:hover { color: var(--devoirs-ds); }
/* Reste dans sa couleur "hover" tant que le panneau info est ouvert pour
   cette carte (demande) : .exo-info-btn.active est le déclencheur `hidden`
   posé par exo_info.js (cf. devoirs.html) — pas de classe dédiée sur la
   carte elle-même, donc :has() jusqu'à ce bouton plutôt qu'un état local. */
.devoirs-card:has(.exo-info-btn.active) .devoirs-card-chevron { color: var(--accent); }
.devoirs-card-ds:has(.exo-info-btn.active) .devoirs-card-chevron { color: var(--devoirs-ds); }

/* Le ⓘ n'est plus affiché sur les cartes DM/DS (demande 2026-08-23, cf.
   devoirs.html) : juste un déclencheur `hidden` pour exo_info.js. Sans ce
   rappel, `.exo-info-btn { display: inline-flex }` (exercise_card.css, même
   spécificité, chargé après la feuille de style UA) l'emporterait sur
   l'attribut `[hidden]` et l'afficherait quand même. */
.devoirs-card .exo-info-btn[hidden] { display: none !important; }

/* ---------- Menu clic droit d'une carte DM/DS (#devoirs-card-ctx-menu) ----------
   .ctx-menu (theme.css) ne stylait que <button> : les liens Sujet/Corrigé/
   Corriger (de vrais <a> avec href, pour garder ouvrir-dans-un-onglet et le
   clic milieu) ont besoin des mêmes règles. Assigner/Désassigner+Corriger
   sont mutuellement exclusifs — basculés par la classe .ctx-menu-assigned
   posée en JS (devoirs.js) selon data-assignment-id de la carte visée. */
#devoirs-card-ctx-menu a {
    display: block;
    background: none;
    border: none;
    color: var(--text);
    font-size: 0.9rem;
    padding: 8px 16px;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}
#devoirs-card-ctx-menu a:hover { background: var(--surface-3); }
#devoirs-card-ctx-menu a svg { vertical-align: -3px; margin-right: 7px; }
#devoirs-card-ctx-menu [data-action="unassign"],
#devoirs-card-ctx-menu [data-action="correct"] { display: none; }
#devoirs-card-ctx-menu.ctx-menu-assigned [data-action="assign"] { display: none; }
#devoirs-card-ctx-menu.ctx-menu-assigned [data-action="unassign"],
#devoirs-card-ctx-menu.ctx-menu-assigned [data-action="correct"] { display: block; }
/* "Corriger" en rose — même couleur que la page de correction elle-même
   (--kind-correction, cf. ds_correction.css) et que .devoirs-card-btn-correction. */
#devoirs-card-ctx-menu a.devoirs-ctx-correct { color: var(--kind-correction-text); }
#devoirs-card-ctx-menu a.devoirs-ctx-correct:hover { background: color-mix(in srgb, var(--kind-correction) 14%, transparent) !important; color: var(--kind-correction-text); }

/* Toast de confirmation (ex: "Modification effectuée" après assignation) */
#devoirs-toast {
    position: fixed;
    bottom: 28px;
    left: 50%;
    transform: translateX(-50%) translateY(12px);
    background: var(--accent-strong);
    color: #fff;
    padding: 10px 20px;
    border-radius: 8px;
    font-size: 0.9rem;
    font-weight: 500;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease, transform 0.2s ease;
    z-index: 2000;
}
#devoirs-toast.devoirs-toast-visible {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}
