/* ==========================================================================
   Campus Manager theme
   Ported from design/design_campus_manager/css/campus-theme.css (hi-fi handoff, tour 2).
   Two layers:
   1) Retint of Tabler's own CSS custom properties (--tblr-*) so every existing
      Bootstrap/Tabler-based template (card/btn/badge/table/form-control/alert/dropdown/
      timeline) picks up the new palette/typography/radius for free, with zero markup
      changes. Loads after tabler.min.css (via importmap('app')), so these redeclarations
      win the cascade at equal specificity.
   2) New .cm-* component classes for structures with no Tabler equivalent (topbar +
      two-level flyout nav, KPI tile, login split panel, theme toggle knob, message/
      ticket conversation bubbles), driven by their own --cm-* tokens.
   Dark mode reuses the app's existing [data-bs-theme="dark"] mechanism (see
   theme_controller.js) - not a second theme attribute.
   ========================================================================== */

:root {
    /* ---- Campus Manager brand tokens (used by .cm-* components) ---- */
    --cm-brand: #1B6BA8;
    --cm-brand-strong: #12507e;
    --cm-navy: #12344d;
    --cm-gold: #c9a04e;
    --cm-gold-strong: #dbb35f;
    /* "Doré foncé" of the handoffs' token list: dark enough to read as text on a white surface, where
       --cm-gold is a rule/fill colour and --cm-gold-tx (further down) is the ink of a gold pill. */
    --cm-gold-deep: #9a7729;

    --cm-bg: #f2f5f8;
    --cm-surface: #ffffff;
    --cm-surface-alt: #f7fafc;
    --cm-border: #dde5ec;
    --cm-border-soft: #eef2f6;
    /* Rule between two table rows: lighter still than --cm-border-soft, which separates blocks
       (toolbar, card footer). Value taken from mockups 25a/25c/25d. */
    --cm-border-faint: #f4f7fa;
    /* Card and panel footer background: an off-white that lifts the action bar away from the form
       body without treating it as a grey zone. */
    --cm-surface-soft: #fbfcfd;
    --cm-ink: #1c2b36;
    --cm-text: #3d4f5c;
    --cm-muted: #5b6c79;
    --cm-faint: #8a99a6;
    /* Grey for an element that is present but inactive (pagination button at the end of a list,
       breadcrumb separator): legible without ever inviting the eye. */
    --cm-disabled: #c3cfd9;

    --cm-blue-bg: #dcebf7;   --cm-blue-tx: #12507e;
    /* Even paler than --cm-blue-bg (which serves row hover and badges): this is the background of a
       blue element *selected but at rest* - the chip of a filled blank, a checked mode card
       (design_handoff_quiz, screens 2a/2c). */
    --cm-blue-bg-soft: #eef5fb;
    /* The searched term highlighted inside a help search result (design_handoff_aide, écran 3).
       A background colour and nothing else here: the text keeps the ink it already had. */
    --cm-highlight-bg: #fdf1cf; --cm-highlight-tx: inherit;
    --cm-gold-bg: #f5e9cf;   --cm-gold-tx: #7a5417;
    --cm-green-bg: #e3ede6;  --cm-green-tx: #25543c;
    --cm-red-bg: #f5e0dc;    --cm-red-tx: #a43e2e;
    --cm-purple-bg: #e9e2f0; --cm-purple-tx: #4e3a66;
    --cm-teal-bg: #dde8ee;   --cm-teal-tx: #2f5468;
    --cm-warn-tx: #b0722a;

    /* Row action colors (.cm-action--*). The UFA handoff spells them out, without exception, across
       its 32 action links: blue for the main action (Modifier, Enregistrer le retour, Réactiver),
       brick for the destructive one (Désactiver). */
    --cm-action-main: #12507e;
    --cm-action-danger: #b0553f;

    /* Messaging's own lighter/cooler "selected" tint (design/design_handoff_messagerie) - a
       distinct shade from --cm-blue-bg above (used for badges/table-hover elsewhere), reused
       consistently there for the current folder, selected list row, checked audience tile,
       program pill, and reading-pane "Non lu" badge. */
    --cm-mail-tint-bg: #eef5fb;
    --cm-mail-tint-border: #c6ddf0;

    /* The standing platform notice under the navbar. A solid deep blue rather than a tinted band:
       it states where the school's reference data lives, and a pale strip reads as decoration. It
       is a shade lighter than --cm-topbar-bg on purpose, so the two do not merge into one block. */
    --cm-notice-bg: #1b5580;
    --cm-notice-tx: #ffffff;
    --cm-notice-bd: #0f3c5c;
    --cm-notice-link: #f0cf8a;

    /* Not in campus-theme.css - row-action "positive" color (Retour/Valider) is given as the
       literal hex #2e7d4f in the spacing handoff, matching this file's own --tblr-success value
       below exactly. Wired through a variable instead of hardcoded in .cm-action--positive so it
       still reacts to [data-bs-theme="dark"] like every other semantic action color - the dark
       value reuses --tblr-success's own dark value for the same reason. */
    --cm-positive-tx: #2e7d4f;

    --cm-topbar-bg: #12344d;
    --cm-topbar-tx: #cfdde9;
    --cm-topbar-dim: #7d99b0;
    /* Height of the authenticated navbar: anchors the elements that must start right below it
       (side panel and its overlay). */
    --cm-topbar-h: 90px;

    /* « Mes machines virtuelles »: the reference's own values, kept exact rather than
       rounded onto a neighbouring token - see the .cm-vm* block further down. */
    --cm-vm-soft: #fbfdfe;
    --cm-vm-dim: #a4b1bc;
    --cm-vm-off-bd: #e6ecf1;
    --cm-vm-off-bg: #f7f9fb;
    --cm-vm-ok-tx: #1f7a54;
    --cm-vm-ok-bg: #e7f4ee;
    --cm-vm-gold-tx: #8a6a1f;
    --cm-vm-gold-bg: #fdf6e8;
    --cm-vm-stop-tx: #a8443a;
    --cm-vm-stop-bd: #e2ccc8;
    --cm-vm-stop-bg: #fdf4f3;
    /* Cahier de texte, période screen (design_handoff_cahier_de_texte_seances): the handoff's own
       values, kept exact rather than rounded onto a neighbouring token - same treatment as the
       --cm-vm-* block above, and for the same reason. The tag colours it does NOT restate are
       --cm-vm-ok-* and --cm-vm-gold-*, whose hexes are already the ones it asks for. */
    --cm-cdt-open-bd: #a8cbe8;
    --cm-cdt-row-hover: #f8fafc;
    --cm-cdt-sel-bg: #12344d;
    --cm-cdt-sel-tx: #ffffff;
    --cm-cdt-sel-dim: #9fb4c6;
    --cm-cdt-sel-ok-bg: rgba(127, 195, 161, .18);
    --cm-cdt-sel-ok-tx: #7fc3a1;
    --cm-cdt-sel-warn-bg: rgba(219, 179, 95, .18);
    --cm-cdt-sel-warn-tx: #dbb35f;
    --cm-cdt-sel-off-bg: rgba(255, 255, 255, .1);
    --cm-cdt-sel-off-tx: #cfdde9;
    --cm-cdt-cal-weekend: #b4bfc9;
    --cm-cdt-cal-sunday: #7fa3c0;
    /* Nuage de mots (design_handoff_nuage_de_mots): the handoff's own values, kept exact rather
       than rounded onto a neighbouring token - same treatment as the --cm-vm-* and --cm-cdt-*
       blocks above, and for the same reason. Only the shades it asks for that no existing token
       already carries are restated here; everything else in the .cm-wc-* block reads --cm-border,
       --cm-ink, --cm-brand and friends, so dark mode follows on its own. */
    --cm-wc-soft: #fbfdff;
    --cm-wc-dim: #c2ccd4;
    --cm-wc-off: #c8d3dc;
    --cm-wc-track: #e8eef3;
    --cm-wc-ok-bg: #e2efe4;
    --cm-wc-ok-tx: #2f6b45;
    --cm-wc-ok-bd: #b7d3bd;
    --cm-wc-no-bg: #faf1ef;
    --cm-wc-no-bd: #e4d5d2;
    --cm-wc-pending-bg: #fdf8ef;
    --cm-wc-pending-bd: #f0e2c8;
    --cm-wc-gold-hover: #faf5ea;
    /* The four rungs of the handoff's colour ladder, on a light ground. The dark board restates
       them below with the dark palette, in both themes - a projection is a projection. */
    --cm-wc-word-top: #9a7729;
    --cm-wc-word-strong: #12344d;
    --cm-wc-word-mid: #3d4f5c;
    --cm-wc-word-faint: #8a99a6;
    /* The board. These four never change with the theme: a projection is a projection, and a
       classroom that has switched the app to light does not want a white wall at the back of the
       room. They are restated in the dark block all the same, unchanged, so that nobody reading it
       wonders whether they were forgotten. */
    --cm-wc-board-top: #16436b;
    --cm-wc-board-bottom: #0d2438;
    --cm-wc-board-tx: #cfdde9;
    --cm-wc-board-dim: #9db4c6;
    --cm-wc-board-faint: #7d99b0;
    --cm-wc-board-live: #2f8f57;

    --cm-radius: 10px;
    --cm-radius-sm: 8px;
    --cm-shadow-pop: 0 12px 28px rgba(18, 52, 77, .18);
    --cm-font-ui: 'Source Sans 3', system-ui, sans-serif;
    --cm-font-title: 'Spectral', Georgia, serif;
    /* System-first, and deliberately not a vendored webfont: every platform the school runs
       already ships the face its own editor and terminal use - SF Mono on macOS, Consolas on
       Windows, DejaVu Sans Mono on Linux - so a code block reads like an IDE without a single
       extra byte over the wire. `ui-monospace` is what picks SF Mono up on Safari. */
    --cm-font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
        'Cascadia Mono', 'JetBrains Mono', 'DejaVu Sans Mono', 'Liberation Mono', monospace;

    /* ---- Retint Tabler's own variables so unmocked screens inherit the theme ---- */
    --tblr-font-sans-serif: var(--cm-font-ui);

    --tblr-primary: var(--cm-brand);
    --tblr-primary-rgb: 27, 107, 168;
    --tblr-link-color: var(--cm-brand);
    --tblr-link-color-rgb: 27, 107, 168;
    --tblr-link-hover-color: var(--cm-brand-strong);

    --tblr-success: #2e7d4f;
    --tblr-success-rgb: 46, 125, 79;
    --tblr-warning: var(--cm-gold);
    --tblr-warning-rgb: 201, 160, 78;
    --tblr-warning-fg: var(--cm-navy);
    --tblr-danger: #a43e2e;
    --tblr-danger-rgb: 164, 62, 46;
    --tblr-purple: #6b4f8c;
    --tblr-purple-rgb: 107, 79, 140;
    --tblr-teal: #4a7d99;
    --tblr-teal-rgb: 74, 125, 153;
    --tblr-info: #2f7ab0;
    --tblr-info-rgb: 47, 122, 176;

    --tblr-body-bg: var(--cm-bg);
    --tblr-body-bg-rgb: 242, 245, 248;
    --tblr-body-color: var(--cm-text);
    --tblr-body-color-rgb: 61, 79, 92;
    --tblr-border-color: var(--cm-border);
    --tblr-card-bg: var(--cm-surface);
    --tblr-bg-surface: var(--cm-surface);
    --tblr-bg-surface-primary: var(--cm-surface);
    --tblr-bg-surface-secondary: var(--cm-surface-alt);
    --tblr-bg-surface-tertiary: var(--cm-surface-alt);

    --tblr-border-radius: var(--cm-radius-sm);
    --tblr-border-radius-sm: 6px;
    --tblr-border-radius-lg: var(--cm-radius);
    --tblr-border-radius-xl: 14px;

    --tblr-table-hover-bg: var(--cm-blue-bg);
    --tblr-table-hover-color: var(--cm-text);
}

[data-bs-theme="dark"] {
    --cm-brand: #4a94cc;
    --cm-brand-strong: #74b0da;
    --cm-bg: #10181f;
    --cm-surface: #1a242e;
    --cm-surface-alt: #202c38;
    --cm-border: #2b3946;
    --cm-border-soft: #243140;
    --cm-border-faint: #202b36;
    --cm-surface-soft: #1e2833;
    --cm-ink: #e8eef4;
    --cm-text: #c3d0da;
    --cm-muted: #8fa3b3;
    --cm-faint: #6b7f8e;
    --cm-disabled: #4a5b6a;

    --cm-blue-bg: #1c3a52;   --cm-blue-tx: #8fc1e8;
    --cm-blue-bg-soft: #162c3e;
    /* The light theme's pale yellow would sit unreadably under light text, so the dark theme
       highlights on a deep gold and states its own ink instead of inheriting. */
    --cm-highlight-bg: #5a4a1c; --cm-highlight-tx: #f6e8c4;
    --cm-gold-bg: #3c3117;   --cm-gold-tx: #e0bc6d;
    --cm-gold-deep: #d9b46b;
    --cm-green-bg: #1d3327;  --cm-green-tx: #8cc3a2;
    --cm-red-bg: #40221c;    --cm-red-tx: #e09384;
    --cm-purple-bg: #2e2440; --cm-purple-tx: #bda6d8;
    --cm-teal-bg: #1d3340;   --cm-teal-tx: #93bdd2;
    --cm-warn-tx: #d99a55;
    --cm-vm-soft: #1e2833;
    --cm-vm-dim: #6b7f8e;
    --cm-vm-off-bd: #2b3946;
    --cm-vm-off-bg: #202b36;
    --cm-vm-ok-tx: #4caf7a;
    --cm-vm-ok-bg: #1b3329;
    --cm-vm-gold-tx: #d9b46a;
    --cm-vm-gold-bg: #33291a;
    --cm-vm-stop-tx: #d98f7f;
    --cm-vm-stop-bd: #4a3330;
    --cm-vm-stop-bg: #2e2321;
    --cm-positive-tx: #4caf7a;
    --cm-action-main: #74b0da;
    --cm-action-danger: #d98f7f;

    /* The selected row of the cahier de texte reads as « darker than the card » in the light theme;
       on a dark page the same idea has to go the other way, so it lifts instead of sinking. */
    --cm-cdt-open-bd: #3c6a90;
    --cm-cdt-row-hover: #212d39;
    --cm-cdt-sel-bg: #1d3a52;
    --cm-cdt-sel-tx: #eaf2f8;
    --cm-cdt-sel-dim: #9fb4c6;
    --cm-cdt-sel-ok-bg: rgba(76, 175, 122, .2);
    --cm-cdt-sel-ok-tx: #7fc3a1;
    --cm-cdt-sel-warn-bg: rgba(219, 179, 95, .2);
    --cm-cdt-sel-warn-tx: #dbb35f;
    --cm-cdt-sel-off-bg: rgba(255, 255, 255, .08);
    --cm-cdt-sel-off-tx: #c3d0da;
    --cm-cdt-cal-weekend: #55697a;
    --cm-cdt-cal-sunday: #7fa3c0;

    --cm-mail-tint-bg: #1c3247;
    --cm-mail-tint-border: #2f4d68;

    --cm-notice-bg: #17466b;
    --cm-notice-tx: #eaf2f8;
    --cm-notice-bd: #0a2b44;
    --cm-notice-link: #e2c07a;

    --cm-wc-soft: #1e2833;
    --cm-wc-dim: #55697a;
    --cm-wc-off: #3a4a58;
    --cm-wc-track: #243140;
    --cm-wc-ok-bg: #1b3329;
    --cm-wc-ok-tx: #7fc3a1;
    --cm-wc-ok-bd: #2f5b44;
    --cm-wc-no-bg: #2e2321;
    --cm-wc-no-bd: #4a3330;
    --cm-wc-pending-bg: #2b2415;
    --cm-wc-pending-bd: #4a3d21;
    --cm-wc-gold-hover: #33291a;
    /* The dark ground's palette, which is the handoff's own « palette sombre » - the pilot preview
       sits on a dark card here, so it wants exactly what the board wants. */
    --cm-wc-word-top: #c9a04e;
    --cm-wc-word-strong: #fff;
    --cm-wc-word-mid: #cfdde9;
    --cm-wc-word-faint: #7d99b0;
    /* Unchanged on purpose - see the light block. */
    --cm-wc-board-top: #16436b;
    --cm-wc-board-bottom: #0d2438;
    --cm-wc-board-tx: #cfdde9;
    --cm-wc-board-dim: #9db4c6;
    --cm-wc-board-faint: #7d99b0;
    --cm-wc-board-live: #2f8f57;

    --cm-topbar-bg: #0d2438;
    --cm-shadow-pop: 0 12px 28px rgba(0, 0, 0, .5);

    --tblr-primary: var(--cm-brand);
    --tblr-primary-rgb: 74, 148, 204;
    --tblr-link-color: var(--cm-brand);
    --tblr-link-color-rgb: 74, 148, 204;
    --tblr-link-hover-color: var(--cm-brand-strong);

    --tblr-success: #4caf7a;
    --tblr-success-rgb: 76, 175, 122;
    --tblr-warning: #dbb35f;
    --tblr-warning-rgb: 219, 179, 95;
    --tblr-warning-fg: #12344d;
    --tblr-danger: #c1584a;
    --tblr-danger-rgb: 193, 88, 74;
    --tblr-purple: #8b6bab;
    --tblr-purple-rgb: 139, 107, 171;
    --tblr-teal: #6a9dbe;
    --tblr-teal-rgb: 106, 157, 190;
    --tblr-info: #5aa0d8;
    --tblr-info-rgb: 90, 160, 216;

    --tblr-body-bg: var(--cm-bg);
    --tblr-body-bg-rgb: 16, 24, 31;
    --tblr-body-color: var(--cm-text);
    --tblr-body-color-rgb: 195, 208, 218;
    --tblr-border-color: var(--cm-border);
    --tblr-card-bg: var(--cm-surface);
    --tblr-bg-surface: var(--cm-surface);
    --tblr-bg-surface-primary: var(--cm-surface);
    --tblr-bg-surface-secondary: var(--cm-surface-alt);
    --tblr-bg-surface-tertiary: var(--cm-surface-alt);

    --tblr-table-hover-bg: var(--cm-blue-bg);
    --tblr-table-hover-color: var(--cm-text);
}

/* ---------- Full-bleed page, whatever the scrollbar does ----------
   Tabler ships `@media (min-width: 992px) { :root { margin-left: calc(100vw - 100%) } }` to keep
   its centered container from shifting when a vertical scrollbar appears. 100vw counts the
   scrollbar, 100% doesn't, so that margin resolves to the scrollbar's own width and pushes the
   whole document right - leaving an unpainted strip down the left edge, glaring against the dark
   topbar at the top-left corner. This app's header/footer are full-bleed, so the trade is the
   wrong way round for us: cancel it and accept the browser's normal reflow instead. Same
   selector and breakpoint as Tabler's own rule, and app.css loads after tabler.min.css. */
@media (min-width: 992px) {
    :root { margin-left: 0; }
}

/* ---------- Headings: Spectral, Tabler has no dedicated heading-font variable ---------- */
h1, h2, h3, h4,
/* Ink and not --cm-text: this is the heading color of the reference (#1c2b36, see mockups
   25a/25c/25e of the UFA handoff), which lifts them off the running text. */
.page-title, .card-title, .h1, .h2, .h3, .h4 {
    font-family: var(--cm-font-title);
    font-weight: 600;
    color: var(--cm-ink);
}

/* ==========================================================================
   Typography and controls: the mockup values, overriding Tabler

   The mockups outrank Tabler's own settings: when a measurement from the
   design contradicts what the framework ships, the framework gives way. Every
   value below is taken verbatim from
   design/design_handoff_ufa/reference/UFA.dc.html (screens 25a to 25f), which
   sets the reference for the whole application and not for its own screens
   alone.

   Each rule is written with at least the specificity of the Tabler rule it
   replaces: app.css loads after tabler.min.css, so on a tie the last
   declaration wins.
   ========================================================================== */

/* 24px in the mockups, 20px in Tabler. */
.page-title { font-size: 24px; }
/* Section heading inside a card. */
.card-title { font-size: 15px; }

/* The label announces the field without competing with its value: smaller and greyer than the
   input, but bold. Tabler does the opposite (14px, dark grey, semi-bold). */
.form-label { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); margin-bottom: 6px; }
/* The required-field asterisk, in the mockups' red rather than Tabler's. A required field carries
   one, an optional field does not: that is the only rule, and Symfony already sets the .required
   class from the field's `required` option. Labels hand-written in a template must therefore carry
   that class themselves as soon as the server makes the field mandatory. */
.required::after, .form-label.required::after { color: #d4483b; }
/* One required field, one asterisk. An expanded choice field breaks that on its own: Symfony marks
   every single choice label .required, so a three-radio group draws four asterisks - one per choice
   plus the legend's. Wrap the choices in .cm-choice-group to keep only the legend's. */
.cm-choice-group .form-check-label.required::after { content: none; }

.form-control, .form-select { font-size: 13.5px; }
.form-control { padding: 9px 12px; }
/* The select arrow is painted as a background image on the right: its room must stay. */
.form-select { padding: 9px 36px 9px 12px; }

/* The mockups only describe the default size: .btn-sm/.btn-lg/.btn-icon keep theirs, hence the
   :not() - which also puts the rule ahead of those three classes' own. */
.btn:not(.btn-sm):not(.btn-lg):not(.btn-icon) { font: 600 13px var(--cm-font-ui); padding: 10px 18px; }
/* Secondary button (Annuler): a discreet outline in the border grey, not Bootstrap's heavy grey,
   turning brand blue on hover. */
.btn-outline-secondary { border-color: var(--cm-border); color: var(--cm-text); padding: 9px 15px; }
.btn-outline-secondary:hover, .btn-outline-secondary:focus {
    background: var(--cm-bg); border-color: var(--cm-border); color: var(--cm-brand-strong);
}

.card-body { padding: 22px 24px; }
.card-footer { padding: 14px 20px; background: var(--cm-surface-soft); }

/* ==========================================================================
   Topbar (.cm-topbar) - applied to the app's existing Bootstrap/Tabler
   .navbar header. Deliberately keeps every Bootstrap navbar/dropdown/dropend
   class and its data-bs-* JS behavior (click-to-open, keyboard nav, outside-
   close) rather than introducing a parallel hover-flyout markup system - the
   design README itself says production should use "keyboard/click handling
   per project conventions", and the project's convention already is
   Bootstrap dropdowns. Only the visual layer changes here.
   ========================================================================== */
/* height (not min-height): Tabler's own .navbar>.container-xl is the real flex row (display:flex,
   justify-content:space-between - not .navbar itself), and it stretches to fit its tallest child by
   default. A *fixed* height is what actually lets the 74px-tall medallion below overflow past the
   bar's edges instead of growing the bar to fit it. */
.cm-topbar.navbar {
    min-height: 58px; padding: 0 26px;
    background: var(--cm-topbar-bg) !important; color: var(--cm-topbar-tx);
    box-shadow: none; border: 0;
    position: relative; z-index: 10; /* the medallion below overflows below the bar */
}
/* Fixed (not min-) height only from navbar-expand-xxl's own 1400px breakpoint up - that's what lets
   the medallion overflow past a hard-capped bar instead of stretching it. Below 1400px the bar must
   stay min-height so it can still grow to fit the opened mobile accordion menu (Bootstrap's
   .navbar-collapse.show), which needs room inside the header, not past it. */
@media (min-width: 1400px) {
    .cm-topbar.navbar { height: 58px; }
}
/* Not applied below navbar-expand-xxl's 1400px breakpoint - the collapsed mobile accordion
   already lays its items out in a column (no shared row to space out), and reserving this gap
   unconditionally on every child of .container-xl (toggler/brand/divider/right-cluster/nav) was
   exactly what caused the inline nav to overflow the bar between ~768px and ~1250px (measured up
   to 167px of overflow at 992px) before the breakpoint was widened above - keeping it gated here
   is extra safety margin on top of that fix, not just pixel fidelity. */
@media (min-width: 1400px) {
    .cm-topbar .container-xl { gap: 24px; }
}
.cm-topbar .navbar-toggler { color: #fff; border-color: rgba(255, 255, 255, .25); }
.cm-topbar__brand {
    display: flex; align-items: center; gap: 15px; text-decoration: none; color: inherit;
}
/* design/design_handoff_menu (14b) - same round white-ringed medallion identity as the login
   page's .cm-authcard__medallion, deliberately overflowing below the bar (top:9px on top of the
   centering flexbox already gives it a natural 8px symmetric overflow at this height, so the net
   effect is 17px below / ~flush above - see that file's own README for the exact math). The header
   must stay position:relative/z-index:10 and never overflow:hidden for this to render correctly,
   and any content immediately below it needs enough top clearance not to be covered (checked/
   adjusted per screen during integration, see .page-header spacing). */
.cm-topbar__medallion {
    /* Explicit content-box: Tabler's own global reset is `*{box-sizing:border-box}`, which would
       otherwise fold the 4px border inward and cap the true outer size at 66px instead of the
       spec's "66x66 + bordure 4, soit 74px hors-tout". */
    box-sizing: content-box;
    position: relative; top: 9px; flex: none;
    width: 66px; height: 66px;
    border-radius: 50%; background: var(--cm-brand);
    border: 4px solid #fbfaf7; box-shadow: 0 8px 20px rgba(0, 0, 0, .3);
    display: grid; place-items: center;
}
.cm-topbar__medallion img { width: 30px; height: 39px; object-fit: contain; display: block; }
.cm-topbar__text { display: block; line-height: 1.1; }
.cm-topbar__name { display: block; font: 600 15px var(--cm-font-title); color: #fff; }
.cm-topbar__since { display: flex; align-items: center; justify-content: center; gap: 7px; margin-top: 4px; }
.cm-topbar__since i { flex: 1 1 0; height: 1px; background: var(--cm-gold); }
.cm-topbar__since b {
    flex: none; font: 600 9px var(--cm-font-ui); letter-spacing: 2.4px; text-transform: uppercase;
    color: var(--cm-gold); padding-left: 2.4px; /* compensates the letter-spacing so both rules stay equal length */
}
.cm-topbar__divider { width: 1px; height: 28px; background: rgba(255, 255, 255, .13); flex: none; }
@media (max-width: 1099.98px) {
    .cm-topbar__medallion { width: 52px; height: 52px; border-width: 3px; }
    .cm-topbar__medallion img { width: 24px; height: 31px; }
}
@media (max-width: 899.98px) {
    .cm-topbar__text { display: none; }
}

.cm-topbar .navbar-nav { font-size: 13.5px; }
.cm-topbar .nav-link { color: var(--cm-topbar-tx); padding: 0 13px; height: 58px; display: flex; align-items: center; }
/* design/design_handoff_menu (14b): 16x16 trait-1.6 pictos before every top-level label, always
   visible (not just on some breakpoints) - stroke-width in CSS overrides the stroke-width="2"
   presentation attribute already on these inline SVGs (lower cascade priority than a stylesheet). */
.cm-topbar .nav-link-icon { width: 16px; height: 16px; margin: 0 7px 0 0; stroke-width: 1.6px; }
/* ".navbar-collapse" is a static class present in the markup at every viewport width (Bootstrap
   only toggles "display" via the min-width:1400px media query above, not the class itself), and
   ".cm-topbar .navbar-collapse .nav-link" (3 classes) otherwise out-specificities the desktop rule
   above (2 classes) regardless of source order - collapsing the nav-link to 40px tall even on
   desktop and leaving a gap of bare topbar background between the active item's gold underline
   and the navbar's actual bottom edge. Gate this comfortable vertical padding to the collapsed
   mobile menu only, matching navbar-expand-xxl's own breakpoint. */
@media (max-width: 1399.98px) {
    .cm-topbar .navbar-collapse .nav-link { height: auto; padding: 9px 13px; }
}
.cm-topbar .nav-link:hover, .cm-topbar .nav-link:focus,
.cm-topbar .nav-item.show > .nav-link { color: #fff; background: rgba(255, 255, 255, .07); }
/* Tabler's own .navbar-nav .nav-link has border-radius: var(--tblr-border-radius) (8px), which
   clips the inset box-shadow below into a curve at both corners instead of a flat line - override
   to 0 on the active link only (its hover/dropdown-open siblings keep the rounded pill). Tabler
   also ships its own horizontal-navbar active indicator (.nav-item.active:after, a 2px bottom
   border in the un-retinted default blue) that would otherwise render stacked underneath ours -
   suppress it here so only the gold bar shows. */
.cm-topbar .nav-item.active:after { content: none; }
.cm-topbar .nav-item.active > .nav-link { color: #fff; font-weight: 600; border-radius: 0; box-shadow: inset 0 -3px 0 var(--cm-gold); }

/* Panel measurements from design_handoff_menu_outils (the only handoff that specifies the inside of
   a top-bar dropdown, and it declares them final). Applied to every top-bar dropdown rather than to
   the Outils panel alone: the menus are one system, and a spec that held for a single panel would be
   an exception to maintain, not a convention. */
.cm-topbar .dropdown-menu {
    background: var(--cm-surface); border: 1px solid var(--cm-border);
    border-radius: 10px; box-shadow: 0 14px 34px rgba(16, 42, 67, .16);
    padding: 10px 0; font-size: 14px;
}
.cm-topbar .dropdown-item {
    padding: 8px 18px; color: var(--cm-ink);
}
.cm-topbar .dropdown-item:hover, .cm-topbar .dropdown-item:focus, .cm-topbar .dropdown-item.active {
    background: var(--cm-bg); color: var(--cm-brand-strong);
}
/* Gold, not grey: a group heading names a family of entries, and the handoff gives the whole menu
   system that colour for it. */
.cm-topbar .dropdown-header {
    padding: 8px 18px 5px;
    font: 700 10.5px var(--cm-font-ui); color: var(--cm-gold-deep);
    text-transform: uppercase; letter-spacing: 1.4px;
}
.cm-topbar .dropdown-divider { margin: 8px 0; border-top-color: var(--cm-border-soft); }
/* The Outils panel is the one the handoff sizes, and it is a flat list - no submenu, so nothing
   widens it from the inside. */
.cm-toolsmenu { min-width: 280px; }

.cm-topbar__right { display: flex; align-items: stretch; gap: 26px; }
/* Its direct children are plain .nav-item divs (Bootstrap/Tabler markup) which don't stretch their
   own children by default - making this one a flex container too lets .cm-mail's align-self:
   stretch below actually fill the full 58px the outer flex row already gives the .nav-item. */
.cm-topbar__right > .nav-item { display: flex; }

/* ---------- Messaging icon (design/design_handoff_messagerie 16d) ----------
   A full-height cell of the bar (58px), treated exactly like a nav-link: no gold rail at rest
   (that was the original bug - the rail used to render unconditionally, so the icon always looked
   selected even outside the messaging section), a plain hover wash, and the rail only when the
   current route is actually inside messaging (.is-active, set server-side in
   templates/layout/app.html.twig). */
.cm-mail {
    position: relative; align-self: stretch; display: flex; align-items: center; justify-content: center;
    padding: 0 13px; color: var(--cm-topbar-tx); cursor: pointer;
}
.cm-mail:hover, .cm-mail:focus-visible { color: #fff; background: rgba(255, 255, 255, .10); }
.cm-mail.is-active { color: #fff; background: rgba(255, 255, 255, .07); box-shadow: inset 0 -3px 0 var(--cm-gold); }
.cm-mail:focus-visible { outline: 2px solid rgba(255, 255, 255, .55); outline-offset: -2px; }
.cm-mail__badge {
    position: absolute; top: 10px; right: 6px;
    min-width: 17px; height: 17px; box-sizing: border-box; padding: 0 4px;
    border-radius: 9px; background: #d4483b; border: 2px solid var(--cm-topbar-bg);
    color: #fff; font-weight: 700; font-size: 10px;
    display: grid; place-items: center;
}

/* ---------- Theme segment control, moved from the bar itself into the user dropdown ---------- */
.cm-usermenu__theme {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 9px 12px; font-size: 13.5px; color: var(--cm-text);
}
.cm-theme {
    display: flex; align-items: center; gap: 4px;
    background: var(--cm-bg); border: 1px solid var(--cm-border); border-radius: 999px; padding: 3px;
}
.cm-theme button {
    width: 22px; height: 22px; border: 0; border-radius: 50%;
    background: none; color: var(--cm-faint); font-size: 11px;
    display: grid; place-items: center; cursor: pointer;
}
.cm-theme button.is-on { background: var(--cm-surface); color: var(--cm-navy); box-shadow: 0 1px 2px rgba(18, 52, 77, .16); }

/* ---------- User identity block ---------- */
.cm-user { display: flex; align-items: center; gap: 10px; position: relative; }
.cm-user__toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; background: none; border: 0; padding: 0; color: inherit; text-decoration: none; }
.cm-user__id { text-align: right; line-height: 1.2; }
.cm-user__name { font-weight: 600; font-size: 13px; color: #fff; }
.cm-user__login { font-size: 11px; color: var(--cm-topbar-dim); }
.cm-user__avatar {
    width: 34px; height: 34px; border-radius: var(--cm-radius-sm); flex: none;
    background: var(--cm-gold); color: var(--cm-navy);
    display: grid; place-items: center; font-weight: 700; font-size: 13px;
    background-size: cover; background-position: center;
}
.cm-user__menu-head {
    margin: -6px -6px 6px; padding: 10px 12px;
    background: var(--cm-surface-alt); border-bottom: 1px solid var(--cm-border-soft);
    border-radius: 10px 10px 0 0;
}
/* Without an identity header, the menu is only as wide as its entries; the theme row
   needs a little room between its label and its toggle, hence the min-width. */
.cm-user__menu { min-width: 200px; }

/* ==========================================================================
   KPI tiles (.cm-kpi) - colored top-border stat cards, no Tabler equivalent
   ========================================================================== */
.cm-kpi { border-radius: var(--cm-radius); padding: 14px 18px; border: 1px solid var(--cm-border); background: var(--cm-surface); }
.cm-kpi__label { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }
.cm-kpi__value { font: 700 28px var(--cm-font-title); color: var(--cm-ink); margin-top: 4px; }
.cm-kpi--blue   { background: var(--cm-blue-bg);   border-top: 4px solid var(--cm-brand); }
.cm-kpi--blue   .cm-kpi__label { color: var(--cm-blue-tx); }
.cm-kpi--gold   { background: var(--cm-gold-bg);   border-top: 4px solid var(--cm-gold); }
.cm-kpi--gold   .cm-kpi__label { color: var(--cm-gold-tx); }
.cm-kpi--green  { background: var(--cm-green-bg);  border-top: 4px solid #2e7d4f; }
.cm-kpi--green  .cm-kpi__label { color: var(--cm-green-tx); }
.cm-kpi--purple { background: var(--cm-purple-bg); border-top: 4px solid #6b4f8c; }
.cm-kpi--purple .cm-kpi__label { color: var(--cm-purple-tx); }

/* ==========================================================================
   Stat cards (.cm-stat) + list filter bar (.cm-filterbar)
   Ported from design/design_handoff_ufa_alternance, turn 33a (Alternances).
   A second, plainer stat treatment than .cm-kpi above - no color band, the
   number first in Spectral, then a sentence-case label and a muted qualifier
   line - so don't merge the two: 33a's cards read as a header strip, .cm-kpi's
   as tinted status tiles (ticket queue).
   ========================================================================== */
.cm-statgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
@media (max-width: 991.98px) { .cm-statgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 575.98px) { .cm-statgrid { grid-template-columns: 1fr; } }
.cm-stat {
    display: flex; flex-direction: column; gap: 3px;
    padding: 15px 18px; border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
    background: var(--cm-surface);
}
.cm-stat__value { font: 600 26px var(--cm-font-title); color: var(--cm-brand-strong); line-height: 1.1; }
.cm-stat__label { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-stat__hint { font-size: 11.5px; color: var(--cm-faint); }

/* One horizontal row of "label + control" pairs, the search box pushed to the far
   right - the filter header of a list card, sitting directly on the card (no
   .card-body, whose padding is bigger than the 14px/20px the design asks for). */
.cm-filterbar {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 14px 20px; border-bottom: 1px solid var(--cm-border-soft);
}
.cm-filterbar__field { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--cm-muted); }
/* appearance:none + our own chevron rather than Tabler's .form-select: that one
   carries its own height/padding/font scale, which is what made these controls
   read as stacked form fields instead of the design's inline pills. */
.cm-filterbar__select {
    appearance: none; -webkit-appearance: none;
    border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
    padding: 6px 27px 6px 10px; color: var(--cm-text); font: 600 13px var(--cm-font-ui); cursor: pointer;
    background: var(--cm-surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath d='M1 1.5 5 5.5 9 1.5' fill='none' stroke='%238a99a6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 10px center;
}
.cm-filterbar__select:hover { border-color: #a8cbe8; }
.cm-filterbar__select:focus-visible { outline: 2px solid var(--cm-brand); outline-offset: 1px; }
/* "Toutes" - the un-narrowed state of a filter reads lighter than a picked value. */
.cm-filterbar__select--any { font-weight: 400; }
.cm-filterbar__segmented { display: flex; border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); overflow: hidden; }
.cm-filterbar__seg {
    display: inline-flex; align-items: center; padding: 7px 13px; border: 0;
    background: var(--cm-surface); color: var(--cm-muted); font: 600 12.5px var(--cm-font-ui);
    text-decoration: none; cursor: pointer;
}
.cm-filterbar__seg + .cm-filterbar__seg { border-left: 1px solid var(--cm-border); }
/* The segments are links, so Tabler's own a:hover underline has to be turned off explicitly. */
.cm-filterbar__seg:hover { text-decoration: none; background: var(--cm-surface-alt); color: var(--cm-ink); }
.cm-filterbar__seg:focus-visible { outline: 2px solid var(--cm-brand); outline-offset: -2px; }
.cm-filterbar__seg.is-active,
.cm-filterbar__seg.is-active:hover { background: var(--cm-brand); color: #fff; }
/* Sits next to the Actives/Toutes segmented control, so it matches that control's own
   12.5px/600 type rather than the lighter .cm-filterbar__field label scale. */
.cm-filterbar__check {
    display: flex; align-items: center; gap: 7px; margin: 0;
    font: 600 12.5px var(--cm-font-ui); color: var(--cm-muted); cursor: pointer;
}
.cm-filterbar__check input { margin: 0; cursor: pointer; }
.cm-filterbar__search {
    display: flex; align-items: center; gap: 9px; width: 230px; margin-left: auto;
    padding: 7px 12px; border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
    background: var(--cm-surface); color: var(--cm-faint);
}
.cm-filterbar__search:focus-within { border-color: var(--cm-brand); }
.cm-filterbar__search input {
    width: 100%; min-width: 0; border: 0; padding: 0; background: transparent;
    font-size: 13px; color: var(--cm-text);
}
.cm-filterbar__search input:focus { outline: none; }
.cm-filterbar__search input::placeholder { color: #b6c2cc; }

/* ---------- Dashboard / ticket queue schedule-style row list ---------- */
.cm-row-list { display: flex; flex-direction: column; gap: 8px; }
.cm-row-item {
    display: flex; align-items: center; gap: 14px; padding: 10px 14px; border-radius: 8px;
    background: var(--cm-blue-bg); border-left: 3px solid var(--cm-brand);
}
.cm-row-item--gold   { background: var(--cm-gold-bg);   border-left-color: var(--cm-gold); }
.cm-row-item--green  { background: var(--cm-green-bg);  border-left-color: #2e7d4f; }
.cm-row-item--purple { background: var(--cm-purple-bg); border-left-color: #6b4f8c; }
.cm-row-item--teal   { background: var(--cm-teal-bg);   border-left-color: #4a7d99; }
.cm-row-item--red    { background: var(--cm-red-bg);    border-left-color: #c0604f; }
.cm-row-item__time { font: 600 13px ui-monospace, monospace; width: 82px; flex: none; }
.cm-row-item__title { font-weight: 600; font-size: 14px; color: var(--cm-ink); }
.cm-row-item__meta { font-size: 12px; color: var(--cm-muted); }

/* ==========================================================================
   Public auth card (.cm-authcard) - login (13b/13d) + the magic-link and
   contact-email confirmation pages that follow it (design/design_handoff_connexion).
   Replaces the old .cm-login split panel entirely. A deliberately separate token set
   from the rest of the app (--cm-auth-*, scoped to .cm-authcard) since this is a public
   "marketing card on a gradient" surface, not the authenticated admin shell - most values
   happen to equal existing --cm-* tokens (reused directly below) but a few differ by a
   shade from their nearest --cm-* equivalent (e.g. dark ink), so those get their own
   pixel-exact token per design/design_handoff_connexion/README.md's token table instead of
   approximating an existing one.
   ========================================================================== */
.cm-authcard {
    --cm-auth-grad-1: #0e2c43; --cm-auth-grad-2: #12344d; --cm-auth-grad-3: #164a6e;
    --cm-auth-circle-1: rgba(255, 255, 255, .04);
    --cm-auth-circle-2: rgba(201, 160, 78, .11);
    --cm-auth-card-bg: #fbfaf7;
    --cm-auth-card-border: transparent;
    --cm-auth-card-shadow: 0 30px 70px rgba(0, 0, 0, .35);
    --cm-auth-medallion-shadow: 0 20px 44px rgba(0, 0, 0, .32);
    --cm-auth-title: #12344d;
    --cm-auth-since-tx: #a8813a;
    --cm-auth-tabs-track: #eef1f5;
    --cm-auth-tab-active-shadow: 0 1px 3px rgba(18, 52, 77, .14);
    --cm-auth-tab-inactive: #5b6c79;
    --cm-auth-tab-inactive-hover-bg: #e4eaf0;
    --cm-auth-label: #1c2b36;
    --cm-auth-field-border: #dde5ec;
    --cm-auth-field-bg: #fff;
    --cm-auth-field-text: #1c2b36;
    --cm-auth-focus-halo: rgba(27, 107, 168, .15);
    --cm-auth-footer-border: #e6ebf0;
    --cm-auth-footer-tx: #8a99a6;
    --cm-auth-footer-phone: #5b6c79;
    --cm-auth-eye: #8a99a6;
    --cm-auth-pill-track-bg: #eef1f5;
    --cm-auth-pill-track-border: #dde5ec;
    --cm-auth-pill-active-bg: #fff;
    --cm-auth-pill-active-tx: #12344d;
    --cm-auth-pill-inactive-tx: #8a99a6;
    --cm-auth-error-bg: #fdecec;
    --cm-auth-error-border: #e6b8b8;
    --cm-auth-error-tx: #8c2f2f;
    --cm-auth-warn-bg: #fdf3e2;
    --cm-auth-warn-border: #ecd7a8;
    --cm-auth-warn-tx: #6b5426;
    --cm-auth-btn-disabled-bg: #e6ebf0;
    --cm-auth-btn-disabled-tx: #9aa7b0;
    --cm-auth-strength-track: #e6ebf0;
    --cm-auth-criteria-tx: #5b6c79;
    --cm-auth-criteria-muted-tx: #8a99a6;
    --cm-auth-criteria-muted-border: #cfd9e2;
    --cm-auth-criteria-muted-dot: #b3c0cb;
}
[data-bs-theme="dark"] .cm-authcard {
    --cm-auth-grad-1: #050f18; --cm-auth-grad-2: #081a28; --cm-auth-grad-3: #124668;
    --cm-auth-circle-1: rgba(255, 255, 255, .03);
    --cm-auth-circle-2: rgba(201, 160, 78, .10);
    --cm-auth-card-bg: #0f1f2c;
    --cm-auth-card-border: #1d3145;
    --cm-auth-card-shadow: 0 30px 70px rgba(0, 0, 0, .5);
    --cm-auth-medallion-shadow: 0 20px 44px rgba(0, 0, 0, .5);
    --cm-auth-title: #e6eef5;
    --cm-auth-since-tx: #c9a04e;
    --cm-auth-tabs-track: #16283a;
    --cm-auth-tab-active-shadow: none;
    --cm-auth-tab-inactive: #8fa8bd;
    --cm-auth-tab-inactive-hover-bg: #1d3145;
    --cm-auth-label: #bcd4e6;
    --cm-auth-field-border: #2c4560;
    --cm-auth-field-bg: #0a1722;
    --cm-auth-field-text: #e6eef5;
    --cm-auth-focus-halo: rgba(27, 107, 168, .22);
    --cm-auth-footer-border: #1d3145;
    --cm-auth-footer-tx: #6c8399;
    --cm-auth-footer-phone: #8fa8bd;
    --cm-auth-eye: #6c8399;
    --cm-auth-pill-track-bg: #18293a;
    --cm-auth-pill-track-border: #24394d;
    --cm-auth-pill-active-bg: #c9a04e;
    --cm-auth-pill-active-tx: #12344d;
    --cm-auth-pill-inactive-tx: #8fa8bd;
    --cm-auth-error-bg: var(--cm-red-bg);
    --cm-auth-error-border: #5a3229;
    --cm-auth-error-tx: var(--cm-red-tx);
    --cm-auth-warn-bg: #2c2416;
    --cm-auth-warn-border: #5a4a26;
    --cm-auth-warn-tx: #e3c98d;
    --cm-auth-btn-disabled-bg: #16283a;
    --cm-auth-btn-disabled-tx: #4d6a85;
    --cm-auth-strength-track: #16283a;
    --cm-auth-criteria-tx: #8fa8bd;
    --cm-auth-criteria-muted-tx: #6c8399;
    --cm-auth-criteria-muted-border: #2b435a;
    --cm-auth-criteria-muted-dot: #4d6a85;
}

/* The forced password-renewal tunnel is the one auth-card screen reached while ALREADY signed in,
   so the viewer's theme preference is known - and the public screens' fixed navy backdrop read as
   "the app just switched itself to dark on me". Only the light side is restated here: the rule
   above is more specific (attribute + class), so in dark theme the existing dark values keep
   winning, which is already what a dark-theme user wants. */
.cm-authcard--themed {
    --cm-auth-grad-1: var(--cm-bg); --cm-auth-grad-2: var(--cm-bg); --cm-auth-grad-3: var(--cm-bg);
    --cm-auth-circle-1: rgba(18, 52, 77, .05);
    --cm-auth-circle-2: rgba(201, 160, 78, .13);
    --cm-auth-card-bg: var(--cm-surface);
    --cm-auth-card-border: var(--cm-border);
    --cm-auth-card-shadow: 0 18px 44px rgba(18, 52, 77, .13);
    --cm-auth-medallion-shadow: 0 12px 28px rgba(18, 52, 77, .18);
}

.cm-authcard {
    min-height: 100vh; min-height: 100dvh; position: relative;
    overflow-x: hidden; overflow-y: auto;
    display: grid; place-items: center;
    background: linear-gradient(150deg, var(--cm-auth-grad-1), var(--cm-auth-grad-2) 55%, var(--cm-auth-grad-3));
}
.cm-authcard__circle { position: absolute; border-radius: 50%; pointer-events: none; }
.cm-authcard__circle--1 { left: -140px; bottom: -180px; width: 520px; height: 520px; border: 80px solid var(--cm-auth-circle-1); }
.cm-authcard__circle--2 { right: -60px; top: -120px; width: 320px; height: 320px; border: 52px solid var(--cm-auth-circle-2); }

/* Tabler sprite icons (<svg class="icon"><use ...>) render unstyled outside of Tabler's own
   scoped contexts (avatar/badge/btn) - the sprite's <path> elements carry no fill/stroke of their
   own, so without this they'd paint as solid black shapes instead of outline icons. */
.cm-authcard .icon { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- Labeled theme pill (distinct from the authenticated navbar's .cm-theme-toggle
   slide-switch above - a second, separate component sharing the same theme_controller.js) ---------- */
.cm-theme-pill {
    position: absolute; top: 24px; right: 34px; z-index: 1;
    display: flex; align-items: center; gap: 6px;
    background: var(--cm-auth-pill-track-bg); border: 1px solid var(--cm-auth-pill-track-border);
    border-radius: 999px; padding: 4px;
}
.cm-theme-pill__seg {
    display: flex; align-items: center; gap: 7px; border: 0; cursor: pointer;
    border-radius: 999px; padding: 6px 13px; font: 600 12.5px var(--cm-font-ui);
    color: var(--cm-auth-pill-inactive-tx); background: transparent;
}
.cm-theme-pill__seg:hover { color: var(--cm-auth-title); }
.cm-theme-pill__seg .icon { width: 15px; height: 15px; }
/* Active segment driven purely by [data-bs-theme] (same convention as the rest of the app), not a
   JS-toggled class - so the correct segment is already highlighted on first paint, no flash. */
.cm-theme-pill__seg--light { background: var(--cm-auth-pill-active-bg); color: var(--cm-auth-pill-active-tx); box-shadow: var(--cm-auth-tab-active-shadow); }
[data-bs-theme="dark"] .cm-theme-pill__seg--light { background: transparent; color: var(--cm-auth-pill-inactive-tx); box-shadow: none; }
[data-bs-theme="dark"] .cm-theme-pill__seg--dark { background: var(--cm-auth-pill-active-bg); color: var(--cm-auth-pill-active-tx); box-shadow: var(--cm-auth-tab-active-shadow); }

/* ---------- Card + medallion ---------- */
/* max-width uses vw, not % - .cm-authcard is a `display: grid; place-items: center` container,
   whose implicit column track auto-sizes to its item's own content (520px) rather than stretching
   to the container's full width, so a percentage-based max-width here would resolve against that
   520px track (i.e. against itself) instead of the actual viewport. */
.cm-authcard__wrap { position: relative; width: 520px; max-width: calc(100vw - 32px); margin-top: 52px; }
.cm-authcard__medallion {
    position: absolute; top: -84px; left: 50%; transform: translateX(-50%);
    width: 168px; height: 168px; border-radius: 50%;
    background: var(--cm-brand); border: 8px solid var(--cm-auth-card-bg);
    box-shadow: var(--cm-auth-medallion-shadow);
    display: grid; place-items: center;
}
[data-bs-theme="dark"] .cm-authcard__medallion { background: var(--cm-brand-strong); }
.cm-authcard__medallion img { width: 78px; height: 101px; object-fit: contain; }
.cm-authcard__card {
    background: var(--cm-auth-card-bg); border: 1px solid var(--cm-auth-card-border);
    border-radius: 20px; padding: 106px 44px 34px; box-shadow: var(--cm-auth-card-shadow);
    display: flex; flex-direction: column; gap: 18px; box-sizing: border-box;
}
.cm-authcard__brand { text-align: center; }
.cm-authcard__brand-name { font: 600 24px var(--cm-font-title); color: var(--cm-auth-title); }
.cm-authcard__since { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 8px; }
.cm-authcard__since span:not(.cm-authcard__since-label) { width: 34px; height: 1px; background: var(--cm-gold); }
.cm-authcard__since-label { font: 600 10.5px var(--cm-font-ui); color: var(--cm-auth-since-tx); letter-spacing: 3px; text-transform: uppercase; }

/* ---------- Mode tabs (Mot de passe / Lien par e-mail) ---------- */
.cm-authcard__tabs { display: flex; background: var(--cm-auth-tabs-track); border-radius: 11px; padding: 4px; gap: 4px; }
.cm-authcard__tab {
    flex: 1; min-width: 0; text-align: center; padding: 10px 6px; border-radius: 8px; border: 0; cursor: pointer;
    background: transparent; font: 600 13.5px var(--cm-font-ui); color: var(--cm-auth-tab-inactive);
    display: flex; align-items: center; justify-content: center; gap: 6px; overflow: hidden; white-space: nowrap;
}
.cm-authcard__tab:hover:not(.is-active) { background: var(--cm-auth-tab-inactive-hover-bg); color: var(--cm-auth-title); }
.cm-authcard__tab.is-active { background: var(--cm-auth-field-bg); color: var(--cm-auth-title); font-weight: 700; box-shadow: var(--cm-auth-tab-active-shadow); }
[data-bs-theme="dark"] .cm-authcard__tab.is-active { background: var(--cm-brand); color: #fff; }
.cm-authcard__tab .icon { width: 15px; height: 15px; }

/* ---------- Fields ---------- */
.cm-authcard__field-group { display: flex; flex-direction: column; gap: 6px; }
.cm-authcard__label { font: 600 12.5px var(--cm-font-ui); color: var(--cm-auth-label); display: flex; flex-wrap: wrap; row-gap: 4px; }
.cm-authcard__label-link { margin-left: auto; font: 600 12px var(--cm-font-ui); color: var(--cm-brand); text-decoration: none; white-space: nowrap; }
.cm-authcard__label-link:hover { color: var(--cm-brand-strong); }
.cm-authcard__input-wrap { position: relative; display: flex; align-items: center; }
.cm-authcard__input {
    width: 100%; border: 1px solid var(--cm-auth-field-border); border-radius: 10px;
    padding: 13px 15px; font-size: 14.5px; color: var(--cm-auth-field-text);
    background: var(--cm-auth-field-bg); box-sizing: border-box;
}
.cm-authcard__input:focus {
    outline: 0; border-color: var(--cm-brand); box-shadow: 0 0 0 3px var(--cm-auth-focus-halo);
}
.cm-authcard__input--password { padding-right: 42px; }
.cm-authcard__eye {
    position: absolute; right: 15px; top: 50%; transform: translateY(-50%);
    background: none; border: 0; padding: 0; cursor: pointer; color: var(--cm-auth-eye); line-height: 0;
}
.cm-authcard__eye .icon { width: 18px; height: 18px; }

/* ---------- Primary button ---------- */
.cm-authcard__btn {
    border: 0; background: var(--cm-brand); color: #fff; border-radius: 10px;
    padding: 15px; font: 700 15px var(--cm-font-ui); cursor: pointer; min-height: 48px;
    display: flex; align-items: center; justify-content: center; gap: 8px;
}
.cm-authcard__btn:hover { background: var(--cm-brand-strong); }
[data-bs-theme="dark"] .cm-authcard__btn:hover { background: #2585c9; }
.cm-authcard__btn:disabled {
    background: var(--cm-auth-btn-disabled-bg); color: var(--cm-auth-btn-disabled-tx); cursor: not-allowed;
}
.cm-authcard__btn:disabled:hover { background: var(--cm-auth-btn-disabled-bg); }
.cm-authcard__btn .spinner-border { width: 16px; height: 16px; border-width: 2px; }

/* ---------- Flash/error banner ---------- */
.cm-authcard__alert {
    background: var(--cm-auth-error-bg); border: 1px solid var(--cm-auth-error-border);
    color: var(--cm-auth-error-tx); border-radius: 10px; padding: 12px 15px; font-size: 13.5px;
}
.cm-authcard__alert--success { background: var(--cm-green-bg); border-color: var(--cm-green-bg); color: var(--cm-positive-tx); }

/* ---------- Help text under the magic-link button ---------- */
.cm-authcard__hint { margin: 0; text-align: center; font-size: 12.5px; line-height: 1.5; color: var(--cm-auth-footer-tx); }
.cm-authcard__hint b { color: var(--cm-auth-label); }

/* ---------- Footer ---------- */
.cm-authcard__footer {
    border-top: 1px solid var(--cm-auth-footer-border); padding-top: 14px;
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    font-size: 12.5px; color: var(--cm-auth-footer-tx); text-align: center;
}
.cm-authcard__footer a { font-weight: 600; text-decoration: none; color: var(--cm-brand); }
[data-bs-theme="dark"] .cm-authcard__footer a { color: #7fb3dc; }
.cm-authcard__footer a:hover { text-decoration: underline; }
.cm-authcard__footer b { color: var(--cm-auth-footer-phone); }
.cm-authcard__footer button.cm-authcard__footer-link {
    background: none; border: 0; padding: 0; font: inherit; font-weight: 600; cursor: pointer; color: var(--cm-brand);
}
[data-bs-theme="dark"] .cm-authcard__footer button.cm-authcard__footer-link { color: #7fb3dc; }

/* ---------- Success/expired/invalid state icon pastilles (contact-email confirm, 7c) ---------- */
.cm-authcard__pastille { width: 66px; height: 66px; border-radius: 50%; display: grid; place-items: center; font-size: 31px; margin: 0 auto; }
.cm-authcard__pastille--success { background: var(--cm-green-bg); color: var(--cm-positive-tx); }
.cm-authcard__pastille--warn { background: var(--cm-gold); color: var(--cm-navy); }
.cm-authcard__pastille--error { background: var(--cm-red-bg); color: var(--cm-red-tx); }
.cm-authcard__pastille--info { background: var(--cm-blue-bg); color: var(--cm-brand); }
.cm-authcard__pastille .icon { width: 31px; height: 31px; }
.cm-authcard__state { text-align: center; }
.cm-authcard__state h2 { margin: 0; font: 600 22px var(--cm-font-title); color: var(--cm-auth-title); }
.cm-authcard__state p { margin: 9px 0 0; font-size: 14px; line-height: 1.65; color: var(--cm-auth-footer-phone); }
.cm-authcard__state p b { color: var(--cm-auth-label); }

/* ---------- Account-recall card (7c) - reuses .cm-user__avatar's square-gold-avatar look ---------- */
.cm-authcard__account {
    background: var(--cm-auth-field-bg); border: 1px solid var(--cm-auth-field-border); border-radius: 10px;
    padding: 14px 18px; font-size: 13px; color: var(--cm-auth-footer-phone);
    display: flex; align-items: center; gap: 12px; box-sizing: border-box;
}
.cm-authcard__account b { color: var(--cm-auth-label); }

/* ---------- Screen title outside of the .cm-authcard__state pastille shape (17) ---------- */
.cm-authcard__title { margin: 0; text-align: center; font: 600 21px var(--cm-font-title); color: var(--cm-auth-title); }

/* ---------- Ambre notice banner (forced renewal's mandatory-step notice, 5-minute propagation
   notice) - a deliberately separate token set from the app-wide --cm-gold-bg/--cm-gold-tx pair
   (used by the authenticated shell's own warning badges), since design/design_handoff_pages_arrivee
   gives this public surface its own pixel-exact values, same convention as the rest of
   --cm-auth-*. ---------- */
.cm-authcard__banner {
    background: var(--cm-auth-warn-bg); border: 1px solid var(--cm-auth-warn-border); border-radius: 10px;
    padding: 11px 14px; display: flex; gap: 11px; align-items: flex-start; box-sizing: border-box;
}
.cm-authcard__banner-icon {
    flex: none; width: 22px; height: 22px; border-radius: 50%; background: var(--cm-gold); color: var(--cm-navy);
    display: grid; place-items: center; font: 700 13px var(--cm-font-ui); line-height: 1;
}
.cm-authcard__banner > span:last-child { font-size: 12.5px; line-height: 1.5; color: var(--cm-auth-warn-tx); }
.cm-authcard__banner b { color: inherit; }

/* ---------- Password strength meter (17) ---------- */
.cm-authcard__strength { display: flex; align-items: center; gap: 9px; margin-top: 3px; }
.cm-authcard__strength-track {
    flex: 1; height: 5px; border-radius: 3px; background: var(--cm-auth-strength-track);
    display: flex; gap: 3px; overflow: hidden;
}
.cm-authcard__strength-seg { flex: 1; border-radius: 2px; }
.cm-authcard__strength-label { font: 600 11.5px var(--cm-font-ui); color: var(--cm-auth-footer-tx); white-space: nowrap; }

/* ---------- Confirmation field match indicator (17) ---------- */
.cm-authcard__match-icon { margin-left: auto; font-size: 14px; line-height: 1; }
.cm-authcard__match-icon--ok { color: var(--cm-positive-tx); }
.cm-authcard__match-icon--bad { color: var(--cm-red-tx); }

/* ---------- Password criteria checklist (17) - 2 columns desktop, 1 column mobile below ---------- */
.cm-authcard__criteria { display: grid; grid-template-columns: 1fr 1fr; gap: 7px 16px; font-size: 12.5px; color: var(--cm-auth-criteria-tx); }
.cm-authcard__criterion { display: flex; align-items: center; gap: 7px; }
.cm-authcard__criterion-dot {
    flex: none; width: 16px; height: 16px; border-radius: 50%; display: grid; place-items: center; font-size: 10px;
    border: 1px solid var(--cm-auth-criteria-muted-border); color: var(--cm-auth-criteria-muted-dot);
}
.cm-authcard__criterion.is-met { color: var(--cm-auth-criteria-tx); }
.cm-authcard__criterion.is-met .cm-authcard__criterion-dot {
    background: var(--cm-green-bg); color: var(--cm-positive-tx); border-color: transparent;
}
.cm-authcard__criterion:not(.is-met) { color: var(--cm-auth-criteria-muted-tx); }

/* Mobile cutoff matches design/design_handoff_pages_arrivee/README.md's "Desktop (≥560px) /
   Mobile (<560px)" split exactly (previously 600px). */
@media (max-width: 559px) {
    .cm-authcard {
        display: flex; flex-direction: column; align-items: center; justify-content: center;
        padding: 16px 0; gap: 16px; overflow-x: hidden; overflow-y: auto;
    }
    /* Theme pill leaves the medallion's airspace entirely on narrow viewports - moves from an
       absolutely-positioned top-right pill to a normal flex item below the card. */
    .cm-theme-pill { position: static; order: 2; }
    .cm-authcard__wrap { order: 1; margin-top: 44px; }
    .cm-authcard__card { padding: 70px 20px 22px; border-radius: 18px; gap: 15px; }
    .cm-authcard__medallion { width: 112px; height: 112px; top: -56px; border-width: 6px; }
    .cm-authcard__medallion img { width: 52px; height: 67px; }
    .cm-authcard__brand-name { font-size: 19px; }
    .cm-authcard__title, .cm-authcard__state h2 { font-size: 18px; }
    .cm-authcard__input, .cm-authcard__btn, .cm-authcard__tab { min-height: 48px; }
    .cm-authcard__input { font-size: 15px; }
    .cm-authcard__criteria { grid-template-columns: 1fr; }
}

/* Visible focus ring on every interactive control in the auth card - keyboard nav requirement
   (design/design_handoff_connexion/README.md's "Focus visible obligatoire"). */
.cm-authcard__tab:focus-visible,
.cm-authcard__btn:focus-visible,
.cm-authcard__eye:focus-visible,
.cm-authcard__input:focus-visible,
.cm-theme-pill__seg:focus-visible,
.cm-authcard__footer a:focus-visible,
.cm-authcard__footer-link:focus-visible {
    outline: 0; box-shadow: 0 0 0 3px rgba(27, 107, 168, .25);
}

/* ==========================================================================
   Message / ticket conversation bubbles - reused by messages/show.html.twig
   and ticket/show.html.twig (no Tabler equivalent; replaces .timeline there)
   ========================================================================== */
.cm-thread { display: flex; flex-direction: column; gap: 14px; }
.cm-bubble { display: flex; gap: 12px; }
.cm-bubble__avatar {
    width: 34px; height: 34px; flex: none; border-radius: 50%;
    display: grid; place-items: center; font-weight: 700; font-size: 12px;
    background: var(--cm-blue-bg); color: var(--cm-blue-tx);
}
.cm-bubble__body {
    flex: 1; min-width: 0; background: var(--cm-surface-alt); border: 1px solid var(--cm-border-soft);
    border-radius: 0 12px 12px 12px; padding: 12px 16px;
}
.cm-bubble--internal .cm-bubble__body { background: var(--cm-gold-bg); border-color: var(--cm-gold); border-left: 3px solid var(--cm-gold); }
.cm-bubble--highlight .cm-bubble__body { background: var(--cm-blue-bg); border-color: var(--cm-brand); }
.cm-bubble__head { display: flex; align-items: center; font-size: 12.5px; gap: 8px; }
.cm-bubble__head time, .cm-bubble__meta { margin-left: auto; color: var(--cm-faint); }
.cm-bubble__text { margin: 8px 0 0; font-size: 13.5px; line-height: 1.6; color: var(--cm-text); }

/* ---------- Properties side panel (ticket detail) ---------- */
.cm-props { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 16px 18px; display: flex; flex-direction: column; gap: 12px; }
.cm-props__title { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); border-bottom: 1px solid var(--cm-border-soft); padding-bottom: 8px; }
.cm-props__field { display: flex; flex-direction: column; gap: 5px; }
.cm-props__field label { font: 600 12px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .4px; }

/* ==========================================================================
   FullCalendar (.cm-calendar) - CSS-var driven, plugin's own class names
   ========================================================================== */
.cm-calendar {
    --fc-border-color: var(--cm-border-soft);
    --fc-page-bg-color: var(--cm-surface);
    --fc-neutral-bg-color: var(--cm-surface-alt);
    --fc-today-bg-color: var(--cm-blue-bg);
    --fc-button-bg-color: var(--cm-surface);
    --fc-button-text-color: var(--cm-brand-strong);
    --fc-button-border-color: var(--cm-border);
    --fc-button-hover-bg-color: var(--cm-bg);
    --fc-button-hover-border-color: var(--cm-border);
    --fc-button-active-bg-color: var(--cm-brand);
    --fc-button-active-border-color: var(--cm-brand);
    --fc-event-border-color: transparent;
    --fc-now-indicator-color: #c0392b;
    background: var(--cm-surface); border: 1px solid var(--cm-border);
    border-radius: var(--cm-radius); overflow: hidden;
}
.cm-calendar .fc-button-active { color: #fff !important; }
.cm-calendar .fc-col-header-cell-cushion,
.cm-calendar .fc-timegrid-slot-label-cushion { color: var(--cm-muted); font: 600 13px var(--cm-font-ui); }
.cm-calendar .fc-event { border-radius: 6px; border-left: 3px solid; padding: 3px 6px; font: 400 11.5px var(--cm-font-ui); }
.cm-calendar .fc-toolbar-title { font: 600 19px var(--cm-font-title); color: var(--cm-ink); }

/* Clickable timetable legend swatches (program/_timetable_legend.html.twig,
   teacher/_timetable_legend.html.twig) - button reset so they look like the old plain-div/badge
   legend, plus a greyed-out .is-inactive state for the ones a click has hidden (toggled by
   assets/controllers/lesson_timetable_controller.js's toggleLegend action). */
.cm-legend-item {
    display: inline-flex; align-items: center; gap: 6px; border: 0; background: none; padding: 0;
    font: inherit; color: var(--cm-ink); cursor: pointer;
}
.cm-legend-item:hover { color: var(--cm-brand-strong); }
.cm-legend-item.is-inactive { color: var(--cm-faint); }
.cm-legend-item.is-inactive .badge { filter: grayscale(1); opacity: .5; }

/* ==========================================================================
   DataTables (datatables.net-bs5) - targets the plugin's own class names
   ========================================================================== */
/* Same headers and same rows for tables hand-written inside a card (.card-table) as for the ones
   DataTables manages just below: the mockups do not distinguish the two, and half the application's
   tables are in that case (the condition list 25c, for instance). */
.card-table thead th {
    background: var(--cm-surface-alt); color: var(--cm-muted);
    font: 600 11px var(--cm-font-ui); text-transform: uppercase; letter-spacing: .7px;
    border-bottom: 1px solid var(--cm-border); padding: 11px 20px;
}
.card-table tbody td { padding: 13px 20px; border-bottom: 1px solid var(--cm-border-faint); font-size: 13.5px; }

/* Toolbar and footnote of a table hand-written inside a card. DataTables generates its own and the
   Stimulus controller dresses them (see styleWrapper()); a plain table has nobody to do it, and its
   content ended up flush against the card edges. Same measurements as the DataTables blocks, so the
   two families of screens look alike. */
/* Rich content displayed without its editor (cahier de texte read without edit rights): a field's
   box, but read-only, with the breathing room of prose rather than of an input. */
.cm-readonly-html { border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); padding: 12px 14px; background: var(--cm-surface); font-size: 13.5px; line-height: 1.6; }
.cm-readonly-html > :last-child { margin-bottom: 0; }

.cm-card-toolbar { display: flex; align-items: center; gap: 14px; padding: 14px 20px; border-bottom: 1px solid var(--cm-border-soft); }
/* A nowrap control (the date nav) would otherwise push itself out of a narrow card instead of
   letting the title shrink - flex items refuse to go below their content width without this. */
.cm-card-toolbar > * { min-width: 0; }
.cm-card-note { padding: 12px 20px; font-size: 12.5px; color: var(--cm-faint); }
/* Legend on the left, the figure it summarises pushed to the right edge of the same line. */
.cm-card-note--split { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }

/* Neither zebra striping nor blue hover: the mockups show white rows separated by a very light
   rule, and a barely tinted hover. Both variables are declared by Tabler on .table itself -
   redeclaring them on :root would therefore have no effect, whatever the specificity.
   Templates keep their .table-striped class: it simply stays without effect, rather than touring
   the twenty-odd tables of the application to remove it. */
.table {
    --tblr-table-striped-bg: transparent;
    --tblr-table-hover-bg: var(--cm-surface-alt);
}

table.dataTable { border-collapse: collapse; width: 100%; color: var(--cm-text); font-size: 13.5px; }
/* ".table" qualifier (present on every DataTable here - see datatable_controller.js) matches
   datatables.bootstrap5.min.css's own "table.table.dataTable > :not(caption) > * > *
   { background-color: var(--bs-table-bg) }" (2 classes) in specificity, same reasoning as the
   cm-dt-group/subtotal/total rules below - a bare "table.dataTable thead th" (1 class) loses to
   it regardless of load order, leaving thead's background transparent. */
table.dataTable.table thead th {
    /* Grey and not brand blue: this is the column header color of the reference mockups
       (25a/25d of the UFA handoff), where blue is reserved for links. */
    background: var(--cm-surface-alt); color: var(--cm-muted);
    font: 600 11px var(--cm-font-ui); text-transform: uppercase; letter-spacing: .7px;
    /* A plain rule, not the 2px blue bar inherited from Tabler: in the mockups blue is reserved
       for links and for the main action. */
    border-bottom: 1px solid var(--cm-border); padding: 11px 20px; text-align: left;
}
table.dataTable tbody td { padding: 13px 20px; border-bottom: 1px solid var(--cm-border-faint); }
/* DataTables marks a column "dt-type-numeric" as soon as its values look like numbers and aligns it
   right. An inventory or room number is not a quantity: the mockups align every column left, only
   the actions column sits right. Tables that really want a right-aligned numeric column say so with
   .text-end, which still wins. */
table.dataTable td.dt-type-numeric, table.dataTable th.dt-type-numeric { text-align: left; }
/* DataTables striping and hover. They are painted not as a background but as a 9999px inset shadow
   on each cell (dataTables.bootstrap5.min.css) - a background on the <tr>, as the previous rules
   did, therefore passed underneath and changed nothing on screen. Two consequences for what is
   written below: box-shadow is what must be neutralised, and the DataTables stylesheet is injected
   by its own Stimulus controller, hence AFTER app.css - whence the repeated ".table", which puts
   these selectors ahead of its own when everything else ties.
   The striping disappears (white rows in the mockups) and the hover takes the very light grey of
   the mockups rather than the blue it had taken here. */
table.table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * { box-shadow: none; }
table.table.table.dataTable.table-hover > tbody > tr:hover > * { box-shadow: inset 0 0 0 9999px var(--cm-surface-alt); }
table.dataTable td.dt-empty { color: var(--cm-muted); }
.dataTables_wrapper .dataTables_length select,
.dataTables_wrapper .dataTables_filter input {
    border: 1px solid var(--cm-border); border-radius: 7px; padding: 5px 10px;
    background: var(--cm-surface); color: var(--cm-ink);
}
.dataTables_wrapper .dataTables_info { color: var(--cm-muted); font-size: 13px; }

/* The three rules above date from DataTables 1.x. The version in service here is 2.x, which names
   its blocks .dt-length / .dt-search / .dt-info: without the selectors below, those controls fell
   back on Tabler's .form-select/.form-control, far from the mockups. */
/* Selectors deliberately more specific than DataTables' own
   ("div.dt-container div.dt-search input{width:auto}", 3 classes): its stylesheet is injected by
   the Stimulus controller, hence after app.css, and would win on a tie. */
div.dt-container div.dt-length select.form-select {
    width: auto; font-size: 13px; padding: 7px 30px 7px 11px; border-radius: var(--cm-radius-sm);
    color: var(--cm-ink);
}
/* Search field: the magnifier is a background image rather than an icon in the template, because
   DataTables generates that block itself and we have no hold on its markup. */
div.dt-container div.dt-search input.form-control {
    width: 230px; max-width: 100%; font-size: 13px; padding: 8px 12px 8px 34px;
    border-radius: var(--cm-radius-sm); color: var(--cm-ink);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23a9b6c1' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='6.5'/%3E%3Cpath d='m16 16 4 4'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: left 12px center; background-size: 14px 14px;
}
.dt-container .dt-info { font-size: 12.5px; color: var(--cm-faint); }

/* Pagination: the mockup's pills (12.5px, very light outline, radius 7, 11px of side breathing
   room), where Bootstrap shipped 14px with no outline, almost no inner padding and everything
   flush. The mockups only show a single-page case, so with Précédente/Suivante inactive: the link
   blue for the active state is an extrapolation, consistent with the rest of the links. */
.dt-container .dt-paging .pagination { gap: 6px; }
.dt-container .dt-paging .page-link {
    font-size: 12.5px; padding: 5px 11px; border-radius: 7px; margin: 0;
    border: 1px solid var(--cm-border-soft); background: var(--cm-surface); color: var(--cm-brand-strong);
}
.dt-container .dt-paging .page-item.active .page-link {
    background: var(--cm-brand); border-color: var(--cm-brand); color: #fff; font-weight: 600;
}
.dt-container .dt-paging .page-item.disabled .page-link { color: var(--cm-disabled); }
.dataTables_wrapper .dataTables_paginate .paginate_button {
    border: 1px solid var(--cm-border) !important; border-radius: 7px !important;
    background: var(--cm-surface) !important; color: var(--cm-brand-strong) !important;
    padding: 5px 12px; margin-left: 4px;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
    background: var(--cm-brand) !important; border-color: var(--cm-brand) !important; color: #fff !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled { color: var(--cm-border) !important; }

/* ==========================================================================
   Row/list actions (.cm-actions) - plain color-coded text links/buttons at
   the end of a table row or list item, replacing the previous Bootstrap
   ghost-button treatment (assets/controllers/datatable_controller.js).
   Not in campus-theme.css verbatim (the handoff file has no dedicated
   action-column pattern) - generalizes the reference mockups' link-style
   actions into one reusable component instead of every screen inventing
   its own button classes. className: 'cm-actions' on a DataTables column
   definition puts this class on both the <th> (hence the separate
   text-align rule) and every <td> in that column.
   Some actions are a <button> (POST/fetch - deactivate/remove/duplicate/
   add/reveal) rather than an <a> (navigation - edit/print/pdf/lend/return/
   select) - both get the same reset + link look, since the distinction is
   about behavior, not appearance.
   ========================================================================== */
/* Qualified the same way as "table.dataTable.table thead th" above (3 classes/types vs. that
   rule's 2/3) - a bare "th.cm-actions" loses the text-align tie-break to that rule, which also
   sets text-align (to "left", for every other column), regardless of source order. */
table.dataTable.table thead th.cm-actions { text-align: right; }
/* Deliberately NOT display:flex: a flex th/td stops being a real table-cell (the browser
   blockifies it out of table layout), so unlike every untouched sibling cell it no longer
   automatically fills the row's height, and - the more visible symptom - its own border-bottom
   ends up painted 1 device pixel off from the row's shared border line, since it's no longer
   subject to the table layout engine's same border pixel-snapping. Text-align + inline-block
   children + margin-left reproduces "right-aligned, evenly gapped, vertically centered" without
   leaving table-cell layout at all. */
td.cm-actions, .cm-actions {
    text-align: right;
    white-space: nowrap;
    font: 600 12.5px var(--cm-font-ui);
}
.cm-actions > * {
    display: inline-block;
    vertical-align: middle;
}
/* :not(:first-child) also bumps specificity to 2 classes/1 type, needed to beat the "margin: 0"
   reset below (1 class/1 type) regardless of source order. */
.cm-actions > *:not(:first-child) {
    margin-left: 14px;
}
/* Tabler's own ".card-table tr td:last-child { padding-right: 1.25rem }" (edge padding so table
   content doesn't touch the card's rounded corner) otherwise wins over the plain 16px td padding
   above - same 2-class/2-type specificity as that selector so load order (app.css last) breaks
   the tie, rather than an !important, to keep the "9px 16px on every side" rule from the créa. */
.card-table tr td.cm-actions { padding-right: 16px; }
.cm-actions a, .cm-actions button {
    background: none; border: 0; padding: 0; margin: 0; cursor: pointer;
    text-decoration: none; font: inherit;
}
.cm-actions a:hover, .cm-actions button:hover { text-decoration: underline; }
/* --warning carries the main action of a row (Modifier, almost everywhere). Its name comes from the
   gold it originally had, taken from the project handoff; the UFA handoff, which is now the
   reference for the whole application, gives it in blue on all 32 of its action links without
   exception. The class keeps its name - renaming it would mean touching a dozen or so templates and
   the DataTables controller without changing anything on screen. */
.cm-actions .cm-action--positive { color: var(--cm-positive-tx); }
.cm-actions .cm-action--neutral  { color: var(--cm-muted); }
.cm-actions .cm-action--warning  { color: var(--cm-action-main); }
.cm-actions .cm-action--danger   { color: var(--cm-action-danger); }
/* An action the row's own state rules out, kept visible so the column keeps the same shape from one
   row to the next ("Courses" on an e-CO parcours that isn't ready yet). Carried by a <span>, never
   by a link: there is nothing to click. */
.cm-actions .cm-action--off      { color: var(--cm-disabled); }

/* The whole of a row's actions folded into one field (machines list). Tighter than a form select
   because it lives inside a table row, and left to size itself on its widest entry so the column
   does not jump between a machine that is running and one that is not. */
.cm-select--action { padding: 4px 7px; font-size: 12px; }
/* Under the select rather than beside it, which is what lets an operation in flight report itself
   without taking the actions away. Qualified with the element so it beats the inline-block and the
   14px gap that .cm-actions gives every child. */
td.cm-actions .cm-actions__note {
    display: block;
    margin-left: 0;
    margin-top: 4px;
    white-space: normal;
}

/* ---------- RowGroup extension: group / subtotal / grand-total rows (tour 2, 2e) ----------
   Selectors are qualified with "table.dataTable" (rather than a bare "tr.cm-dt-*") to out-
   specificity datatables.bootstrap5.min.css's own `table.table.dataTable > :not(caption) > * > *
   { background-color: var(--bs-table-bg) }` (2 classes), which otherwise wins the cascade over a
   1-class selector regardless of source order and leaves these rows fully transparent. */
table.dataTable tr.cm-dt-group td {
    background: var(--cm-blue-bg); color: var(--cm-blue-tx);
    font: 700 12.5px var(--cm-font-ui) !important; padding: 9px 16px;
}
table.dataTable tr.cm-dt-subtotal td {
    background: color-mix(in srgb, var(--cm-blue-bg) 50%, var(--cm-surface));
    color: var(--cm-blue-tx); font: 600 12.5px var(--cm-font-ui) !important; font-style: italic;
    padding: 8px 16px;
}
table.dataTable tr.cm-dt-total td, table.dataTable tr.cm-dt-total th {
    background: var(--cm-blue-bg); color: var(--cm-blue-tx);
    font: 700 13px var(--cm-font-ui) !important;
    border-top: 2px solid var(--cm-brand) !important; padding: 10px 16px;
}

/* ==========================================================================
   Tom Select (base: tom-select.bootstrap5.css) - wrapper inherits .form-select
   ========================================================================== */
.ts-wrapper.form-select, .ts-wrapper .ts-control {
    background-color: var(--cm-surface); border-color: var(--cm-border);
    border-radius: var(--cm-radius-sm); color: var(--cm-ink); font: 400 14px var(--cm-font-ui);
}
.ts-wrapper.focus .ts-control {
    border-color: var(--cm-brand);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-brand) 18%, transparent);
}
.ts-wrapper.disabled .ts-control { background: var(--cm-border-soft); opacity: .7; }
.ts-wrapper.multi .ts-control > .item {
    position: relative; overflow: visible;
    background: var(--cm-blue-bg); color: var(--cm-blue-tx);
    font: 600 12px var(--cm-font-ui); padding: 4px 12px; border-radius: 7px; margin: 2px 8px 2px 0;
}
/* The remove_button plugin zeroes the right padding with !important, to clear the room it needs for
   a cross pinned against the chip's right edge. Ours is a badge sitting on the corner and needs no
   room at all, so the padding has to be put back - without this the label touches the edge and the
   chip reads lopsided. Applies to every multi picker in the app. */
.ts-wrapper.multi.plugin-remove_button:not(.rtl) .ts-control > .item { padding-right: 12px !important; }
.ts-wrapper.multi .ts-control > .item .remove {
    position: absolute; top: -6px; right: -6px; box-sizing: border-box;
    width: 15px; height: 15px; border-radius: 50%; border: 2px solid var(--cm-surface);
    background: var(--cm-navy); color: #fff; font-size: 9px; line-height: 11px;
    text-align: center; padding: 0; margin: 0;
}
.ts-wrapper.multi .ts-control > .item .remove:hover { background: #a43e2e; }
.ts-dropdown {
    background: var(--cm-surface); border: 1px solid var(--cm-border);
    border-radius: 0 0 10px 10px; box-shadow: var(--cm-shadow-pop); color: var(--cm-text);
}
.ts-dropdown .optgroup-header {
    font: 700 11px var(--cm-font-ui); color: var(--cm-faint);
    text-transform: uppercase; letter-spacing: .5px; background: transparent;
}
.ts-dropdown .option { padding: 9px 12px; font-size: 13.5px; }
.ts-dropdown .option:hover { background: var(--cm-surface-alt); }
.ts-dropdown .active { background: var(--cm-blue-bg); color: var(--cm-ink); }
.ts-dropdown .create { color: var(--cm-blue-tx); background: var(--cm-surface-alt); border-top: 1px solid var(--cm-border-soft); }

/* ==========================================================================
   HugeRTE (fork TinyMCE - classes .tox)
   ========================================================================== */
.tox-tinymce { border: 1px solid var(--cm-border) !important; border-radius: var(--cm-radius-sm) !important; }
.tox .tox-toolbar, .tox .tox-toolbar__primary, .tox .tox-statusbar { background: var(--cm-surface-alt) !important; }
.tox .tox-tbtn { color: var(--cm-text) !important; }
.tox .tox-tbtn:hover { background: var(--cm-border-soft) !important; }
.tox .tox-tbtn--enabled { background: var(--cm-blue-bg) !important; }

/* ==========================================================================
   The rail (.cm-rail) - GLOBAL pattern for "here are the N things you are
   working through, and this is the one you are on".

   Not a variant of .cm-tabs: tabs name the *facets* of one subject (Membres,
   Groupes, Périodes...) and are part of a screen's furniture. A rail names the
   *items* of a batch - the séquences of a progression being placed, the
   quizzes of an import waiting to be checked - so it is a row of pills, it
   carries a rank, and each entry can say how it is doing with a coloured dot.

   Both mechanics use the same skin: an entry is a link when moving means a new
   page (progression placement), and a button when the panels are already on the
   page and only one shows (quiz batch import).
   ========================================================================== */
.cm-rail { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.cm-rail__label { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-rail__item {
  display: inline-flex; align-items: center; gap: 7px; max-width: 320px;
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text);
  border-radius: 999px; padding: 6px 14px; font: 600 12.5px var(--cm-font-ui);
  cursor: pointer; text-decoration: none;
}
.cm-rail__item:hover { background: var(--cm-bg); }
.cm-rail__item.is-active { border-color: var(--cm-brand); background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-rail__index { font: 600 11px var(--cm-font-ui); color: var(--cm-faint); }
.cm-rail__item.is-active .cm-rail__index { color: inherit; }
/* An entry the teacher took out of the batch: still there, still readable, plainly not going. */
.cm-rail__item.is-off { opacity: .55; }
.cm-rail__item.is-off .cm-rail__title { text-decoration: line-through; }
.cm-rail__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-rail__dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.cm-rail__dot--ok     { background: var(--cm-green-tx); }
.cm-rail__dot--warn   { background: var(--cm-gold-tx); }
.cm-rail__dot--danger { background: var(--cm-red-tx); }

/* ==========================================================================
   Card-attached tabs (.cm-tabs) - GLOBAL pattern for every tabbed admin
   screen (UFA, Configuration, Pédagogique, Ordinateurs portables...),
   replacing the previous Bootstrap "nav nav-pills card-header-pills" inside
   .card-header. Tabs sit ABOVE the card; the active tab fuses with the card
   below it (shared border + negative margin), and the card itself drops its
   own top border/radius via .cm-card--tabbed. See README.md sections 8a/8b.
   ========================================================================== */
.cm-tabs {
    display: flex; align-items: flex-end; gap: 2px;
    border-bottom: 1px solid var(--cm-border);
}
.cm-tabs__tab {
    padding: 9px 16px; font: 600 13.5px var(--cm-font-ui); color: var(--cm-muted);
    cursor: pointer; border-radius: 8px 8px 0 0; text-decoration: none; border: 1px solid transparent;
}
.cm-tabs__tab:hover { background: color-mix(in srgb, var(--cm-brand) 8%, var(--cm-bg)); color: var(--cm-brand-strong); }
.cm-tabs__tab.is-active {
    color: var(--cm-brand-strong); background: var(--cm-surface);
    border-color: var(--cm-border); border-bottom-color: var(--cm-surface);
    margin-bottom: -1px; cursor: default;
}
/* Tab-row action button ("+ Ajouter...", "Exporter") - right-aligned, bottom-anchored within
   the row so it lines up with the tab labels rather than the row's cross-axis center. */
.cm-tabs__action { margin-left: auto; padding-bottom: 8px; }
/* The count some tabs carry beside their label - « Campagnes 9 ». Inherits the tab's own color so
   it reads as part of the label rather than as a separate badge, and turns brand-colored with it. */
.cm-tabs__count {
    display: inline-block;
    margin-left: 6px;
    padding: 0 6px;
    border-radius: 999px;
    background: color-mix(in srgb, currentColor 12%, transparent);
    font-size: 11px;
    font-weight: 600;
    line-height: 18px;
    vertical-align: 1px;
}
.cm-card--tabbed { border-top: 0; border-radius: 0 0 var(--cm-radius) var(--cm-radius); padding: 26px; }
/* Content that hangs off a tab row without a card of its own - a grid of stat tiles, of link tiles,
   of host cards. It takes the same inset .cm-card--tabbed gives a table, so a screen made of tiles
   lines up with a screen made of rows instead of butting against the tabs. Bottom padding is left
   to the content, which already carries its own margin between blocks. */
.cm-tabbed-content { padding: 26px 26px 0; }

/* ---------- Deployment notice, at the very top of the window ----------
   Above the navbar and not under it, unlike .cm-globalnotice: the standing notice states a fact
   about the platform, this one warns that the platform is about to stop answering, and the two
   must not read as a pair of stripes saying equally little.

   Warning amber, and it announces itself once - `role="status"`, not `alert`: a screen reader
   should hear it when it arrives without having whatever the reader is doing interrupted.

   Sticky, because the window it covers is a quarter of an hour and the person it is written for is
   the one scrolled far down a long form. */
.cm-deploynotice {
    position: sticky;
    top: 0;
    z-index: 1040;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    padding: 9px 20px;
    background: var(--cm-gold-bg);
    color: var(--cm-gold-tx);
    border-bottom: 2px solid var(--cm-gold);
    font-size: 13px;
    font-weight: 500;
    line-height: 1.45;
    text-align: center;
}
.cm-deploynotice__icon { display: inline-flex; flex: 0 0 auto; }
.cm-deploynotice__text strong { font-weight: 700; }
.cm-deploynotice__text strong::after { content: ' · '; font-weight: 400; }

/* ---------- Standing platform notice, immediately under the navbar ----------
   A full-width strip rather than an .alert inside the container: it belongs to the shell, like the
   « ZONE DE TEST » bar it sits above, and must not move with the page's own gutters. Informational
   blue rather than a warning tint - it states where the reference data lives, it does not report a
   problem. The link is styled explicitly because this strip has no Tabler ancestor to inherit from. */
.cm-globalnotice {
    padding: 10px 20px;
    background: var(--cm-notice-bg);
    color: var(--cm-notice-tx);
    border-bottom: 2px solid var(--cm-notice-bd);
    font-size: 13px;
    font-weight: 500;
    line-height: 1.45;
    text-align: center;
}
/* The link is the one thing on the strip somebody is meant to click, so it carries the accent
   rather than inheriting the band's own colour: underlined, and gold against the deep blue in both
   themes - the same pair the medallion and the "depuis 1634" rule use in the top bar. */
.cm-globalnotice a { color: var(--cm-notice-link); font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }
.cm-globalnotice a:hover { color: #fff; }

/* ---------- « Vous utilisez le compte de … » (impersonation) ----------
   Under the navbar and above every other strip, in the red pair rather than the blue of
   .cm-globalnotice or the amber of .cm-deploynotice and the « ZONE DE TEST » bar: the three others
   state something about the platform, this one states that the screen is not showing the reader's
   own account, and a fourth band of the same colour would be read as another one of them.

   Sticky, and for a stronger reason than the deploy notice: an administrator who scrolls the
   warning away goes on acting under somebody else's name with nothing on screen saying so. */
.cm-impersonation {
    position: sticky;
    top: 0;
    z-index: 1040;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 9px 20px;
    background: var(--cm-red-bg);
    color: var(--cm-red-tx);
    border-bottom: 2px solid #c0604f;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.45;
    text-align: center;
}
.cm-impersonation__badge {
    padding: 2px 9px;
    border: 1px solid currentColor;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}
/* The way back is the only thing on the strip meant to be clicked, so it is underlined and bolder
   than the sentence it follows - same treatment as .cm-globalnotice's own link. */
.cm-impersonation a { color: inherit; font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }
.cm-impersonation a:hover { color: inherit; text-decoration-thickness: 2px; }

/* ---------- Breadcrumb + page head (sub-pages only, never menu root pages) ----------
   Every segment (including the current page) is a real <a href>. */
/* Colors and separators aligned on 25e-pret-formulaire.png: parent links in brand blue, current
   segment in ink and bold, chevrons rather than slashes. */
/* Positions read off 25e-pret-formulaire.png: 17px between the bottom of the navbar and the
   breadcrumb (Tabler puts 24), then 13px between the breadcrumb and the title - which leaves the
   title exactly where the mockup places it. */
/* Selector qualified like Tabler's own (".page-wrapper .page-header{margin:var(--tblr-page-padding-y) 0 0}"),
   without which the original margin wins whatever the load order. */
.page-wrapper .page-header { margin-top: 17px; }
/* Same logic for the page body: Tabler applies var(--tblr-page-padding-y) (24px), we halve it.
   Selector qualified so it beats ".page-body" whatever the load order. */
.page-wrapper .page-body { margin-top: 12px; }
.cm-breadcrumb { display: flex; gap: 6px; align-items: center; font-size: 12.5px; color: var(--cm-faint); margin-bottom: 13px; }
.cm-breadcrumb .sep { color: var(--cm-faint); }
.cm-breadcrumb__home { color: var(--cm-brand-strong); flex: none; }

/* Color chip of an <option> in a tom-select (equipment conditions): 11px inside a field, 13px at
   the head of a list row, where it stands as the main visual cue (mockup 25c). */
.cm-select-dot { display: inline-block; width: 11px; height: 11px; border-radius: 50%; margin-right: 9px; vertical-align: baseline; }
.cm-select-dot--lg { width: 13px; height: 13px; margin-right: 11px; }

/* Reorder handle: the mockup's six dots, in border grey, turning text grey on hover to signal that
   it can be grabbed. */
.cm-grip { color: #c3cfd9; cursor: grab; display: inline-grid; place-items: center; width: 18px; }
.cm-grip:hover { color: var(--cm-muted); }

/* Hexadecimal color code: monospaced, like any technical value read character by character
   (mockup 25c). */
.cm-hex { font: 12.5px ui-monospace, Menlo, Consolas, monospace; color: var(--cm-faint); }

/* The cell that identifies its row (student name, inventory number): ink rather than text grey, as
   in the mockups, where it is read before anything else on the row. */
.text-ink { color: var(--cm-ink); }

/* Short fields: an inventory number or a condition choice do not need the full width of the panel,
   and the mockups give them a fixed leash (25b, 25e). */
.cm-field--sm { max-width: 260px; }
.cm-field--md { max-width: 280px; }

/* Summary banner of a loan at the top of the return form (mockup 25f) and reminder of the condition
   at lending time: two light grey boxes, not editable. */
.cm-loan-summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; background: var(--cm-surface-alt); border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius); padding: 14px 16px; }
.cm-loan-summary__label { display: block; font: 600 12px var(--cm-font-ui); color: var(--cm-muted); margin-bottom: 3px; }
.cm-loan-summary__value { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-loan-summary__value--late { color: var(--cm-red-tx); }
.cm-loan-reminder { background: var(--cm-surface-soft); border: 1px solid #e6ecf2; border-radius: 9px; padding: 13px 15px; font-size: 13px; line-height: 1.6; color: var(--cm-muted); }
.cm-breadcrumb a { color: var(--cm-brand-strong); text-decoration: none; }
.cm-breadcrumb a:hover { color: var(--cm-brand-strong); text-decoration: underline; }
.cm-breadcrumb a.current { color: var(--cm-ink); font-weight: 600; }
/* The medallion is 74px wide overall and sits `top: 9px` inside a 58px topbar, so it hangs ~16px
   BELOW the bar, straight over the top-left corner of .page-header - where the breadcrumb, being
   that header's first line, would otherwise start flush at the container edge and read as tucked
   under the logo. Indented past it exactly as the design specifies (design_handoff_progression's
   reference markup: padding-left:81px = the medallion's 74px + a 7px gap). The <h2> page title
   underneath is deliberately NOT indented - it clears the medallion vertically and keeps the
   container's own alignment, which is what the design shows.
   Only from navbar-expand-xxl up: below that the navbar stacks, the brand centres itself inside a
   taller bar and nothing overhangs the header, so an indent there would just be a stray gap. */
@media (min-width: 1400px) {
    .cm-breadcrumb { padding-left: 81px; }
}

/* ---------- Sub-section head inside a card (title + hint line) ---------- */
.cm-subhead { border-bottom: 1px solid var(--cm-border-soft); padding-bottom: 10px; }
.cm-subhead__title { font: 600 16px var(--cm-font-title); color: var(--cm-ink); }
.cm-subhead__hint { font-size: 12.5px; color: var(--cm-faint); margin-top: 3px; }

/* ---------- Contextual help callout (replaces long help paragraphs under a field) ---------- */
.cm-note {
    display: flex; align-items: flex-start; gap: 10px;
    background: var(--cm-blue-bg); border: 1px solid color-mix(in srgb, var(--cm-brand) 30%, var(--cm-border));
    border-left: 3px solid var(--cm-brand); border-radius: 0 8px 8px 0;
    padding: 11px 14px; font-size: 12.5px; color: var(--cm-blue-tx); line-height: 1.6;
}
.cm-note__icon {
    flex: none; width: 18px; height: 18px; border-radius: 50%; margin-top: 2px;
    background: var(--cm-brand); color: #fff;
    display: grid; place-items: center; font-weight: 700; font-size: 11px;
}

/* ---------- Named option row (label + sub-label on the left, control on the right) ---------- */
.cm-optionrow { display: grid; grid-template-columns: 1fr 200px; gap: 12px; align-items: center; padding: 4px 0; }
.cm-optionrow__name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-optionrow__sub { font-size: 12px; color: var(--cm-faint); }
.cm-optionrow + .cm-optionrow { border-top: 1px solid var(--cm-border-soft); }

/* ---------- Single footer action bar (ONE per page - no intermediate save buttons) ---------- */
.cm-actionbar { display: flex; align-items: center; gap: 12px; border-top: 1px solid var(--cm-border-soft); margin-top: 24px; padding-top: 16px; }
.cm-actionbar__meta { font-size: 12.5px; color: var(--cm-faint); }
.cm-actionbar__buttons { margin-left: auto; display: flex; gap: 10px; }

/* ---------- Per-option WYSIWYG block (8b pattern: Mod. Examen / Mod. Contrat) ---------- */
.cm-editorblock { border: 1px solid var(--cm-border); border-radius: 10px; overflow: hidden; }
.cm-editorblock + .cm-editorblock { margin-top: 12px; }
.cm-editorblock__head { display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: var(--cm-surface-alt); border-bottom: 1px solid var(--cm-border-soft); }
.cm-editorblock__name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-editorblock__sub { font-size: 12px; color: var(--cm-faint); }
/* The "capped, scrollable content pane" from the spec comes from HugeRTE's own `height` option
   (a fixed total widget height, toolbar/statusbar pinned, internal content scrolling on
   overflow) - not from wrapping the whole widget in an outer max-height/overflow, which would
   just add a second, redundant scrollbar around the toolbar itself. */
/* HugeRTE's container class is .tox-hugerte, not TinyMCE's .tox-tinymce */
.cm-editorblock__body .tox-hugerte { border: 0 !important; border-radius: 0 !important; }
/* Empty state: no editor instantiated - a status line + "Personnaliser" link that swaps it in. */
.cm-editorblock__empty {
    border-top: 1px dashed var(--cm-border); padding: 12px 16px; font-size: 13px; color: var(--cm-faint);
    background: var(--cm-surface-alt); display: flex; align-items: center; gap: 8px;
}
.cm-editorblock__empty-icon {
    flex: none; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid var(--cm-border);
    color: var(--cm-faint); display: grid; place-items: center; font-size: 10px; font-weight: 700;
}
.cm-editorblock__customize { margin-left: auto; font: 600 13px var(--cm-font-ui); color: var(--cm-brand-strong); background: none; border: 0; padding: 0; cursor: pointer; }
.cm-editorblock__customize:hover { text-decoration: underline; }
/* Variant showing the full inherited default text (Mod. Contrat 22a/24c) instead of the brief
   one-line status message above - stacks the rendered HTML preview over the toggle instead of a
   single flex row. */
.cm-editorblock__empty--preview { display: block; }
.cm-editorblock__default-preview { font-size: 13.5px; color: var(--cm-ink); line-height: 1.6; margin-bottom: 10px; }
.cm-editorblock__empty--preview .cm-editorblock__customize { margin-left: 0; }

/* ---------- Collapsible override blocks (.cm-collapse) - design_handoff_ufa 24c/24d ----------
   One block per contract type (24c) or per option (24d), carrying its inheritance state in the
   head: grey badge + "Surcharger" switch when inherited, green badge + "Rétablir le défaut"
   when overridden. Behavior in override_block_controller.js. */
.cm-collapse { border: 1px solid var(--cm-border); border-radius: 10px; overflow: hidden; background: var(--cm-surface-alt); }
.cm-collapse.is-open { background: var(--cm-surface); border-color: #cfe0ee; }
.cm-collapse + .cm-collapse { margin-top: 0; }
.cm-collapse__head { display: flex; align-items: center; gap: 12px; padding: 12px 16px; cursor: pointer; }
.cm-collapse__head:hover { background: var(--cm-surface-alt); }
.cm-collapse__chevron { color: var(--cm-faint); flex: none; display: flex; transition: transform .15s ease; }
.cm-collapse.is-open .cm-collapse__chevron { transform: rotate(90deg); }
.cm-collapse__title { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); }
.cm-collapse__hint { font-size: 12.5px; color: var(--cm-faint); }
.cm-collapse__state { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.cm-collapse__state .form-check { margin: 0; }
.cm-collapse__reset { font: 600 12.5px var(--cm-font-ui); color: var(--cm-red-tx); background: none; border: 0; padding: 0; cursor: pointer; }
.cm-collapse__reset:hover { text-decoration: underline; }
.cm-collapse__body { display: none; border-top: 1px solid var(--cm-border-soft); }
.cm-collapse.is-open .cm-collapse__body { display: block; }
/* HugeRTE's container class is .tox-hugerte (rebranded from TinyMCE's .tox-tinymce) */
.cm-collapse__body .tox-hugerte { border: 0 !important; border-radius: 0 !important; }
/* "Par option" separator label between the default block and the per-option blocks (24d) */
.cm-collapse-group-label { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }

/* ---------- Secondary-groups picker: checkbox chips grouped by GroupType (9a pattern) ---------- */
.cm-groupchips__section + .cm-groupchips__section { margin-top: 12px; }
.cm-groupchips__heading {
    font: 700 11px var(--cm-font-ui); color: var(--cm-faint);
    text-transform: uppercase; letter-spacing: .5px; margin-bottom: 7px;
}
.cm-groupchips__list { display: flex; flex-wrap: wrap; gap: 8px; }
.cm-groupchip {
    position: relative; display: inline-flex; align-items: center; gap: 7px;
    border: 1px solid var(--cm-border); border-radius: 16px; padding: 6px 13px;
    font-size: 13px; color: var(--cm-ink); cursor: pointer;
}
.cm-groupchip:hover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
/* form_widget() on a single expanded-choice view renders the full Bootstrap5 .form-check
   wrapper (input + its own form-check-label carrying the choice's label text, i.e. the group
   name again) rather than a bare <input> - hidden here instead of left to nest inside/duplicate
   this chip's own name/box markup, same convention as .cm-radiocard. Nested <label>s are also
   why the chip used to be unclickable: the outer <label class="cm-groupchip"> still toggles the
   (now-hidden) input natively via label-wraps-input, same as .cm-radiocard. */
.cm-groupchip .form-check { display: contents; }
.cm-groupchip .form-check-label { position: absolute; opacity: 0; width: 1px; height: 1px; overflow: hidden; }
.cm-groupchip__input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-groupchip:has(.cm-groupchip__input:checked) { border-color: var(--cm-brand); background: var(--cm-blue-bg); color: var(--cm-brand-strong); font-weight: 600; }
.cm-groupchip__box {
    flex: none; width: 14px; height: 14px; border-radius: 4px; border: 1.5px solid var(--cm-border-soft);
    display: grid; place-items: center; font-size: 10px; color: #fff;
}
.cm-groupchip:has(.cm-groupchip__input:checked) .cm-groupchip__box { background: var(--cm-brand); border-color: var(--cm-brand); }
.cm-groupchip:has(.cm-groupchip__input:checked) .cm-groupchip__box::after { content: '✓'; }
/* Read-only "groupes issus de l'annuaire" display (user edit screen, directory/user_form.html.twig)
   - a plain non-interactive span (no <input> at all, LDAP owns this membership, not the form), a
   lock icon standing in for .cm-groupchip__box's checkbox. */
.cm-groupchip--locked { cursor: default; background: var(--cm-surface-alt); border-color: var(--cm-border-soft); color: var(--cm-muted); }
.cm-groupchip--locked:hover { border-color: var(--cm-border-soft); color: var(--cm-muted); }
.cm-groupchip__lock { flex: none; color: var(--cm-faint); }

/* ---------- Locked identity panel (user edit screen: prénom/nom/identifiant/type) ---------- */
.cm-readonly-panel {
    display: grid; grid-template-columns: 1fr 1fr; gap: 14px 16px;
    background: var(--cm-surface-alt); border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm);
    padding: 13px 15px;
}
.cm-readonly-panel__label {
    display: flex; align-items: center; gap: 5px;
    font: 700 11px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .5px;
}
.cm-readonly-panel__label svg { flex: none; color: var(--cm-faint); }
.cm-readonly-panel__value { font-size: 14px; color: var(--cm-ink); margin-top: 5px; }
/* The annuaire error line: full width of the two-column grid, since a consumer-script log is a
   raw multi-line dump, not a short value like the fields around it. */
.cm-readonly-panel__row--wide { grid-column: 1 / -1; }
.cm-readonly-panel__value--log {
    font: 12.5px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
    color: var(--cm-red-tx); white-space: pre-wrap; overflow-wrap: anywhere;
    max-height: 9em; overflow-y: auto;
}

/* ---------- Courrier pro addresses list (user edit screen, students only) ----------
   A grid rather than a <table>: every row carries a form field, an error message under that field
   and a button, which table cells align badly. The columns are fixed at both ends (radio, origin,
   removal) and elastic in the middle, so the address - the only variable-length value - takes
   whatever room is left. */
.cm-alias-list { border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm); overflow: hidden; }
.cm-alias { display: grid; grid-template-columns: 76px 1fr 150px 52px; gap: 12px; align-items: start; padding: 10px 14px; }
.cm-alias + .cm-alias { border-top: 1px solid var(--cm-border-soft); }
.cm-alias--head {
    background: var(--cm-surface-alt); align-items: center;
    font: 700 11px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .5px;
}
.cm-alias__default { display: flex; justify-content: center; padding-top: 8px; }
.cm-alias--head .cm-alias__default { padding-top: 0; text-align: center; }
.cm-alias__radio { display: flex; align-items: center; cursor: pointer; margin: 0; }
.cm-alias__fixed { padding-top: 7px; font-size: 14px; color: var(--cm-muted); overflow-wrap: anywhere; }
.cm-alias__domain { color: var(--cm-faint); }
/* The domain reads as the continuation of what is typed, without looking like a second field:
   it is not something one picks (MAIL_STUDENT_DOMAIN). */
.cm-alias__input { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.cm-alias__input .form-control { flex: 1 1 12rem; min-width: 0; }
.cm-alias__input .cm-alias__domain { font-size: 14px; }
.cm-alias__origin { padding-top: 8px; }
.cm-alias--head .cm-alias__origin { padding-top: 0; }
.cm-alias__remove { display: flex; justify-content: flex-end; }
.cm-alias__remove .btn { flex: none; }
@media (max-width: 767.98px) {
    .cm-alias { grid-template-columns: 44px 1fr 44px; }
    /* The origin drops under the address, the middle column, rather than shrinking it. */
    .cm-alias__origin { grid-column: 2; padding-top: 0; }
    .cm-alias--head .cm-alias__origin { display: none; }
}

/* ==========================================================================
   Outils > Tirage au sort (.cm-draw-*) - classroom random-draw roulette.
   Ported from design/design_campus_manager/reference/Tirage au sort.dc.html - no Tabler
   equivalent (slot-machine display, fullscreen mode, confetti winner overlay).
   Fullscreen is toggled by assets/controllers/random_draw_controller.js adding/removing
   .cm-draw-card--fs alongside the real Fullscreen API call (not relying on the :fullscreen
   pseudo-class alone, for reliable cross-browser behavior including the Esc-key exit path).
   ========================================================================== */
.cm-draw-body { display: flex; flex-direction: column; gap: 16px; }
.cm-draw-toolbar { display: flex; justify-content: flex-end; }
.cm-draw-slot {
    border-radius: 22px; border: 3px solid var(--cm-gold);
    background: linear-gradient(180deg, #0a1e30, var(--cm-navy) 50%, #0a1e30);
    box-shadow: 0 20px 60px rgba(0, 0, 0, .4), inset 0 0 40px rgba(0, 0, 0, .5);
    height: 210px; display: grid; place-items: center; overflow: hidden; box-sizing: border-box;
    font: 600 min(6vw, 64px) var(--cm-font-title); color: #fff; text-align: center; padding: 0 30px;
}
.cm-draw-slot--spinning { color: #9db4c6; filter: blur(2px); animation: cm-draw-jitter .1s infinite; }
.cm-draw-controls { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.cm-draw-option { display: flex; align-items: center; gap: 8px; }
.cm-draw-option__label { font-size: 13px; color: var(--cm-muted); }
.cm-draw-option .form-select { width: auto; }
.cm-draw-switch {
    width: 46px; height: 24px; border-radius: 12px; background: #c8d3dc; flex: none; cursor: pointer;
    box-sizing: border-box; padding: 3px; transition: background .2s; position: relative;
}
.cm-draw-switch::after {
    content: ''; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%;
    background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .25); transition: left .2s;
}
.cm-draw-switch--on { background: var(--cm-brand); }
.cm-draw-switch--on::after { left: 25px; }
.cm-draw-switch__label { font-size: 13px; color: var(--cm-muted); cursor: pointer; }
.cm-draw-remaining { font-size: 13.5px; color: var(--cm-faint); }
.cm-draw-button { margin-left: auto; padding-left: 34px; padding-right: 34px; }

/* ---------- Fullscreen mode: real Fullscreen API on .cm-draw-card, dark full-bleed theme ---------- */
.cm-draw-fs-actions { display: none; }
.cm-draw-card--fs {
    background: radial-gradient(1200px 800px at 50% -10%, #16436b 0%, #0d2438 62%) !important;
    border: 0 !important; display: flex; align-items: center; justify-content: center;
}
.cm-draw-card--fs .cm-draw-body { align-items: center; justify-content: center; gap: 34px; width: 100%; max-width: 1200px; }
.cm-draw-card--fs .cm-draw-toolbar, .cm-draw-card--fs .cm-draw-controls { display: none; }
.cm-draw-card--fs .cm-draw-slot { width: min(88vw, 1200px); height: min(38vh, 320px); font-size: min(10vw, 110px); }
.cm-draw-card--fs .cm-draw-fs-actions { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.cm-draw-fs-exit {
    position: absolute; top: 20px; right: 26px; display: flex; align-items: center; gap: 7px;
    border: 1px solid rgba(255, 255, 255, .22); border-radius: 8px; padding: 8px 14px;
    background: none; color: #9db4c6; font: 600 13px var(--cm-font-ui); cursor: pointer;
}
.cm-draw-fs-exit:hover { color: #fff; border-color: #fff; }
.cm-draw-fs-button {
    border: 0; background: var(--cm-gold); color: var(--cm-navy); border-radius: 14px; padding: 16px 56px;
    font: 700 20px var(--cm-font-ui); letter-spacing: .3px; cursor: pointer;
    box-shadow: 0 8px 24px rgba(201, 160, 78, .3);
}
.cm-draw-fs-button:hover { background: var(--cm-gold-strong); }
.cm-draw-fs-remaining { font-size: 14px; color: #7d99b0; }

/* ---------- Winner overlay + confetti ---------- */
.cm-draw-overlay {
    position: fixed; inset: 0; background: rgba(9, 26, 40, .88); backdrop-filter: blur(5px);
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
    z-index: 1060; cursor: pointer; overflow: hidden; font-family: var(--cm-font-ui);
}
.cm-draw-overlay__eyebrow { font: 600 15px var(--cm-font-ui); letter-spacing: 4px; color: var(--cm-gold); text-transform: uppercase; }
.cm-draw-overlay__name {
    font: 600 min(10vw, 104px) var(--cm-font-title); color: #fff; text-align: center; padding: 0 40px;
    animation: cm-draw-pop-in .5s cubic-bezier(.2, 1.4, .4, 1);
}
.cm-draw-overlay__hint { margin-top: 8px; font: 600 14px var(--cm-font-ui); color: #7d99b0; }
.cm-draw-confetti-piece { position: absolute; top: -22px; width: 10px; height: 16px; animation-name: cm-draw-confetti-fall; animation-timing-function: linear; animation-iteration-count: infinite; }

@keyframes cm-draw-confetti-fall { to { transform: translateY(112vh) rotate(560deg); } }
@keyframes cm-draw-pop-in { from { transform: scale(.55); opacity: 0; } }
@keyframes cm-draw-jitter { 0% { transform: translateY(-2px); } 50% { transform: translateY(2px); } 100% { transform: translateY(-2px); } }

/* ==========================================================================
   Outils > Création de groupes (.cm-grp-*) - ported from design/design_campus_manager/designs/
   Outils pédagogiques.dc.html's "Création de groupes" page.
   ========================================================================== */
.cm-grp-lots { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 12px 16px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.cm-grp-lots__label { font: 700 11px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .5px; flex: none; }
.cm-grp-lots__chips { display: flex; flex-wrap: wrap; gap: 7px; }
.cm-grp-lot-chip { display: inline-flex; align-items: center; border: 1px solid var(--cm-border); border-radius: 16px; overflow: hidden; background: var(--cm-surface); font: 600 12.5px var(--cm-font-ui); }
.cm-grp-lot-chip__load { cursor: pointer; color: var(--cm-blue-tx); padding: 6px 10px 6px 13px; border: 0; background: none; font: inherit; }
.cm-grp-lot-chip__load:hover { background: var(--cm-blue-bg); }
.cm-grp-lot-chip__del { color: var(--cm-red-tx); cursor: pointer; padding: 6px 11px 5px 9px; border: 0; border-left: 1px solid var(--cm-border-soft); background: none; display: inline-flex; align-items: center; }
.cm-grp-lot-chip__del:hover { background: var(--cm-red-bg); }
.cm-grp-lot-chip__shared { font: 600 10.5px var(--cm-font-ui); color: var(--cm-faint); padding: 0 9px 0 2px; letter-spacing: .2px; }
.cm-grp-lot-chip--shared { border-style: dashed; }
/* Nothing but the load button inside, since the owner's name moved to the tooltip: the pill has to
   pad itself on both sides again. */
.cm-grp-lot-chip--shared .cm-grp-lot-chip__load { color: var(--cm-ink); padding-right: 13px; }
.cm-grp-share-list { display: flex; flex-direction: column; gap: 2px; max-height: 260px; overflow-y: auto; }
.cm-grp-share-item { display: flex; align-items: center; gap: 9px; padding: 6px 8px; border-radius: 8px; font: 500 13.5px var(--cm-font-ui); color: var(--cm-ink); cursor: pointer; }
.cm-grp-share-item:hover { background: var(--cm-surface-alt); }
.cm-grp-share-empty { font-size: 13px; color: var(--cm-muted); }

.cm-grp-layout { display: grid; grid-template-columns: 330px minmax(0, 1fr); gap: 18px; align-items: start; }
@media (max-width: 900px) { .cm-grp-layout { grid-template-columns: 1fr; } }
.cm-grp-panel { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 18px; display: flex; flex-direction: column; gap: 18px; }
.cm-grp-section { display: flex; flex-direction: column; gap: 9px; }
.cm-grp-label { font: 700 11px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .5px; }
.cm-grp-hint { font-size: 11.5px; color: var(--cm-faint); margin-top: -3px; }

.cm-grp-segmented { display: flex; border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); overflow: hidden; font: 600 12.5px var(--cm-font-ui); }
.cm-grp-segmented__opt { flex: 1; text-align: center; padding: 8px 0; cursor: pointer; background: var(--cm-surface); color: var(--cm-muted); border: 0; font: inherit; }
.cm-grp-segmented__opt.is-active { background: var(--cm-brand); color: #fff; }
.cm-grp-segmented__opt:hover:not(.is-active) { background: var(--cm-bg); }

.cm-grp-stepper { display: flex; align-items: center; gap: 10px; }
.cm-grp-stepper__btn {
    width: 32px; height: 32px; border: 1px solid var(--cm-border); background: var(--cm-surface); border-radius: var(--cm-radius-sm);
    color: var(--cm-muted); font-size: 16px; cursor: pointer; line-height: 1;
}
.cm-grp-stepper__btn:hover { background: var(--cm-bg); }
.cm-grp-stepper__btn:disabled { opacity: .45; cursor: default; }
.cm-grp-stepper__value { min-width: 30px; text-align: center; font: 700 18px var(--cm-font-title); color: var(--cm-ink); }
.cm-grp-stepper__unit { font-size: 12.5px; color: var(--cm-faint); }

.cm-grp-search { position: relative; }
.cm-grp-suggestions { border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); overflow: hidden; margin-top: -4px; background: var(--cm-surface); }
.cm-grp-suggestion { padding: 8px 11px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-text); cursor: pointer; border-bottom: 1px solid var(--cm-border-soft); background: none; width: 100%; text-align: left; border-left: 0; border-right: 0; border-top: 0; }
.cm-grp-suggestion:last-child { border-bottom: 0; }
.cm-grp-suggestion:hover { background: var(--cm-bg); }
.cm-grp-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.cm-grp-tag { display: inline-flex; align-items: center; gap: 7px; border: 1px solid #e4d5d2; border-radius: 14px; padding: 4px 10px; font: 600 12px var(--cm-font-ui); color: var(--cm-red-tx); background: var(--cm-red-bg); }
.cm-grp-tag button { border: 0; background: none; padding: 0; color: inherit; opacity: .7; cursor: pointer; font-size: 11px; }
.cm-grp-tag button:hover { opacity: 1; }

.cm-grp-pair-row { display: flex; gap: 6px; }
.cm-grp-pair-row .form-select { flex: 1; min-width: 0; }
.cm-grp-pair-actions { display: flex; gap: 6px; }
.cm-grp-pair-btn {
    flex: 1; border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-muted); border-radius: var(--cm-radius-sm);
    padding: 8px 0; font: 600 12.5px var(--cm-font-ui); cursor: pointer;
}
.cm-grp-pair-btn:hover { background: var(--cm-bg); }
.cm-grp-pair-chip { display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--cm-border); border-radius: 14px; padding: 4px 10px; font: 600 11.5px var(--cm-font-ui); color: var(--cm-text); background: var(--cm-surface-alt); }
.cm-grp-pair-chip button { border: 0; background: none; padding: 0; color: var(--cm-faint); cursor: pointer; font-size: 11px; }
.cm-grp-pair-chip button:hover { color: var(--cm-red-tx); }

.cm-grp-create-btn { border: 0; background: var(--cm-brand); color: #fff; border-radius: var(--cm-radius-sm); padding: 13px; font: 700 14.5px var(--cm-font-ui); cursor: pointer; }
.cm-grp-create-btn:hover { background: var(--cm-brand-strong); }
.cm-grp-reshuffle-btn {
    margin-top: -8px; border: 1px solid var(--cm-gold); background: var(--cm-surface); color: var(--cm-warn-tx);
    border-radius: var(--cm-radius-sm); padding: 11px; font: 600 13px var(--cm-font-ui); cursor: pointer;
}
.cm-grp-reshuffle-btn:hover { background: var(--cm-gold-bg); }
.cm-grp-summary { font-size: 12.5px; color: var(--cm-faint); margin-top: -8px; }
.cm-grp-error { font-size: 12.5px; color: var(--cm-red-tx); background: var(--cm-red-bg); border-radius: var(--cm-radius-sm); padding: 9px 11px; }

.cm-grp-results { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.cm-grp-toolbar { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 12px 14px; display: flex; flex-direction: column; gap: 9px; }
.cm-grp-toolbar__row { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.cm-grp-toolbar__row .form-control { flex: 1; min-width: 160px; max-width: 340px; }
.cm-grp-toolbar__spacer { margin-left: auto; }

.cm-grp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(225px, 1fr)); gap: 12px; }
.cm-grp-card { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); overflow: hidden; }
.cm-grp-card.is-locked { border-color: #ead9ae; box-shadow: 0 0 0 2px rgba(201, 160, 78, .25); }
.cm-grp-card__head { display: flex; align-items: center; gap: 8px; padding: 11px 13px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-grp-card__title { font: 600 14px var(--cm-font-title); color: var(--cm-ink); }
.cm-grp-card__count { font-size: 11.5px; color: var(--cm-faint); }
.cm-grp-card__lock {
    margin-left: auto; font: 600 10.5px var(--cm-font-ui); padding: 3px 9px; border-radius: 9px; cursor: pointer;
    background: var(--cm-surface); color: var(--cm-faint); border: 1px solid var(--cm-border);
}
.cm-grp-card.is-locked .cm-grp-card__lock { background: var(--cm-gold-bg); color: var(--cm-warn-tx); border-color: #ead9ae; }
.cm-grp-card__body { padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; min-height: 20px; }
.cm-grp-member {
    display: flex; align-items: center; gap: 8px; border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm);
    padding: 7px 10px; font: 600 13px var(--cm-font-ui); color: var(--cm-ink); background: var(--cm-surface-alt); cursor: grab;
}
.cm-grp-member.is-dragging { opacity: .4; }
.cm-grp-member__tag { flex: none; font: 700 9.5px var(--cm-font-ui); padding: 2px 6px; border-radius: 7px; }
.cm-grp-dnd-hint { font-size: 12px; color: var(--cm-faint); }
.cm-grp-empty { background: var(--cm-surface); border: 1px dashed var(--cm-border); border-radius: var(--cm-radius); padding: 46px 26px; text-align: center; color: var(--cm-faint); font-size: 13.5px; }

/* ---------- Fullscreen projector mode (Plein écran on a saved/generated result) ---------- */
.cm-grp-fs {
    min-height: 100vh; background: radial-gradient(1200px 800px at 50% -10%, #16436b 0%, #0d2438 62%);
    color: #e8eef3; padding: 64px 50px; box-sizing: border-box; position: relative;
}
.cm-grp-fs-exit {
    position: absolute; top: 20px; right: 26px; display: flex; align-items: center; gap: 7px;
    border: 1px solid rgba(255, 255, 255, .22); border-radius: 8px; padding: 8px 14px;
    background: none; color: #9db4c6; font: 600 13px var(--cm-font-ui); cursor: pointer; text-decoration: none;
}
.cm-grp-fs-exit:hover { color: #fff; border-color: #fff; }
.cm-grp-fs-title { text-align: center; margin-bottom: 38px; font: 600 14px var(--cm-font-ui); letter-spacing: 4px; color: var(--cm-gold); text-transform: uppercase; }
.cm-grp-fs-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 22px; max-width: 1500px; margin: 0 auto; }
.cm-grp-fs-card { background: rgba(255, 255, 255, .05); border: 1px solid rgba(255, 255, 255, .16); border-radius: 16px; padding: 20px 24px; }
.cm-grp-fs-card__title { font: 600 13px var(--cm-font-ui); letter-spacing: 2px; color: var(--cm-gold); text-transform: uppercase; margin-bottom: 12px; }
.cm-grp-fs-card__member { font: 600 26px var(--cm-font-title); color: #fff; padding: 4px 0; }

/* ---------- Delete-lot confirmation modal + toast ---------- */
.cm-grp-modal-backdrop { position: fixed; inset: 0; background: rgba(9, 26, 40, .55); backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center; z-index: 1070; }
.cm-grp-modal { background: var(--cm-surface); border-radius: 14px; padding: 26px 28px; width: 380px; max-width: calc(100vw - 60px); box-shadow: 0 24px 60px rgba(0, 0, 0, .35); display: flex; flex-direction: column; gap: 14px; }
.cm-grp-modal__title { font: 600 17px var(--cm-font-title); color: var(--cm-ink); }
.cm-grp-modal__body { font-size: 13.5px; color: var(--cm-muted); line-height: 1.6; }
.cm-grp-modal__actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 4px; }
.cm-grp-toast {
    position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%); background: var(--cm-navy); color: #fff;
    padding: 11px 22px; border-radius: 10px; font: 600 13px var(--cm-font-ui); box-shadow: 0 10px 30px rgba(0, 0, 0, .3); z-index: 1080;
}

/* ---------- Side panel (rule 6: 560px, right, below the topbar) - used for short
   create/edit forms over a list that stays visible/dimmed behind it (e.g. 23b, a critère de
   comportement). The backdrop is a plain <a> back to the list route (closing = navigating away),
   so no JS is needed to open/close - the slide-in is a pure CSS animation that just runs once
   whenever the panel markup is present in the DOM. */
/* La maquette fait démarrer le panneau juste sous la barre de navigation, qui y mesure 58px. Celle
   de l'application en fait 90 (emblème et double ligne de titre) : reprendre le 58 littéralement
   faisait chevaucher le panneau sur la barre. C'est la hauteur de barre qui compte, pas le nombre. */
.cm-panel-backdrop { position: fixed; inset: var(--cm-topbar-h) 0 0 0; background: rgba(18, 52, 77, .28); z-index: 1030; }
.cm-panel {
    position: fixed; top: var(--cm-topbar-h); right: 0; bottom: 0; width: 560px; max-width: 100vw;
    background: var(--cm-surface); border-left: 1px solid var(--cm-border);
    box-shadow: -12px 0 30px rgba(0, 0, 0, .18); z-index: 1031;
    display: flex; flex-direction: column; overflow: hidden;
    animation: cm-panel-slide-in .2s ease-out;
}
@keyframes cm-panel-slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }
.cm-panel__header { display: flex; align-items: center; justify-content: space-between; padding: 18px 24px; border-bottom: 1px solid var(--cm-border-soft); flex: none; }
.cm-panel__title { font: 600 17px var(--cm-font-title); color: var(--cm-ink); }
.cm-panel__close { background: none; border: 0; color: var(--cm-faint); cursor: pointer; font-size: 22px; line-height: 1; padding: 0; }
.cm-panel__close:hover { color: var(--cm-ink); }
/* The <form> sits between the panel and its two zones: without making it flexible itself, the body
   does not stretch and the action bar floats mid-panel instead of holding its bottom. */
.cm-panel form { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.cm-panel__body { padding: 22px 24px; flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 16px; }
/* The panel body spaces its own fields (gap); the bottom margin of Symfony's .mb-3 would duplicate
   it and break the mockup's 16px rhythm. */
.cm-panel__body > .mb-3 { margin-bottom: 0 !important; }
.cm-panel__footer { display: flex; align-items: center; justify-content: space-between; padding: 14px 24px; border-top: 1px solid var(--cm-border-soft); background: var(--cm-surface-soft); flex: none; }
.cm-panel__footer-buttons { display: flex; gap: 10px; margin-left: auto; }

/* ==========================================================================
   Générateur de quiz (.cm-diff-*, .cm-qbank-*, .cm-answer-*, .cm-imagedrop) - Gestion > Mes quiz
   (1a) and the question editor (1b). Ported from design/design_campus_manager/reference/
   Générateur de quiz.dc.html.
   ========================================================================== */

/* ---------- ●●○ difficulty dot indicator (1a table, 1b question bank rows) ---------- */
.cm-diffdots { display: inline-flex; gap: 2px; letter-spacing: 0; }
.cm-diffdots span { width: 6px; height: 6px; border-radius: 50%; background: var(--cm-border); }
.cm-diffdots span.is-filled { background: var(--cm-gold); }

/* ---------- Difficulty chip picker (question editor - replaces the plain radio widget) ---------- */
/* form_widget() on an expanded choice wraps every radio in one container div, so the flex has to
   apply to that child too - otherwise the chips stack one per line instead of sitting side by side
   as the mockup shows them. */
.cm-diffpicker, .cm-diffpicker > div { display: flex; flex-wrap: wrap; gap: 6px; }
.cm-diffpicker .form-check { margin: 0; }
.cm-diffpicker .form-check-input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-diffpicker .form-check-label {
    display: inline-flex; align-items: center; border: 1px solid var(--cm-border); border-radius: 8px;
    padding: 8px 14px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-muted); cursor: pointer;
}
.cm-diffpicker .form-check-label:hover { border-color: var(--cm-gold); color: var(--cm-warn-tx); }
.cm-diffpicker .form-check-input:checked + .form-check-label { border-color: var(--cm-gold); background: var(--cm-gold-bg); color: var(--cm-warn-tx); }

/* ---------- Question bank list (1b left pane) ---------- */
.cm-qbank { border: 1px solid var(--cm-border); border-radius: 10px; overflow: hidden; }
.cm-qbank__row {
    display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--cm-border-soft);
    color: inherit; text-decoration: none;
}
.cm-qbank__row:last-child { border-bottom: 0; }
.cm-qbank__row:hover { background: var(--cm-surface-alt); }
.cm-qbank__row.is-active { background: var(--cm-blue-bg); }
.cm-qbank__index { font: 600 12px ui-monospace, monospace; color: var(--cm-faint); width: 22px; flex: none; }
.cm-qbank__label { font-size: 13.5px; color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-qbank__row.is-active .cm-qbank__label { font-weight: 600; color: var(--cm-blue-tx); }
.cm-qbank__dots { margin-left: auto; flex: none; }

/* ---------- Optional énoncé image dropzone ---------- */
.cm-imagedrop {
    display: flex; align-items: center; gap: 10px; border: 1.5px dashed color-mix(in srgb, var(--cm-brand) 35%, var(--cm-border));
    border-radius: 8px; padding: 10px 14px; font-size: 12.5px; color: var(--cm-faint); cursor: pointer;
}
.cm-imagedrop__icon { flex: none; width: 26px; height: 26px; border-radius: 7px; background: var(--cm-blue-bg); color: var(--cm-brand); display: grid; place-items: center; }
.cm-imagedrop input[type="file"] { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-imagedrop__preview { max-height: 120px; border-radius: 8px; margin-top: 8px; }

/* ---------- Réponses row list (add/remove/reorder, correct-answer toggle) ---------- */
.cm-answers { display: flex; flex-direction: column; gap: 8px; }
.cm-answer-row { display: flex; align-items: center; gap: 10px; border: 1px solid var(--cm-border); border-radius: 8px; padding: 9px 12px; counter-increment: cm-answer; }
.cm-answer-row:has(.cm-answer-toggle.is-correct) { border-color: color-mix(in srgb, var(--cm-positive-tx) 45%, var(--cm-border)); background: var(--cm-green-bg); }
.cm-answer-toggle {
    flex: none; width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid #b3c6d6; background: none;
    display: grid; place-items: center; font-size: 11px; color: transparent; padding: 0; cursor: pointer;
}
.cm-answer-toggle.is-correct { border-color: var(--cm-positive-tx); background: var(--cm-positive-tx); color: #fff; }
.cm-answer-input { flex: 1 1 auto; border: 0; font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); background: none; padding: 0; }
.cm-answer-input:focus { outline: none; }
.cm-answer-move { flex: none; border: 0; background: none; color: var(--cm-faint); cursor: pointer; padding: 0 2px; font-size: 11px; }
.cm-answer-move:hover { color: var(--cm-brand); }
.cm-answer-remove { flex: none; border: 0; background: none; color: var(--cm-border); cursor: pointer; padding: 0; font-size: 14px; }
.cm-answer-remove:hover { color: var(--cm-red-tx); }
.cm-answers--ordre { counter-reset: cm-answer; }
.cm-answers--ordre .cm-answer-toggle { visibility: hidden; }
.cm-answers--ordre .cm-answer-row::before { content: counter(cm-answer) '.'; flex: none; font: 600 12.5px var(--cm-font-ui); color: var(--cm-faint); width: 14px; }
.cm-answers--ordre .cm-answer-move { display: none; }
.cm-answers--vraifaux .cm-answer-input { color: var(--cm-faint); }
.cm-answer-position { display: none; flex: none; width: auto; }
.cm-answers--ordre .cm-answer-position { display: inline-block; }

/* ==========================================================================
   Texte à trous (.cm-blanks-*, .cm-chip, .cm-toggle) - design/design_handoff_quiz,
   screens 2a/2b (editor) and 2c/2d (attempt).
   ========================================================================== */

/* ---------- « Texte à compléter »: a transparent textarea laid over a rendering layer ----------
   The mockups show the « ... » highlighted as blue chips *inside the input area*, which no
   <textarea> can do. The .highlight layer therefore replays the text with its chips while the
   <textarea>, strictly superimposed and without background, keeps every native behavior (caret,
   selection, undo, mobile input) - unlike a contenteditable, which would have lost them.
   Font, line height and padding must stay identical on both sides: the slightest gap offsets the
   text being read from the text being typed. */
.cm-blanks-field { position: relative; }
/* Outside « texte à trous », the Énoncé field stays an ordinary .form-control: only .is-blanks
   reveals the layer and blanks the textarea. It is the same field in both cases - the statement and
   the text to complete are one and the same datum (QuizQuestion::$label). */
.cm-blanks-field__highlight { display: none; }
.cm-blanks-field.is-blanks .cm-blanks-field__highlight {
    display: block; font: 400 14px/1.8 var(--cm-font-ui); padding: 12px 13px; min-height: 76px;
    border: 1px solid var(--cm-border); border-radius: 8px;
    white-space: pre-wrap; overflow-wrap: break-word;
    color: var(--cm-ink); background: var(--cm-surface);
}
.cm-blanks-field.is-blanks:focus-within .cm-blanks-field__highlight {
    border-color: var(--cm-brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-brand) 12%, transparent);
}
/* The layer is in the flow: it is what gives the field its height, and it grows with the text. The
   textarea laid over it therefore never needs a scrollbar of its own. */
.cm-blanks-field.is-blanks .cm-blanks-field__input {
    position: absolute; inset: 0; width: 100%; height: 100%; resize: none; overflow: hidden;
    font: 400 14px/1.8 var(--cm-font-ui); padding: 12px 13px; border: 0; border-radius: 8px;
    color: transparent; caret-color: var(--cm-ink); background: transparent; box-shadow: none;
}
.cm-blanks-field.is-blanks .cm-blanks-field__input:focus { outline: none; box-shadow: none; }
/* The selection stays visible despite the transparent text (the layer itself is not selectable). */
.cm-blanks-field.is-blanks .cm-blanks-field__input::selection { background: color-mix(in srgb, var(--cm-brand) 25%, transparent); }
.cm-blank-chip {
    background: var(--cm-blue-bg-soft); border: 1px solid #c6ddf0; border-radius: 5px;
    padding: 1px 8px; font: 600 13px ui-monospace, Menlo, monospace; color: var(--cm-blue-tx);
}
.cm-blanks-count { font-size: 12px; font-weight: 600; color: var(--cm-positive-tx); }
.cm-blanks-count.is-empty { color: var(--cm-faint); }

/* ---------- Answers per blank ---------- */
.cm-blank-row { display: flex; align-items: center; gap: 10px; }
.cm-blank-index { flex: none; width: 52px; font: 600 12px ui-monospace, Menlo, monospace; color: var(--cm-faint); }
.cm-blank-row .form-control { flex: 1; }
.cm-blank-group { display: flex; flex-direction: column; gap: 8px; border: 1px solid var(--cm-border-soft); border-radius: 10px; padding: 12px 14px; }
.cm-blank-group__head { display: flex; align-items: baseline; gap: 8px; }
.cm-blank-context { font-size: 12px; color: var(--cm-disabled); }
.cm-blank-context b { color: var(--cm-faint); }

/* ---------- Pastilles supprimables (variantes acceptées, mots intrus) ---------- */
.cm-chip-list { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.cm-chip {
    display: flex; align-items: center; gap: 7px; border: 1px solid var(--cm-border);
    background: var(--cm-surface-alt); border-radius: 8px; padding: 7px 11px;
    font: 600 13px var(--cm-font-ui); color: var(--cm-ink);
}
.cm-chip--accepted { border-color: #cde3d6; background: #eaf3ee; color: #25603d; }
.cm-chip__remove { border: 0; background: none; padding: 0; line-height: 1; cursor: pointer; color: var(--cm-disabled); }
.cm-chip--accepted .cm-chip__remove { color: #9bbfa9; }
.cm-chip__remove:hover { color: var(--cm-red-tx); }
.cm-chip-add { border: 0; background: none; padding: 7px 4px; font: 600 13px var(--cm-font-ui); color: var(--cm-brand); cursor: pointer; }
.cm-chip-add:hover { text-decoration: underline; }

/* ---------- Interrupteur 40x22 (tokens du handoff) ---------- */
.cm-toggle-row { display: flex; align-items: center; gap: 9px; font-size: 13px; color: var(--cm-ink); }
.cm-toggle { flex: none; position: relative; width: 40px; height: 22px; }
.cm-toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-toggle__track {
    display: block; width: 40px; height: 22px; border-radius: 11px; background: #c8d3dc;
    cursor: pointer; transition: background .15s ease;
}
.cm-toggle__track::after {
    content: ''; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px;
    border-radius: 50%; background: #fff; transition: transform .15s ease;
}
/* :has() on the switch rather than "input:checked + .cm-toggle__track": when the checkbox comes
   from form_widget(), Symfony wraps it in a <div class="form-check">, so the input is no longer the
   track's preceding sibling and the adjacent-sibling selector never matched. The "display: contents"
   set on .form-check further down does NOT help: it removes the box, not the sibling relationship
   the combinator depends on. Symptom: the three switches of the "Nouveau travail" wizard did not
   move when clicked (only their label changed, since that is driven from JS), while the fill-in-the-
   blanks ones - which write their input by hand, as a direct sibling - worked. :has() covers both
   shapes. */
.cm-toggle:has(input:checked) .cm-toggle__track { background: var(--cm-brand); }
.cm-toggle:has(input:checked) .cm-toggle__track::after { transform: translateX(18px); }
.cm-toggle:has(input:focus-visible) .cm-toggle__track { outline: 2px solid var(--cm-brand); outline-offset: 2px; }

/* ---------- Attempt (2c/2d): the text and its blanks ---------- */
.cm-blanks-text { font: 400 17px/2.2 var(--cm-font-title); color: var(--cm-ink); }
.cm-blanks-hint { font-size: 12.5px; color: var(--cm-faint); }
.cm-blank-slot {
    display: inline-flex; align-items: center; justify-content: center; min-width: 64px;
    border-radius: 7px; padding: 3px 14px; font: 600 15px var(--cm-font-ui);
    border: 1.5px dashed var(--cm-disabled); background: var(--cm-surface-alt); color: var(--cm-disabled);
    cursor: pointer;
}
.cm-blank-slot:hover { border-color: var(--cm-brand); }
/* Current blank: blue dashes + halo, before any word is even dropped in. */
.cm-blank-slot.is-active { background: var(--cm-surface); border-color: var(--cm-brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-brand) 12%, transparent); }
/* Filled blank: solid blue chip; clicking it sends the word back to the bank. */
.cm-blank-slot.is-filled {
    border-style: solid; border-width: 1px; border-color: var(--cm-brand);
    background: var(--cm-blue-bg-soft); color: var(--cm-blue-tx);
}
.cm-blank-slot.is-filled:hover { background: var(--cm-blue-bg); }
/* Free input: a real field, sized on its content by the Stimulus controller. */
.cm-blank-input {
    display: inline-flex; min-width: 88px; border: 1px solid var(--cm-disabled); border-radius: 7px;
    padding: 3px 12px; font: 600 15px var(--cm-font-ui); color: var(--cm-ink); background: var(--cm-surface);
}
.cm-blank-input::placeholder { font-weight: 400; font-size: 13px; color: var(--cm-disabled); }
.cm-blank-input:not(:placeholder-shown) { border-color: var(--cm-brand); }
.cm-blank-input:focus { outline: none; border-color: var(--cm-brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-brand) 15%, transparent); }

/* ---------- Attempt (2c): the word bank ---------- */
.cm-wordbank { display: flex; flex-direction: column; gap: 8px; border-top: 1px solid var(--cm-border-soft); padding-top: 14px; }
.cm-wordbank__title { font: 600 12px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .5px; }
.cm-wordbank__word {
    border: 1px solid var(--cm-border); background: var(--cm-surface); border-radius: 8px; padding: 8px 14px;
    font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); cursor: pointer;
}
.cm-wordbank__word:hover { border-color: var(--cm-brand); background: #f6f9fb; }
.cm-wordbank__word.is-selected { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); color: var(--cm-blue-tx); box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-brand) 15%, transparent); }
/* Word already dropped into a blank: struck through and greyed, but left in place so the bank does not move. */
.cm-wordbank__word.is-used { background: var(--cm-surface-alt); color: var(--cm-disabled); text-decoration: line-through; cursor: default; box-shadow: none; }
.cm-wordbank__word.is-used:hover { border-color: var(--cm-border); background: var(--cm-surface-alt); }

/* ---------- Correction (1m): each blank in green/red ---------- */
.cm-blank-slot--correct { border: 1px solid #cde3d6; background: #eaf3ee; color: #25603d; cursor: default; }
.cm-blank-slot--wrong { border: 1px solid color-mix(in srgb, var(--cm-red-tx) 35%, var(--cm-border)); background: var(--cm-red-bg); color: var(--cm-red-tx); text-decoration: line-through; cursor: default; }
.cm-blank-expected { font: 600 12.5px var(--cm-font-ui); color: var(--cm-positive-tx); }

/* ==========================================================================
   Lancer un quiz (.cm-radiocard, .cm-diffslider, .cm-diffbar) - screen 1c.
   ========================================================================== */

/* ---------- Radio-card picker (Mode) - :has() drives the selected style, no JS needed ---------- */
.cm-radiocard {
    position: relative; display: flex; align-items: flex-start; gap: 10px;
    border: 1px solid var(--cm-border); border-radius: 10px; padding: 12px 14px; cursor: pointer;
}
.cm-radiocard:hover { border-color: var(--cm-brand); }
/* Compact variant for a two-option toggle that sits under its own field rather than in a grid of
   cards - the padded card would dwarf the input it qualifies. */
.cm-radiocard--inline { align-items: center; padding: 6px 12px; gap: 8px; }
.cm-radiocard--inline .cm-radiocard__title { font-size: 13px; }
/* form_widget() on a single expanded-choice view renders the full Bootstrap5 .form-check
   wrapper (input + its own form-check-label), not a bare <input> - the markup here supplies its
   own richer title/description spans instead, so Symfony's default label/wrapper are hidden
   rather than left to double up visually. The outer <label class="cm-radiocard"> still toggles
   the (now-hidden) input natively via label-wraps-input, no `for` association needed. */
.cm-radiocard .form-check { display: contents; }
.cm-radiocard .form-check-label { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-radiocard input[type="radio"] { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-radiocard__dot {
    flex: none; width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid #b3c6d6; margin-top: 1px;
    display: grid; place-items: center;
}
.cm-radiocard__dot::after { content: ''; width: 7px; height: 7px; border-radius: 50%; background: #fff; opacity: 0; }
.cm-radiocard__title { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-radiocard__desc { font-size: 12.5px; color: var(--cm-faint); }
.cm-radiocard:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-blue-bg); }
.cm-radiocard:has(input:checked) .cm-radiocard__dot { background: var(--cm-brand); border-color: var(--cm-brand); }
.cm-radiocard:has(input:checked) .cm-radiocard__dot::after { opacity: 1; }
.cm-radiocard:has(input:checked) .cm-radiocard__title { color: var(--cm-blue-tx); }

/* ---------- Difficulty slider - native <input type="range"> restyled to match the mockup ---------- */
.cm-diffslider {
    width: 100%; appearance: none; -webkit-appearance: none; height: 6px; border-radius: 3px;
    background: var(--cm-border); outline: none; margin: 0;
}
.cm-diffslider::-webkit-slider-thumb {
    -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--cm-surface);
    border: 2.5px solid var(--cm-brand); box-shadow: 0 1px 3px rgba(18, 52, 77, .3); cursor: grab;
}
.cm-diffslider::-moz-range-thumb {
    width: 18px; height: 18px; border-radius: 50%; background: var(--cm-surface);
    border: 2.5px solid var(--cm-brand); box-shadow: 0 1px 3px rgba(18, 52, 77, .3); cursor: grab;
}
.cm-diffslider-labels { display: flex; font-size: 11.5px; color: var(--cm-faint); margin-top: 4px; }
.cm-diffslider-labels span:last-child { margin-left: auto; }

/* ---------- Stacked facile/moyen/difficile distribution bar + legend ---------- */
.cm-diffbar { display: flex; height: 12px; border-radius: 6px; overflow: hidden; }
.cm-diffbar__facile { background: var(--cm-positive-tx); }
.cm-diffbar__moyen { background: var(--cm-gold); }
.cm-diffbar__difficile { background: var(--cm-red-tx); }
.cm-difflegend { display: flex; gap: 16px; font-size: 12px; color: var(--cm-muted); flex-wrap: wrap; }
.cm-difflegend__item { display: flex; align-items: center; gap: 5px; }
.cm-difflegend__swatch { width: 9px; height: 9px; border-radius: 3px; flex: none; }

/* ---------- Merged question pool ("rajouter des quiz") - launch screen 1c and Concours live ----------
   The first row is the quiz the launch started from and has no select: it can be added to, never
   swapped out (on the live screen it is a select like the others, hence .cm-quizpool__select
   carrying the shared width rather than the row). */
.cm-quizpool { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.cm-quizpool__base {
    display: flex; align-items: baseline; gap: 8px; width: 100%;
    background: var(--cm-surface-soft); border: 1px solid var(--cm-border); border-radius: 9px;
    padding: 9px 12px;
}
.cm-quizpool__name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-quizpool__count { font-size: 12px; color: var(--cm-faint); margin-left: auto; }
.cm-quizpool__rows { width: 100%; }
.cm-quizpool__rows:empty { display: none; }
.cm-quizpool__row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.cm-quizpool__select { flex: 1; min-width: 0; font-size: 13px; }
.cm-quizpool__remove {
    border: 0; background: none; color: var(--cm-faint); font-size: 15px; line-height: 1;
    cursor: pointer; padding: 4px; flex: none;
}
.cm-quizpool__remove:hover { color: var(--cm-red-tx); }
.cm-quizpool .cm-dashed-add:disabled { opacity: .5; cursor: not-allowed; }

/* ---------- Gold variant of .cm-note (Ressources > Application e-CO "install before race day") ---------- */
.cm-note--gold {
    background: var(--cm-gold-bg); border-color: color-mix(in srgb, var(--cm-gold) 45%, var(--cm-border));
    border-left-color: var(--cm-gold); color: var(--cm-gold-tx);
}
.cm-note--danger {
    border-left-color: var(--cm-red-tx);
    background: var(--cm-red-bg);
    color: var(--cm-red-tx);
}
.cm-note--danger .cm-note__icon { background: var(--cm-red-tx); }

/* ---------- App download hero banner (Ressources > Application mobile/e-CO) ---------- */
.cm-app-hero {
    display: flex; align-items: center; gap: 22px; border-radius: var(--cm-radius);
    padding: 24px 26px; color: #fff;
}
.cm-app-hero--brand { background: var(--cm-brand); }
.cm-app-hero--navy { background: var(--cm-navy); }
.cm-app-hero__icon { flex: none; width: 84px; height: 84px; border-radius: 22px; }
.cm-app-hero__body { flex: 1; }
.cm-app-hero__name { font: 600 20px var(--cm-font-title); }
.cm-app-hero__desc { font-size: 13px; line-height: 1.55; margin-top: 4px; opacity: .9; }
.cm-app-hero__meta { font-size: 13px; margin-top: 4px; opacity: .8; }

/* ---------- Platform download cards (Android / iOS) ---------- */
.cm-app-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 700px) { .cm-app-cards { grid-template-columns: 1fr; } }
.cm-app-card {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
    padding: 18px 20px; display: flex; flex-direction: column; gap: 14px;
}
.cm-app-card__head { display: flex; align-items: center; gap: 10px; }
.cm-app-card__platform { font: 600 15px var(--cm-font-ui); color: var(--cm-ink); }
.cm-app-card__meta { margin-left: auto; font-size: 11.5px; color: var(--cm-faint); }
.cm-app-card__body { display: flex; align-items: center; gap: 16px; }
.cm-app-card__qr { flex: none; width: 84px; height: 84px; border: 1px solid var(--cm-border-soft); border-radius: 6px; }
.cm-app-card__qr svg { display: block; width: 100%; height: 100%; }
.cm-app-card__actions { flex: 1; display: flex; flex-direction: column; gap: 9px; }
.cm-app-card__download {
    border-radius: var(--cm-radius-sm); padding: 11px; text-align: center;
    font: 600 13.5px var(--cm-font-ui); color: #fff; text-decoration: none;
}
.cm-app-card__download--brand { background: var(--cm-brand); }
.cm-app-card__download--brand:hover { background: var(--cm-brand-strong); color: #fff; }
.cm-app-card__download--navy { background: var(--cm-navy); }
.cm-app-card__download--navy:hover { background: color-mix(in srgb, var(--cm-navy) 85%, #fff); color: #fff; }
.cm-app-card__copy {
    background: none; border: 0; padding: 0; font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong);
    text-align: center; cursor: pointer;
}
.cm-app-card__copy:hover { text-decoration: underline; }
.cm-app-card__install {
    border-top: 1px solid var(--cm-border-soft); padding-top: 12px; display: flex; flex-direction: column; gap: 10px;
}
.cm-app-card__install-title {
    font: 600 12px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .5px;
}
.cm-app-card__step { display: flex; gap: 10px; font-size: 12.5px; color: var(--cm-text); line-height: 1.55; }
.cm-app-card__step-index {
    flex: none; width: 20px; height: 20px; border-radius: 50%; background: var(--cm-blue-bg); color: var(--cm-blue-tx);
    display: grid; place-items: center; font: 700 11px var(--cm-font-ui);
}

/* ---- Concours live (Kahoot-style projector/player screens) - Turns 1q/1u/1h/1i/1j/1l -----------
   Deliberately hardcoded dark, independent of the app's light/dark theme toggle: these are meant
   to read as a projected classroom screen (or a full-bleed mobile game screen), same as the
   mockup itself. */
.cm-quiz-live {
    min-height: 100vh; display: flex; flex-direction: column; color: #e8eef3; padding: 28px 36px; box-sizing: border-box;
    background: radial-gradient(1200px 800px at 50% -10%, #16436b 0%, #0d2438 62%);
    font-family: var(--cm-font-ui);
}
.cm-quiz-live--mobile { padding: 20px 18px; min-height: 100dvh; }
/* Bootstrap's `.d-flex`/`.d-grid` utility classes carry `!important`, which otherwise beats the
   native `[hidden] { display: none }` UA rule (no !important) on the same element - the
   quiz_live_host_controller.js/quiz_live_play_controller.js phase-toggling relies on `hidden`
   actually hiding the phase, so it must win here regardless of what utility classes sit next to it. */
.cm-quiz-live [hidden] { display: none !important; }
.cm-quiz-live__topbar { display: flex; align-items: center; gap: 18px; }
.cm-quiz-live__eyebrow { font: 600 14px var(--cm-font-ui); letter-spacing: 3px; color: var(--cm-gold); text-transform: uppercase; }
.cm-quiz-live__meta { font: 600 15px var(--cm-font-ui); color: #9db4c6; }
.cm-quiz-live__spacer { margin-left: auto; }
.cm-quiz-live__timer {
    width: 64px; height: 64px; border-radius: 50%; background: var(--cm-navy); border: 3px solid var(--cm-gold);
    display: grid; place-items: center; font: 700 24px var(--cm-font-ui); color: #fff; flex: none;
}
.cm-quiz-live__timer--sm { width: 42px; height: 42px; font-size: 16px; border-width: 2.5px; }

.cm-quiz-live-chip {
    background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.18); border-radius: 20px;
    padding: 9px 18px; font: 600 15px var(--cm-font-ui); color: #fff;
}

.cm-quiz-live-countdown {
    width: 230px; height: 230px; border-radius: 50%; border: 6px solid rgba(201,160,78,.25);
    display: grid; place-items: center; margin: 0 auto;
}
.cm-quiz-live-countdown--mobile { width: 170px; height: 170px; border-width: 5px; }
.cm-quiz-live-countdown__number { font: 700 120px var(--cm-font-title); color: #fff; }
.cm-quiz-live-countdown--mobile .cm-quiz-live-countdown__number { font-size: 86px; }

.cm-quiz-live-question__label { font: 600 44px var(--cm-font-title); color: #fff; text-align: center; max-width: 1000px; line-height: 1.3; margin: 0 auto; }
.cm-quiz-live-question__image { max-height: 220px; border-radius: 8px; margin: 16px auto; display: block; }
.cm-quiz-live-answers { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.cm-quiz-live-answer {
    display: flex; align-items: center; gap: 16px; border-radius: 14px; padding: 22px 24px; color: #fff;
    font: 600 24px var(--cm-font-ui);
}
.cm-quiz-live-answer__shape { font-size: 26px; opacity: .85; }
.cm-quiz-live-answer--blue { background: var(--cm-brand); }
.cm-quiz-live-answer--gold { background: var(--cm-gold); color: var(--cm-navy); }
.cm-quiz-live-answer--gold .cm-quiz-live-answer__shape { color: rgba(18,52,77,.75); }
.cm-quiz-live-answer--green { background: var(--cm-positive-tx); }
.cm-quiz-live-answer--red { background: var(--cm-red-tx); }

.cm-quiz-live-shapes { flex: 1; display: grid; grid-template-rows: 1fr 1fr 1fr 1fr; gap: 12px; }
.cm-quiz-live-shape {
    border: 0; border-radius: 14px; display: flex; align-items: center; justify-content: center;
    font-size: 34px; color: #fff; cursor: pointer;
}
.cm-quiz-live-shape:disabled { opacity: .55; cursor: default; }
.cm-quiz-live-shape--selected { outline: 3px solid #fff; outline-offset: -3px; }
.cm-quiz-live-shape--blue { background: var(--cm-brand); }
.cm-quiz-live-shape--gold { background: var(--cm-gold); color: var(--cm-navy); }
.cm-quiz-live-shape--green { background: var(--cm-positive-tx); }
.cm-quiz-live-shape--red { background: var(--cm-red-tx); }

.cm-quiz-live-rank { display: flex; align-items: center; gap: 16px; }
.cm-quiz-live-rank__position { width: 34px; font: 700 18px var(--cm-font-ui); color: #9db4c6; text-align: right; }
.cm-quiz-live-rank__name { font: 600 20px var(--cm-font-ui); color: #fff; width: 200px; flex: none; }
.cm-quiz-live-rank__bar-wrap { flex: 1; height: 34px; display: flex; align-items: center; }
.cm-quiz-live-rank__bar {
    height: 100%; border-radius: 8px; background: var(--cm-brand); display: flex; align-items: center;
    justify-content: flex-end; padding: 0 14px; font: 700 16px var(--cm-font-ui); color: #fff; box-sizing: border-box;
}
.cm-quiz-live-rank__score { margin-left: auto; font: 700 16px var(--cm-font-ui); color: var(--cm-gold); }

.cm-quiz-live-podium { flex: 1; display: flex; align-items: flex-end; justify-content: center; gap: 22px; padding-bottom: 8px; }
.cm-quiz-live-podium__slot { display: flex; flex-direction: column; align-items: center; gap: 12px; width: 230px; }
.cm-quiz-live-podium__slot--first { width: 250px; }
.cm-quiz-live-podium__avatar {
    border-radius: 14px; background: var(--cm-navy); display: grid; place-items: center; color: #fff;
    font: 700 22px var(--cm-font-ui); outline: 3px solid rgba(255,255,255,.25); width: 64px; height: 64px;
}
.cm-quiz-live-podium__slot--first .cm-quiz-live-podium__avatar { width: 74px; height: 74px; background: var(--cm-gold); color: var(--cm-navy); outline: 4px solid rgba(201,160,78,.4); }
.cm-quiz-live-podium__bar { width: 100%; border-radius: 14px 14px 0 0; display: grid; place-items: center; font: 700 54px var(--cm-font-title); color: var(--cm-navy); }
.cm-quiz-live-podium__slot--first .cm-quiz-live-podium__bar { height: 230px; background: linear-gradient(180deg, #d9b268, var(--cm-gold)); font-size: 68px; }
.cm-quiz-live-podium__slot--second .cm-quiz-live-podium__bar { height: 170px; background: linear-gradient(180deg, #9db4c6, #5b7c96); }
.cm-quiz-live-podium__slot--third .cm-quiz-live-podium__bar { height: 120px; background: linear-gradient(180deg, #a97a52, #8a5a3b); color: #fff; }

.cm-quiz-live-btn {
    border: 1px solid rgba(255,255,255,.25); border-radius: 10px; padding: 11px 20px; font: 600 14px var(--cm-font-ui);
    color: #e8eef3; background: transparent; cursor: pointer;
}
.cm-quiz-live-btn:hover { color: #fff; border-color: #fff; }
.cm-quiz-live-btn--primary { background: var(--cm-gold); color: var(--cm-navy); border: 0; font-weight: 700; }
.cm-quiz-live-btn--primary:hover { background: var(--cm-gold-strong); color: var(--cm-navy); }

/* ==========================================================================
   Carnet de notes - pastel grade-band colors (design's gradeColor(), 4 bands over a
   /20-normalized value - see App\Service\EvaluationAverageCalculator::gradeColorClass()).
   Fixed rgba values (not --cm-* tokens) straight from the handoff, semi-transparent enough to
   stay legible over both the light and dark surface backgrounds without a separate override.
   ========================================================================== */
.cm-grade-band-none { color: #c2ccd4; background: transparent; }
.cm-grade-band-1 { color: #8a1f33; background: rgba(214,45,74,.42); }
.cm-grade-band-2 { color: #8a4a0e; background: rgba(242,140,22,.52); }
.cm-grade-band-3 { color: #6b5c0e; background: rgba(230,208,35,.62); }
.cm-grade-band-4 { color: #5b6c79; background: rgba(150,165,178,.34); }
[data-bs-theme="dark"] .cm-grade-band-1 { color: #ffb3c0; }
[data-bs-theme="dark"] .cm-grade-band-2 { color: #ffd199; }
[data-bs-theme="dark"] .cm-grade-band-3 { color: #f0e29a; }
[data-bs-theme="dark"] .cm-grade-band-4 { color: #cfd8de; }

/* ==========================================================================
   Carnet de notes (design/design_handoff_carnet_de_notes) - the five screens of the module:
   teacher grid, evaluation form, rubric, quick entry, student screen.

   The designs are authoritative below the page title: everything that follows takes their
   measurements verbatim. Colors go through the --cm-* tokens whenever the handoff value is one the
   application has already named (#1B6BA8 = --cm-brand, #12344d = --cm-navy, #8a99a6 = --cm-faint...),
   so dark mode follows without a second stylesheet; the few hues specific to the module (the grid's
   thick rule, the type/modality/status badge backgrounds) have their own local variable below,
   redeclared in dark.

   The page header (navbar, breadcrumb, title) stays the one from layout/app.html.twig and is not
   taken from the designs, which show their own mockup chrome.
   ========================================================================== */
:root {
    /* Thick rule between the grid header and its rows (#e4ebf1): between --cm-border and
       --cm-border-soft, it exists nowhere else in the application. */
    --cm-gb-rule: #e4ebf1;
    /* Background of the entry cells on focus. */
    --cm-gb-focus-bg: #f6fafe;
}
[data-bs-theme="dark"] {
    --cm-gb-rule: #33424f;
    --cm-gb-focus-bg: #1f2f3d;
}

/* ---------- Toolbar (matière / period / filters) ---------- */
.cm-gb-toolbar {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 12px;
    padding: 14px 16px; display: flex; flex-direction: column; gap: 12px; margin-bottom: 14px;
}
.cm-gb-toolbar__row { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.cm-gb-toolbar__rule { height: 1px; background: var(--cm-border-soft); }
.cm-gb-field { display: flex; align-items: center; gap: 9px; }
/* The handoff's uppercase label, shared by all of its columns and sections. */
.cm-gb-label {
    font: 700 11px var(--cm-font-ui); color: var(--cm-faint);
    text-transform: uppercase; letter-spacing: .5px;
}
.cm-gb-select {
    border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); padding: 8px 10px;
    font: 600 13px var(--cm-font-ui); color: var(--cm-navy); background: var(--cm-surface);
    outline: none; cursor: pointer;
}
[data-bs-theme="dark"] .cm-gb-select { color: var(--cm-ink); }

/* Filters: text chips, not <select> - a single active state per group. */
.cm-gb-filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cm-gb-filters__group { display: flex; gap: 3px; font: 600 11.5px var(--cm-font-ui); }
.cm-gb-filters__sep { color: var(--cm-border); }
.cm-gb-chip {
    padding: 4px 9px; border-radius: 12px; cursor: pointer; background: transparent;
    color: var(--cm-faint); white-space: nowrap; border: 0; font: inherit;
}
.cm-gb-chip:hover { color: var(--cm-muted); }
.cm-gb-chip.is-active { background: #e4ecf3; color: var(--cm-navy); }
[data-bs-theme="dark"] .cm-gb-chip.is-active { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }

/* ---------- Class grid ----------
   Not a <table>: the student column is `position: sticky` while the evaluation columns scroll,
   which a table does not do reliably across every configuration. Hence flex columns, each
   responsible for its own row heights (44px) - which must stay identical from one column to the
   next. */
.cm-gb-grid {
    overflow-x: auto; background: var(--cm-surface);
    border: 1px solid var(--cm-border); border-radius: 12px;
}
.cm-gb-grid__inner { display: flex; min-width: max-content; }
.cm-gb-col--students {
    position: sticky; left: 0; z-index: 3; background: var(--cm-surface);
    box-shadow: 6px 0 8px -6px rgba(0, 0, 0, .12);
}
.cm-gb-col--avg { width: 96px; flex: none; background: var(--cm-surface-soft); border-right: 1px solid var(--cm-gb-rule); }
.cm-gb-col--eval { width: 132px; flex: none; border-right: 1px solid var(--cm-border-soft); }
.cm-gb-head {
    height: 116px; box-sizing: border-box; border-bottom: 2px solid var(--cm-gb-rule);
    display: flex; flex-direction: column; gap: 6px; padding: 9px 9px 8px;
}
.cm-gb-head--students { padding: 0 16px 12px; flex-direction: row; align-items: flex-end; }
.cm-gb-head--avg { align-items: center; justify-content: flex-end; padding-bottom: 12px; }
.cm-gb-row {
    height: 44px; box-sizing: border-box; border-bottom: 1px solid var(--cm-border-faint);
    display: flex; align-items: center; gap: 9px; padding: 0 16px;
}
.cm-gb-row--avg { display: grid; place-items: center; padding: 0; }
.cm-gb-foot {
    height: 44px; box-sizing: border-box; border-top: 2px solid var(--cm-gb-rule);
    display: grid; place-items: center; font: 700 13px var(--cm-font-ui); color: var(--cm-muted);
}
.cm-gb-foot--students { display: flex; align-items: center; padding: 0 16px; font-size: 12px; }
.cm-gb-foot--avg { font: 700 14px var(--cm-font-title); color: var(--cm-navy); }
[data-bs-theme="dark"] .cm-gb-foot--avg { color: var(--cm-ink); }
.cm-gb-student { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); white-space: nowrap; }
/* The student's option (SLAM / SISR...): the shape comes from the designs, the background color is
   the one stored on the Option and set inline - that is already how every other screen displays an
   option (short name, colored background, white text: program/_user_card.html.twig). */
.cm-gb-tag {
    margin-left: auto; font: 700 9.5px var(--cm-font-ui); padding: 2px 6px; border-radius: 7px;
    background: var(--cm-brand); color: #fff; white-space: nowrap;
}
.cm-gb-avg {
    font: 700 13.5px var(--cm-font-ui); padding: 3px 9px; border-radius: 7px;
}

/* Header of an evaluation column. */
.cm-gb-evname {
    font: 700 12.5px var(--cm-font-ui); color: var(--cm-ink); line-height: 1.2; height: 30px;
    overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    cursor: pointer;
}
.cm-gb-evmeta { font-size: 10px; color: var(--cm-faint); line-height: 1.3; }
.cm-gb-evhidden {
    display: flex; align-items: center; gap: 4px; font: 700 9px var(--cm-font-ui);
    color: var(--cm-gold-tx); background: var(--cm-gold-bg); border-radius: 5px; padding: 2px 5px;
    align-self: flex-start; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
.cm-gb-evactions { margin-top: auto; display: flex; align-items: center; gap: 4px; }
.cm-gb-iconbtn {
    display: grid; place-items: center; border: 1px solid var(--cm-border); background: var(--cm-surface);
    color: var(--cm-muted); border-radius: 6px; padding: 4px 5px; cursor: pointer;
}
.cm-gb-iconbtn:hover { background: var(--cm-bg); }
.cm-gb-iconbtn--primary { border-color: #cfe0ee; background: #f4f9fd; color: var(--cm-blue-tx); }
.cm-gb-iconbtn--primary:hover { background: #e6f1fa; }
[data-bs-theme="dark"] .cm-gb-iconbtn--primary {
    border-color: var(--cm-mail-tint-border); background: var(--cm-mail-tint-bg); color: var(--cm-blue-tx);
}
.cm-gb-iconbtn--danger { border-color: #eadada; background: #fdf6f5; color: var(--cm-red-tx); }
.cm-gb-iconbtn--danger:hover { background: #f9ebe9; }
[data-bs-theme="dark"] .cm-gb-iconbtn--danger { border-color: var(--cm-red-bg); background: var(--cm-red-bg); color: var(--cm-red-tx); }
.cm-gb-sortbtn {
    margin-left: auto; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; cursor: pointer; border: 1px solid var(--cm-border); border-radius: 6px; padding: 4px 5px;
    background: transparent;
}
.cm-gb-sortbtn:hover { background: var(--cm-bg); }
.cm-gb-sortbtn--avg { margin-left: 0; padding: 4px 8px; }

/* Cellule de note. */
.cm-gb-cell {
    height: 44px; box-sizing: border-box; border-bottom: 1px solid var(--cm-border-faint);
    display: grid; place-items: center; position: relative; cursor: pointer;
}
.cm-gb-cell--readonly { cursor: default; }
.cm-gb-cell__input {
    width: 82px; text-align: center; border: 2px solid var(--cm-brand); border-radius: 6px;
    padding: 4px 2px; font: 700 12px var(--cm-font-ui); color: var(--cm-ink); outline: none;
    background: var(--cm-surface); box-sizing: border-box;
}
.cm-gb-val { font: 700 13px var(--cm-font-ui); padding: 3px 9px; border-radius: 6px; }
.cm-gb-val--empty { color: var(--cm-disabled); }
.cm-gb-val--abs { font-weight: 600; font-size: 12px; color: #7a8790; letter-spacing: .3px; }
.cm-gb-val--nt { font-weight: 600; font-size: 12px; color: #96a3ad; letter-spacing: .3px; }
.cm-gb-val--ne { font-weight: 600; font-size: 12px; color: #adb8c1; letter-spacing: .3px; }
/* Grade not counted: the dashed outline is the only mark that stays legible without a background color. */
.cm-gb-val--paren {
    font: 600 12.5px var(--cm-font-ui); font-style: italic; padding: 3px 7px; border-radius: 6px;
    background: transparent; border: 1px dashed currentColor; opacity: .8;
}
.cm-gb-cell__audio {
    position: absolute; bottom: 2px; right: 3px; color: var(--cm-blue-tx);
    display: grid; place-items: center; cursor: pointer; border: 0; background: transparent; padding: 0;
}
.cm-gb-legend {
    margin-top: 12px; display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
    font-size: 12px; color: var(--cm-faint);
}
.cm-gb-legend__item { display: inline-flex; align-items: center; gap: 7px; }
/* Round chip at the top of the delete modal (44px in the designs, against the 66px of
   .cm-authcard__pastille, which belongs to the login screens). */
.cm-gb-confirm__icon {
    width: 44px; height: 44px; border-radius: 50%; margin: 0 auto 12px;
    background: var(--cm-red-bg); color: var(--cm-red-tx); display: grid; place-items: center;
}

/* ---------- Écrans pleine largeur : formulaire, barème, saisie ---------- */
.cm-gb-page { width: 100%; margin: 2px 0 0; display: flex; flex-direction: column; gap: 16px; box-sizing: border-box; }
.cm-gb-back {
    display: inline-flex; align-items: center; gap: 6px; color: var(--cm-muted);
    font: 600 13px var(--cm-font-ui); text-decoration: none; align-self: flex-start;
}
.cm-gb-back:hover { color: var(--cm-brand-strong); }
.cm-gb-recap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cm-gb-recap__label { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-gb-recap__chip {
    font: 700 12px var(--cm-font-ui); color: var(--cm-navy); background: #e4ecf3;
    border-radius: 6px; padding: 3px 10px;
}
[data-bs-theme="dark"] .cm-gb-recap__chip { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-gb-panel {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 12px;
    padding: 24px; display: flex; flex-direction: column; gap: 20px;
}
/* Evaluation form: two columns separated by a vertical rule. The rule disappears below 1000px,
   where the two columns stack - a horizontal line would duplicate the spacing that already
   separates the field groups. */
.cm-gb-panel--split { flex-direction: row; align-items: stretch; gap: 40px; padding: 28px; }
.cm-gb-panel__col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 20px; }
.cm-gb-panel__rule { width: 1px; flex: none; background: var(--cm-border-soft); }
@media (max-width: 1000px) {
    .cm-gb-panel--split { flex-direction: column; gap: 20px; }
    .cm-gb-panel__rule { display: none; }
}
.cm-gb-fieldset { display: flex; flex-direction: column; gap: 9px; }
.cm-gb-fieldrow { display: flex; gap: 18px; flex-wrap: wrap; }
.cm-gb-fieldrow > .cm-gb-fieldset { flex: 1; min-width: 150px; }
.cm-gb-fieldset--narrow { flex: none !important; width: 110px; min-width: 0 !important; }
.cm-gb-hint { font-size: 11.5px; color: var(--cm-faint); }
.cm-gb-hint--indent { padding-left: 25px; }
.cm-gb-check {
    display: flex; align-items: center; gap: 9px; cursor: pointer;
    font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink);
}
/* Same treatment as .cm-radiocard: on an expanded choice view or a checkbox, Symfony's Bootstrap
   layouts render the whole .form-check block (input plus its own label) and not a bare <input>.
   The templates here write their own label, so Symfony's is hidden rather than left duplicated; the
   wrapping <label> keeps checking the input natively, with no `for` attribute. */
.cm-gb-check .form-check,
.cm-gb-seg__opt .form-check,
.cm-gb-pickcard .form-check { display: contents; }
.cm-gb-check .form-check-label,
.cm-gb-seg__opt .form-check-label,
.cm-gb-pickcard .form-check-label { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-gb-check input[type="checkbox"] {
    width: 16px; height: 16px; accent-color: var(--cm-brand); cursor: pointer; margin: 0; flex: none;
}

/* Choice cards (Type d'évaluation) and segments (Modalité / Statut / Visibilité). */
.cm-gb-pickcards { display: flex; gap: 9px; flex-wrap: wrap; }
.cm-gb-pickcard {
    flex: 1; min-width: 130px; display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
    padding: 10px 12px; border-radius: 9px; cursor: pointer; border: 1.5px solid var(--cm-border);
    background: var(--cm-surface); color: var(--cm-muted);
}
.cm-gb-pickcard b { font: 700 13.5px var(--cm-font-ui); }
.cm-gb-pickcard span { font-size: 11px; opacity: .8; }
.cm-gb-pickcard input[type="radio"] { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-gb-pickcard:has(input:checked) { border-color: currentColor; }
.cm-gb-pickcard--written:has(input:checked) { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-gb-pickcard--oral:has(input:checked) { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }
.cm-gb-pickcard--practical:has(input:checked) { background: var(--cm-purple-bg); color: var(--cm-purple-tx); }
.cm-gb-seg {
    display: flex; border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
    overflow: hidden; font: 600 13px var(--cm-font-ui);
}
.cm-gb-seg__opt {
    flex: 1; text-align: center; padding: 9px 0; cursor: pointer;
    background: var(--cm-surface); color: var(--cm-muted); margin: 0;
}
.cm-gb-seg__opt input[type="radio"] { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-gb-seg__opt:has(input:checked) { background: var(--cm-brand); color: #fff; }
.cm-gb-seg--pad .cm-gb-seg__opt { flex: none; padding: 9px 18px; }
/* Navigation segment (period, student side): same shapes, but links and not radios. */
.cm-gb-seg__link {
    flex: none; padding: 9px 18px; text-align: center; text-decoration: none;
    background: var(--cm-surface); color: var(--cm-muted);
}
.cm-gb-seg__link:hover { color: var(--cm-brand-strong); }
.cm-gb-seg__link.is-active { background: var(--cm-brand); color: #fff; }
.cm-gb-actions { display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
/* The date+time pair of scheduled visibility. Its own class rather than .d-flex: the Bootstrap
   utilities carry !important and would beat the hidden attribute that hides this block for as long
   as visibility is immediate. */
.cm-gb-visfields { display: flex; align-items: center; gap: 8px; }
.cm-gb-visfields[hidden] { display: none; }

/* ---------- Rubric editor ---------- */
/* Sections in a 2-column grid, top-aligned: a short section does not stretch to its neighbour's
   height. The « + Ajouter une section » tile is a cell of that grid. */
.cm-gb-sections { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; align-items: start; }
@media (max-width: 900px) {
    .cm-gb-sections { grid-template-columns: 1fr; }
}
.cm-gb-bar-section {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 12px;
    padding: 18px; display: flex; flex-direction: column; gap: 12px;
}
.cm-gb-bar-name {
    flex: 1; min-width: 0; border: 0; border-bottom: 2px solid var(--cm-gb-rule); padding: 6px 2px;
    font: 600 16px var(--cm-font-title); color: var(--cm-ink); outline: none; background: transparent;
}
.cm-gb-bar-name:focus { border-bottom-color: var(--cm-brand); }
.cm-gb-bar-qrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cm-gb-bar-qlabel { font: 600 12px var(--cm-font-ui); color: var(--cm-faint); flex: none; }
.cm-gb-bar-input {
    border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); padding: 9px 11px;
    font: 600 13px var(--cm-font-ui); color: var(--cm-ink); outline: none; background: var(--cm-surface);
}
.cm-gb-bar-input:focus { border-color: var(--cm-brand); }
.cm-gb-dashed {
    border: 1px dashed #c5d2dd; background: var(--cm-surface); color: var(--cm-brand-strong);
    border-radius: var(--cm-radius-sm); padding: 7px 14px; font: 600 12.5px var(--cm-font-ui); cursor: pointer;
    align-self: flex-start;
}
.cm-gb-dashed:hover { background: #f4f9fd; }
[data-bs-theme="dark"] .cm-gb-dashed { border-color: var(--cm-border); }
[data-bs-theme="dark"] .cm-gb-dashed:hover { background: var(--cm-surface-alt); }
.cm-gb-dashed--block {
    align-self: stretch; border-width: 1.5px; border-radius: 12px; padding: 13px;
    font-size: 13.5px; background: transparent; text-align: center;
}
/* « Section grid tile » variant: same minimum height as an empty section. */
.cm-gb-dashed--tile { min-height: 120px; }

/* ---------- Saisie rapide ---------- */
.cm-gb-hero {
    background: var(--cm-navy); color: #fff; border-radius: 12px; padding: 20px 24px;
    display: flex; flex-direction: column; gap: 14px;
}
.cm-gb-hero__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.cm-gb-hero__title { font: 600 19px var(--cm-font-title); }
.cm-gb-hero__meta { font-size: 12px; color: #9fb4c6; }
.cm-gb-hero__stats { display: flex; gap: 26px; text-align: right; }
.cm-gb-hero__num { font: 700 22px var(--cm-font-title); }
.cm-gb-hero__num span { font-size: 14px; color: #9fb4c6; }
.cm-gb-hero__cap { font-size: 11px; color: #9fb4c6; text-transform: uppercase; letter-spacing: .5px; }
.cm-gb-hero__ghost {
    align-self: flex-start; display: inline-flex; align-items: center; gap: 7px;
    border: 1px solid rgba(255, 255, 255, .35); background: transparent; color: #cfdde9;
    border-radius: var(--cm-radius-sm); padding: 7px 13px; font: 600 12px var(--cm-font-ui);
    cursor: pointer; text-decoration: none;
}
.cm-gb-hero__ghost:hover { background: rgba(255, 255, 255, .12); color: #fff; }
.cm-gb-progress { height: 7px; background: rgba(255, 255, 255, .15); border-radius: 6px; overflow: hidden; }
.cm-gb-progress__bar { height: 100%; background: var(--cm-gold); border-radius: 6px; }
.cm-gb-entry {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 12px; overflow-x: auto;
}
.cm-gb-entry__row {
    display: flex; align-items: center; gap: 10px; padding: 10px 16px;
    border-bottom: 1px solid #f1f4f7; min-width: max-content;
}
[data-bs-theme="dark"] .cm-gb-entry__row { border-bottom-color: var(--cm-border-faint); }
.cm-gb-entry__row.is-done { background: var(--cm-surface-soft); }
.cm-gb-entry__rank { width: 22px; text-align: center; font: 700 12px var(--cm-font-ui); color: #b4bfc9; flex: none; }
.cm-gb-entry__name { width: 230px; flex: none; display: flex; align-items: center; gap: 8px; min-width: 0; }
.cm-gb-entry__name > span:first-child {
    font: 600 14px var(--cm-font-ui); color: var(--cm-ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cm-gb-entry__name .cm-gb-tag { margin-left: 0; padding: 2px 7px; }
/* The grade already recorded is recalled as colored text to the left of the field: the pastel band
   of .cm-grade-band-* serves only as ink color there, its background being neutralised (later rule,
   equal specificity) - a 96px-wide flat area would unbalance the row. */
.cm-gb-entry__status {
    width: 96px; text-align: right; flex: none; white-space: nowrap;
    font: 600 12px var(--cm-font-ui); background: transparent;
}
.cm-gb-entry__status--empty { color: #b4bfc9; }
.cm-gb-entry__input {
    width: 66px; height: 42px; text-align: center; border: 1.5px solid var(--cm-border);
    border-radius: 9px; font: 700 18px var(--cm-font-ui); color: var(--cm-ink); outline: none;
    background: var(--cm-surface);
}
.cm-gb-entry__input:focus { border-color: var(--cm-brand); background: var(--cm-gb-focus-bg); }
.cm-gb-entry__scale { font-size: 12px; color: #b4bfc9; width: 28px; flex: none; }
.cm-gb-quick { display: flex; gap: 5px; flex: none; }
.cm-gb-quick button {
    border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-muted);
    border-radius: var(--cm-radius-sm); padding: 8px 11px; font: 700 11px var(--cm-font-ui); cursor: pointer;
}
.cm-gb-quick button:hover { background: var(--cm-bg); }
.cm-gb-quick .cm-gb-quick--abs { border-color: #f0d9c2; background: #fdf6ef; color: #b26a1f; }
.cm-gb-quick .cm-gb-quick--ne { border-color: #e2e7ec; background: #f7f9fb; color: var(--cm-faint); }
[data-bs-theme="dark"] .cm-gb-quick .cm-gb-quick--abs { border-color: var(--cm-gold-bg); background: var(--cm-gold-bg); color: var(--cm-gold-tx); }
[data-bs-theme="dark"] .cm-gb-quick .cm-gb-quick--ne { border-color: var(--cm-border); background: var(--cm-surface-alt); }
/* Question columns (rubric mode). */
.cm-gb-qhead { padding: 10px 16px 0; border-bottom: 2px solid var(--cm-gb-rule); background: var(--cm-surface-soft); min-width: max-content; }
.cm-gb-qhead__sections { display: flex; gap: 10px; align-items: flex-end; }
.cm-gb-qhead__cols { display: flex; align-items: flex-end; gap: 10px; padding: 7px 0 10px; }
.cm-gb-qhead__section {
    flex: none; text-align: center; font: 700 11px var(--cm-font-ui); color: var(--cm-brand-strong);
    background: var(--cm-blue-bg); border-radius: 6px; padding: 3px 6px; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
}
.cm-gb-qhead__q { width: 54px; flex: none; text-align: center; display: flex; flex-direction: column; gap: 1px; }
.cm-gb-qhead__num { font: 700 12px var(--cm-font-ui); color: var(--cm-brand-strong); }
.cm-gb-qhead__pts { font-size: 9.5px; color: var(--cm-faint); }
.cm-gb-qhead__total { width: 90px; flex: none; text-align: center; }
.cm-gb-qinput {
    width: 54px; height: 38px; flex: none; text-align: center; border: 1.5px solid var(--cm-border);
    border-radius: var(--cm-radius-sm); font: 700 14px var(--cm-font-ui); color: var(--cm-ink);
    outline: none; background: var(--cm-surface); box-sizing: border-box;
}
.cm-gb-qinput:focus { border-color: var(--cm-brand); background: var(--cm-gb-focus-bg); }
.cm-gb-qinput.is-invalid { border-color: var(--cm-red-tx); }
.cm-gb-qtotal {
    width: 90px; flex: none; text-align: center; font: 700 16px var(--cm-font-ui);
    white-space: nowrap; background: transparent;
}
/* Audio comment at the end of the row. */
.cm-gb-audio { display: flex; align-items: center; gap: 6px; flex: none; margin-left: auto; }
/* The record button carries its label: a lone microphone glyph at the far right of a row of grade
   inputs reads as decoration, and nothing else on this screen says what it does. */
.cm-gb-audio__record {
    display: flex; align-items: center; gap: 7px; border: 1px solid var(--cm-border);
    background: var(--cm-surface); color: var(--cm-muted); border-radius: var(--cm-radius-sm);
    padding: 7px 12px; font: 700 11px var(--cm-font-ui); white-space: nowrap; cursor: pointer;
}
.cm-gb-audio__record:hover { background: var(--cm-bg); color: var(--cm-red-tx); }
.cm-gb-audio__btn {
    display: grid; place-items: center; border: 1px solid var(--cm-border); background: var(--cm-surface);
    color: var(--cm-muted); border-radius: var(--cm-radius-sm); padding: 7px; cursor: pointer;
}
.cm-gb-audio__btn:hover { background: var(--cm-bg); color: var(--cm-red-tx); }
.cm-gb-audio__btn--danger { border-color: #eadada; background: #fdf6f5; color: var(--cm-red-tx); }
[data-bs-theme="dark"] .cm-gb-audio__btn--danger { border-color: var(--cm-red-bg); background: var(--cm-red-bg); }
.cm-gb-audio__play {
    display: flex; align-items: center; gap: 6px; border: 1px solid #cfe0ee; background: #f4f9fd;
    color: var(--cm-blue-tx); border-radius: var(--cm-radius-sm); padding: 7px 11px;
    font: 700 11px var(--cm-font-ui); cursor: pointer;
}
.cm-gb-audio__play:hover { background: #e6f1fa; }
[data-bs-theme="dark"] .cm-gb-audio__play { border-color: var(--cm-mail-tint-border); background: var(--cm-mail-tint-bg); }
.cm-gb-audio__rec {
    display: flex; align-items: center; gap: 7px; border: 0; background: var(--cm-red-tx); color: #fff;
    border-radius: var(--cm-radius-sm); padding: 8px 12px; font: 700 11px var(--cm-font-ui); cursor: pointer;
}
.cm-gb-audio__dot { width: 8px; height: 8px; border-radius: 2px; background: #fff; }
.cm-gb-audio__listen { font: 700 9.5px var(--cm-font-ui); padding: 2px 7px; border-radius: 7px; white-space: nowrap; }
.cm-gb-audio__listen--yes { background: var(--cm-green-bg); color: var(--cm-green-tx); }
.cm-gb-audio__listen--no { background: var(--cm-red-bg); color: var(--cm-red-tx); }
/* Sort control sitting just above the class list. */
.cm-gb-listbar { display: flex; justify-content: flex-end; }
.cm-gb-sort {
    display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--cm-border);
    background: var(--cm-surface); color: var(--cm-ink); border-radius: var(--cm-radius-sm);
    padding: 7px 12px; font: 600 12px var(--cm-font-ui); cursor: pointer;
}
.cm-gb-sort:hover { background: var(--cm-bg); }
.cm-gb-warn {
    background: #fdf4ef; border: 1px solid #f0dcc9; color: #9a5a1f; border-radius: 9px;
    padding: 10px 14px; font: 600 12.5px var(--cm-font-ui);
}
[data-bs-theme="dark"] .cm-gb-warn { background: var(--cm-gold-bg); border-color: var(--cm-gold-bg); color: var(--cm-gold-tx); }
.cm-gb-footbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
/* The help sentence may wrap; the buttons stay on the right in every case. */
.cm-gb-footbar__hint { font-size: 12px; color: var(--cm-faint); flex: 1 1 320px; }
.cm-gb-footbar > :last-child { margin-left: auto; }

/* ---------- Badges type / modalité / statut (grille, saisie, écran élève) ---------- */
.cm-gb-badge {
    font: 700 10px var(--cm-font-ui); padding: 3px 9px; border-radius: 7px; white-space: nowrap;
}
.cm-gb-badge--sm { font-size: 9px; padding: 2px 6px; border-radius: 6px; }
.cm-gb-badge--written { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-gb-badge--oral { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }
.cm-gb-badge--practical { background: var(--cm-purple-bg); color: var(--cm-purple-tx); }
.cm-gb-badge--individual { background: var(--cm-border-soft); color: var(--cm-muted); }
.cm-gb-badge--group { background: var(--cm-teal-bg); color: var(--cm-teal-tx); }
.cm-gb-badge--planned { background: var(--cm-green-bg); color: var(--cm-green-tx); }
.cm-gb-badge--surprise { background: var(--cm-gold-bg); color: var(--cm-warn-tx); }

/* ---------- Écran élève ---------- */
.cm-gb-shero {
    background: linear-gradient(120deg, #12344d, #1c4a6b); color: #fff; border-radius: 14px;
    padding: 22px 26px; display: flex; align-items: center; justify-content: space-between;
    gap: 20px; flex-wrap: wrap;
}
.cm-gb-shero__title { font: 600 21px var(--cm-font-title); }
.cm-gb-shero__meta { font-size: 13px; color: #a9c0d2; }
.cm-gb-shero__cap { font-size: 11px; color: #a9c0d2; text-transform: uppercase; letter-spacing: .5px; }
.cm-gb-shero__sub { font-size: 12px; color: #8fa6ba; }
.cm-gb-shero__avg { font: 700 30px var(--cm-font-title); padding: 8px 18px; border-radius: 12px; }
.cm-gb-card {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 12px;
    padding: 14px 18px; display: flex; flex-direction: column; gap: 11px;
}
/* Three fixed columns, never a flex-wrap: a card takes exactly 1/3 of the row and its title
   ellipsises - explicitly asked for by the handoff. */
.cm-gb-recent { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.cm-gb-recent__item {
    border: 1px solid var(--cm-border-soft); border-radius: 10px; padding: 10px 12px;
    display: flex; align-items: center; gap: 12px; min-width: 0;
}
.cm-gb-recent__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.cm-gb-recent__name {
    font: 600 13px var(--cm-font-ui); color: var(--cm-ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cm-gb-recent__meta { font-size: 11px; color: var(--cm-faint); }
.cm-gb-note { font: 700 14px var(--cm-font-ui); padding: 4px 10px; border-radius: 7px; flex: none; }
@media (max-width: 820px) {
    .cm-gb-recent { grid-template-columns: 1fr; }
}
.cm-gb-subjects { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 12px; overflow: hidden; }
.cm-gb-subject {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 14px 18px; cursor: pointer; border-bottom: 1px solid var(--cm-border-soft);
    background: var(--cm-surface); width: 100%; border-left: 0; border-right: 0; border-top: 0; text-align: left;
}
.cm-gb-subject:hover { background: var(--cm-surface-soft); }
.cm-gb-subject__chevron {
    width: 22px; height: 22px; border-radius: 6px; background: #eef3f8; color: var(--cm-muted);
    display: grid; place-items: center; font: 700 15px var(--cm-font-ui); flex: none; line-height: 1;
    transition: transform .15s ease;
}
/* A single chevron in the template, rotated when the panel is expanded - it is the attribute
   Bootstrap already keeps up to date on the collapse button. */
.cm-gb-subject[aria-expanded="true"] .cm-gb-subject__chevron { transform: rotate(90deg); }
[data-bs-theme="dark"] .cm-gb-subject__chevron { background: var(--cm-surface-alt); }
.cm-gb-subject__name { font: 600 15px var(--cm-font-title); color: var(--cm-ink); }
.cm-gb-subject__sub { font-size: 11.5px; color: var(--cm-faint); }
.cm-gb-subject__avg { font: 700 16px var(--cm-font-title); padding: 5px 13px; border-radius: 9px; }
.cm-gb-evrow {
    display: flex; align-items: center; gap: 12px; padding: 11px 18px 11px 49px;
    border-bottom: 1px solid var(--cm-border-faint);
}
.cm-gb-evrow__name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-gb-evrow__badges { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.cm-gb-evrow__meta { font-size: 11px; color: var(--cm-faint); }
.cm-gb-evrow__audio { position: relative; flex: none; }
.cm-gb-evrow__unlistened {
    position: absolute; top: -4px; right: -4px; width: 9px; height: 9px; border-radius: 50%;
    background: #e0483a; border: 1.5px solid var(--cm-surface);
}

/* ==========================================================================
   Autoévaluation (design_handoff_carnet_de_notes, PROMPT_MODIFICATIONS §9, screens 5b/5c/5d):
   the student's own estimate, the comparison screen, the teacher's tracking. Same vocabulary of
   shapes as the carnet de notes above (gradient banner, cards, chips), hence the reuse of its
   tokens rather than a second palette.
   ========================================================================== */
.cm-sa-hero {
    background: linear-gradient(120deg, #12344d, #1c4a6b); color: #fff; border-radius: 13px;
    padding: 18px 22px; display: flex; flex-direction: column; gap: 7px;
}
.cm-sa-hero--split { flex-direction: row; align-items: center; gap: 20px; flex-wrap: wrap; }
.cm-sa-hero__kicker { font-size: 11px; color: #9fb4c6; text-transform: uppercase; letter-spacing: 1px; }
.cm-sa-hero__title { font: 600 19px var(--cm-font-title); }
.cm-sa-hero__meta { font-size: 12.5px; color: #a9c0d2; }
/* The two grades of 5c: the estimate in blue, the teacher's grade in gold. */
.cm-sa-score {
    background: rgba(255, 255, 255, .1); border: 1px solid rgba(255, 255, 255, .18);
    border-radius: 11px; padding: 12px 18px; text-align: center;
}
.cm-sa-score__cap { font-size: 10.5px; color: #9fb4c6; text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; }
.cm-sa-score__value { font: 700 24px var(--cm-font-title); color: #7fb3dd; }
.cm-sa-score__value span { font-size: 13px; color: #9fb4c6; }
.cm-sa-score--teacher { background: rgba(201, 160, 78, .16); border-color: rgba(201, 160, 78, .4); }
.cm-sa-score--teacher .cm-sa-score__cap { color: #e7cf9d; }
.cm-sa-score--teacher .cm-sa-score__value { color: var(--cm-gold); }
.cm-sa-score--teacher .cm-sa-score__value span { color: #e7cf9d; }
.cm-sa-stat {
    background: rgba(255, 255, 255, .1); border: 1px solid rgba(255, 255, 255, .18);
    border-radius: 11px; padding: 12px 18px; text-align: center;
}
.cm-sa-stat__value { font: 700 22px var(--cm-font-title); }
.cm-sa-stat__value span { font-size: 13px; color: #9fb4c6; }
.cm-sa-stat__cap { font-size: 10.5px; color: #9fb4c6; text-transform: uppercase; letter-spacing: .6px; }

.cm-sa-card {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 12px; overflow: hidden;
}
.cm-sa-section {
    display: flex; align-items: center; gap: 10px; padding: 12px 18px;
    background: var(--cm-surface-soft); border-bottom: 1px solid var(--cm-border-soft);
}
.cm-sa-section__letter { font: 700 13px var(--cm-font-title); color: var(--cm-gold); }
.cm-sa-section__name { font: 600 14px var(--cm-font-title); color: var(--cm-ink); }
.cm-sa-section__scale { margin-left: auto; font: 600 11.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-sa-row {
    display: flex; align-items: center; gap: 12px; padding: 11px 18px;
    border-bottom: 1px solid var(--cm-border-faint);
}
.cm-sa-row__label { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); flex: 1; }
.cm-sa-row__hint { font-size: 11.5px; color: var(--cm-faint); }
.cm-sa-row__max { font-size: 12px; color: #b4bfc9; width: 30px; }
.cm-sa-input {
    width: 58px; height: 38px; text-align: center; border: 1.5px solid var(--cm-border);
    border-radius: var(--cm-radius-sm); font: 700 14px var(--cm-font-ui); color: var(--cm-ink);
    outline: none; background: var(--cm-surface);
}
.cm-sa-input:focus { border-color: var(--cm-brand); background: var(--cm-gb-focus-bg); }
/* Question still pending: amber, as in the mockup - this is what blocks submission. */
.cm-sa-input.is-missing { border-color: #d4a53e; background: #fdf8ec; }
[data-bs-theme="dark"] .cm-sa-input.is-missing { background: var(--cm-gold-bg); }
.cm-sa-total {
    display: flex; align-items: center; gap: 12px; padding: 13px 18px;
    background: var(--cm-navy); color: #fff;
}
.cm-sa-total__label { font: 600 13px var(--cm-font-ui); }
.cm-sa-total__meta { font-size: 11.5px; color: #9fb4c6; }
.cm-sa-total__value { margin-left: auto; font: 700 20px var(--cm-font-title); }
.cm-sa-total__scale { font-size: 13px; color: #9fb4c6; }

.cm-sa-transmitted {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 12px;
    padding: 18px 22px; display: flex; flex-direction: column; gap: 6px;
}
.cm-sa-transmitted__title { font: 600 15px var(--cm-font-title); color: var(--cm-ink); }
.cm-sa-transmitted__meta { font-size: 13px; color: var(--cm-muted); }

.cm-sa-summary {
    display: flex; align-items: center; gap: 9px; background: var(--cm-green-bg);
    border: 1px solid #d5e0d8; color: var(--cm-green-tx); border-radius: 10px;
    padding: 10px 16px; font: 600 12.5px var(--cm-font-ui);
}
[data-bs-theme="dark"] .cm-sa-summary { border-color: var(--cm-green-bg); }
.cm-sa-legend {
    display: flex; align-items: center; gap: 12px; padding: 11px 18px;
    background: var(--cm-surface-soft); border-bottom: 1px solid var(--cm-border-soft);
}
.cm-sa-legend__title { font: 700 10.5px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .5px; flex: 1; }
.cm-sa-legend__item { display: inline-flex; align-items: center; gap: 6px; font: 600 11.5px var(--cm-font-ui); }
.cm-sa-legend__swatch { width: 10px; height: 10px; border-radius: 3px; }
.cm-sa-legend__item--you { color: var(--cm-brand-strong); }
.cm-sa-legend__item--you .cm-sa-legend__swatch { background: var(--cm-brand); }
.cm-sa-legend__item--teacher { color: var(--cm-gold-tx); }
.cm-sa-legend__item--teacher .cm-sa-legend__swatch { background: var(--cm-gold); }
.cm-sa-comparerow {
    display: flex; align-items: center; gap: 14px; padding: 11px 18px;
    border-bottom: 1px solid var(--cm-border-faint);
}
.cm-sa-comparerow__label { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); width: 110px; flex: none; }
.cm-sa-comparerow__you { font: 700 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); width: 54px; text-align: right; }
.cm-sa-comparerow__teacher { font: 700 12.5px var(--cm-font-ui); color: var(--cm-gold-tx); width: 54px; text-align: right; }
/* Two stacked bars rather than side by side: the gap reads at a single vertical glance. */
.cm-sa-bars { flex: 1; display: flex; flex-direction: column; gap: 4px; min-width: 80px; }
.cm-sa-bar { height: 8px; border-radius: 4px; background: var(--cm-border-soft); overflow: hidden; }
.cm-sa-bar__fill { height: 100%; border-radius: 4px; }
.cm-sa-bar__fill--you { background: var(--cm-brand); }
.cm-sa-bar__fill--teacher { background: var(--cm-gold); }
.cm-sa-gap {
    display: inline-flex; justify-content: center; padding: 2px 9px; border-radius: 999px;
    font: 600 11px var(--cm-font-ui); min-width: 52px;
}
.cm-sa-gap--fair { background: var(--cm-green-bg); color: var(--cm-green-tx); }
.cm-sa-gap--over { background: var(--cm-gold-bg); color: var(--cm-warn-tx); }
.cm-sa-gap--under { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-sa-gap--none { background: transparent; color: var(--cm-disabled); }

/* Teacher tracking (5d): same column widths in the header and in the rows. */
.cm-sa-trackhead, .cm-sa-trackrow { display: flex; align-items: center; gap: 12px; padding: 11px 18px; }
.cm-sa-trackhead {
    background: var(--cm-surface-soft); border-bottom: 1px solid var(--cm-border-soft);
    font: 700 10.5px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .5px;
}
.cm-sa-trackrow { border-bottom: 1px solid var(--cm-border-faint); }
.cm-sa-trackrow.is-pending { background: #fdf8ec; }
[data-bs-theme="dark"] .cm-sa-trackrow.is-pending { background: var(--cm-gold-bg); }
.cm-sa-trackrow__name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-sa-trackcol--student { flex: 1; display: flex; align-items: center; gap: 9px; min-width: 0; }
.cm-sa-trackcol--student .cm-gb-tag { margin-left: 0; }
.cm-sa-trackcol--status { width: 130px; flex: none; }
.cm-sa-trackcol--estimate { width: 82px; flex: none; text-align: right; font: 700 13px var(--cm-font-ui); color: var(--cm-brand-strong); }
.cm-sa-trackcol--grade { width: 82px; flex: none; text-align: right; font: 700 13px var(--cm-font-ui); color: var(--cm-gold-tx); }
.cm-sa-trackcol--gap { width: 64px; flex: none; display: flex; justify-content: center; }
.cm-sa-trackcol--action { width: 74px; flex: none; text-align: right; font: 600 12px var(--cm-font-ui); }
.cm-sa-status { font: 600 11.5px var(--cm-font-ui); }
.cm-sa-status--done { color: var(--cm-green-tx); }
.cm-sa-status--draft { color: var(--cm-warn-tx); }
.cm-sa-status--pending { color: var(--cm-faint); }
.cm-sa-trackfoot { padding: 10px 18px; border-top: 1px solid var(--cm-border-soft); font-size: 12px; color: var(--cm-faint); }
/* The two possible feedbacks, as radio cards of the « Ajouter un travail » modal (5a). */
.cm-feedback-choice { display: flex; flex-direction: column; gap: 8px; }

/* ==========================================================================
   Profile page (7a) - identity card. Tabler's own .avatar is square-ish but sized/colored for a
   generic secondary-bg placeholder, not the spec's 72x72 gold square used here and in the topbar's
   own .cm-user__avatar.
   ========================================================================== */
.cm-profile-avatar {
    width: 72px; height: 72px; border-radius: var(--cm-radius-sm); flex: none;
    background: var(--cm-gold); color: var(--cm-navy);
    display: grid; place-items: center; font: 700 26px var(--cm-font-title);
    background-size: cover; background-position: center;
}
.cm-profile-name { font: 600 18px var(--cm-font-title); color: var(--cm-ink); }

/* ==========================================================================
   Messaging (design/design_handoff_messagerie) - three-pane inbox (folders / list / reading
   pane) replacing the old DataTables-backed list pages, plus the redesigned compose screen.
   Colors reuse existing --cm-* tokens; --cm-mail-tint-bg/--cm-mail-tint-border (declared in the
   :root/dark blocks above) cover the mockup's lighter "selected" blue used throughout this
   section, distinct from --cm-blue-bg used elsewhere in the app.
   ========================================================================== */
.cm-mail-app {
    display: grid; grid-template-columns: 228px 404px 1fr; gap: 14px; align-items: stretch;
    height: calc(100dvh - 250px); min-height: 540px;
}
.cm-mail-app > * { min-height: 0; }

/* ---- Folders pane ---- */
.cm-mail-folders { display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.cm-mail-folders__new {
    border: 0; background: var(--cm-brand); color: #fff; border-radius: 9px; padding: 11px 14px;
    font: 600 13.5px var(--cm-font-ui); cursor: pointer; display: flex; align-items: center;
    justify-content: center; gap: 8px; box-shadow: 0 2px 6px rgba(27, 107, 168, .28);
}
.cm-mail-folders__new:hover { background: var(--cm-brand-strong); color: #fff; }
.cm-mail-folders__card {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
    padding: 7px; display: flex; flex-direction: column; gap: 1px; overflow-y: auto;
}
.cm-mail-folders__item {
    display: flex; align-items: center; gap: 10px; padding: 9px 11px; border-radius: 8px;
    color: var(--cm-text); font-size: 13.5px; cursor: pointer; text-decoration: none;
}
.cm-mail-folders__item svg { flex: none; }
.cm-mail-folders__item:hover { background: var(--cm-bg); color: var(--cm-text); }
.cm-mail-folders__item.is-active { background: var(--cm-mail-tint-bg); color: var(--cm-brand-strong); font-weight: 600; }
.cm-mail-folders__item.is-active:hover { color: var(--cm-brand-strong); }
.cm-mail-folders__count { margin-left: auto; font-size: 12px; color: var(--cm-faint); }
.cm-mail-folders__badge {
    margin-left: auto; min-width: 20px; height: 19px; padding: 0 5px; box-sizing: border-box;
    border-radius: 10px; background: #d4483b; color: #fff; font: 700 11px var(--cm-font-ui);
    display: grid; place-items: center;
}
.cm-mail-folders__sep { height: 1px; background: var(--cm-border-soft); margin: 5px 7px; }

/* Down to icon-only cells between 1024 and 1280px (design's own tier) - labels/badges hidden,
   fixed 64px column. */
@media (max-width: 1279.98px) {
    .cm-mail-app { grid-template-columns: 64px 380px 1fr; }
    .cm-mail-folders__new span, .cm-mail-folders__item span:not(.cm-mail-folders__badge) { display: none; }
    .cm-mail-folders__new { padding: 11px; }
    .cm-mail-folders__item { justify-content: center; padding: 11px; }
    .cm-mail-folders__badge { position: absolute; top: 2px; right: 2px; margin-left: 0; }
    .cm-mail-folders__item { position: relative; }
}
/* Below 1024px: one pane at a time (list, or reading pane when a thread is open - see
   .cm-mail-app--reading below), folders become a drawer opened via .cm-mail-drawer-toggle. */
@media (max-width: 1023.98px) {
    .cm-mail-app { grid-template-columns: 1fr; height: auto; min-height: calc(100dvh - 250px); }
    .cm-mail-folders {
        position: fixed; top: 0; left: 0; bottom: 0; z-index: 1030; width: 280px;
        background: var(--cm-bg); padding: 14px; box-shadow: var(--cm-shadow-pop);
        transform: translateX(-100%); transition: transform .2s ease;
    }
    .cm-mail-app.is-drawer-open .cm-mail-folders { transform: translateX(0); }
    .cm-mail-app.is-drawer-open::before {
        content: ""; position: fixed; inset: 0; background: rgba(18, 52, 77, .35); z-index: 1029;
    }
    .cm-mail-list, .cm-mail-thread, .cm-mail-empty { display: none; }
    .cm-mail-app:not(.is-reading) .cm-mail-list { display: flex; }
    .cm-mail-app:not(.is-reading) .cm-mail-empty { display: flex; }
    .cm-mail-app.is-reading .cm-mail-thread { display: flex; }
    .cm-mail-drawer-toggle { display: inline-flex !important; }
}
.cm-mail-drawer-toggle {
    display: none; align-items: center; justify-content: center; width: 32px; height: 32px;
    border: 1px solid var(--cm-border); border-radius: 8px; background: var(--cm-surface);
    color: var(--cm-text); cursor: pointer; flex: none;
}

/* ---- List pane ---- */
.cm-mail-list {
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
    display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
.cm-mail-list__toolbar { padding: 12px 14px; border-bottom: 1px solid var(--cm-border-soft); display: flex; flex-direction: column; gap: 10px; }
.cm-mail-list__toolbar-row { display: flex; align-items: center; gap: 8px; }
.cm-mail-list__search {
    display: flex; align-items: center; gap: 9px; border: 1px solid var(--cm-border); border-radius: 8px;
    padding: 8px 11px; background: var(--cm-surface-alt); color: var(--cm-faint);
}
.cm-mail-list__search input {
    border: 0; background: transparent; outline: 0; font-size: 13px; color: var(--cm-ink); width: 100%;
}
.cm-mail-list__search input::placeholder { color: var(--cm-faint); }
.cm-mail-list__filters { display: flex; align-items: center; gap: 8px; }
.cm-mail-list__filter {
    font: 600 12px var(--cm-font-ui); color: var(--cm-muted); border: 1px solid transparent;
    border-radius: 7px; padding: 4px 10px; cursor: pointer; background: none;
}
.cm-mail-list__filter:hover { background: var(--cm-bg); }
.cm-mail-list__filter.is-active { color: var(--cm-brand-strong); background: var(--cm-mail-tint-bg); border-color: var(--cm-mail-tint-border); }
.cm-mail-list__mark-all { margin-left: auto; font-size: 12px; color: var(--cm-faint); cursor: pointer; background: none; border: 0; }
.cm-mail-list__mark-all:hover { color: var(--cm-brand-strong); text-decoration: underline; }
.cm-mail-list__body { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; }
.cm-mail-list__day {
    padding: 7px 14px; background: var(--cm-surface-alt); border-bottom: 1px solid var(--cm-border-soft);
    font: 600 11px var(--cm-font-ui); letter-spacing: 1.1px; text-transform: uppercase; color: var(--cm-faint);
    position: sticky; top: 0; z-index: 1;
}

.cm-mail-row {
    display: flex; gap: 11px; padding: 13px 14px; border-bottom: 1px solid var(--cm-border-soft);
    border-left: 3px solid transparent; cursor: pointer; text-decoration: none; color: inherit;
}
.cm-mail-row:hover { background: var(--cm-surface-alt); }
.cm-mail-row.is-selected { border-left-color: var(--cm-brand); background: color-mix(in srgb, var(--cm-mail-tint-bg) 70%, transparent); }
.cm-mail-row__avatar {
    width: 34px; height: 34px; flex: none; border-radius: 8px; background: var(--cm-border-soft);
    color: var(--cm-muted); display: grid; place-items: center; font: 700 12.5px var(--cm-font-ui);
}
.cm-mail-row.is-unread .cm-mail-row__avatar--0 { background: var(--cm-brand); color: #fff; }
.cm-mail-row.is-unread .cm-mail-row__avatar--1 { background: var(--cm-navy); color: #fff; }
.cm-mail-row.is-unread .cm-mail-row__avatar--2 { background: #7a5ea8; color: #fff; }
.cm-mail-row__main { flex: 1; min-width: 0; }
.cm-mail-row__head { display: flex; align-items: baseline; gap: 8px; }
.cm-mail-row__sender { font: 600 13.5px var(--cm-font-ui); color: var(--cm-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-mail-row.is-unread .cm-mail-row__sender, .cm-mail-row.is-unread .cm-mail-row__subject { font-weight: 700; color: var(--cm-ink); }
.cm-mail-row__time { margin-left: auto; font-size: 11.5px; color: var(--cm-faint); flex: none; }
.cm-mail-row.is-unread .cm-mail-row__time { color: var(--cm-muted); }
.cm-mail-row__subject {
    font: 600 13.5px var(--cm-font-ui); color: var(--cm-text); margin-top: 2px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cm-mail-row__snippet {
    font-size: 12.5px; color: var(--cm-faint); line-height: 1.45; margin-top: 2px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cm-mail-row__meta { display: flex; align-items: center; gap: 8px; margin-top: 6px; font-size: 11.5px; color: var(--cm-muted); }
.cm-mail-row__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--cm-brand); flex: none; }
.cm-mail-row__meta-item { display: flex; align-items: center; gap: 4px; }
.cm-mail-row__broadcast-badge { font: 600 10.5px var(--cm-font-ui); color: var(--cm-gold-tx); background: var(--cm-gold-bg); border-radius: 5px; padding: 1px 6px; }
.cm-mail-list__empty-search { padding: 32px 20px; text-align: center; color: var(--cm-faint); font-size: 13px; }
.cm-mail-list__skeleton-row { padding: 13px 14px; display: flex; gap: 11px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-mail-list__skeleton-block { background: var(--cm-border-soft); border-radius: 6px; animation: cm-mail-pulse 1.4s ease-in-out infinite; }
@keyframes cm-mail-pulse { 0%, 100% { opacity: .6; } 50% { opacity: 1; } }

.cm-mail-list__footer {
    padding: 9px 14px; border-top: 1px solid var(--cm-border-soft); background: var(--cm-surface-alt);
    font-size: 12px; color: var(--cm-faint); display: flex; align-items: center; flex: none;
}
.cm-mail-list__load-more { margin-left: auto; color: var(--cm-brand-strong); font-weight: 600; cursor: pointer; background: none; border: 0; font-size: 12px; }
.cm-mail-list__load-more:hover { text-decoration: underline; }
.cm-mail-list__load-more:disabled { color: var(--cm-faint); cursor: default; text-decoration: none; }

/* ---- Reading pane ---- */
.cm-mail-thread { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.cm-mail-thread__head { padding: 16px 22px 14px; border-bottom: 1px solid var(--cm-border-soft); display: flex; flex-direction: column; gap: 12px; flex: none; }
.cm-mail-thread__title-row { display: flex; align-items: flex-start; gap: 14px; }
.cm-mail-thread__subject { font: 600 18px var(--cm-font-title); color: var(--cm-ink); line-height: 1.3; }
.cm-mail-thread__subline { display: flex; align-items: center; gap: 8px; margin-top: 6px; font-size: 12.5px; color: var(--cm-faint); }
.cm-mail-thread__unread-badge { font: 600 11px var(--cm-font-ui); color: var(--cm-brand-strong); background: var(--cm-mail-tint-bg); border: 1px solid var(--cm-mail-tint-border); border-radius: 5px; padding: 1px 7px; }
.cm-mail-icon-btn {
    width: 32px; height: 32px; display: grid; place-items: center; border: 1px solid var(--cm-border);
    border-radius: 8px; color: var(--cm-text); cursor: pointer; background: var(--cm-surface); flex: none;
}
.cm-mail-icon-btn:hover { background: var(--cm-bg); }
.cm-mail-icon-btn--danger { color: var(--cm-red-tx); }
.cm-mail-icon-btn--danger:hover { background: var(--cm-red-bg); }
.cm-mail-thread__sender { display: flex; align-items: center; gap: 11px; }
.cm-mail-thread__sender-avatar { width: 38px; height: 38px; flex: none; border-radius: 9px; background: var(--cm-brand); color: #fff; display: grid; place-items: center; font: 700 13px var(--cm-font-ui); }
.cm-mail-thread__sender-name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-mail-thread__sender-role { font-weight: 400; color: var(--cm-faint); }
.cm-mail-thread__sender-meta { font-size: 12.5px; color: var(--cm-faint); }
.cm-mail-thread__body { flex: 1; min-height: 0; overflow-y: auto; padding: 20px 22px; display: flex; flex-direction: column; gap: 16px; }
.cm-mail-thread__message + .cm-mail-thread__message { padding-top: 16px; border-top: 1px solid var(--cm-border-soft); }
.cm-mail-thread__text { font-size: 14px; line-height: 1.75; color: var(--cm-text); }
.cm-mail-thread__text p { margin: 0 0 12px; }
.cm-mail-thread__text p:last-child { margin-bottom: 0; }
.cm-mail-attachments-label { font: 600 11px var(--cm-font-ui); letter-spacing: 1.2px; text-transform: uppercase; color: var(--cm-faint); }
.cm-mail-attachment {
    display: flex; align-items: center; gap: 11px; border: 1px solid var(--cm-border); border-radius: 9px;
    padding: 10px 13px; width: 290px; max-width: 100%; cursor: pointer; background: var(--cm-surface-alt);
    text-decoration: none; color: inherit;
}
.cm-mail-attachment:hover { background: var(--cm-mail-tint-bg); color: inherit; }
.cm-mail-attachment__type { width: 32px; height: 32px; flex: none; border-radius: 7px; background: #d4483b; color: #fff; display: grid; place-items: center; font: 700 9.5px var(--cm-font-ui); }
.cm-mail-attachment__name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-mail-attachment__size { font-size: 11.5px; color: var(--cm-faint); }
.cm-mail-attachment__download { margin-left: auto; flex: none; color: var(--cm-brand-strong); }

.cm-mail-reply { border-top: 1px solid var(--cm-border-soft); padding: 14px 22px 16px; background: var(--cm-surface-alt); display: flex; flex-direction: column; gap: 10px; flex: none; }
.cm-mail-reply__head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.cm-mail-reply__head-title { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-mail-reply__head-subject { font-size: 12px; color: var(--cm-faint); }
.cm-mail-reply__actions { display: flex; align-items: center; gap: 10px; }
.cm-mail-btn-primary {
    border: 0; background: var(--cm-brand); color: #fff; border-radius: 8px; padding: 9px 17px;
    font: 600 13px var(--cm-font-ui); cursor: pointer; display: inline-flex; align-items: center; gap: 7px;
}
.cm-mail-btn-primary:hover { background: var(--cm-brand-strong); color: #fff; }
.cm-mail-btn-primary:disabled { background: var(--cm-faint); cursor: not-allowed; }
.cm-mail-btn-outline {
    border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-brand-strong);
    border-radius: 8px; padding: 9px 15px; font: 600 13px var(--cm-font-ui); cursor: pointer;
}
.cm-mail-btn-outline:hover { background: var(--cm-mail-tint-bg); color: var(--cm-brand-strong); }
.cm-mail-btn-link { font: 600 13px var(--cm-font-ui); color: var(--cm-muted); cursor: pointer; background: none; border: 0; }
.cm-mail-btn-link:hover { color: var(--cm-text); }

/* ---- Empty state (folder with no messages, or "no thread selected" placeholder) ----
   .cm-mail-empty--span is the full "Rien dans ce dossier" card (design/design_handoff_messagerie
   #4), spanning where the list+reading columns would be, used when a folder has zero threads at
   all. Applied alone (no --span, combined with .cm-mail-thread) it's instead the lighter "select
   a message" placeholder that sits in the reading pane's own column while the list is still
   showing real rows next to it. */
.cm-mail-empty {
    background: var(--cm-surface); border: 1px solid var(--cm-border);
    border-radius: var(--cm-radius); display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
.cm-mail-empty--span { grid-column: 2 / 4; }
.cm-mail-empty__head { padding: 12px 14px; border-bottom: 1px solid var(--cm-border-soft); display: flex; align-items: center; gap: 8px; flex: none; }
.cm-mail-empty__pill { font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); background: var(--cm-mail-tint-bg); border: 1px solid var(--cm-mail-tint-border); border-radius: 7px; padding: 4px 10px; }
.cm-mail-empty__hint { font-size: 12.5px; color: var(--cm-faint); }
.cm-mail-empty__body { flex: 1; display: grid; place-items: center; padding: 24px; }
.cm-mail-empty__content { display: flex; flex-direction: column; align-items: center; gap: 14px; max-width: 380px; text-align: center; }
.cm-mail-empty__icon { width: 66px; height: 66px; border-radius: 50%; background: var(--cm-mail-tint-bg); border: 1px solid var(--cm-mail-tint-border); display: grid; place-items: center; color: var(--cm-brand); flex: none; }
.cm-mail-empty__title { font: 600 17px var(--cm-font-title); color: var(--cm-ink); }
.cm-mail-empty__text { margin: 0; font-size: 13.5px; line-height: 1.65; color: var(--cm-muted); }
.cm-mail-empty__actions { display: flex; align-items: center; gap: 10px; }
@media (max-width: 1023.98px) { .cm-mail-empty { grid-column: auto; } }

.cm-mail-alert-danger {
    background: var(--cm-red-bg); color: var(--cm-red-tx); border: 1px solid var(--cm-red-bg);
    border-radius: 9px; padding: 10px 14px; font-size: 13px; display: flex; align-items: center; gap: 8px;
}

/* ---- Compose ---- */
.cm-mail-compose { width: 1000px; max-width: 100%; margin: 0 auto; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 12px; display: flex; flex-direction: column; min-height: 0; overflow: hidden; box-shadow: 0 2px 10px rgba(18, 52, 77, .06); }
.cm-mail-compose__head { padding: 16px 24px; border-bottom: 1px solid var(--cm-border-soft); display: flex; align-items: center; gap: 12px; flex: none; }
.cm-mail-compose__title { font: 600 17px var(--cm-font-title); color: var(--cm-ink); }
.cm-mail-compose__close { margin-left: auto; color: var(--cm-faint); font-size: 20px; line-height: 1; text-decoration: none; }
.cm-mail-compose__close:hover { color: var(--cm-text); }
.cm-mail-compose__body { flex: 1; min-height: 0; overflow-y: auto; padding: 20px 24px; display: flex; flex-direction: column; gap: 18px; }

.cm-mail-audience__head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.cm-mail-audience__head label { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-mail-audience__hint { margin-left: auto; font-size: 12px; color: var(--cm-faint); }
.cm-mail-audience__grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 9px; margin-top: 10px; }
@media (max-width: 767.98px) { .cm-mail-audience__grid { grid-template-columns: repeat(2, 1fr); } }
.cm-mail-audience__tile {
    position: relative; border: 1px solid var(--cm-border); border-radius: 9px; padding: 11px 12px;
    display: flex; flex-direction: column; gap: 7px; cursor: pointer; background: var(--cm-surface);
}
.cm-mail-audience__tile:hover { background: var(--cm-surface-alt); }
.cm-mail-audience__tile input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-mail-audience__tile svg { color: var(--cm-muted); }
.cm-mail-audience__tile-label { font: 600 12.5px var(--cm-font-ui); color: var(--cm-text); }
.cm-mail-audience__check { position: absolute; top: 10px; right: 10px; width: 15px; height: 15px; border-radius: 4px; border: 1.5px solid #c3cfd9; background: var(--cm-surface); }
.cm-mail-audience__tile:has(input:checked) { border: 2px solid var(--cm-brand); padding: 10px 11px; background: var(--cm-mail-tint-bg); }
.cm-mail-audience__tile:has(input:checked) svg { color: var(--cm-brand-strong); }
.cm-mail-audience__tile:has(input:checked) .cm-mail-audience__tile-label { color: var(--cm-brand-strong); }
.cm-mail-audience__tile:has(input:checked) .cm-mail-audience__check {
    background: var(--cm-brand); border-color: var(--cm-brand); color: #fff;
    display: grid; place-items: center; font: 700 10px var(--cm-font-ui);
}
.cm-mail-audience__tile:has(input:checked) .cm-mail-audience__check::before { content: "\2713"; }
.cm-mail-audience__tile:has(input:focus-visible) { outline: 2px solid var(--cm-brand); outline-offset: 1px; }

.cm-mail-programs {
    border: 1px solid var(--cm-mail-tint-border); border-left: 3px solid var(--cm-brand);
    border-radius: 0 10px 10px 0; background: var(--cm-mail-tint-bg); padding: 13px 15px;
    display: flex; flex-direction: column; gap: 11px; margin-top: 10px;
}
.cm-mail-programs__head { display: flex; align-items: center; gap: 9px; }
.cm-mail-programs__head-label { font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); }
.cm-mail-programs__count { font: 600 11px var(--cm-font-ui); color: var(--cm-brand-strong); background: var(--cm-surface); border: 1px solid var(--cm-mail-tint-border); border-radius: 999px; padding: 2px 8px; }
.cm-mail-programs__clear { margin-left: auto; font-size: 12px; color: var(--cm-brand-strong); cursor: pointer; background: none; border: 0; }
.cm-mail-programs__clear:hover { text-decoration: underline; }
.cm-mail-programs__pills { display: flex; flex-wrap: wrap; gap: 7px; }
.cm-mail-pill {
    position: relative; display: flex; align-items: center; gap: 7px; border-radius: 8px;
    padding: 6px 11px; font: 600 12.5px var(--cm-font-ui); cursor: pointer; border: 1px solid var(--cm-border);
    background: var(--cm-surface); color: var(--cm-text);
}
.cm-mail-pill input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-mail-pill:hover { background: var(--cm-mail-tint-bg); }
.cm-mail-pill__check { width: 14px; height: 14px; border-radius: 3px; border: 1.5px solid #c3cfd9; flex: none; }
.cm-mail-pill__count { color: var(--cm-faint); font-weight: 400; }
.cm-mail-pill:has(input:checked) { background: var(--cm-brand); border-color: var(--cm-brand); color: #fff; }
.cm-mail-pill:has(input:checked) .cm-mail-pill__count { color: rgba(255, 255, 255, .75); }
.cm-mail-pill:has(input:checked) .cm-mail-pill__check { background: rgba(255, 255, 255, .25); border-color: transparent; display: grid; place-items: center; color: #fff; font: 700 9px var(--cm-font-ui); }
.cm-mail-pill:has(input:checked) .cm-mail-pill__check::before { content: "\2713"; }
.cm-mail-pill--overflow { border-style: dashed; border-color: var(--cm-mail-tint-border); color: var(--cm-brand-strong); cursor: pointer; }
.cm-mail-pill--overflow:hover { background: var(--cm-mail-tint-bg); }
.cm-mail-programs__pills.is-collapsed .cm-mail-pill--extra { display: none; }
.cm-mail-programs__sep { height: 1px; background: var(--cm-mail-tint-border); }
.cm-mail-programs__roles { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.cm-mail-programs__roles-label { font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); }
.cm-mail-role-pill {
    position: relative; display: flex; align-items: center; gap: 8px; background: var(--cm-surface);
    border: 1px solid var(--cm-border); border-radius: 8px; padding: 6px 11px; font: 600 12.5px var(--cm-font-ui);
    color: var(--cm-text); cursor: pointer;
}
.cm-mail-role-pill input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-mail-role-pill__check { width: 15px; height: 15px; border-radius: 4px; border: 1.5px solid #c3cfd9; flex: none; }
.cm-mail-role-pill__count { color: var(--cm-faint); font-weight: 400; }
.cm-mail-role-pill:has(input:checked) { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-mail-role-pill:has(input:checked) .cm-mail-role-pill__check { background: var(--cm-brand); border-color: var(--cm-brand); display: grid; place-items: center; color: #fff; font: 700 9px var(--cm-font-ui); }
.cm-mail-role-pill:has(input:checked) .cm-mail-role-pill__check::before { content: "\2713"; }

.cm-mail-recipients {
    border: 1px solid var(--cm-mail-tint-border); background: var(--cm-mail-tint-bg)33; background: color-mix(in srgb, var(--cm-mail-tint-bg) 55%, var(--cm-surface));
    border-radius: 9px; padding: 11px 12px; display: flex; flex-wrap: wrap; gap: 7px; align-items: center; margin-top: 10px;
}
.cm-mail-person-pill {
    display: flex; align-items: center; gap: 7px; background: var(--cm-surface); border: 1px solid var(--cm-mail-tint-border);
    border-radius: 999px; padding: 3px 5px 3px 3px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong);
}
.cm-mail-person-pill__avatar { width: 22px; height: 22px; border-radius: 50%; background: var(--cm-brand); color: #fff; display: grid; place-items: center; font: 700 10px var(--cm-font-ui); flex: none; }
.cm-mail-person-pill__remove { color: var(--cm-faint); cursor: pointer; padding: 0 3px; background: none; border: 0; line-height: 1; }
.cm-mail-person-pill__remove:hover { color: var(--cm-red-tx); }
.cm-mail-audience-pill { display: flex; align-items: center; gap: 7px; background: var(--cm-surface); border: 1px solid var(--cm-gold); border-radius: 999px; padding: 3px 9px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-gold-tx); }
.cm-mail-audience-pill__remove { color: #b98a4a; cursor: pointer; padding: 0 3px; background: none; border: 0; line-height: 1; }
.cm-mail-recipients__input { flex: 1; min-width: 160px; }
.cm-mail-recipients__counter { margin-left: auto; font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); background: var(--cm-surface); border: 1px solid var(--cm-mail-tint-border); border-radius: 999px; padding: 4px 11px; white-space: nowrap; }

.cm-mail-editor { flex: 1; min-height: 220px; display: flex; flex-direction: column; gap: 6px; }
.cm-mail-editor label { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }

.cm-mail-dropzone {
    flex: none; width: 300px; max-width: 100%; display: flex; align-items: center; gap: 11px;
    border: 1px dashed var(--cm-mail-tint-border); background: var(--cm-mail-tint-bg); border-radius: 9px;
    padding: 11px 13px; cursor: pointer;
}
.cm-mail-dropzone:hover, .cm-mail-dropzone.is-dragover { background: color-mix(in srgb, var(--cm-mail-tint-bg) 60%, var(--cm-brand) 10%); }
.cm-mail-dropzone__title { font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); }
.cm-mail-dropzone__hint { font-size: 11.5px; color: var(--cm-faint); }
.cm-mail-file-chip { flex: 1; display: flex; align-items: center; gap: 11px; border: 1px solid var(--cm-border); border-radius: 9px; padding: 11px 13px; background: var(--cm-surface); }
.cm-mail-file-chip__type { width: 30px; height: 30px; flex: none; border-radius: 7px; background: #d4483b; color: #fff; display: grid; place-items: center; font: 700 9px var(--cm-font-ui); }
.cm-mail-file-chip__name { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-mail-file-chip__size { font-size: 11.5px; color: var(--cm-faint); }
.cm-mail-file-chip__remove { margin-left: auto; color: var(--cm-faint); cursor: pointer; font-size: 16px; line-height: 1; background: none; border: 0; }
.cm-mail-file-chip__remove:hover { color: var(--cm-red-tx); }
.cm-mail-attachments-row { display: flex; gap: 10px; align-items: stretch; flex-wrap: wrap; }

.cm-mail-compose__footer { border-top: 1px solid var(--cm-border-soft); background: var(--cm-surface-alt); padding: 14px 24px; display: flex; align-items: center; gap: 14px; flex: none; flex-wrap: wrap; }
.cm-mail-compose__footer-hint { font-size: 13px; color: var(--cm-muted); }
.cm-mail-compose__footer-actions { margin-left: auto; display: flex; align-items: center; gap: 12px; }

/* UFA Alternance guided-wizard stepper (28-31): 4-role progress strip + step-tabs + progress bar.
   No Tabler equivalent exists for this shape - built from the --cm- tokens already defined
   above, same convention as the cm-mail-... and cm-action-... blocks elsewhere in this file. */
.cm-role-progress { display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap; margin-bottom: 18px; }
.cm-role-progress__chip { display: flex; align-items: center; gap: 8px; }
.cm-role-progress__icon { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; font: 700 12px var(--cm-font-ui); flex: none; }
.cm-role-progress__icon--done { background: var(--cm-green-bg); color: var(--cm-green-tx); }
.cm-role-progress__icon--current { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-role-progress__icon--waiting { background: var(--cm-bg); color: var(--cm-faint); border: 1px solid var(--cm-border); }
.cm-role-progress__label { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); line-height: 1.2; }
.cm-role-progress__status { font-size: 11px; color: var(--cm-muted); }
.cm-role-progress__arrow { color: var(--cm-border); font-size: 14px; }

.cm-stepper { border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 15px 20px; background: var(--cm-surface); margin-bottom: 18px; }
.cm-stepper__steps { display: flex; align-items: center; }
.cm-stepper__step { display: flex; align-items: center; gap: 8px; }
.cm-stepper__step-circle { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; font: 700 12px var(--cm-font-ui); flex: none; }
.cm-stepper__step-circle--done { background: var(--cm-green-bg); color: var(--cm-green-tx); }
.cm-stepper__step-circle--current { background: var(--cm-brand); color: #fff; }
.cm-stepper__step-circle--future { background: var(--cm-bg); color: var(--cm-faint); border: 1px solid var(--cm-border); }
/* Walked past rather than done: the séquence assistant's "convertir" step for a teacher who already
   holds the JSON. It keeps its place in the row so the remaining steps do not renumber themselves. */
.cm-stepper__step-circle--skipped { background: var(--cm-bg); color: var(--cm-faint); border: 1px dashed var(--cm-border); }
.cm-stepper__step-name { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); white-space: nowrap; }
.cm-stepper__rule { flex: 1; height: 1.5px; background: var(--cm-border); margin: 0 10px; }
.cm-stepper__rule--done { background: var(--cm-green-bg); }
.cm-stepper__progress { height: 4px; background: var(--cm-bg); border-radius: 999px; margin-top: 14px; overflow: hidden; }
.cm-stepper__progress-fill { height: 100%; background: var(--cm-brand); border-radius: 999px; }
.cm-stepper__caption { font-size: 12px; color: var(--cm-muted); margin-top: 8px; }

.cm-wizard-readonly-badge { display: inline-flex; align-items: center; gap: 6px; font: 600 11.5px var(--cm-font-ui); color: var(--cm-faint); background: var(--cm-bg); border-radius: 999px; padding: 3px 10px; margin-bottom: 14px; }
.cm-wizard-readonly-value { background: var(--cm-surface-alt); color: var(--cm-brand-strong); font-weight: 600; border-radius: 6px; padding: 6px 10px; display: inline-block; }
.cm-wizard-readonly-block { background: var(--cm-surface-alt); border: 1px solid var(--cm-border-soft); border-radius: 8px; padding: 12px 14px; }

/* A competency's own explanation (Skill::$descriptionHtml), under its label on every screen that
   rates it. It holds HugeRTE HTML, so the paragraph margins have to be flattened: a one-sentence
   description would otherwise push the rating control a whole line away from its row. */
.cm-skill-description { font-size: 12.5px; color: var(--cm-muted); margin-top: 2px; }
.cm-skill-description > :first-child { margin-top: 0; }
.cm-skill-description > :last-child { margin-bottom: 0; }
.cm-skill-description p { margin-bottom: 4px; }
.cm-skill-description ul, .cm-skill-description ol { margin-bottom: 4px; padding-left: 18px; }

/* ==========================================================================
   Dashboards par profil (design/design_handoff_dashboards)
   ========================================================================== */

/* Action banner - one thing to say, the most urgent one (shared grammar §1.2).
   Amber = action expected, green = "vous êtes à jour" (tutor), red = overdue (tutor/staff). */
.cm-dash-banner {
  display: flex; align-items: center; gap: 14px;
  border: 1px solid #e0b25f; background: #fdf6e8; color: #8a6a1f;
  border-radius: 11px; padding: 15px 20px; margin-bottom: 16px;
}
.cm-dash-banner__icon { flex: none; }
.cm-dash-banner__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cm-dash-banner__title { font: 600 15px var(--cm-font-title); }
.cm-dash-banner__text { font-size: 13px; }
.cm-dash-banner__action { margin-left: auto; flex: none; }
.cm-dash-banner--ok { background: #e7f4ee; border-color: #9ecdb4; color: #1f7a54; }
.cm-dash-banner--danger { background: #fbeeea; border-color: #dfab9e; color: #b0553f; }
[data-bs-theme=dark] .cm-dash-banner { background: #3c3117; border-color: #7a5417; color: #e0bc6d; }
[data-bs-theme=dark] .cm-dash-banner--ok { background: #1d3327; border-color: #2e5c44; color: #8cc3a2; }
[data-bs-theme=dark] .cm-dash-banner--danger { background: #40221c; border-color: #7a4033; color: #e09384; }

/* « Un problème à signaler ? » - full-width strip closing the student and teacher dashboards.
   Same skeleton as .cm-dash-banner (icon / body / action pushed right), and deliberately NOT its
   colours: gold there means "act on this now", and a standing door to the ticket manager is not an
   alert. Plain surface, brand-tinted icon, margin on top rather than bottom because this one closes
   the page instead of opening it. On a narrow screen the action drops under the text rather than
   squeezing the sentence to two words a line. */
.cm-dash-report {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text);
  border-radius: 11px; padding: 15px 20px; margin-top: 16px;
}
.cm-dash-report__icon { flex: none; color: var(--cm-brand); }
.cm-dash-report__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 260px; }
.cm-dash-report__title { font: 600 15px var(--cm-font-title); color: var(--cm-ink); }
.cm-dash-report__text { font-size: 13px; color: var(--cm-muted); }
.cm-dash-report__action { margin-left: auto; flex: none; }

/* Date chip (day + month) - events and work to do */
.cm-date-chip {
  flex: none; width: 46px; text-align: center;
  border: 1px solid var(--cm-border); border-radius: 8px; padding: 5px 0;
  background: var(--cm-surface);
}
.cm-date-chip__day { display: block; font: 600 16px var(--cm-font-title); color: var(--cm-blue-tx); line-height: 1.1; }
.cm-date-chip__month { display: block; font: 600 9.5px var(--cm-font-ui); letter-spacing: .6px; text-transform: uppercase; color: var(--cm-faint); }
/* Overdue: the same red the "Travail à faire" list tints its late rows with, the dashboard card
   having no section header to say it instead. */
.cm-date-chip--late { background: #fdf3f2; border-color: #ecc8c3; }
.cm-date-chip--late .cm-date-chip__day,
.cm-date-chip--late .cm-date-chip__month { color: #b8493d; }
[data-bs-theme=dark] .cm-date-chip--late { background: #2a1a17; border-color: #7a3a30; }
[data-bs-theme=dark] .cm-date-chip--late .cm-date-chip__day,
[data-bs-theme=dark] .cm-date-chip--late .cm-date-chip__month { color: #e09384; }

/* Upcoming events - up to four thumbnails side by side (shared grammar §1.3) */
.cm-event-row { display: flex; gap: 14px; align-items: stretch; }
/* max-width, not just flex:1 - a lone event otherwise stretched across the whole strip.
   Capped at a quarter of the row so any number of events keeps the same tile proportions, with
   the leftover space simply staying empty. A full row of four shrinks just under the cap to
   absorb the gaps, which is why flex-shrink stays enabled. */
.cm-event-tile {
  flex: 1 1 0; min-width: 0; max-width: 25%; display: flex; gap: 13px; align-items: flex-start;
  border: 1px solid var(--cm-border); border-radius: 10px; padding: 14px 16px;
  background: var(--cm-surface-alt);
}
.cm-event-tile__body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cm-event-tile__title { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-event-tile__meta { font-size: 12px; color: var(--cm-faint); }
/* Stacked: the quarter-width cap would leave a sliver of a tile. */
@media (max-width: 768px) { .cm-event-row { flex-direction: column; } .cm-event-tile { max-width: none; } }

/* Two equal columns (shared grammar §1.4) */
.cm-dash-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 992px) { .cm-dash-cols { grid-template-columns: 1fr; } }

/* Day timetable - one row per séance, colored vertical rule */
.cm-sched { display: flex; flex-direction: column; }
.cm-sched__row { display: flex; gap: 13px; align-items: stretch; padding: 9px 0; }
.cm-sched__row + .cm-sched__row { border-top: 1px solid var(--cm-border-soft); }
.cm-sched__time { flex: none; width: 88px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-faint); padding-top: 2px; }
.cm-sched__time--current { color: var(--cm-blue-tx); }
.cm-sched__bar { flex: none; width: 3px; border-radius: 2px; background: var(--cm-border); }
.cm-sched__body { display: flex; flex-direction: column; min-width: 0; padding-bottom: 2px; }
.cm-sched__title { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-sched__title--past { color: var(--cm-faint); }
.cm-sched__meta { font-size: 12px; color: var(--cm-faint); }
.cm-sched__side { margin-left: auto; align-self: center; flex: none; display: flex; align-items: center; gap: 10px; }

/* Program chip (student with a double enrolment) + program dot on the items */
.cm-formation-chip {
  display: inline-flex; align-items: center; gap: 9px;
  border: 1px solid var(--cm-border); border-radius: 9px; padding: 8px 14px;
  background: var(--cm-surface); text-align: left;
}
.cm-formation-chip__dot, .cm-dot-badge__dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.cm-formation-chip__name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); line-height: 1.2; }
.cm-formation-chip__sub { font-size: 11.5px; color: var(--cm-faint); }
.cm-dot-badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 9px; border-radius: 999px;
  background: var(--cm-bg); color: var(--cm-muted); font: 600 11px var(--cm-font-ui);
  white-space: nowrap;
}

/* Matrice classes × horaires (staff, emploi du temps du jour toutes classes).
   A time axis, not a column-per-start-hour grid: every row is a track whose full width is the
   day's span, and each session block is positioned/sized in % of it, so its width is literally
   its duration and its end lines up with the hourly ticks. --cm-matrix-hours (set inline, the
   number of whole hours on the axis) is what spaces the gridlines. */
.cm-matrix { display: flex; flex-direction: column; gap: 6px; }
.cm-matrix__row { display: flex; align-items: stretch; gap: 8px; }
.cm-matrix__class { display: flex; align-items: center; gap: 10px; padding: 6px 0; min-width: 0; flex: none; width: 210px; }
.cm-matrix__axis { position: relative; flex: 1; min-width: 0; height: 22px; }
.cm-matrix__tick {
  position: absolute; top: 0; font: 600 11px var(--cm-font-ui); color: var(--cm-faint);
  padding-left: 3px; border-left: 1px solid var(--cm-border-soft);
}
/* The closing tick sits exactly on the right edge - flip it inwards so it stays readable. */
.cm-matrix__tick:last-child { padding: 0 3px 0 0; border-left: 0; border-right: 1px solid var(--cm-border-soft); transform: translateX(-100%); }
.cm-matrix__track {
  position: relative; flex: 1; min-width: 0; min-height: 58px;
  background: var(--cm-surface-alt); border-radius: 6px;
  background-image: repeating-linear-gradient(to right, var(--cm-border-soft) 0 1px, transparent 1px calc(100% / var(--cm-matrix-hours, 1)));
}
.cm-matrix__block {
  position: absolute; top: 0; bottom: 0; overflow: hidden;
  display: flex; flex-direction: column; justify-content: center; gap: 1px;
  background: var(--cm-surface); border: 1px solid var(--cm-border-soft);
  border-left: 3px solid var(--cm-border); border-radius: 0 6px 6px 0;
  padding: 6px 9px; text-decoration: none;
}
.cm-matrix__block > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-matrix__block-hours { font: 600 11px var(--cm-font-ui); color: var(--cm-brand-strong); }
.cm-matrix__block-title { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-matrix__block-meta { font-size: 11.5px; color: var(--cm-faint); }
.cm-matrix__block--more { cursor: pointer; }
.cm-matrix__block--more:hover { background: var(--cm-surface-alt); }
.cm-matrix__block--more:hover .cm-matrix__block-title { text-decoration: underline; }
.cm-matrix__block-more { display: inline-block; padding: 0 5px; border-radius: 999px; background: var(--cm-blue-bg); color: var(--cm-blue-tx); font-size: 10.5px; }
@media (max-width: 768px) {
  .cm-matrix__row { flex-direction: column; gap: 4px; }
  .cm-matrix__class { width: auto; }
}
.cm-matrix__class-code {
  flex: none; width: 44px; height: 34px; border-radius: 8px;
  display: grid; place-items: center; font: 700 11px var(--cm-font-ui);
  background: var(--cm-blue-bg); color: var(--cm-blue-tx);
}
/* Staff matrix: the tag is the row's only label, so it holds the whole Program name instead of a
   5-character code - it has to size to its text (and ellipsis past the 210px column) rather than
   stay the fixed 44px square the teacher dashboard's own chips use. line-height doing the vertical
   centering, not place-items: an anonymous grid item can't be ellipsised. */
.cm-matrix__class-code--wide {
  display: block; width: auto; max-width: 100%; padding: 0 10px;
  font-size: 11.5px; line-height: 34px; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cm-matrix__legend { display: flex; align-items: center; gap: 16px; font-size: 12px; color: var(--cm-muted); padding-top: 12px; border-top: 1px solid var(--cm-border-soft); margin-top: 12px; }
/* Also the card's only filtering control now that the formation <select> is gone - hence a real
   <button>, reset to look like the plain legend text it replaces. */
.cm-matrix__legend-item {
    display: inline-flex; align-items: center; gap: 6px;
    background: none; border: 0; padding: 0; font: inherit; color: inherit;
    cursor: pointer; transition: opacity .12s ease;
}
.cm-matrix__legend-item:hover { color: var(--cm-ink); }
/* Hidden class: dimmed, with the swatch hollowed out so the state reads without relying on
   opacity alone. */
.cm-matrix__legend-item.is-off { opacity: .45; text-decoration: line-through; }
.cm-matrix__legend-item.is-off .cm-matrix__legend-swatch { background: transparent !important; box-shadow: inset 0 0 0 1.5px currentColor; }
.cm-matrix__legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }

/* ---------- Date navigation (staff dashboard timetable card, lesson-log session list) ----------
   ‹ date › - the arrows are links (the server picks the previous/next day or week that actually
   has classes), the date opens the native calendar. Named after its first use, the staff
   dashboard's day nav, but it carries the lesson log's week nav unchanged. */
.cm-daynav { display: inline-flex; align-items: center; gap: 2px; }
.cm-daynav__arrow {
    display: grid; place-items: center; width: 28px; height: 28px; border-radius: 7px;
    color: var(--cm-muted); text-decoration: none;
}
.cm-daynav__arrow:hover { background: var(--cm-surface-alt, rgba(127, 127, 127, .1)); color: var(--cm-brand-strong); }
/* Kept in the flow rather than hidden, so the date never shifts when a year runs out on one side. */
.cm-daynav__arrow.is-disabled { opacity: .3; pointer-events: none; }
.cm-daynav__date-wrap { position: relative; display: inline-flex; }
.cm-daynav__date {
    display: inline-flex; align-items: center; gap: 6px;
    background: none; border: 0; border-radius: 7px; padding: 4px 8px;
    font: 600 12.5px var(--cm-font-ui); color: var(--cm-muted); cursor: pointer; white-space: nowrap;
}
.cm-daynav__date:hover { background: var(--cm-surface-alt, rgba(127, 127, 127, .1)); color: var(--cm-brand-strong); }
/* Transparent, NOT display:none: showPicker() only opens on a rendered input, and this is what the
   calendar popup anchors itself to. pointer-events off so the button underneath keeps the clicks. */
.cm-daynav__dateinput {
    position: absolute; inset: 0; width: 100%; height: 100%;
    opacity: 0; pointer-events: none; border: 0; padding: 0;
}

/* Chain of the 4 roles as boxed chips (Mon alternance, tutor, dashboards) - boxed variant of the
   wizards' .cm-role-progress */
.cm-role-progress--boxed { justify-content: flex-start; margin-bottom: 0; }
.cm-role-progress--boxed .cm-role-progress__chip {
  border: 1px solid var(--cm-border); border-radius: 9px; padding: 8px 13px;
  background: var(--cm-surface);
}
.cm-role-progress--boxed .cm-role-progress__chip.is-done { background: var(--cm-green-bg); border-color: color-mix(in srgb, var(--cm-green-tx) 30%, var(--cm-border)); }
.cm-role-progress--boxed .cm-role-progress__chip.is-current { background: var(--cm-blue-bg); border-color: color-mix(in srgb, var(--cm-blue-tx) 30%, var(--cm-border)); }
.cm-role-progress--boxed .cm-role-progress__chip.is-late { background: var(--cm-red-bg); border-color: color-mix(in srgb, var(--cm-red-tx) 30%, var(--cm-border)); }
.cm-role-progress--boxed .cm-role-progress__chip.is-waiting { background: var(--cm-bg); }
.cm-role-progress__icon--late { background: var(--cm-red-bg); color: var(--cm-red-tx); }

/* Dashboard page header: greeting + subtitle, actions on the right at title level */
.cm-dash-head { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 16px; }
.cm-dash-head__greeting { margin: 0; font: 600 25px var(--cm-font-title); color: var(--cm-ink); }
.cm-dash-head__sub { font-size: 13px; color: var(--cm-muted); margin-top: 4px; }
.cm-dash-head__side { margin-left: auto; flex: none; display: flex; align-items: center; gap: 10px; }

/* Bascule "Mes rôles" (admin) */
.cm-role-switch { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.cm-role-switch__btn {
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-muted);
  border-radius: 9px; padding: 7px 16px; font: 600 13px var(--cm-font-ui);
  text-decoration: none; display: inline-block;
}
.cm-role-switch__btn:hover { color: var(--cm-brand-strong); background: var(--cm-bg); }
.cm-role-switch__btn.is-active { color: var(--cm-brand-strong); border-color: var(--cm-brand); background: var(--cm-blue-bg); }

/* Element hidden by the dashboard filter (dashboard_filter_controller.js) - !important to beat
   inline displays (e.g. display: contents on the staff matrix rows) */
.cm-filter-hidden { display: none !important; }

/* Buttons + badges of the design system (design_handoff_dashboards/css/campus-theme.css) - until
   now only Tabler's .btn were used; the dashboards introduce the mockup's gold/outline variants */
.cm-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border-radius: var(--cm-radius-sm); padding: 8px 16px;
  font: 600 13px var(--cm-font-ui); cursor: pointer;
  border: 1px solid transparent; text-decoration: none;
}
.cm-btn--primary { background: var(--cm-brand); color: #fff; }
.cm-btn--primary:hover { background: var(--cm-brand-strong); color: #fff; }
.cm-btn--outline { background: var(--cm-surface); border-color: var(--cm-border); color: var(--cm-brand-strong); }
.cm-btn--outline:hover { background: var(--cm-bg); color: var(--cm-brand-strong); }
.cm-btn--gold { background: var(--cm-gold); color: #12344d; font-weight: 700; }
.cm-btn--gold:hover { background: var(--cm-gold-strong); color: #12344d; }

.cm-badge {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font: 600 11px var(--cm-font-ui); white-space: nowrap;
}
.cm-badge--blue   { background: var(--cm-blue-bg);   color: var(--cm-blue-tx); }
.cm-badge--gold   { background: var(--cm-gold-bg);   color: var(--cm-gold-tx); }
.cm-badge--green  { background: var(--cm-green-bg);  color: var(--cm-green-tx); }
.cm-badge--red    { background: var(--cm-red-bg);    color: var(--cm-red-tx); }
.cm-badge--purple { background: var(--cm-purple-bg); color: var(--cm-purple-tx); }
.cm-badge--teal   { background: var(--cm-teal-bg);   color: var(--cm-teal-tx); }
.cm-badge--gray   { background: var(--cm-border-soft); color: var(--cm-faint); }

/* ===========================================================================
   Progression pédagogique (design_handoff_progression) - screens 4a/4b/3a/3c/5a/2a/2b.
   Everything is expressed in --cm-* tokens: dark mode follows with no dedicated rule.
   =========================================================================== */

.cm-btn--sm { padding: 5px 12px; font-size: 12px; }

/* -- Filter bar shared by 4a/4b ------------------------------------------ */
.cm-prog-filters { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; }
.cm-prog-filters__field { display: flex; align-items: center; gap: 7px; font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-prog-filters__select {
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-ink);
  border-radius: var(--cm-radius-sm); padding: 7px 12px;
  font: 600 12.5px var(--cm-font-ui); min-width: 150px;
}
.cm-prog-filters__action { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.cm-prog-filters__month { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }

/* -- 4a: ten month columns with horizontal scrolling --------------------- */
.cm-prog-months {
  display: grid; grid-auto-flow: column; grid-auto-columns: 316px; gap: 12px;
  overflow-x: auto; padding-bottom: 10px; align-items: start;
}
.cm-prog-month {
  background: var(--cm-surface); border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius); display: flex; flex-direction: column;
}
.cm-prog-month.is-current { border-color: var(--cm-brand); outline: 2px solid var(--cm-mail-tint-border); outline-offset: -1px; }
.cm-prog-month__head {
  display: flex; align-items: baseline; gap: 8px;
  padding: 11px 14px; border-bottom: 1px solid var(--cm-border-soft);
}
.cm-prog-month__name { font: 600 14px var(--cm-font-title); color: var(--cm-ink); }
.cm-prog-month__now {
  padding: 2px 8px; border-radius: 999px;
  background: var(--cm-blue-bg); color: var(--cm-blue-tx);
  font: 600 10px var(--cm-font-ui);
}
.cm-prog-month__head > .cm-btn { margin-left: auto; }
.cm-prog-month__body { padding: 10px 12px; display: flex; flex-direction: column; gap: 10px; }
.cm-prog-month__empty { margin: 0; font-size: 11.5px; color: var(--cm-faint); }

/* -- 4b : colonnes de semaines ------------------------------------------- */
.cm-prog-weeks { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; align-items: start; }
.cm-prog-week { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); }
.cm-prog-week__head { padding: 11px 14px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-prog-week__name { font: 600 13px var(--cm-font-title); color: var(--cm-ink); }
.cm-prog-week__range { font-size: 11px; color: var(--cm-faint); }
.cm-prog-week__body { padding: 10px 12px; display: flex; flex-direction: column; gap: 7px; }

/* Grouping label, shared by the class blocks (4a) and the day blocks (4b) */
.cm-prog-classblock { display: flex; flex-direction: column; gap: 6px; }
.cm-prog-classblock__head { display: flex; align-items: center; gap: 8px; }
.cm-prog-classblock__label {
  font: 700 10.5px var(--cm-font-ui); color: var(--cm-blue-tx);
  letter-spacing: .8px; text-transform: uppercase;
}
.cm-prog-classblock__rule { flex: 1; height: 1px; background: var(--cm-border-soft); }

/* -- Cartes des calendriers ---------------------------------------------- */
.cm-prog-card {
  border: 1px solid var(--cm-border); border-left: 3px solid var(--cm-navy);
  border-radius: var(--cm-radius-sm); padding: 8px 11px; background: var(--cm-surface-alt);
}
.cm-prog-card--diagnostic { border-color: var(--cm-brand); border-left-color: var(--cm-brand); background: var(--cm-blue-bg); }
.cm-prog-card--formative  { border-color: var(--cm-gold);  border-left-color: var(--cm-gold);  background: var(--cm-gold-bg); }
.cm-prog-card--summative  { border-color: var(--cm-red-tx); border-left-color: var(--cm-red-tx); background: var(--cm-red-bg); }
.cm-prog-card__head { display: flex; align-items: center; gap: 6px; margin-bottom: 5px; }
.cm-prog-card__badge {
  padding: 2px 8px; border-radius: 999px; background: var(--cm-brand); color: #fff;
  font: 600 10px var(--cm-font-ui);
}
.cm-prog-card__time { font: 600 10.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-prog-card__topic { font: 600 11px var(--cm-font-ui); color: var(--cm-faint); }
/* Group chip on a 4b card - a séance reproduced once per group produces one card per créneau,
   and this is what names which half of the class each one is for. Tinted from the Option's own
   colour so the same group reads the same way here and in the 2b picker. */
.cm-prog-card__group {
  display: inline-flex; padding: 1px 6px; border-radius: 999px; margin-left: 4px;
  background: var(--cm-blue-bg); color: var(--cm-blue-tx); font: 600 9.5px var(--cm-font-ui);
  vertical-align: middle;
}
.cm-prog-card--diagnostic .cm-prog-card__topic { color: var(--cm-blue-tx); }
.cm-prog-card--formative  .cm-prog-card__topic { color: var(--cm-gold-tx); }
.cm-prog-card--summative  .cm-prog-card__topic { color: var(--cm-red-tx); }
.cm-prog-card__title { font: 600 12px var(--cm-font-ui); color: var(--cm-ink); }
/* The title carries the link to the fiche séquence. Underlined only on hover: a calendar column is
   a dense stack of cards, and permanently underlining every title turns it into a wall of rules */
.cm-prog-card__link { color: inherit; text-decoration: none; }
.cm-prog-card__link:hover, .cm-prog-card__link:focus-visible { color: var(--cm-brand); text-decoration: underline; }
/* §4.3 / §4.7 : « signalée visuellement sur la vue de progression » */
.cm-prog-card__flag { margin-top: 4px; font: 600 10.5px var(--cm-font-ui); color: var(--cm-warn-tx); }

/* -- 3a: class chips + table --------------------------------------------- */
.cm-prog-chips { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.cm-prog-chips__label { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-prog-chip {
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text);
  border-radius: 999px; padding: 6px 14px; font: 600 12.5px var(--cm-font-ui);
  cursor: pointer; text-decoration: none;
}
.cm-prog-chip:hover { background: var(--cm-bg); }
.cm-prog-chip.is-active { border-color: var(--cm-brand); background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-prog-dfs { font: 600 12px var(--cm-font-ui); color: var(--cm-text); }

/* -- Reorderable rows, shared by 3c / 5a / 2a ---------------------------- */
.cm-prog-rows { display: flex; flex-direction: column; gap: 9px; }
.cm-prog-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  padding: 11px 14px; background: var(--cm-surface-alt);
}
.cm-prog-row--warn   { border-color: var(--cm-gold); background: var(--cm-gold-bg); }
.cm-prog-row--danger { border-color: var(--cm-red-tx); background: var(--cm-red-bg); }
.cm-prog-row--muted, .cm-prog-row.is-removed { opacity: .62; }
.cm-prog-row.is-removed .cm-prog-row__title { text-decoration: line-through; }
.cm-prog-row--eval { border-left: 3px solid var(--cm-red-tx); }
.cm-prog-row--diagnostic { border-left-color: var(--cm-brand); }
.cm-prog-row--formative  { border-left-color: var(--cm-gold); }
.cm-prog-row--summative  { border-left-color: var(--cm-red-tx); }
.cm-prog-row__handle { color: var(--cm-faint); font-size: 13px; cursor: grab; }
.cm-prog-row__index {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  background: var(--cm-blue-bg); border: 2px solid var(--cm-brand); color: var(--cm-blue-tx);
  display: grid; place-items: center; font: 700 10.5px var(--cm-font-ui);
}
.cm-prog-row__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.cm-prog-row__title { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-prog-row__meta { font-size: 11.5px; color: var(--cm-faint); }
.cm-prog-row__check, .cm-prog-row__date { display: inline-flex; align-items: center; gap: 6px; font: 600 11.5px var(--cm-font-ui); color: var(--cm-text); }
.cm-prog-row__date input { border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-ink); border-radius: var(--cm-radius-sm); padding: 4px 9px; font: 600 12px var(--cm-font-ui); }
.cm-prog-row__remove { border: 0; background: none; color: var(--cm-faint); cursor: pointer; display: grid; place-items: center; padding: 2px; }
.cm-prog-row__remove:hover { color: var(--cm-red-tx); }

.cm-prog-chip-status { display: inline-flex; padding: 3px 9px; border-radius: 999px; font: 600 11px var(--cm-font-ui); white-space: nowrap; }
.cm-prog-chip-status--ok     { background: var(--cm-green-bg); color: var(--cm-green-tx); }
.cm-prog-chip-status--warn   { background: var(--cm-gold-bg);  color: var(--cm-gold-tx); }
.cm-prog-chip-status--danger { background: var(--cm-red-bg);   color: var(--cm-red-tx); }
.cm-prog-chip-status--muted  { background: var(--cm-border-soft); color: var(--cm-faint); }
/* Co-animation: a statement of fact rather than a state to act on, hence the neutral blue
   next to the ok/warn/danger tones the séquence statuses use. */
.cm-prog-chip-status--info   { background: var(--cm-blue-bg);  color: var(--cm-blue-tx); }
/* Co-animation: the teacher holding a créneau, printed on the pill. Not cosmetic - two créneaux on
   the same day at the same hour are otherwise the same pill twice, and picking the wrong one is
   invisible. */
.cm-prog-slot__who { font: 600 11px var(--cm-font-ui); color: var(--cm-blue-tx); }
.cm-prog-picker__slot-who { display: block; font: 600 11.5px var(--cm-font-ui); color: var(--cm-blue-tx); margin-top: 2px; }
.cm-prog-picker__warning { margin: 0 18px 10px; padding: 9px 12px; border-radius: var(--cm-radius-sm); background: var(--cm-gold-bg); color: var(--cm-gold-tx); font-size: 12.5px; }

.cm-prog-tag {
  display: inline-flex; padding: 2px 8px; border-radius: 999px; margin-left: 4px;
  background: var(--cm-blue-bg); color: var(--cm-blue-tx); font: 600 10.5px var(--cm-font-ui);
}
/* "Éval. sommative" on a séance carrying its own evaluation - same three nature colours the
   calendar cards use (.cm-prog-card--*), so a nature reads identically wherever it shows up. */
.cm-prog-tag--eval.cm-prog-eval--diagnostic { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-prog-tag--eval.cm-prog-eval--formative  { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }
.cm-prog-tag--eval.cm-prog-eval--summative  { background: var(--cm-red-bg);  color: var(--cm-red-tx); }
.cm-prog-pill {
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-blue-tx);
  border-radius: 999px; padding: 3px 10px; font: 600 11px var(--cm-font-ui); cursor: pointer;
}
.cm-prog-pill:hover { background: var(--cm-blue-bg); }
.cm-prog-pill:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-blue-bg); }
.cm-prog-pill input { margin-right: 4px; }

/* -- 5a : colonne principale + rail -------------------------------------- */
.cm-prog-layout { display: grid; grid-template-columns: 1fr 318px; gap: 16px; align-items: start; }
@media (max-width: 992px) { .cm-prog-layout { grid-template-columns: 1fr; } }
.cm-prog-rail { display: flex; flex-direction: column; gap: 14px; }
.cm-prog-rail__counts { margin: 10px 0 0; font-size: 12.5px; color: var(--cm-muted); }
.cm-prog-rail__link { display: block; font: 600 12.5px var(--cm-font-ui); margin-top: 6px; }
.cm-prog-rail__hint { margin: 6px 0 10px; font-size: 11.5px; color: var(--cm-faint); }
.cm-prog-rail__empty { margin: 0; font-size: 12px; color: var(--cm-faint); }
.cm-prog-rail__item {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 0; border-top: 1px solid var(--cm-border-soft);
}
.cm-prog-rail__item-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.cm-prog-rail__item-title { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-prog-rail__item-meta { font-size: 11px; color: var(--cm-faint); }
.cm-prog-cardhead { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cm-prog-cardhead__hint { font-size: 12px; color: var(--cm-faint); }
.cm-prog-cardhead__actions { margin-left: auto; display: flex; gap: 8px; }
.cm-prog-hint { margin: 10px 0 0; font-size: 11.5px; color: var(--cm-faint); }

/* Collapsed forms (add séquence / evaluation / séance): a <form> in its own right, never nested in
   another one - see the bug class found in the UFA audit */
.cm-prog-inlineform {
  display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
  padding: 14px 18px; border-top: 1px solid var(--cm-border-soft);
}
.cm-prog-inlineform__field { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 160px; font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-prog-inlineform__field--narrow { flex: 0 0 120px; min-width: 120px; }

/* Class-copy sheets: the line that says whose copy is on screen, and the banner every edit screen
   carries. Deliberately loud - the copy and its library model are shown by near-identical screens,
   so this is what tells them apart */
.cm-instance-scope { font-size: 12px; color: var(--cm-muted); margin-bottom: 12px; }
.cm-instance-banner {
  display: flex; gap: 12px; align-items: flex-start; margin-bottom: 16px; padding: 12px 14px;
  border: 1px solid var(--cm-gold); border-left-width: 4px; background: var(--cm-gold-bg); border-radius: 6px;
}
.cm-instance-banner__icon { flex: 0 0 auto; font-size: 15px; color: var(--cm-gold-tx); line-height: 1.3; }
.cm-instance-banner__title { margin: 0 0 4px; font: 600 13.5px var(--cm-font-ui); color: var(--cm-gold-tx); }
.cm-instance-banner__text { margin: 0 0 2px; font-size: 12.5px; color: var(--cm-text); }
.cm-instance-banner__text:last-child { margin-bottom: 0; }

/* Read-only prose blocks of a fiche séquence / séance */
.cm-prose-blocks { display: flex; flex-direction: column; gap: 14px; }
.cm-prose-blocks__title { font: 600 12px var(--cm-font-ui); text-transform: uppercase; letter-spacing: .5px; color: var(--cm-faint); margin: 0 0 4px; }
.cm-deflist { display: grid; grid-template-columns: max-content 1fr; gap: 6px 18px; margin: 0; }
.cm-deflist dt { font: 600 12px var(--cm-font-ui); color: var(--cm-faint); }
.cm-deflist dd { margin: 0; font-size: 13px; color: var(--cm-text); }
.cm-cell-prose { font-size: 12.5px; color: var(--cm-muted); max-width: 260px; }

/* 2a - "Créneaux utilisés": the séquence's own placement options, its own <form> above the
   séance rows so nothing ends up nested in it */
.cm-prog-options { display: flex; align-items: flex-end; gap: 14px 18px; flex-wrap: wrap; }
.cm-prog-options__field { display: flex; flex-direction: column; gap: 4px; flex: 1 1 260px; min-width: 260px; font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-prog-options__field--narrow { flex: 0 1 170px; min-width: 170px; }
/* Their own row: sharing one with the selects left the matière and composition labels - both
   full sentences - truncated to "Uniquement B1 - Base de d…", which is where the choice is */
.cm-prog-options__checks { display: flex; flex-wrap: wrap; gap: 10px 28px; flex: 1 1 100%; }
.cm-prog-options__check { display: flex; align-items: flex-start; gap: 8px; font: 500 12.5px var(--cm-font-ui); color: var(--cm-text); cursor: pointer; }
.cm-prog-options__check input { margin-top: 2px; flex: 0 0 auto; }
.cm-prog-options__check small { display: block; font-weight: 400; font-size: 11px; color: var(--cm-faint); }
.cm-prog-options > button { margin-left: auto; }

/* -- 3c: steps of the creation form -------------------------------------- */
.cm-prog-form { display: flex; flex-direction: column; gap: 18px; }
.cm-prog-step { display: flex; flex-direction: column; gap: 8px; }
.cm-prog-step__title { margin: 0; font: 600 12px var(--cm-font-ui); color: var(--cm-muted); letter-spacing: .4px; text-transform: uppercase; }
.cm-prog-step__hint { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--cm-faint); }
.cm-prog-radiogrid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.cm-prog-cardgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.cm-prog-cardgrid--2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 768px) { .cm-prog-radiogrid, .cm-prog-cardgrid, .cm-prog-cardgrid--2 { grid-template-columns: 1fr; } }
.cm-prog-radio, .cm-prog-optioncard {
  display: flex; align-items: flex-start; gap: 11px;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  padding: 11px 13px; cursor: pointer; background: var(--cm-surface);
}
.cm-prog-radio:hover, .cm-prog-optioncard:hover { background: var(--cm-bg); }
.cm-prog-radio:has(input:checked), .cm-prog-optioncard:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-blue-bg); }
.cm-prog-radio > span:last-child, .cm-prog-optioncard > span:last-child { display: flex; flex-direction: column; gap: 2px; }
.cm-prog-radio__name, .cm-prog-optioncard__name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-prog-radio__sub, .cm-prog-optioncard__sub { font-size: 11.5px; color: var(--cm-faint); }

.cm-prog-search { position: relative; display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.cm-prog-search__input {
  flex: 1; min-width: 220px;
  border: 2px solid var(--cm-brand); background: var(--cm-surface); color: var(--cm-ink);
  border-radius: var(--cm-radius); padding: 8px 13px; font: 400 13px var(--cm-font-ui);
}
.cm-prog-search__hint { font-size: 11.5px; color: var(--cm-faint); }
.cm-prog-search__results {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20;
  background: var(--cm-surface); border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius); box-shadow: var(--cm-shadow-pop);
  display: flex; flex-direction: column; max-height: 260px; overflow-y: auto;
}
.cm-prog-search__result {
  display: flex; align-items: center; gap: 9px; justify-content: space-between;
  border: 0; background: none; text-align: left; cursor: pointer;
  padding: 8px 13px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink);
}
.cm-prog-search__result:hover { background: var(--cm-bg); }
.cm-prog-search__result span { font-weight: 400; font-size: 11px; color: var(--cm-faint); }

.cm-prog-note { background: var(--cm-surface-alt); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 12px 15px; }
.cm-prog-note__title { display: block; font: 600 12px var(--cm-font-ui); color: var(--cm-muted); letter-spacing: .4px; text-transform: uppercase; }
.cm-prog-note__list { margin: 6px 0 0; padding-left: 18px; font-size: 12px; line-height: 1.55; color: var(--cm-muted); }

/* -- 2a: créneaux linked to a séance ------------------------------------- */
.cm-prog-slots { display: flex; flex-direction: column; gap: 4px; }
.cm-prog-slot { display: flex; align-items: center; gap: 7px; }
.cm-prog-slot__group { padding: 2px 9px; border-radius: 999px; background: var(--cm-navy); color: #fff; font: 600 10.5px var(--cm-font-ui); }
.cm-prog-slot__button {
  border: 1px solid var(--cm-mail-tint-border); background: var(--cm-blue-bg); color: var(--cm-blue-tx);
  border-radius: var(--cm-radius-sm); padding: 5px 11px;
  font: 600 12px var(--cm-font-ui); cursor: pointer; white-space: nowrap;
}
.cm-prog-slot__button:hover { background: var(--cm-mail-tint-bg); }
.cm-prog-slot__button--empty { border-style: dashed; background: var(--cm-surface); color: var(--cm-muted); }
.cm-prog-slot__flag { font: 600 10.5px var(--cm-font-ui); color: var(--cm-warn-tx); }

/* -- 2b: créneau picker --------------------------------------------------- */
.cm-prog-picker {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  background: var(--cm-surface); color: var(--cm-text);
  width: min(560px, 92vw); padding: 0; box-shadow: var(--cm-shadow-pop);
}
.cm-prog-picker::backdrop { background: rgba(18, 52, 77, .45); }
.cm-prog-picker__head { display: flex; align-items: center; gap: 11px; padding: 16px 20px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-prog-picker__icon { flex: none; width: 32px; height: 32px; border-radius: var(--cm-radius-sm); background: var(--cm-blue-bg); display: grid; place-items: center; }
.cm-prog-picker__head > span:nth-child(2) { display: flex; flex-direction: column; }
.cm-prog-picker__title { font: 600 15px var(--cm-font-title); color: var(--cm-ink); }
.cm-prog-picker__sub { font-size: 12px; color: var(--cm-faint); }
.cm-prog-picker__close { margin-left: auto; border: 0; background: var(--cm-bg); color: var(--cm-muted); border-radius: var(--cm-radius-sm); width: 28px; height: 28px; cursor: pointer; }
.cm-prog-picker__body { padding: 14px 20px; display: flex; flex-direction: column; gap: 8px; max-height: 46vh; overflow-y: auto; }
.cm-prog-picker__slot, .cm-prog-picker__option {
  display: flex; align-items: center; gap: 11px;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  padding: 10px 13px; cursor: pointer;
}
.cm-prog-picker__slot:hover, .cm-prog-picker__option:hover { background: var(--cm-bg); }
.cm-prog-picker__slot:has(input:checked), .cm-prog-picker__option:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-blue-bg); }
.cm-prog-picker__slot > span, .cm-prog-picker__option > span { display: flex; flex-direction: column; flex: 1; }
.cm-prog-picker__slot-name, .cm-prog-picker__option-name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-prog-picker__slot-meta, .cm-prog-picker__option-sub { font-size: 11.5px; color: var(--cm-faint); }
.cm-prog-picker__mode, .cm-prog-picker__duration { padding: 12px 20px; border-top: 1px solid var(--cm-border-soft); display: flex; flex-direction: column; gap: 8px; }
.cm-prog-picker__duration { flex-direction: row; align-items: center; gap: 6px; flex-wrap: wrap; font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-prog-picker__legend { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); letter-spacing: .4px; text-transform: uppercase; }
.cm-prog-picker__foot { padding: 13px 20px; border-top: 1px solid var(--cm-border-soft); display: flex; justify-content: flex-end; gap: 8px; }

/* D/F/S chip on the Carnet de notes column header (evaluation_grid_controller.js) */
.cm-prog-natureDot {
  display: inline-grid; place-items: center; width: 16px; height: 16px; margin-right: 5px;
  border-radius: 4px; font: 700 10px var(--cm-font-ui); vertical-align: 1px;
}
.cm-prog-natureDot--diagnostic { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-prog-natureDot--formative  { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }
.cm-prog-natureDot--summative  { background: var(--cm-red-bg);  color: var(--cm-red-tx); }

/* ==========================================================================
   Cahier de texte (design_handoff_cahier_de_texte 2a/2b) - three parts, each with its own
   visibility, its documents and its assignments.
   ========================================================================== */

/* Part header: numbered chip, title, visibility state and its setting. */
.cm-log-section__head { display: flex; align-items: center; gap: 12px; padding: 14px 20px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-step { width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; font: 700 11px var(--cm-font-ui); flex: none; }
.cm-step--gold { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }
.cm-step--blue { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-step--gray { background: var(--cm-border-soft); color: var(--cm-muted); }

/* Visibility menu: four choices as cards, the one in force brought forward. */
.cm-visibility-menu { width: 288px; padding: 12px; }
.cm-visibility-menu__title { font: 600 11px var(--cm-font-ui); text-transform: uppercase; letter-spacing: .4px; color: var(--cm-muted); margin-bottom: 8px; }
.cm-visibility-menu__note { font-size: 11.5px; color: var(--cm-faint); margin: 8px 0 0; line-height: 1.5; }
.cm-visibility-option {
    display: block; width: 100%; text-align: left; border: 1px solid var(--cm-border); background: var(--cm-surface);
    border-radius: var(--cm-radius-sm); padding: 10px 12px; margin-bottom: 6px; font: 600 13px var(--cm-font-ui);
    color: var(--cm-text); cursor: pointer;
}
.cm-visibility-option:hover { border-color: var(--cm-brand); }
.cm-visibility-option.is-selected { border-color: var(--cm-brand); background: var(--cm-mail-tint-bg); color: var(--cm-brand-strong); }

/* Documents of a part, as removable chips. */
.cm-doc-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.cm-doc-row__label { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-doc-chip { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); padding: 5px 10px; font-size: 12.5px; background: var(--cm-surface); }
.cm-doc-chip a { color: var(--cm-brand-strong); text-decoration: none; }
.cm-doc-chip__meta { color: var(--cm-faint); }
.cm-doc-chip__remove { border: 0; background: none; color: var(--cm-faint); cursor: pointer; padding: 0; line-height: 1; }
.cm-doc-chip__remove:hover { color: var(--cm-red-tx); }
.cm-doc-chip form { display: inline; }
.cm-inline-add { font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-inline-add:hover { text-decoration: underline; color: var(--cm-brand-strong); }

/* « Reprendre le contenu de la séance », under an empty editor. The [hidden] rule is not
   redundant: a display declaration on a class outranks the user agent's own [hidden] rule, so
   without it the box would stay visible once the part is filled. */
.cm-seance-default { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.cm-seance-default[hidden] { display: none; }
.cm-seance-default__note { font-size: 12px; color: var(--cm-faint); }

/* Assignments given on a part. */
.cm-work-list { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.cm-work { display: flex; align-items: center; gap: 12px; border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm); padding: 12px 14px; background: var(--cm-surface-soft); }
.cm-work__icon { width: 32px; height: 32px; border-radius: var(--cm-radius-sm); background: var(--cm-mail-tint-bg); color: var(--cm-brand-strong); display: grid; place-items: center; font: 700 13px var(--cm-font-ui); flex: none; }
.cm-work__body { min-width: 0; flex: 1; }
.cm-work__title { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-work__meta { font-size: 12px; color: var(--cm-faint); }

/* Modal (2b) - same family as the side panel, but centered. */
.cm-modal {
    position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 620px; max-width: calc(100vw - 40px);
    max-height: calc(100vh - 80px); background: var(--cm-surface); border-radius: var(--cm-radius);
    box-shadow: 0 24px 60px rgba(0, 0, 0, .28); z-index: 1031; display: flex; flex-direction: column; overflow: hidden;
}
.cm-modal form { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.cm-modal__header { display: flex; align-items: center; justify-content: space-between; padding: 18px 24px; border-bottom: 1px solid var(--cm-border-soft); flex: none; }
.cm-modal__title { font: 600 17px var(--cm-font-title); color: var(--cm-ink); }
.cm-modal__body { padding: 22px 24px; flex: 1; overflow-y: auto; }
.cm-modal__footer { display: flex; justify-content: flex-end; gap: 10px; padding: 14px 24px; border-top: 1px solid var(--cm-border-soft); background: var(--cm-surface-soft); flex: none; }

/* Work-type grid: three columns of selectable cards, the radio itself hidden. */
.cm-field-label { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); margin-bottom: 6px; }
.cm-nature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 18px; }
.cm-nature-card { border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); padding: 10px 12px; cursor: pointer; display: block; }
.cm-nature-card input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-nature-card__label { display: block; font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-nature-card__hint { display: block; font-size: 11.5px; color: var(--cm-faint); }
.cm-nature-card:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-mail-tint-bg); }

/* Single action bar at the foot of the screen (one per page, cf. the existing .cm-actionbar). */
.cm-footer-actions { display: flex; justify-content: flex-end; margin-bottom: 16px; }

/* The Bootstrap theme already renders a label for each radio; the type card carries its own, plus
   its sub-text, and the required-field asterisk makes no sense on a choice among six. */
.cm-nature-card .form-check-label, .cm-nature-card .form-check { display: contents; }
.cm-nature-card > .form-check > .form-check-label { display: none; }

/* Work to do, student side (4a): one card per assignment, grouped by deadline. */
.cm-group-label { font: 600 11px var(--cm-font-ui); text-transform: uppercase; letter-spacing: .4px; color: var(--cm-muted); margin: 18px 0 8px; }
.cm-group-label--late { color: var(--cm-red-tx); }
.cm-work-card { display: flex; align-items: center; gap: 14px; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 14px 18px; margin-bottom: 8px; }
.cm-work-card--late { background: #fdf3f2; border-color: #ecc8c3; }
.cm-work-card__body { flex: 1; min-width: 0; }
.cm-work-card__body a { color: var(--cm-brand-strong); }
.cm-work-card__due { font: 600 12.5px var(--cm-font-ui); color: var(--cm-faint); white-space: nowrap; }
.cm-work-card__due.is-late { color: var(--cm-red-tx); }
.cm-work-card__action { flex: none; }

/* Course view of the cahier de texte (1b): list of séances on the left, preview on the right. */
.cm-course-view { display: grid; grid-template-columns: 330px 1fr; gap: 16px; align-items: start; }
.cm-session-list { display: flex; flex-direction: column; }
.cm-session { display: flex; align-items: center; gap: 10px; padding: 12px 20px; border-bottom: 1px solid var(--cm-border-faint); text-decoration: none; color: inherit; }
.cm-session:hover { background: var(--cm-surface-alt); }
.cm-session.is-selected { background: var(--cm-mail-tint-bg); border-left: 3px solid var(--cm-brand); padding-left: 17px; }
.cm-session__dot { width: 9px; height: 9px; border-radius: 50%; flex: none; display: inline-block; }
/* Two states only: a log counts as kept when the "during the session" content is there. */
.cm-session__dot--filled { background: var(--cm-positive-tx); }
.cm-session__dot--empty { background: var(--cm-border); }
.cm-session__body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.cm-session__date { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-session__title { font-size: 12px; color: var(--cm-faint); }
.cm-session__state { font-size: 11.5px; color: var(--cm-faint); }
.cm-preview__date { font: 600 15px var(--cm-font-title); color: var(--cm-ink); }
.cm-preview-row { display: flex; align-items: center; gap: 12px; border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm); padding: 12px 14px; margin-bottom: 8px; }

@media (max-width: 992px) {
    .cm-course-view { grid-template-columns: 1fr; }
}

/* Séquence timeline at the top of the cahier de texte (2a): one bar per séance, the state reads
   from the color - past, current, upcoming. */
.cm-seq-strip { display: flex; gap: 12px; margin-top: 14px; }
.cm-seq-strip__item { flex: 1; min-width: 0; }
.cm-seq-bar { display: block; height: 7px; border-radius: 999px; background: var(--cm-border); }
.cm-seq-bar--done { background: var(--cm-positive-tx); }
.cm-seq-bar--current { background: var(--cm-brand); outline: 2px solid var(--cm-mail-tint-border); outline-offset: 1px; }
.cm-seq-label { display: block; font-size: 11.5px; color: var(--cm-faint); margin-top: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-seq-label.is-current { color: var(--cm-brand-strong); font-weight: 600; }

/* Choice chips (mockup 2b): deadline and submission formats. The checkboxes keep their role - only
   their dressing changes, the tick itself disappearing under the chip. */
.cm-pill-row { display: flex; flex-wrap: wrap; gap: 8px; }
.cm-pill {
    border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text);
    border-radius: 999px; padding: 7px 14px; font: 600 12.5px var(--cm-font-ui); cursor: pointer;
}
.cm-pill:hover { border-color: var(--cm-brand); }
.cm-pill.is-selected { border-color: var(--cm-brand); background: var(--cm-mail-tint-bg); color: var(--cm-brand-strong); }
.cm-pill-choice .form-check { display: inline-block; margin: 0 8px 0 0; padding: 0; }
.cm-pill-choice .form-check-input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-pill-choice .form-check-label {
    display: inline-block; border: 1px solid var(--cm-border); border-radius: 999px; padding: 7px 14px;
    font: 600 12.5px var(--cm-font-ui); color: var(--cm-text); cursor: pointer;
}
.cm-pill-choice .form-check:has(input:checked) .form-check-label { border-color: var(--cm-brand); background: var(--cm-mail-tint-bg); color: var(--cm-brand-strong); }

/* « Importer depuis une séance » menu (2a): the suggestions first, the full list after. */
.cm-import-menu { width: 400px; padding: 12px; max-height: 70vh; overflow-y: auto; }
.cm-import-row { display: flex; align-items: center; gap: 12px; padding: 9px 10px; border-radius: var(--cm-radius-sm); }
.cm-import-row:hover { background: var(--cm-surface-alt); }
.cm-import-row__body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.cm-import-row__kind { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-import-row__meta { font-size: 12px; color: var(--cm-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The visibility radios are inside the choice cards, not next to them: the checked card is
   recognised by its background, like the work-type grid. */
.cm-visibility-option input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-visibility-option .form-check { display: contents; }

/* An assignment's title is a link to its page: it keeps the look of a title, the link color
   appearing only on hover. */
.cm-work__title a { color: inherit; text-decoration: none; }
.cm-work__title a:hover { color: var(--cm-brand-strong); text-decoration: underline; }

/* ==========================================================================
   Applications - "My job search" (design_handoff_stage_alternance, screen 2b)
   ========================================================================== */

/* The mockup lays out a single card: a header that counts, factual rows, and a foot that owns up
   to what it isn't showing. No banner and no guidance column - the README rules them out by name
   for this screen. */
.cm-jobapp-head {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 18px; border-bottom: 1px solid var(--cm-border-soft);
}
.cm-jobapp-head__title { font: 600 15px var(--cm-font-title); color: var(--cm-ink); }
.cm-jobapp-head__count { font-size: 12px; color: var(--cm-faint); }
.cm-jobapp-head__filters { margin-left: auto; display: flex; gap: 6px; }

.cm-jobapp-filter {
  border: 1px solid var(--cm-border); background: var(--cm-bg); color: var(--cm-text);
  border-radius: 999px; padding: 4px 10px; font: 600 11px var(--cm-font-ui); text-decoration: none;
}
.cm-jobapp-filter:hover { color: var(--cm-brand-strong); text-decoration: none; }
.cm-jobapp-filter.is-active,
.cm-jobapp-filter.is-active:hover { background: var(--cm-brand); border-color: var(--cm-brand); color: #fff; }

.cm-jobapp-row {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 18px; border-bottom: 1px solid var(--cm-border-soft);
}
.cm-jobapp-row:last-of-type { border-bottom: 0; }
/* The very pale green background of the mockup's first row: the last news received, and only that.
   It is not a verdict on the reply (principle #1), it says "this just came in". */
.cm-jobapp-row.is-latest-reply { background: #f6fbf8; }
.cm-jobapp-row__dot { width: 9px; height: 9px; border-radius: 50%; flex: none; background: var(--cm-faint); }
.cm-jobapp-row__dot--0 { background: #1f7a54; }
.cm-jobapp-row__dot--1 { background: #e0b25f; }
.cm-jobapp-row__dot--2 { background: #7a5ea8; }
.cm-jobapp-row__dot--3 { background: #b0553f; }
.cm-jobapp-row__dot--4 { background: #12507e; }
.cm-jobapp-row__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cm-jobapp-row__name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-jobapp-row__summary { font-size: 12px; color: var(--cm-muted); }
.cm-jobapp-row__side { margin-left: auto; display: flex; align-items: center; gap: 12px; flex: none; }
.cm-jobapp-row__mails { font-size: 11.5px; color: var(--cm-faint); white-space: nowrap; }
.cm-jobapp-row__link { font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; white-space: nowrap; }
.cm-jobapp-row__link:hover { color: var(--cm-brand-strong); text-decoration: underline; }

/* State chips: strictly factual - a reply exists, a send failed, a send has been waiting longer
   than the D+10 reminder, or simply the date of the last mail. */
.cm-jobapp-chip {
  font: 600 11px var(--cm-font-ui); border-radius: 999px; padding: 3px 10px;
  white-space: nowrap; border: 1px solid transparent;
}
.cm-jobapp-chip--reply { color: var(--cm-blue-tx); background: var(--cm-mail-tint-bg); border-color: var(--cm-mail-tint-border); }
.cm-jobapp-chip--waiting { color: #8a6a1f; background: #fdf6e8; border-color: #e0b25f; }
.cm-jobapp-chip--neutral { color: var(--cm-faint); background: var(--cm-bg); border-color: #e3eaf0; }
.cm-jobapp-chip--failed { color: #b0553f; background: #fbeeea; border-color: #dfab9e; }

.cm-jobapp-more { padding: 11px 18px; font-size: 12px; color: var(--cm-faint); border-top: 1px solid var(--cm-border-soft); }
.cm-jobapp-more a { color: var(--cm-brand-strong); text-decoration: none; }
.cm-jobapp-more a:hover { text-decoration: underline; }

[data-bs-theme=dark] .cm-jobapp-row.is-latest-reply { background: #1d3327; }
[data-bs-theme=dark] .cm-jobapp-chip--waiting { color: #e0bc6d; background: #3c3117; border-color: #7a5417; }
[data-bs-theme=dark] .cm-jobapp-chip--failed { color: #e09384; background: #40221c; border-color: #7a4033; }

/* -- School mail, the external mailbox (screen 3b) -------------------------
   The three-pane skeleton is internal Messaging's (.cm-mail-*): same templates, same responsive
   behaviour. Only what is specific to outside mail is added here. */
.cm-schoolmail-identity {
  margin: 0 0 12px; font-size: 12.5px; color: var(--cm-muted);
}
.cm-schoolmail-identity b { color: var(--cm-brand-strong); }

.cm-schoolmail-context {
  background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  padding: 13px 14px; display: flex; flex-direction: column; gap: 8px;
}
.cm-schoolmail-context__label { font: 600 12px var(--cm-font-ui); letter-spacing: .6px; text-transform: uppercase; color: var(--cm-faint); }
.cm-schoolmail-context__item { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--cm-text); text-decoration: none; }
.cm-schoolmail-context__item:hover, .cm-schoolmail-context__item.is-active { color: var(--cm-brand-strong); }
.cm-schoolmail-context__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-schoolmail-context__count { margin-left: auto; font-size: 11px; color: var(--cm-faint); }
.cm-schoolmail-context__all { font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-schoolmail-context__all:hover { text-decoration: underline; }

/* A mail's state chip: "Reply received", "Delivered", "Failed". Factual, never a reading of what
   the company answered. */
.cm-schoolmail-tag {
  font: 600 10.5px var(--cm-font-ui); color: var(--cm-blue-tx); background: var(--cm-mail-tint-bg);
  border: 1px solid var(--cm-mail-tint-border); border-radius: 5px; padding: 2px 7px; white-space: nowrap;
}
.cm-schoolmail-clear-filter { font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-schoolmail-clear-filter:hover { text-decoration: underline; }

/* The avatar takes the linked company's colour: the same landmark as screen 2b's dots. */
.cm-schoolmail-avatar--0 { background: #1f7a54; color: #fff; }
.cm-schoolmail-avatar--1 { background: #e0b25f; color: #3d3013; }
.cm-schoolmail-avatar--2 { background: #7a5ea8; color: #fff; }
.cm-schoolmail-avatar--3 { background: #b0553f; color: #fff; }
.cm-schoolmail-avatar--4 { background: #12507e; color: #fff; }

.cm-schoolmail-headers { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--cm-faint); }
.cm-schoolmail-headers__meta { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; min-width: 0; }
.cm-schoolmail-headers b { color: var(--cm-text); }
.cm-schoolmail-linked {
  margin-left: auto; font: 600 11px var(--cm-font-ui); color: var(--cm-blue-tx);
  background: var(--cm-mail-tint-bg); border: 1px solid var(--cm-mail-tint-border);
  border-radius: 999px; padding: 3px 10px; text-decoration: none; white-space: nowrap;
}
.cm-schoolmail-linked:hover { color: var(--cm-blue-tx); border-color: var(--cm-brand); }

.cm-schoolmail-attachments { margin-top: 14px; display: flex; flex-wrap: wrap; gap: 8px; }
.cm-schoolmail-attachment {
  display: flex; align-items: center; gap: 7px; border: 1px solid #bfe3d1; background: #f6fbf8;
  border-radius: 8px; padding: 7px 12px; font-size: 12px; color: #1f7a54;
}
[data-bs-theme=dark] .cm-schoolmail-attachment { background: #1d3327; border-color: #2e5c44; color: #8cc3a2; }

/* -- School mail: composing a message (screen 3d, with 3g's linking inline) -----------------
   Single card, 840px in the mockup: header, header rows, body, signature, attachments, footer. */
.cm-compose {
  max-width: 860px; background: var(--cm-surface); border: 1px solid var(--cm-border);
  border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(18, 52, 77, .08);
}
.cm-compose__head { padding: 13px 18px; background: var(--cm-navy); display: flex; align-items: center; gap: 10px; }
.cm-compose__title { font: 600 14px var(--cm-font-title); color: #fff; }
.cm-compose__error {
  margin: 0; padding: 11px 18px; background: #fbeeea; border-bottom: 1px solid #f0cec4; color: #b0553f;
  font: 600 12.5px var(--cm-font-ui);
}
.cm-compose__fields { padding: 0 18px; display: flex; flex-direction: column; }
.cm-compose__row {
  display: flex; align-items: center; gap: 10px; padding: 11px 0;
  border-bottom: 1px solid var(--cm-border-soft); font-size: 13px;
}
.cm-compose__row--case { align-items: flex-start; }
.cm-compose__label { width: 84px; flex: none; color: var(--cm-faint); padding-top: 2px; }
.cm-compose__from { color: var(--cm-ink); }
.cm-compose__input {
  flex: 1; min-width: 0; border: 1px solid transparent; background: transparent; color: var(--cm-ink);
  font: 400 13px var(--cm-font-ui); padding: 5px 7px; border-radius: 7px;
}
.cm-compose__input:focus { outline: none; border-color: var(--cm-border); background: var(--cm-surface-alt); }
.cm-compose__case { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.cm-compose__hint { margin: 0; font-size: 11.5px; color: var(--cm-faint); }

/* Kept for the forms that borrow this field shape outside the compose screen (the practice
   application, and the admin's "Rattacher" form). */
.cm-compose__create-fields { display: flex; gap: 12px; flex-wrap: wrap; }
.cm-compose__create-field { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 180px; }
.cm-compose__create-field span {
  font: 600 10.5px var(--cm-font-ui); letter-spacing: .6px; text-transform: uppercase; color: var(--cm-faint);
}
.cm-compose__create-field input {
  border: 1px solid var(--cm-border); border-radius: 8px; padding: 8px 11px;
  font: 400 13px var(--cm-font-ui); color: var(--cm-ink); background: var(--cm-surface);
}
.cm-compose__create-field input:focus { outline: none; border-color: var(--cm-brand); }

.cm-compose__body { padding: 15px 18px; display: flex; flex-direction: column; gap: 10px; }
.cm-compose__textarea {
  border: 1px solid var(--cm-border); border-radius: 9px; padding: 12px 14px; resize: vertical;
  font: 400 13px var(--cm-font-ui); color: var(--cm-text); line-height: 1.7; background: var(--cm-surface);
}
.cm-compose__textarea:focus { outline: none; border-color: var(--cm-brand); }
.cm-compose__signature {
  border-left: 2px solid var(--cm-gold); padding-left: 12px; font-size: 13px;
  color: var(--cm-text); line-height: 1.55;
}
.cm-compose__attachments { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cm-compose__attach {
  display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--cm-border);
  background: var(--cm-surface-alt); border-radius: 8px; padding: 7px 12px; font-size: 12px;
  color: var(--cm-text); cursor: pointer;
}
.cm-compose__attach:hover { color: var(--cm-brand-strong); }
.cm-compose__attach input { display: none; }
.cm-compose__footer {
  padding: 13px 18px; border-top: 1px solid var(--cm-border-soft); background: var(--cm-surface-soft);
  display: flex; align-items: center; gap: 9px;
}
.cm-compose__notes { margin-left: auto; font-size: 11.5px; color: var(--cm-faint); line-height: 1.4; text-align: right; }

[data-bs-theme=dark] .cm-compose__error { background: #40221c; border-color: #7a4033; color: #e09384; }
.cm-schoolmail-actions { display: flex; justify-content: flex-end; margin-bottom: 12px; }
.cm-schoolmail-reply { margin-top: 16px; }

/* -- School mail: drafts, trash and the signature screen (3f) --------------- */
.cm-compose__status { margin-left: auto; font-size: 11.5px; color: var(--cm-topbar-dim); }
.cm-schoolmail-delete { margin-top: 16px; }

.cm-signature { max-width: 760px; }
.cm-signature__row {
  display: flex; align-items: center; gap: 12px; padding: 12px 18px;
  border-bottom: 1px solid var(--cm-border-faint); font-size: 13px;
}
.cm-signature__label { width: 170px; flex: none; color: var(--cm-faint); }
.cm-signature__input {
  flex: 1; min-width: 0; border: 1px solid transparent; background: transparent; color: var(--cm-ink);
  font: 400 13px var(--cm-font-ui); padding: 6px 8px; border-radius: 7px;
}
.cm-signature__input:focus { outline: none; border-color: var(--cm-brand); background: var(--cm-surface); }

/* -- Job search tracking, one class (screen 1a) ----------------------------- */
/* The mockup's tiles read label-first, in small caps, with the figure underneath - the reverse of
   the .cm-stat cards elsewhere, hence this one modifier rather than a second component. */
.cm-stat__label--kpi {
  font: 600 11px var(--cm-font-ui); letter-spacing: .6px; text-transform: uppercase; color: var(--cm-faint);
}
.cm-statgrid .cm-stat__label--kpi + .cm-stat__value { color: var(--cm-ink); margin-top: 4px; }
.cm-stat__value--danger { color: var(--cm-action-danger); }

.cm-jobsearch-table td, .cm-jobsearch-table th { vertical-align: middle; }
.cm-jobsearch-student { display: flex; align-items: center; gap: 9px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-jobsearch-avatar {
  width: 24px; height: 24px; flex: none; border-radius: 50%; background: var(--cm-mail-tint-bg);
  color: var(--cm-blue-tx); font: 700 9.5px var(--cm-font-ui); display: grid; place-items: center;
}
.cm-jobsearch-failed { color: var(--cm-action-danger); font-weight: 600; }
.cm-jobsearch-actions { display: inline-flex; align-items: center; justify-content: flex-end; gap: 12px; }
.cm-jobsearch-actions form { display: inline; }
.cm-jobsearch-link {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: 600 12px var(--cm-font-ui); color: var(--cm-faint); white-space: nowrap;
}
.cm-jobsearch-link:hover { color: var(--cm-brand-strong); text-decoration: underline; }
/* A row whose mailbox is not open yet: a state, not an action - so it must not look clickable. */
.cm-jobsearch-idle { font: 600 12px var(--cm-font-ui); color: var(--cm-disabled); white-space: nowrap; }

/* -- Student application sheet (screen 2a) ---------------------------------- */
.cm-sheet { display: grid; grid-template-columns: 1fr 330px; gap: 14px; align-items: start; }
@media (max-width: 1199.98px) { .cm-sheet { grid-template-columns: 1fr; } }
.cm-sheet__main { min-width: 0; }
.cm-sheet__side { min-width: 0; }

.cm-sheet__application { border-bottom: 1px solid var(--cm-border-soft); }
.cm-sheet__application:last-of-type { border-bottom: 0; }
.cm-sheet__application-head {
  display: flex; align-items: center; gap: 12px; padding: 12px 18px; cursor: pointer; list-style: none;
}
.cm-sheet__application-head::-webkit-details-marker { display: none; }
.cm-sheet__application-head:hover { background: var(--cm-surface-alt); }
.cm-sheet__application-name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-sheet__context { font-size: 12px; color: var(--cm-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-sheet__meta { margin-left: auto; display: flex; align-items: center; gap: 10px; flex: none; }

.cm-sheet__mails { padding: 0 18px 14px; display: flex; flex-direction: column; gap: 8px; background: var(--cm-surface-soft); }
.cm-sheet__mail { border: 1px solid var(--cm-border-soft); background: var(--cm-surface); border-radius: 9px; padding: 10px 14px; }
.cm-sheet__mail-head { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; }
.cm-sheet__mail-kind { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-sheet__mail-subject { font-size: 12.5px; color: var(--cm-muted); }
.cm-sheet__mail-tag {
  font: 600 10px var(--cm-font-ui); color: var(--cm-text); background: var(--cm-bg);
  border: 1px solid var(--cm-border); border-radius: 5px; padding: 2px 6px;
}
.cm-sheet__mail-date { margin-left: auto; font-size: 11.5px; color: var(--cm-faint); }
.cm-sheet__mail-addresses { margin-top: 6px; font-size: 11.5px; color: var(--cm-faint); }
.cm-sheet__mail-body { margin-top: 8px; font-size: 12.5px; color: var(--cm-text); line-height: 1.6; }

.cm-sheet__note { padding: 12px 18px; border-bottom: 1px solid var(--cm-border-faint); }
.cm-sheet__note-body { margin: 0 0 4px; font-size: 12.5px; color: var(--cm-text); line-height: 1.5; }
.cm-sheet__note-meta { display: flex; align-items: center; gap: 10px; font-size: 11.5px; color: var(--cm-faint); }
.cm-sheet__note-meta form { margin-left: auto; }
.cm-sheet__note-form { padding: 12px 18px; display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.cm-sheet__note-form textarea { width: 100%; }

/* -- Unlinked mails queue (screen 5a) --------------------------------------- */
.cm-unlinked__row {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 14px 18px; border-bottom: 1px solid var(--cm-border-soft);
}
.cm-unlinked__row:last-of-type { border-bottom: 0; }
.cm-unlinked__main { flex: 1; min-width: 0; }
.cm-unlinked__subject { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-unlinked__meta { font-size: 12px; color: var(--cm-faint); margin-top: 2px; }
.cm-unlinked__actions { display: flex; align-items: center; gap: 8px; flex: none; }
.cm-unlinked__delete { color: var(--cm-action-danger); border-color: #dfab9e; }
.cm-unlinked__delete:hover { background: #fbeeea; color: var(--cm-action-danger); }
.cm-unlinked__form {
  flex-basis: 100%; display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap;
  border: 1px solid var(--cm-border); background: var(--cm-surface-alt); border-radius: 9px; padding: 12px 14px;
}
.cm-unlinked__form .cm-compose__create-field { flex: 1; min-width: 220px; }
.cm-schoolmail-attachment:hover { color: #1f7a54; border-color: #8fcfae; text-decoration: none; }
a.cm-sheet__mail-tag { text-decoration: none; }
a.cm-sheet__mail-tag:hover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }

/* ==========================================================================
   Practice application workflow (design_handoff_workflow_postulation, screens 7b-7c, 8a-8e)
   ========================================================================== */

/* The badge the mockup puts next to anything fictitious - an offer, a whole screen. It exists so a
   student never mistakes a practice run for a real opening. */
.cm-training-badge {
  display: inline-block; font: 600 10.5px var(--cm-font-ui); letter-spacing: .6px; text-transform: uppercase;
  color: var(--cm-blue-tx); background: var(--cm-mail-tint-bg); border: 1px solid var(--cm-mail-tint-border);
  border-radius: 5px; padding: 2px 8px; vertical-align: middle;
}
/* .page-title is a flex container, which swallows the whitespace text node the template puts
   between the heading and the badge - the gap has to be an explicit margin. */
.page-title .cm-training-badge { margin-left: 10px; }

.cm-training-intro { font-size: 13px; color: var(--cm-muted); line-height: 1.6; margin-bottom: 14px; }
.cm-training-offer {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  border: 1px solid var(--cm-border); border-radius: 8px; padding: 10px 14px; margin-bottom: 8px;
}
.cm-training-offer__title { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-training-offer__doc { font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-training-offer__doc:hover { text-decoration: underline; }
.cm-training-offer__action { margin-left: auto; }

.cm-training-check { display: flex; align-items: center; gap: 14px; padding: 14px 18px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-training-check:last-of-type { border-bottom: 0; }
.cm-training-check.is-refused { background: #fffdf6; }
.cm-training-check__icon {
  width: 26px; height: 26px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: var(--cm-border-soft); color: var(--cm-faint); font: 700 13px var(--cm-font-ui);
}
.cm-training-check__icon.is-validated { background: #e7f4ee; color: #1f7a54; }
.cm-training-check__icon.is-refused { background: #fdf6e8; color: #8a6a1f; border: 1px solid #e0b25f; }
.cm-training-check__main { min-width: 0; flex: 1; }
.cm-training-check__title { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-training-check__meta { font-size: 12px; color: var(--cm-faint); margin-top: 2px; }

.cm-training-progress { display: flex; align-items: center; gap: 12px; margin-top: 14px; font-size: 12px; color: var(--cm-faint); }
.cm-training-progress__bar { flex: 1; height: 7px; border-radius: 4px; background: var(--cm-border-soft); overflow: hidden; }
.cm-training-progress__bar span { display: block; height: 100%; background: var(--cm-gold); }
.cm-training-progress__text b { color: var(--cm-ink); }

/* design_handoff_postulation_redaction: square on the accented edge, rounded on the other three -
   the 3px rule reads as the edge of the block, not as a border it sits inside. */
.cm-training-notice {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--cm-mail-tint-bg); border: 1px solid var(--cm-mail-tint-border);
  border-left: 3px solid var(--cm-brand); border-radius: 0 8px 8px 0;
  padding: 11px 14px; margin-bottom: 14px;
  font-size: 12.5px; color: var(--cm-blue-tx); line-height: 1.6;
}
.cm-training-notice__icon {
  flex: none; width: 18px; height: 18px; margin-top: 2px; border-radius: 50%;
  background: var(--cm-brand); color: #fff; display: grid; place-items: center;
  font: 700 11px var(--cm-font-ui);
}
.cm-training-chip {
  display: inline-flex; align-items: center; font: 600 12.5px var(--cm-font-ui); color: var(--cm-blue-tx);
  background: var(--cm-mail-tint-bg); border: 1px solid var(--cm-mail-tint-border); border-radius: 999px; padding: 4px 12px;
}

/* Writing a practice application (design_handoff_postulation_redaction, screens 8b and 8f): a mail
   card, and nothing around it that looks like a form. The rows carry no field borders on purpose -
   what the student sees is the mail they are writing, not the boxes it is typed into. */
.cm-postul {
  background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 10px;
  overflow: hidden; font-family: var(--cm-font-ui);
}
.cm-postul__error {
  margin: 12px 18px 0; border: 1px solid var(--cm-red-tx); background: var(--cm-red-bg);
  color: var(--cm-red-tx); border-radius: 8px; padding: 9px 12px; font-size: 12.5px;
}
.cm-postul__fields { padding: 6px 18px; }
.cm-postul__row {
  display: flex; align-items: center; gap: 10px; padding: 11px 0;
  border-bottom: 1px solid var(--cm-border-soft); font-size: 13px;
}
.cm-postul__label { width: 64px; flex: none; color: var(--cm-faint); }
.cm-postul__input {
  flex: 1; min-width: 0; border: 0; background: none; padding: 0;
  font: 400 13px var(--cm-font-ui); color: var(--cm-ink);
}
.cm-postul__input:focus { outline: none; }
.cm-postul__textarea {
  display: block; width: 100%; border: 0; background: none; resize: vertical;
  padding: 15px 18px; font: 400 13px var(--cm-font-ui); color: var(--cm-text); line-height: 1.75;
}
.cm-postul__textarea:focus { outline: none; }
.cm-postul__signature {
  margin: 0 18px; border-top: 1px solid var(--cm-border-soft); padding: 12px 0;
  font-size: 12px; color: var(--cm-muted); line-height: 1.6;
}
.cm-postul__signature b { color: var(--cm-ink); }
.cm-postul__attachments {
  padding: 12px 18px; border-top: 1px solid var(--cm-border-soft);
  display: flex; flex-direction: column; gap: 10px;
}
.cm-postul__attachhead { display: flex; align-items: center; gap: 8px; }
.cm-postul__attachlabel { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-postul__send {
  margin-left: auto; border: 0; background: var(--cm-brand-strong); color: #fff;
  border-radius: 8px; padding: 10px 18px; font: 600 13px var(--cm-font-ui); cursor: pointer;
}
/* #0e405f, the handoff's own hover for this button - one notch below --cm-brand-strong, which no
   token carries. */
.cm-postul__send:hover { background: #0e405f; }
.cm-postul__chips { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cm-postul__chip {
  display: flex; align-items: center; gap: 7px;
  border: 1px solid var(--cm-border); background: var(--cm-surface-alt); border-radius: 8px;
  padding: 7px 12px; font-size: 12px; color: var(--cm-text);
}
.cm-postul__chipremove {
  border: 0; background: none; padding: 0; margin-left: 2px;
  color: var(--cm-faint); cursor: pointer; font-size: 14px; line-height: 1;
}
/* A file already sent reads as a link, because it is one: the remark asking for a correction is
   answered by opening the document it speaks about. */
.cm-postul__chiplink { color: inherit; text-decoration: none; border-bottom: 1px dotted currentColor; }
.cm-postul__chiplink:hover { color: var(--cm-brand-strong); }
.cm-postul__chipremove:hover { color: var(--cm-red-tx); }
/* Dashed, and the only way in: one entry point that takes any number of files, never a slot per
   kind of document. */
.cm-postul__attach {
  display: flex; align-items: center; gap: 7px;
  border: 1px dashed #b5c3ce; background: var(--cm-surface); border-radius: 8px;
  padding: 7px 12px; font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); cursor: pointer;
}
.cm-postul__attach:hover { border-color: var(--cm-brand); background: var(--cm-surface-alt); }
.cm-postul__file { display: none; }
.cm-postul__note { margin: 10px 0 0; font-size: 12px; color: var(--cm-faint); line-height: 1.55; }
.cm-postul__note a { color: var(--cm-brand-strong); }

[data-bs-theme=dark] .cm-postul__attach { border-color: var(--cm-border); }
/* --cm-brand-strong is the LIGHTER of the two blues in dark, so the handoff's pairing would put
   white on pale blue - dark takes --cm-brand instead, as every other primary button does. */
[data-bs-theme=dark] .cm-postul__send { background: var(--cm-brand); }
[data-bs-theme=dark] .cm-postul__send:hover { background: var(--cm-brand-strong); }

.cm-training-unlocked {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  border: 1px solid #bfe3d1; background: #e7f4ee; color: #1f7a54;
  border-radius: 10px; padding: 13px 16px; margin-bottom: 14px; font-size: 13px;
}
.cm-training-unlocked a { margin-left: auto; }

/* Screen 8e: the validator's answer beside the mail it speaks about. The chips of the files
   already sent carry no ×, because there is nothing to take back - joining a file replaces the
   list, it does not edit it. */
.cm-postul__chip--sent { color: var(--cm-muted); background: var(--cm-surface-soft); }
/* Screen 8d: who wrote the corrections the student is working on, and when. It sits at the head of
   the validation panel because it dates what the rest of the panel says - a validator reopening the
   application has to know whether they are reading their own words or somebody else's. */
.cm-training-feedback {
  margin: 12px 18px; padding: 9px 12px;
  border: 1px solid #f0e0c0; border-radius: 8px; background: #fffdf6;
  font-size: 12.5px; color: #8a6a1f; line-height: 1.5;
}
.cm-training-verdict__remark { margin: 7px 0 0; font-size: 12.5px; color: var(--cm-text); line-height: 1.55; }
.cm-training-verdict__remark--muted { color: var(--cm-faint); }
/* The state reads in the colour of what it asks for: green nothing to do, amber your turn, grey
   waiting on somebody else. */
.cm-training-verdict__state.is-refused { color: #8a6a1f; }
.cm-training-verdict__state.is-pending { color: var(--cm-faint); }

/* Screen 8c: the queue sits at the head of the class tracking page, in the amber of things that
   wait on somebody. */
.cm-training-queue { border: 1px solid #e0b25f; border-radius: var(--cm-radius); background: var(--cm-surface); overflow: hidden; }
.cm-training-queue__head { display: flex; align-items: center; gap: 10px; padding: 13px 18px; background: #fffdf6; border-bottom: 1px solid #f0e0c0; }
.cm-training-queue__count {
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  border: 1px solid #e0b25f; color: #8a6a1f; font: 600 11px var(--cm-font-ui);
}
.cm-training-queue__meta { margin-left: auto; font-size: 12px; color: var(--cm-faint); }

/* Screen 8d: one verdict block per element, the acquired ones locked shut. */
.cm-training-verdict { padding: 12px 18px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-training-verdict.is-validated { background: var(--cm-surface-soft); }
.cm-training-verdict__head { display: flex; align-items: center; gap: 10px; font-size: 13.5px; color: var(--cm-ink); }
.cm-training-verdict__state { margin-left: auto; font-size: 12px; color: #1f7a54; }
.cm-training-verdict__actions { display: flex; gap: 8px; margin: 8px 0; }
.cm-training-verdict__choice {
  display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--cm-border);
  border-radius: 8px; padding: 6px 12px; font: 600 12px var(--cm-font-ui); color: var(--cm-text); cursor: pointer;
}
.cm-training-verdict__choice:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-mail-tint-bg); color: var(--cm-brand-strong); }
/* The remark box is a block on its own, not an inline field: without a width it falls back to the
   textarea's default column count and leaves the right of the panel empty. */
.cm-training-verdict .cm-compose__textarea { display: block; width: 100%; }
.cm-training-previous {
  border: 1px solid #e0b25f; background: #fffdf6; color: #8a6a1f; border-radius: 8px;
  padding: 10px 13px; margin-bottom: 12px; font-size: 12.5px; line-height: 1.55;
}

/* The review panel is narrow: its footer stacks instead of squeezing the button to three lines. */
.cm-sheet__side .cm-compose__footer { flex-direction: column; align-items: stretch; gap: 10px; }
.cm-sheet__side .cm-compose__notes { margin-left: 0; text-align: left; }

/* -- Screen 7c: the offer form ---------------------------------------------
   Two panels side by side, as the mockup lays them out: what the offer says on the left, who sees
   it on the right. They stack below the tablet breakpoint, where side by side would squeeze the
   group chips into one per line. */
.cm-offerform { display: grid; grid-template-columns: 1.2fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 991.98px) { .cm-offerform { grid-template-columns: 1fr; } }
.cm-offerform__panel {
  background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  padding: 20px 24px; display: flex; flex-direction: column; gap: 16px;
}
.cm-offerform__error { border-radius: var(--cm-radius); margin-bottom: 16px; border: 1px solid #f0cec4; }
.cm-offerform__field { display: flex; flex-direction: column; gap: 6px; }
.cm-offerform__label { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-offerform__hint { font-size: 11.5px; color: var(--cm-faint); line-height: 1.5; }
.cm-offerform__input {
  border: 1px solid var(--cm-border); border-radius: 8px; padding: 9px 12px;
  font: 13.5px var(--cm-font-ui); color: var(--cm-ink); background: var(--cm-surface);
}
.cm-offerform__input:focus { outline: none; border-color: var(--cm-brand); }

.cm-offerform__dropzone {
  border: 1.5px dashed #a8cbe8; border-radius: var(--cm-radius); background: var(--cm-surface-alt);
  padding: 22px; display: flex; flex-direction: column; align-items: center; gap: 8px;
  text-align: center; cursor: pointer;
}
.cm-offerform__dropzone:hover, .cm-offerform__dropzone.is-dragover { background: var(--cm-mail-tint-bg); border-color: var(--cm-brand); }
.cm-offerform__dropzone-title { font-size: 13px; color: var(--cm-text); }
.cm-offerform__dropzone-title b { color: var(--cm-brand-strong); }
.cm-offerform__dropzone-hint { font-size: 11.5px; color: var(--cm-faint); }

.cm-offerform__bucket { display: flex; flex-direction: column; gap: 7px; }
.cm-offerform__bucket-label {
  font: 700 11px var(--cm-font-ui); letter-spacing: .5px; text-transform: uppercase; color: var(--cm-faint);
}
.cm-offerform__groups { display: flex; flex-wrap: wrap; gap: 8px; }
.cm-offerform__group {
  display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--cm-border);
  border-radius: 16px; padding: 6px 13px; font-size: 13px; color: var(--cm-ink); cursor: pointer;
}
.cm-offerform__group:hover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-offerform__group input { position: absolute; opacity: 0; width: 1px; height: 1px; }
/* The 14px square of the mockup: an empty outline until ticked, then the brand blue with its tick. */
.cm-offerform__box {
  width: 14px; height: 14px; flex: none; border-radius: 4px; border: 1.5px solid #b3c6d6;
  display: grid; place-items: center; font-size: 10px; color: transparent;
}
.cm-offerform__group:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-mail-tint-bg); color: var(--cm-brand-strong); font-weight: 600; }
.cm-offerform__group:has(input:checked) .cm-offerform__box { background: var(--cm-brand); border-color: var(--cm-brand); color: #fff; }

.cm-offerform__actions {
  margin-top: 16px; display: flex; align-items: center; gap: 10px;
  background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 13px 20px;
}
/* Both buttons travel together to the right, as one group - pushing only the first one left the
   other stranded in the middle of the bar. */
.cm-offerform__actions-group { margin-left: auto; display: flex; gap: 10px; }

[data-bs-theme=dark] .cm-training-check.is-refused,
[data-bs-theme=dark] .cm-training-previous,
[data-bs-theme=dark] .cm-training-feedback,
[data-bs-theme=dark] .cm-training-queue__head { background: #3c3117; }
[data-bs-theme=dark] .cm-training-feedback { border-color: #5a4a20; color: #e0b25f; }
[data-bs-theme=dark] .cm-training-unlocked { background: #1d3327; border-color: #2e5c44; color: #8cc3a2; }
/* #8a6a1f is amber read on paper: on the dark surface it sinks into the background, so the "your
   turn" state takes the same lighter amber the rest of the dark theme uses. */
[data-bs-theme=dark] .cm-training-verdict__state.is-refused { color: #e0b25f; }

/* ============================================================================
   Création d'un travail (2a) and the assignment list (2b)
   design/design_handoff_creation_travail — the measurements come from the
   handoff's README, which outranks the matching Tabler values.
   ============================================================================ */

/* ---------- 2a · The wizard ---------- */
.cm-wizard-card {
  background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 11px;
  overflow: hidden; display: flex; flex-direction: column;
}
.cm-wizard-form { display: flex; flex-direction: column; }
/* minmax(0, 1fr) and not 1fr: a 1fr column takes its content's intrinsic width as a floor, and a
   single over-wide production row then pushed the whole card off screen. */
.cm-wizard { display: grid; grid-template-columns: 230px minmax(0, 1fr); min-height: 440px; }

/* The step column: freely clickable, the state reads from the chip. */
.cm-wizard__rail {
  border-right: 1px solid var(--cm-border-soft); background: var(--cm-surface-soft);
  padding: 16px 12px; display: flex; flex-direction: column; gap: 4px;
}
.cm-wizard__step {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  border: 0; background: none; border-radius: var(--cm-radius-sm); padding: 9px 10px; cursor: pointer;
}
.cm-wizard__step:hover { background: var(--cm-surface-alt); }
.cm-wizard__step.is-current { background: var(--cm-blue-bg-soft); }
.cm-wizard__num {
  flex: none; width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  background: var(--cm-border-soft); color: var(--cm-faint); font: 600 11px var(--cm-font-ui);
}
.cm-wizard__step.is-current .cm-wizard__num { background: var(--cm-brand); color: #fff; }
/* A completed step carries a tick, not its number: the tick is what says « c'est fait ». */
.cm-wizard__step.is-done .cm-wizard__num { background: #1f7a54; color: #fff; font-size: 0; }
.cm-wizard__step.is-done .cm-wizard__num::after { content: '✓'; font-size: 11px; }
.cm-wizard__steptext { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.cm-wizard__steplabel { font: 600 12.5px var(--cm-font-ui); color: var(--cm-text); }
.cm-wizard__step.is-current .cm-wizard__steplabel { color: var(--cm-brand-strong); }
.cm-wizard__stepsub { font-size: 11px; color: var(--cm-faint); }

.cm-wizard__panel { padding: 20px 24px; display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.cm-wizard__step-panel { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
/* The rich editor of the instructions is one field among others, not the page: its height is set at
   mount time (data-hugerte-editor-height-value), all that is left here is to keep it from
   overflowing. */
.cm-wizard__panel .tox-hugerte { max-width: 100%; }
.cm-wizard__conditional { display: flex; flex-direction: column; gap: 8px; }
.cm-wizard__label {
  font: 600 12px var(--cm-font-ui); color: var(--cm-muted);
  letter-spacing: .4px; text-transform: uppercase;
}
.cm-wizard__label-note { font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint); text-transform: none; letter-spacing: 0; }
.cm-wizard__note { font-size: 11.5px; color: var(--cm-faint); }

/* Amber warning banner: missing class, multiple deadlines. */
.cm-wizard__banner {
  display: flex; align-items: flex-start; gap: 9px;
  border: 1px solid #eadfc3; background: #fdf6e8; color: #8a6a1f;
  border-radius: var(--cm-radius); padding: 10px 13px; font-size: 12.5px; line-height: 1.45;
}

.cm-wizard__foot {
  display: flex; align-items: center; gap: 8px;
  padding: 14px 22px; border-top: 1px solid var(--cm-border-soft);
}
.cm-wizard__hint { font-size: 12px; color: var(--cm-faint); flex: 1; }
.cm-wizard__foot .cm-btn:disabled { background: var(--cm-disabled); border-color: var(--cm-disabled); cursor: not-allowed; }

/* Chips to tick or to pick: classes, options, nature, grading. The radio or the checkbox
   disappears under the chip, which carries the state on its own. */
.cm-pillpick { display: flex; flex-wrap: wrap; gap: 6px; }
.cm-pillpick__item { display: inline-flex; margin: 0; cursor: pointer; }
.cm-pillpick__item input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-pillpick__item > span {
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text);
  border-radius: 999px; padding: 7px 14px; font: 600 12.5px var(--cm-font-ui);
}
.cm-pillpick__item:hover > span { border-color: var(--cm-brand); }
.cm-pillpick__item:has(input:checked) > span { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong); }
.cm-pillpick__item:has(input:focus-visible) > span { outline: 2px solid var(--cm-brand); outline-offset: 2px; }
/* The Bootstrap theme wraps each radio in a .form-check with its own label (and its required-field
   asterisk): the chip already carries its own, so the wrapper is blanked out. Same treatment as
   .cm-nature-card. */
.cm-pillpick__item .form-check, .cm-segment .form-check, .cm-typecard .form-check,
.cm-visibility .cm-radiocard .form-check { display: contents; }
.cm-pillpick__item .form-check-label, .cm-segment .form-check-label, .cm-typecard .form-check-label,
.cm-visibility .cm-radiocard .form-check-label { display: none; }

/* The four « au sein de la classe » segments: square cards, not chips. */
.cm-segment-row { display: flex; flex-wrap: wrap; gap: 8px; }
.cm-segment { display: inline-flex; margin: 0; cursor: pointer; }
.cm-segment input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-segment > span {
  border: 1.5px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text);
  border-radius: var(--cm-radius-sm); padding: 8px 14px; font: 600 12.5px var(--cm-font-ui);
}
.cm-segment:hover > span { border-color: var(--cm-brand); }
.cm-segment:has(input:checked) > span { border-color: var(--cm-brand); background: #f6fafe; color: var(--cm-brand-strong); }
.cm-audience-pane { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }

/* Student selection: a tick list with its own search, the class counting some thirty students -
   no need to go to the server for them. */
.cm-studentpick { display: flex; flex-direction: column; border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius); overflow: hidden; }
.cm-studentpick__head {
  display: flex; align-items: center; gap: 10px; padding: 9px 14px;
  border-bottom: 1px solid var(--cm-border-faint); background: var(--cm-surface-soft);
}
.cm-studentpick__search { flex: 1; min-width: 0; border: 0; background: none; outline: none; font-size: 12.5px; color: var(--cm-ink); }
.cm-studentpick__count { font: 600 11px var(--cm-font-ui); color: var(--cm-brand-strong); background: var(--cm-blue-bg-soft); border-radius: 9px; padding: 2px 9px; }
.cm-studentpick__list { max-height: 260px; overflow-y: auto; }
.cm-studentpick__row {
  display: flex; align-items: center; gap: 10px; margin: 0;
  padding: 9px 14px; border-bottom: 1px solid var(--cm-border-faint); cursor: pointer;
}
.cm-studentpick__row:last-child { border-bottom: 0; }
.cm-studentpick__row:hover { background: var(--cm-surface-alt); }
.cm-studentpick__row input { accent-color: var(--cm-brand); margin: 0; }
.cm-studentpick__name { font-size: 13px; color: var(--cm-muted); }
.cm-studentpick__row:has(input:checked) .cm-studentpick__name { font-weight: 600; color: var(--cm-ink); }
/* A student from another class, or filtered out by the search: out of the list without leaving the
   DOM, so their checkbox is still submitted if it happened to be ticked. */
.cm-studentpick__row.is-off-program, .cm-studentpick__row.is-filtered { display: none; }

.cm-groupchips { display: flex; flex-wrap: wrap; gap: 8px; }
.cm-groupchip {
  border: 1px solid var(--cm-border-soft); background: var(--cm-surface-soft);
  border-radius: 9px; padding: 8px 12px; font-size: 12px; color: var(--cm-text);
}
.cm-inline-field { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; font-size: 13px; color: var(--cm-text); }
.cm-inline-field__label { font-size: 13px; color: var(--cm-text); }
.cm-inline-field__hint { font-size: 11.5px; color: var(--cm-faint); }

/* Grid of the seven work types: three columns of cards, without grey help text - the mockup does
   not want any. */
.cm-typegrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.cm-typecard { border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 12px 14px; margin: 0; cursor: pointer; }
.cm-typecard input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-typecard__label { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-typecard:hover { border-color: var(--cm-brand); }
.cm-typecard:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); }
.cm-typecard:has(input:checked) .cm-typecard__label { color: var(--cm-brand-strong); }

/* Green switch: the mockup's says "on", not "selected". */
.cm-toggle--green:has(input:checked) .cm-toggle__track { background: #1f7a54; }
/* A switch rendered from a Symfony checkbox: the Bootstrap theme wraps it in a .form-check carrying
   its own label, while the row already states its state in words next to the track. We erase the
   wrapper, as for .cm-pillpick__item.
   Beware: this very wrapper is what breaks an adjacent-sibling selector on the input - see the
   warning on .cm-toggle further up. */
.cm-toggle .form-check { display: contents; }
.cm-toggle .form-check-label { display: none; }
.cm-toggle input.form-check-input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }

.cm-input {
  width: 100%; border: 1px solid var(--cm-border); border-radius: 9px; padding: 10px 13px;
  font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); background: var(--cm-surface); outline: none;
}
.cm-input:focus { border-color: var(--cm-brand); }
.cm-input::placeholder { font-weight: 400; color: var(--cm-faint); }
.cm-input--date { font-weight: 400; font-size: 13px; }
.cm-select {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); padding: 9px 10px;
  font: 600 12.5px var(--cm-font-ui); color: var(--cm-text); background: var(--cm-surface); cursor: pointer; outline: none;
}
.cm-select:focus { border-color: var(--cm-brand); }
.cm-select--time { border-radius: 999px; padding: 7px 13px; }

/* Supports and examples: chips of the files/links kept, and the drop zone below them. */
.cm-attachpick { display: flex; flex-direction: column; gap: 8px; }
.cm-dropzone {
  border: 1.5px dashed var(--cm-disabled); border-radius: 9px; padding: 10px;
  text-align: center; color: var(--cm-faint); font-size: 12.5px; background: var(--cm-surface-soft);
}
.cm-dropzone.is-hover { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); }
.cm-linkish { border: 0; background: none; padding: 0; font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); cursor: pointer; text-decoration: underline; }
/* A support already attached that is being detached: the chip is struck through instead of
   disappearing - its checkbox must stay in the form until saving, and one must be able to change
   one's mind before that. */
.cm-chip--kept { cursor: pointer; }
.cm-chip--kept input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cm-chip--kept.is-dropped { opacity: .55; }
.cm-chip--kept.is-dropped > span:first-of-type { text-decoration: line-through; }

/* Expected productions: one row per production. A deadline of its own turns the row amber - that is
   the only thing that tells the two cases apart at a glance. */
.cm-prod-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.cm-prod-row__name { flex: 1; min-width: 0; font-size: 13px; padding: 9px 12px; }
/* Fixed widths: these three controls are read as columns from one row to the next, and the
   Bootstrap theme would otherwise stretch each <select> to the full available width. */
.cm-prod-row__format { flex: none; width: 140px; }
.cm-prod-row__mode { flex: none; width: 200px; }
.cm-prod-row__date { flex: none; width: 195px; }
.cm-prod-row.is-custom-due .cm-prod-row__mode,
.cm-prod-row.is-custom-due .cm-prod-row__date { border-color: #c9a04e; background: #fdf6e8; color: #8a6a1f; }
.cm-prod-row__remove { border: 0; background: none; color: var(--cm-faint); font-size: 15px; line-height: 1; cursor: pointer; padding: 4px; flex: none; }
.cm-prod-row__remove:hover { color: var(--cm-red-tx); }
.cm-dashed-add {
  align-self: flex-start; border: 1px dashed var(--cm-disabled); background: var(--cm-surface-soft);
  color: var(--cm-brand-strong); border-radius: 9px; padding: 7px 14px; font: 600 12.5px var(--cm-font-ui); cursor: pointer;
}
.cm-dashed-add:hover { background: var(--cm-blue-bg-soft); }

.cm-visibility { display: flex; flex-direction: column; gap: 7px; }

/* Publication confirmation screen. */
.cm-published { padding: 46px 40px; display: flex; flex-direction: column; align-items: center; gap: 12px; text-align: center; }
.cm-published__check { width: 52px; height: 52px; border-radius: 50%; background: #e9f4ee; color: #1f7a54; display: grid; place-items: center; font-size: 24px; }
.cm-published__title { font: 600 19px var(--cm-font-title); color: var(--cm-ink); }
.cm-published__summary { font-size: 13px; color: var(--cm-muted); max-width: 560px; line-height: 1.5; margin: 0; }
.cm-published__actions { display: flex; gap: 9px; margin-top: 6px; }

/* Mounted as a modal or as a panel: the same card, laid over the calling screen. */
.cm-wizard-shell {
  position: fixed; background: var(--cm-surface); z-index: 1031;
  display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 24px 60px rgba(0, 0, 0, .28);
}
.cm-wizard-shell--modal {
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 980px; max-width: calc(100vw - 40px); max-height: calc(100vh - 80px); border-radius: 11px;
}
.cm-wizard-shell--panel { top: 0; right: 0; bottom: 0; width: 900px; max-width: 100vw; }
.cm-wizard-shell__header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 22px; border-bottom: 1px solid var(--cm-border-soft); flex: none;
}
.cm-wizard-shell__title { font: 600 17px var(--cm-font-title); color: var(--cm-ink); }
.cm-wizard-shell .cm-wizard-card { border: 0; border-radius: 0; flex: 1; min-height: 0; overflow-y: auto; }

/* ---------- 2b · The assignment list ---------- */
/* The toolbar lives on the page background, above the card - not at the head of the table. */
.cm-worktoolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.cm-worktoolbar__search {
  display: flex; align-items: center; gap: 8px; width: 300px;
  border: 1px solid var(--cm-border); background: var(--cm-surface); border-radius: var(--cm-radius-sm);
  padding: 9px 13px; color: var(--cm-faint);
}
.cm-worktoolbar__search input { flex: 1; min-width: 0; border: 0; background: none; outline: none; font-size: 13px; color: var(--cm-ink); }
.cm-worktoolbar__select {
  appearance: none; -webkit-appearance: none;
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text);
  border-radius: var(--cm-radius-sm); padding: 9px 30px 9px 14px; font: 600 13px var(--cm-font-ui); cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath d='M1 1.5 5 5.5 9 1.5' fill='none' stroke='%238a99a6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center;
}
.cm-worktoolbar__select:hover { color: var(--cm-brand-strong); }

.cm-worktable { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 11px; overflow: hidden; }
.cm-worktable__table { width: 100%; border-collapse: collapse; }
.cm-worktable__table thead th {
  padding: 10px 18px; border-bottom: 1px solid var(--cm-border-soft); text-align: left;
  font: 600 11px var(--cm-font-ui); color: var(--cm-faint); letter-spacing: .4px; text-transform: uppercase;
}
.cm-worktable__table tbody td { padding: 13px 18px; border-bottom: 1px solid var(--cm-border-faint); vertical-align: middle; }
.cm-worktable__table tbody tr:last-child td { border-bottom: 0; }
.cm-worktable__due { width: 105px; }
.cm-worktable__type { width: 100px; font: 600 12px var(--cm-font-ui); color: var(--cm-text); }
.cm-worktable__class { width: 130px; }
.cm-worktable__progress { width: 190px; }
.cm-worktable__action { width: 96px; text-align: right; }
.cm-worktable__action a { font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-worktable__action a:hover { text-decoration: underline; }
.cm-worktable__empty { color: var(--cm-faint); font-size: 13px; }

/* An assignment's state reads from the color of its deadline, the whole row turning pink once it
   has passed - it is the only row that calls for an action right away. */
.cm-workrow__date { display: block; font: 600 12.5px var(--cm-font-ui); color: #12344d; line-height: 1.25; }
.cm-workrow__time { display: block; font-size: 10.5px; color: inherit; }
.cm-workrow--overdue { background: #fdf8f7; }
.cm-workrow--overdue .cm-workrow__date, .cm-workrow--overdue .cm-workrow__time { color: var(--cm-red-tx); }
.cm-workrow--imminent .cm-workrow__date, .cm-workrow--imminent .cm-workrow__time { color: #8a6a1f; }
.cm-workrow--hidden .cm-workrow__date, .cm-workrow--hidden .cm-workrow__time { color: var(--cm-faint); }
.cm-workrow__title { display: block; font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-workrow__meta { display: block; font-size: 11.5px; color: var(--cm-faint); }
.cm-workrow__progress { font: 600 12px var(--cm-font-ui); color: var(--cm-text); }
.cm-workrow__progress.is-alert { color: var(--cm-red-tx); }
.cm-workrow__badge {
  display: inline-flex; padding: 3px 10px; border-radius: 999px;
  background: var(--cm-bg); color: var(--cm-muted); font: 600 11.5px var(--cm-font-ui);
}
.cm-classbadge {
  display: inline-flex; padding: 3px 10px; border-radius: 999px; white-space: nowrap;
  background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong); font: 600 11.5px var(--cm-font-ui);
}
/* Amber as soon as the assignment targets only part of the class: the nuance reads without opening it. */
.cm-classbadge--targeted { background: #fdf6e8; color: #8a6a1f; }

.cm-worksummary { display: flex; flex-wrap: wrap; gap: 28px; }
.cm-worksummary__label { display: block; font: 600 11px var(--cm-font-ui); color: var(--cm-faint); letter-spacing: .4px; text-transform: uppercase; }
.cm-worksummary__value { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-worksummary__own-due { color: #8a6a1f; font-weight: 600; }

@media (max-width: 992px) {
  .cm-wizard { grid-template-columns: 1fr; }
  .cm-wizard__rail { flex-direction: row; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--cm-border-soft); }
  .cm-typegrid { grid-template-columns: 1fr; }
  .cm-prod-row { flex-wrap: wrap; }
}

[data-bs-theme=dark] .cm-wizard__banner,
[data-bs-theme=dark] .cm-prod-row.is-custom-due .cm-prod-row__mode,
[data-bs-theme=dark] .cm-prod-row.is-custom-due .cm-prod-row__date,
[data-bs-theme=dark] .cm-classbadge--targeted { background: #3c3117; border-color: #7a5417; color: #e0bc6d; }
[data-bs-theme=dark] .cm-workrow--overdue { background: #2a1a17; }
[data-bs-theme=dark] .cm-workrow__date { color: var(--cm-ink); }
[data-bs-theme=dark] .cm-published__check { background: #1d3327; color: #8cc3a2; }
[data-bs-theme=dark] .cm-segment:has(input:checked) > span { background: var(--cm-blue-bg-soft); }

/* ==========================================================================
   Travail à faire (student) - design_handoff_travail_a_faire, screens 3a/3b/3c.
   The handoff gives its colors literally and calls them contractual, so the reds
   and greens below are its own hexes rather than the app's --cm-red-tx and
   --cm-green-tx pairs, which sit a shade darker. Kept in one block, prefixed
   .cm-tw-, so the difference is visibly local to this screen, not a theme drift.
   ========================================================================== */

.cm-tw { display: grid; grid-template-columns: minmax(0, 880px) 320px; gap: 24px; align-items: start; }
.cm-tw--single { grid-template-columns: minmax(0, 880px); }
.cm-tw__list { display: flex; flex-direction: column; gap: 9px; min-width: 0; }
.cm-tw__list--history { gap: 8px; }
.cm-tw__side { display: flex; flex-direction: column; gap: 9px; }
.cm-tw__sidehead { display: flex; align-items: baseline; gap: 8px; }
.cm-tw__sidehead a { margin-left: auto; font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-tw__sidehead a:hover { text-decoration: underline; }
.cm-tw__group { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); letter-spacing: .4px; text-transform: uppercase; }
.cm-tw__group--late { color: #b8493d; }
.cm-tw__group--spaced { margin-top: 6px; }
.cm-tw__empty {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius); background: var(--cm-surface);
  padding: 18px; font-size: 13px; color: var(--cm-faint);
}
.cm-tw__empty--side { padding: 14px; font-size: 12px; }

.cm-tw-filter {
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text);
  border-radius: var(--cm-radius-sm); padding: 9px 16px; font: 600 13px var(--cm-font-ui); cursor: pointer;
}
.cm-tw-filter:hover { background: var(--cm-bg); color: var(--cm-brand-strong); }

/* The date rule: one per day, whatever the number of rows that follow it. */
.cm-tw-daysep { display: flex; align-items: center; gap: 10px; }
.cm-tw-daysep__rule { flex: 1; height: 1px; background: var(--cm-border); }
.cm-tw-daysep__label { font: 700 12.5px var(--cm-font-ui); color: var(--cm-navy); text-transform: uppercase; letter-spacing: .6px; }
.cm-tw-daysep--late .cm-tw-daysep__rule { background: #ecc8c3; }
.cm-tw-daysep--late .cm-tw-daysep__label { color: #b8493d; }

.cm-tw-row {
  display: flex; align-items: center; gap: 14px;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  padding: 12px 16px 12px 12px; background: var(--cm-surface);
}
.cm-tw-row__time { flex: none; width: 46px; text-align: center; font: 600 13px var(--cm-font-ui); color: var(--cm-muted); }
.cm-tw-row__rule { flex: none; width: 1px; align-self: stretch; background: var(--cm-border-soft); }
.cm-tw-row__body { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.cm-tw-row__head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.cm-tw-row__title { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
/* The expected production a line stands for, when the assignment asks for several: read as a
   continuation of the title, one notch quieter, so that three lines of the same work still read as
   one work seen three times. */
.cm-tw-row__production { font: 500 13px var(--cm-font-ui); color: var(--cm-muted); }
.cm-tw-row__production::before { content: '— '; color: var(--cm-faint); }
.cm-tw-row__meta { font-size: 12px; color: var(--cm-faint); }
.cm-tw-row__actions { display: flex; align-items: center; gap: 10px; flex: none; }

.cm-tw-row--late { background: #fdf3f2; border-color: #ecc8c3; }
.cm-tw-row--late .cm-tw-row__rule { background: #ecc8c3; }
.cm-tw-row--submitted { background: #f7fbf8; border-color: #cfe3d4; }
.cm-tw-row--submitted .cm-tw-row__rule { background: #dcebe1; }
/* Set aside: dashed outline and everything dimmed - present, but no longer asking for anything. */
.cm-tw-row--dismissed { background: #f7f9fa; border-style: dashed; }
.cm-tw-row--dismissed .cm-tw-row__time { color: #b3bec7; }
.cm-tw-row--dismissed .cm-tw-row__title,
.cm-tw-row--dismissed .cm-tw-row__production { color: var(--cm-faint); }
.cm-tw-row--dismissed .cm-tw-row__meta { color: #b3bec7; }
.cm-tw-row--dismissed .cm-tw-link { color: var(--cm-faint); }

.cm-tw-link {
  border: 0; background: transparent; padding: 0;
  font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: underline; cursor: pointer;
}
.cm-tw-link:hover { color: var(--cm-brand); }

.cm-tw-tag {
  flex: none; white-space: nowrap; display: inline-flex; align-items: center;
  padding: 2px 9px; border-radius: 999px; font: 600 11px var(--cm-font-ui);
}
.cm-tw-tag--done { background: #e9f4ee; color: #1f7a54; }
.cm-tw-tag--muted { background: #eceff2; color: var(--cm-faint); }
.cm-tw-tag--missed { background: #f7e5e2; color: #b8493d; }

.cm-tw-btn {
  border: 0; border-radius: var(--cm-radius-sm); padding: 8px 15px;
  font: 600 12.5px var(--cm-font-ui); cursor: pointer; white-space: nowrap; text-decoration: none;
  display: inline-flex; align-items: center;
}
.cm-tw-btn--primary { background: var(--cm-brand); color: #fff; }
.cm-tw-btn--primary:hover { background: var(--cm-brand-strong); color: #fff; }
.cm-tw-btn--ghost { border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text); }
.cm-tw-btn--ghost:hover { background: var(--cm-bg); color: var(--cm-brand-strong); }
.cm-tw-btn--danger { background: #b8493d; color: #fff; }
.cm-tw-btn--danger:hover { background: #a43e2e; color: #fff; }
.cm-tw-btn--sm { padding: 7px 13px; }
.cm-tw-row--submitted .cm-tw-btn--ghost { border-color: #cfe3d4; }

/* "Ignorer" is deliberately the quietest thing on the row - it only reddens on hover. */
.cm-tw-ignore {
  border: 0; background: transparent; color: var(--cm-faint);
  border-radius: var(--cm-radius-sm); padding: 8px 6px; font: 600 12px var(--cm-font-ui); cursor: pointer;
}
.cm-tw-ignore:hover { color: #b8493d; }

.cm-tw-submit { display: contents; }
.cm-tw-submit__input { display: none; }
.cm-tw-dismiss { position: relative; display: flex; }

.cm-tw-pop {
  display: none; position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; width: 264px;
  background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  box-shadow: 0 14px 30px rgba(18, 52, 77, .18); padding: 14px 16px; flex-direction: column; gap: 10px;
}
.cm-tw-pop.is-open { display: flex; }
.cm-tw-pop__title { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tw-pop__text { font-size: 12.5px; line-height: 1.5; color: var(--cm-muted); }
.cm-tw-pop__actions { display: flex; justify-content: flex-end; gap: 8px; }

.cm-tw-recent {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 12px 14px;
  background: #fbfdfe; display: flex; flex-direction: column; gap: 6px;
}
.cm-tw-recent--missed { border-color: #ecc8c3; background: #fdf3f2; }
.cm-tw-recent__head { display: flex; align-items: center; gap: 8px; }
.cm-tw-recent__icon {
  flex: none; width: 22px; height: 22px; border-radius: 6px;
  background: #e9f4ee; color: #1f7a54; display: grid; place-items: center; font-size: 12px;
}
.cm-tw-recent--missed .cm-tw-recent__icon { background: #f7e5e2; color: #b8493d; }
.cm-tw-recent__title { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); flex: 1; min-width: 0; }
.cm-tw-recent__meta { font-size: 11.5px; color: var(--cm-faint); padding-left: 30px; }

/* ---- The consigne modal (3b) ---- */
.cm-tw-modal {
  display: none; position: fixed; inset: 0; z-index: 1055;
  background: rgba(18, 52, 77, .34); padding: 44px 16px; overflow-y: auto;
  justify-content: center; align-items: flex-start;
}
.cm-tw-modal.is-open { display: flex; }
.cm-tw-modal__panel {
  width: 620px; max-width: 100%; background: var(--cm-surface);
  border: 1px solid var(--cm-border); border-radius: 12px; box-shadow: 0 24px 50px rgba(18, 52, 77, .28);
  display: flex; flex-direction: column; overflow: hidden;
}
.cm-tw-modal__header { padding: 18px 22px 16px; border-bottom: 1px solid var(--cm-border-soft); display: flex; align-items: flex-start; gap: 12px; }
.cm-tw-modal__heading { min-width: 0; flex: 1; }
.cm-tw-modal__title { font: 600 17px var(--cm-font-title); color: var(--cm-ink); }
.cm-tw-modal__sub { font-size: 12.5px; color: var(--cm-faint); margin-top: 4px; }
.cm-tw-modal__close {
  flex: none; display: grid; place-items: center; width: 28px; height: 28px;
  border: 0; border-radius: 7px; background: transparent; color: var(--cm-faint); cursor: pointer;
}
.cm-tw-modal__close:hover { background: var(--cm-bg); color: var(--cm-ink); }
.cm-tw-modal__body { padding: 18px 22px; display: flex; flex-direction: column; gap: 16px; }
.cm-tw-modal__brief { font-size: 13.5px; line-height: 1.65; color: var(--cm-text); }
.cm-tw-modal__brief > :first-child { margin-top: 0; }
.cm-tw-modal__brief > :last-child { margin-bottom: 0; }
.cm-tw-modal__section { display: flex; flex-direction: column; gap: 8px; }
.cm-tw-modal__label { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); letter-spacing: .4px; text-transform: uppercase; }
.cm-tw-modal__footer { padding: 14px 22px; border-top: 1px solid var(--cm-border-soft); display: flex; align-items: center; gap: 8px; }
.cm-tw-modal__given { font-size: 12px; color: var(--cm-faint); }
.cm-tw-modal__footer .cm-tw-btn { margin-left: auto; padding: 9px 16px; font-size: 13px; }

.cm-tw-deposit {
  display: flex; align-items: center; gap: 11px;
  border: 1px solid var(--cm-border); border-radius: 9px; padding: 10px 13px;
}
.cm-tw-deposit--done { border-color: #cfe3d4; background: #f7fbf8; }
.cm-tw-deposit--done .cm-tw-btn--ghost { border-color: #cfe3d4; }
.cm-tw-deposit__body { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.cm-tw-deposit__head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.cm-tw-deposit__name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); min-width: 0; }
.cm-tw-deposit__meta { font-size: 11.5px; color: var(--cm-faint); }
.cm-tw-deposit .cm-tw-btn { flex: none; padding: 7px 14px; font-size: 12px; }

.cm-tw-attachment {
  display: flex; align-items: center; gap: 11px;
  border: 1px solid var(--cm-border); border-radius: 9px; padding: 10px 13px;
}
.cm-tw-attachment__kind {
  flex: none; width: 32px; height: 32px; border-radius: var(--cm-radius-sm);
  display: grid; place-items: center; font: 700 9.5px var(--cm-font-ui);
  background: var(--cm-bg); color: var(--cm-muted);
}
.cm-tw-attachment__kind--pdf { background: #fdf3f2; color: #b8493d; }
.cm-tw-attachment__kind--link { background: #eef5fb; color: var(--cm-brand-strong); }
.cm-tw-attachment__body { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.cm-tw-attachment__name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tw-attachment__meta { font-size: 11.5px; color: var(--cm-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-tw-attachment .cm-tw-link { flex: none; font-size: 12.5px; text-decoration: none; }
.cm-tw-attachment .cm-tw-link:hover { text-decoration: underline; }

/* "Objectif minimum" (2a): the box holds the number and its unit together. */
.cm-suffix-field {
  display: inline-flex; align-items: baseline; gap: 5px; width: max-content;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
  padding: 8px 12px; background: var(--cm-surface);
}
.cm-suffix-field:focus-within { border-color: var(--cm-brand); }
.cm-suffix-field .cm-input { border: 0; padding: 0; width: 4.5em; border-radius: 0; }
.cm-suffix-field__unit { font: 400 13px var(--cm-font-ui); color: var(--cm-faint); }
.cm-wizard__label--spaced { margin-top: 6px; }

@media (max-width: 1200px) {
  .cm-tw { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
  .cm-tw-row { flex-wrap: wrap; }
  .cm-tw-row__actions { width: 100%; justify-content: flex-end; }
}

[data-bs-theme=dark] .cm-tw-row--late,
[data-bs-theme=dark] .cm-tw-recent--missed { background: #2a1a17; border-color: #7a3a30; }
[data-bs-theme=dark] .cm-tw-row--submitted,
[data-bs-theme=dark] .cm-tw-deposit--done { background: #17281f; border-color: #2f5a43; }
[data-bs-theme=dark] .cm-tw-row--dismissed,
[data-bs-theme=dark] .cm-tw-recent { background: var(--cm-surface-alt); }
[data-bs-theme=dark] .cm-tw-tag--done { background: #1d3327; color: #8cc3a2; }
[data-bs-theme=dark] .cm-tw-tag--missed,
[data-bs-theme=dark] .cm-tw-attachment__kind--pdf { background: #3a211d; color: #e0a094; }
[data-bs-theme=dark] .cm-tw-recent__icon { background: #1d3327; color: #8cc3a2; }
[data-bs-theme=dark] .cm-tw-recent--missed .cm-tw-recent__icon { background: #3a211d; color: #e0a094; }

/* ---------------------------------------------------------------------------------------------
   e-CO — parcours list (mockup design/design_handoff_eco/screenshots/web-1d-parcours.png).

   A table of its own rather than a dressed-up .card/.card-table: the e-CO mockup gives its tables
   brand-blue column headers underlined by a 2px rule and zebra-striped rows, where the rest of the
   application keeps grey headers with no striping (see the comments on ".card-table thead th" and
   on ".table" further up, which come from the UFA handoff). The two conventions therefore coexist,
   each on its own scope, instead of one distorting the other.

   Everything that already exists elsewhere, however, is reused as is and NOT restated in the
   e-CO mockup's colours: the row actions keep .cm-action--positive/--warning/--neutral/--danger
   (the UFA handoff's blue for "Configurer", not the mockup's gold) and the status pills keep the
   application's gold and green tint variables. This screen owns no palette of its own.

   The mockup draws each row as a "2fr .9fr 1.4fr 1fr 1fr 320px" grid with 16px of inner padding on
   both sides; a grid with no gap does not separate its columns, so only the edge cells carry that
   padding here. The widths are converted to percentages of the table width, measured on the mockup:
   16 + 2fr = 319px out of 1308, i.e. 24.42%, and so on up to 320 + 16 = 336px, i.e. 25.70%. The
   mockup's proportions therefore hold at any width - the application's container is narrower than
   the mockup (container-xl's 1140px against 1308) and everything scales together.

   Emphatically NO calc() mixing % and px ("calc((100% - 352px) * 2 / 6.3)", which would express the
   grid literally): Chrome silently ignores it on a table column under table-layout: fixed and falls
   back to equal shares - verified, all six columns came out the same width. A bare percentage, on
   the other hand, is honoured.
   --------------------------------------------------------------------------------------------- */
.cm-eco-list {
    background: var(--cm-surface); border: 1px solid var(--cm-border);
    border-radius: var(--cm-radius); overflow: hidden;
}
.cm-eco-list__table {
    width: 100%; min-width: 860px; table-layout: fixed; border-collapse: collapse;
    font: 400 13.5px var(--cm-font-ui); color: var(--cm-ink);
}
.cm-eco-list__col-name        { width: 24.42%; }
.cm-eco-list__col-checkpoints { width: 10.44%; }
.cm-eco-list__col-status      { width: 16.24%; }
.cm-eco-list__col-courses,
.cm-eco-list__col-date        { width: 11.60%; }
.cm-eco-list__col-actions     { width: 25.70%; }
.cm-eco-list__table thead th.cm-eco-list__col-actions { text-align: right; }

.cm-eco-list__table thead th {
    background: var(--cm-surface-alt); color: var(--cm-blue-tx);
    font: 600 11.5px var(--cm-font-ui); text-transform: uppercase; letter-spacing: .4px;
    border-bottom: 2px solid var(--cm-brand);
    padding: 10px 0; text-align: left;
}
.cm-eco-list__table tbody td {
    padding: 11px 0; vertical-align: middle;
    border-bottom: 1px solid var(--cm-border-soft);
}
.cm-eco-list__table th:first-child, .cm-eco-list__table td:first-child { padding-left: 16px; }
.cm-eco-list__table th:last-child, .cm-eco-list__table td:last-child { padding-right: 16px; }
/* The footer's rule takes over under the last row, as in the mockup. */
.cm-eco-list__table tbody tr:last-child td { border-bottom: 0; }
.cm-eco-list__table tbody tr:nth-child(even) { background: var(--cm-surface-alt); }
.cm-eco-list__table tbody tr:hover { background: var(--cm-blue-bg); }

.cm-eco-list__name { font-weight: 600; }
.cm-eco-list__date { color: var(--cm-muted); }
.cm-eco-list__empty { color: var(--cm-faint); }
.cm-eco-list__foot {
    padding: 10px 16px; border-top: 1px solid var(--cm-border-soft);
    font-size: 13px; color: var(--cm-faint);
}

/* Status pill. The glyph belongs to the drawing, not to the text: "●" for a ready parcours, "◐" for
   a partly located one, "○" for a draft - hence the ::before rather than a character glued into the
   translations.

   The tints are the application's own pill variables (gold and green, cf. .cm-audio-pill), not the
   values specific to the e-CO mockup: they follow the dark theme with no local palette to maintain.
   Borders are derived from the tint with color-mix, like those of .cm-note and .cm-note--gold above.

   Careful when editing this comment: an asterisk immediately followed by a slash - as one would
   write to name two variables with a free suffix - closes the comment and makes the rule below
   disappear. It happened right here. */
.cm-eco-pill {
    display: inline-block; border-radius: 10px; padding: 2px 10px;
    font: 600 11.5px var(--cm-font-ui); border: 1px solid transparent;
}
.cm-eco-pill::before { content: '○\00a0'; }
.cm-eco-pill--draft { background: var(--cm-border-soft); border-color: var(--cm-border); color: var(--cm-muted); }
.cm-eco-pill--to_locate {
    background: var(--cm-gold-bg); color: var(--cm-gold-tx);
    border-color: color-mix(in srgb, var(--cm-gold) 45%, var(--cm-border));
}
.cm-eco-pill--to_locate::before { content: '◐\00a0'; }
.cm-eco-pill--ready {
    background: var(--cm-green-bg); color: var(--cm-green-tx);
    border-color: color-mix(in srgb, var(--cm-green-tx) 30%, var(--cm-border));
}
.cm-eco-pill--ready::before { content: '●\00a0'; }

/* ---------------------------------------------------------------------------------------------
   e-CO — map of a runner's course (Résultats screen, assets/controllers/eco_result_map_controller.js).
   The markers are Leaflet divIcons: no image from the Leaflet CSS is used.
   --------------------------------------------------------------------------------------------- */
.eco-map { height: 380px; border-bottom-left-radius: inherit; border-bottom-right-radius: inherit; }
.eco-map .leaflet-container { font: 400 12px var(--cm-font-ui); }

.eco-map__checkpoint-dot {
  display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%;
  background: var(--cm-surface); border: 2px solid var(--cm-faint); color: var(--cm-faint);
  font: 700 12px var(--cm-font-ui); box-shadow: 0 1px 4px rgba(18, 52, 77, .35);
}
/* Validated by this runner: the checkpoint reads in green, like the table just below. */
.eco-map__checkpoint-dot.is-validated { border-color: #1f7a54; color: #1f7a54; }

/* Parcours map (1e): a located checkpoint is filled in, Départ and Arrivée keep the navy. */
.eco-map__checkpoint-dot--located { background: var(--cm-positive-tx); border-color: var(--cm-positive-tx); color: #fff; }
.eco-map__checkpoint-dot--anchor {
  background: var(--cm-brand-strong); border-color: var(--cm-brand-strong); color: #fff;
  outline: 3px solid rgba(18, 80, 126, .25);
}

.eco-map__arrow svg { width: 14px; height: 14px; overflow: visible; }

/* Live tracking (1h): one pill per runner, at their last known position. */
.eco-map__runner-pill {
  display: inline-block; white-space: nowrap; padding: 3px 9px; border-radius: 12px;
  font: 700 11px var(--cm-font-ui); box-shadow: 0 2px 8px rgba(18, 52, 77, .35);
}
.eco-map__runner-pill--racing { background: var(--cm-brand); color: #fff; }
.eco-map__runner-pill--stale { background: var(--cm-gold); color: var(--cm-navy); }
.eco-map__runner-pill--sos { background: var(--cm-red-tx); color: #fff; box-shadow: 0 0 0 4px rgba(164, 62, 46, .25); }
/* The pill sizes itself on its text: Leaflet centres a size-less divIcon on its own point. */
.eco-map__runner { width: auto !important; height: auto !important; margin: -11px 0 0 -20px; }

/* Performance analysis: stops, refused scans and the map legend. */
.eco-map__refused-mark {
  display: grid; place-items: center; width: 20px; height: 20px; border-radius: 50%;
  background: #fdf3f2; border: 2px solid #b8493d; color: #b8493d;
  font: 700 11px var(--cm-font-ui); box-shadow: 0 1px 4px rgba(18, 52, 77, .3);
}
.eco-map__legend { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; font-size: 12px; color: var(--cm-muted); }
.eco-map__legend-item { display: inline-flex; align-items: center; gap: 6px; }
.eco-map__legend-swatch { width: 16px; height: 3px; border-radius: 2px; background: var(--cm-brand); }
.eco-map__legend-swatch--compared { background: repeating-linear-gradient(90deg, #6b4f8c 0 5px, transparent 5px 9px); }
.eco-map__legend-swatch--stop { width: 10px; height: 10px; border-radius: 50%; background: #f5e9cf; border: 2px solid #b0722a; }
.eco-map__legend-swatch--located { width: 10px; height: 10px; border-radius: 50%; background: var(--cm-positive-tx); }
.eco-map__legend-swatch--tolocate { width: 10px; height: 10px; border-radius: 50%; background: none; border: 2px dashed var(--cm-gold); }
.eco-map__legend-swatch--live-racing { width: 10px; height: 10px; border-radius: 50%; background: var(--cm-brand); }
.eco-map__legend-swatch--live-stale { width: 10px; height: 10px; border-radius: 50%; background: var(--cm-gold); }
.eco-map__legend-swatch--live-sos { width: 10px; height: 10px; border-radius: 50%; background: var(--cm-red-tx); }
.eco-map__legend-swatch--refused { width: 10px; height: 10px; border-radius: 50%; background: #fdf3f2; border: 2px solid #b8493d; }

/* Fullscreen map: the Leaflet button, and the fixed overlay fallback when the browser refuses the
   Fullscreen API. */
.eco-map__fullscreen a { font-size: 16px; line-height: 26px; text-align: center; }
.eco-map--expanded {
  position: fixed; inset: 0; z-index: 1080; height: auto; border-radius: 0;
}
/* Native fullscreen takes the element out of the flow: without an explicit height it keeps its 380px. */
.eco-map:fullscreen { height: 100%; border-radius: 0; }
.eco-map__legend-swatch--worst { background: #b8493d; }
.eco-map__legend-swatch--best { background: #1f7a54; }

/* ---------------------------------------------------------------------------------------------
   e-CO - course statistics (handoff screen 1j): the eight headline tiles, the collapsible Coureurs
   block with its filter and its sorts, and the two Segments / Balises tables.
   --------------------------------------------------------------------------------------------- */
/* The join code, shown in the creation panel before the course even exists (1g). */
.eco-generated-code {
  background: var(--cm-surface-alt); border: 1px dashed var(--cm-mail-tint-border);
  border-radius: var(--cm-radius-sm); padding: 12px; text-align: center;
}
.eco-generated-code__label {
  font: 600 11.5px var(--cm-font-ui); color: var(--cm-faint);
  letter-spacing: .6px; text-transform: uppercase;
}
.eco-generated-code__value {
  font: 700 26px ui-monospace, monospace; color: var(--cm-brand-strong);
  letter-spacing: 5px; margin-top: 3px;
}

/* SOS banner of the live screen (1h): full width, above everything else on the page. */
.eco-sos {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--cm-red-bg); border: 1px solid #ecc4bd; border-radius: var(--cm-radius);
  padding: 10px 16px; margin-bottom: 12px;
  font: 600 13px var(--cm-font-ui); color: var(--cm-red-tx);
}
.eco-sos__icon {
  flex: none; display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%;
  background: var(--cm-red-tx); color: #fff; font-size: 12px;
}
.eco-sos__text { flex: 1 1 auto; }
.eco-sos__actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }
.eco-sos__button {
  border: 1px solid #ecc4bd; background: var(--cm-surface); color: var(--cm-red-tx);
  border-radius: var(--cm-radius-sm); padding: 7px 16px; cursor: pointer;
  font: 600 13px var(--cm-font-ui); text-decoration: none;
}
.eco-sos__button--primary { background: var(--cm-red-tx); border-color: var(--cm-red-tx); color: #fff; }
.eco-sos__button--primary:hover { background: #8c3324; color: #fff; }

/* The live map fills the height of its column, unlike the results one. */
.eco-map--live { height: 460px; }

.eco-kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
@media (max-width: 1200px) { .eco-kpis { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .eco-kpis { grid-template-columns: 1fr; } }

.eco-kpi {
  background: var(--cm-surface); border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius); padding: 14px 16px;
}
.eco-kpi__label {
  font: 600 11.5px var(--cm-font-ui); color: var(--cm-faint);
  text-transform: uppercase; letter-spacing: .5px;
}
.eco-kpi__value { font: 600 24px var(--cm-font-title); color: var(--cm-ink); margin-top: 2px; }
.eco-kpi__value--positive { color: var(--cm-green-tx); }
.eco-kpi__value--warning { color: var(--cm-warn-tx); }
.eco-kpi__suffix { font: 600 13px var(--cm-font-ui); color: var(--cm-faint); }

/* The join code, everywhere it is read: monospaced, spaced out, in the brand blue. */
.eco-code { font: 700 15px ui-monospace, monospace; color: var(--cm-brand-strong); letter-spacing: 2px; }

.eco-stats-header { display: flex; align-items: center; gap: 12px; }
.eco-stats-toggle {
  display: inline-flex; align-items: center; gap: 9px;
  border: 0; background: none; padding: 0; cursor: pointer; color: var(--cm-ink);
}
.eco-stats-toggle__title { font: 600 15px var(--cm-font-title); }
.eco-stats-toggle__count { font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.eco-stats-toggle__chevron { color: var(--cm-muted); transition: transform .15s ease; }
.eco-stats-toggle[aria-expanded='false'] .eco-stats-toggle__chevron { transform: rotate(-90deg); }

.eco-filter {
  display: inline-flex; align-items: center; gap: 2px;
  background: var(--cm-surface-alt); border: 1px solid var(--cm-border);
  border-radius: 9px; padding: 3px;
}
.eco-filter__option {
  border: 0; background: none; border-radius: 7px; padding: 5px 12px; cursor: pointer;
  font: 600 12px var(--cm-font-ui); color: var(--cm-muted);
}
.eco-filter__option:hover { background: var(--cm-border-soft); }
.eco-filter__option.is-active { background: var(--cm-brand); color: #fff; }
.eco-filter__count { color: var(--cm-faint); }
.eco-filter__option.is-active .eco-filter__count { color: inherit; opacity: .75; }

.eco-sort {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: inherit; text-transform: inherit; letter-spacing: inherit;
}
.eco-sort:hover { color: var(--cm-ink); }
.eco-sort__arrow { color: #b3c6d6; }
.eco-sort.is-active .eco-sort__arrow { color: inherit; }

/* Figures in monospace - the handoff's rule, applied to times, distances and gaps. */
.eco-num { font: 600 12.5px ui-monospace, monospace; color: var(--cm-ink); }
.eco-num__gap { font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint); }
.eco-num--anchor { color: var(--cm-brand-strong); }
/* The runner behind a best time or a minimum detour: on hover only. */
.eco-num__owner { border-bottom: 1px dotted #b3c6d6; cursor: help; }

/* An SOS raised during the race tints the row for the rest of the course's life. */
.eco-row--sos > td { background: var(--cm-red-bg); color: var(--cm-red-tx); }
.eco-row--sos > td.text-secondary { color: var(--cm-red-tx); }
.eco-row--sos .eco-num { color: var(--cm-red-tx); }

/* ===========================================================================
   Audio recordings (design/design_handoff_enregistrements_audio)

   The teacher tool that replaced the gradebook's audio comments: list, step 1 "Paramètres", step 2
   "Fichiers audio", listening statistics. The handoff's tokens are the platform's own (see :root
   above) - only the gold of the record-from-mic button and the red of a recording in progress are
   specific to these screens.
   ======================================================================== */

.cm-audio-filter { margin: 0; }
.cm-audio-filter .form-select { min-width: 190px; }

.cm-audio-table th {
  font: 700 11px var(--cm-font-ui); letter-spacing: .5px; text-transform: uppercase;
  color: var(--cm-faint);
}
.cm-audio-table td { font: 400 13px var(--cm-font-ui); color: var(--cm-text); vertical-align: middle; }
.cm-audio-table__name {
  font-weight: 600; color: var(--cm-ink);
  max-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cm-audio-table__target { width: 190px; }
.cm-audio-table__type   { width: 130px; }
.cm-audio-table__files  { width: 190px; }
.cm-audio-table__status { width: 130px; }
.cm-audio-table__action { width: 160px; text-align: right; }

/* Status chip - the list (Brouillon/Complet/Travail créé) and the statistics table
   (Terminé/En cours/Non commencé), which share the same shape. */
.cm-audio-pill {
  display: inline-block; border-radius: 11px; padding: 3px 10px;
  font: 600 11px var(--cm-font-ui); white-space: nowrap;
}
.cm-audio-pill--draft,
.cm-audio-pill--not_started { background: var(--cm-border-soft); color: var(--cm-muted); }
.cm-audio-pill--complete,
.cm-audio-pill--in_progress { background: var(--cm-blue-bg); color: var(--cm-blue-tx); }
.cm-audio-pill--work { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }
/* "Terminé" is the only green chip: it is earned at 100% on every one of the student's files. */
.cm-audio-stats .cm-audio-pill--complete { background: #e2efe4; color: #2f6b45; }

.cm-audio-ghost {
  display: inline-block; border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
  padding: 6px 14px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand);
  background: var(--cm-surface); text-decoration: none;
}
.cm-audio-ghost:hover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-audio-ghost:disabled { opacity: .45; cursor: default; }
.cm-audio-ghost:disabled:hover { border-color: var(--cm-border); color: var(--cm-brand); }

/* ---- The two-step stepper ---- */

.cm-audio-stepper {
  display: flex; align-items: center; gap: 0; list-style: none; margin: 0 0 20px; padding: 0;
}
.cm-audio-stepper__step { display: flex; align-items: center; gap: 9px; }
/* The 56px rule between the two dots, laid before the second step. */
.cm-audio-stepper__step + .cm-audio-stepper__step::before {
  content: ''; width: 56px; height: 1px; background: #c8d3dc; margin: 0 14px;
}
.cm-audio-stepper__dot {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  border: 1px solid #c8d3dc; color: var(--cm-faint);
  font: 600 12px var(--cm-font-ui);
}
.cm-audio-stepper__label { font: 600 13px var(--cm-font-ui); color: var(--cm-faint); }
.cm-audio-stepper__step.is-current .cm-audio-stepper__dot { background: var(--cm-navy); border-color: var(--cm-navy); color: #fff; }
.cm-audio-stepper__step.is-current .cm-audio-stepper__label { color: var(--cm-ink); }
.cm-audio-stepper__step.is-done .cm-audio-stepper__dot { background: #2f6b45; border-color: #2f6b45; color: #fff; }
.cm-audio-stepper__step.is-done .cm-audio-stepper__label { color: var(--cm-ink); }

/* ---- Step 1: the settings form ---- */

.cm-audio-form {
  display: flex; flex-direction: column; gap: 20px;
  max-width: 640px; padding: 26px;
  background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
}
.cm-audio-form__field { display: flex; flex-direction: column; gap: 7px; }
.cm-audio-form__label {
  font: 700 11px var(--cm-font-ui); letter-spacing: .5px; text-transform: uppercase;
  color: var(--cm-faint);
}
.cm-audio-form__error { font: 400 12px var(--cm-font-ui); color: var(--cm-red-tx); }
.cm-audio-form__footer {
  display: flex; justify-content: flex-end; gap: 10px;
  margin-top: 16px; padding-top: 18px; border-top: 1px solid var(--cm-border-soft);
}

.cm-audio-options { display: flex; flex-wrap: wrap; gap: 8px; }
.cm-audio-option {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  border: 1px solid #dde5ec; border-radius: 16px; padding: 6px 14px;
  font: 600 12.5px var(--cm-font-ui); color: var(--cm-text);
}
.cm-audio-option input { position: absolute; opacity: 0; pointer-events: none; }
.cm-audio-option:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong); }
/* The tick of a selected chip, drawn in CSS rather than in markup: it has nothing to say to a
   screen reader, the checked box already says it. */
.cm-audio-option:has(input:checked)::before { content: '✓'; font-size: 11px; }

.cm-audio-modes { display: flex; gap: 12px; flex-wrap: wrap; }
.cm-audio-mode {
  display: flex; align-items: center; gap: 10px; flex: 1 1 240px; cursor: pointer;
  border: 1px solid #dde5ec; border-radius: var(--cm-radius-sm); padding: 14px 16px;
}
.cm-audio-mode input { position: absolute; opacity: 0; pointer-events: none; }
.cm-audio-mode__dot {
  width: 16px; height: 16px; border-radius: 50%; flex: none;
  border: 1px solid #c8d3dc; background: var(--cm-surface);
}
.cm-audio-mode__label { font: 400 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-audio-mode:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); }
.cm-audio-mode:has(input:checked) .cm-audio-mode__dot { border: 5px solid var(--cm-brand); }

/* ---- Step 2: the file cards ---- */

.cm-audio-warn {
  background: var(--cm-red-bg); color: var(--cm-red-tx); border-radius: var(--cm-radius-sm);
  padding: 10px 14px; margin-bottom: 14px; font: 400 12.5px var(--cm-font-ui);
}
.cm-audio-card { margin-bottom: 16px; }
.cm-audio-card__head {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 20px; border-bottom: 1px solid var(--cm-border-soft);
}
.cm-audio-card__label {
  font: 700 11px var(--cm-font-ui); letter-spacing: .5px; text-transform: uppercase;
  color: var(--cm-faint);
}
.cm-audio-card__count { font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-audio-card__head .cm-audio-record { margin-left: auto; }
.cm-audio-card__body { padding: 14px 20px; display: flex; flex-direction: column; gap: 8px; }
.cm-audio-card__body--rows { padding: 0; gap: 0; }

/* The gold record-from-mic button - the only way to add a file. */
.cm-audio-record {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  border: 1px solid var(--cm-gold); border-radius: var(--cm-radius-sm); background: var(--cm-surface);
  padding: 7px 14px; font: 600 12.5px var(--cm-font-ui); color: #9a7729;
}
.cm-audio-record:disabled { opacity: .45; cursor: default; }
.cm-audio-record__dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
.cm-audio-record.is-recording { border-color: var(--cm-red-tx); color: var(--cm-red-tx); }

.cm-audio-stop {
  border: 1px solid var(--cm-red-tx); border-radius: var(--cm-radius-sm); background: var(--cm-surface);
  padding: 5px 12px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-red-tx); cursor: pointer;
}
.cm-audio-live { display: inline-flex; align-items: center; gap: 8px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-red-tx); }
.cm-audio-live__dot { width: 9px; height: 9px; border-radius: 50%; background: currentColor; flex: none; }

.cm-audio-file {
  display: flex; align-items: center; gap: 12px;
  background: var(--cm-surface-soft); border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm);
  padding: 9px 12px;
}
.cm-audio-file__play {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 32px; height: 32px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-brand-strong);
}
.cm-audio-file__name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-audio-file__duration { font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-audio-file__delete {
  margin-left: auto; border: 0; background: none; padding: 4px; cursor: pointer; color: var(--cm-red-tx);
}

.cm-audio-student {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 20px; border-bottom: 1px solid var(--cm-border-faint);
}
.cm-audio-student:last-child { border-bottom: 0; }
.cm-audio-student__name { width: 170px; flex: none; font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-audio-student__tags { display: inline-flex; gap: 4px; }
.cm-audio-student__slot { margin-left: auto; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }

.cm-audio-tag {
  display: inline-block; border-radius: 4px; padding: 2px 7px;
  font: 700 10px var(--cm-font-ui); letter-spacing: .3px; text-transform: uppercase; color: #fff;
}

/* "▶ Réécouter ✕": the mockup puts neither file name nor duration in it. */
.cm-audio-chip {
  display: inline-flex; align-items: center;
  border: 1px solid #dde5ec; border-radius: 16px; color: var(--cm-blue-tx);
}
.cm-audio-chip__play {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  border: 0; background: none; padding: 5px 6px 5px 13px;
  font: 600 12.5px var(--cm-font-ui); color: inherit;
}
.cm-audio-chip__remove {
  border: 0; background: none; cursor: pointer; padding: 5px 11px 5px 6px;
  font: 400 12px var(--cm-font-ui); color: var(--cm-faint);
}
.cm-audio-chip__remove:hover { color: var(--cm-red-tx); }

/* The recording recalled inside the assignment wizard, Listening nature. */
.cm-audio-workpick {
  display: inline-flex; align-items: center; gap: 9px;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
  background: var(--cm-blue-bg-soft); padding: 9px 14px;
  font: 600 13px var(--cm-font-ui); color: var(--cm-ink);
}
.cm-audio-workpick__count { font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }

/* ---- Listening statistics ---- */

.cm-audio-summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 16px; }
.cm-audio-summary__tile {
  background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  padding: 20px 22px;
}
.cm-audio-summary__num { font: 600 30px var(--cm-font-title); color: var(--cm-ink); line-height: 1.1; }
.cm-audio-summary__num--green  { color: #2f6b45; }
.cm-audio-summary__num--blue   { color: var(--cm-blue-tx); }
.cm-audio-summary__num--muted  { color: var(--cm-faint); }
.cm-audio-summary__cap { margin-top: 6px; font: 400 12.5px var(--cm-font-ui); color: var(--cm-muted); }

.cm-audio-filestat {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 20px; border-bottom: 1px solid var(--cm-border-faint);
}
.cm-audio-filestat:last-child { border-bottom: 0; }
.cm-audio-filestat__name {
  width: 230px; flex: none; font: 600 13px var(--cm-font-ui); color: var(--cm-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cm-audio-filestat__duration { width: 44px; flex: none; text-align: right; font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-audio-filestat__percent { width: 46px; flex: none; text-align: right; font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-audio-filestat__count { width: 170px; flex: none; text-align: right; font: 400 12px var(--cm-font-ui); color: var(--cm-faint); }

.cm-audio-bar { flex: 1; height: 6px; border-radius: 3px; background: #e8eef3; overflow: hidden; display: block; }
.cm-audio-bar__fill { display: block; height: 100%; background: var(--cm-brand); }
.cm-audio-bar__fill--done { background: #2f6b45; }
.cm-audio-bar--mini { height: 4px; width: 100%; }

.cm-audio-stats th {
  font: 700 11px var(--cm-font-ui); letter-spacing: .5px; text-transform: uppercase;
  color: var(--cm-faint);
}
.cm-audio-stats td { vertical-align: middle; font: 400 13px var(--cm-font-ui); color: var(--cm-text); }
.cm-audio-stats__student { font-weight: 600; color: var(--cm-ink); }
.cm-audio-stats__student .cm-audio-tag { margin-left: 6px; }
.cm-audio-stats__file { width: 110px; }
.cm-audio-stats__percent { display: block; margin-top: 3px; font: 400 11px var(--cm-font-ui); color: var(--cm-faint); }
.cm-audio-stats__na { color: var(--cm-faint); }
.cm-audio-stats__total { width: 100px; font-weight: 600; color: var(--cm-ink); }
.cm-audio-stats__last { width: 140px; }
.cm-audio-stats__status { width: 130px; }

@media (max-width: 900px) {
  .cm-audio-summary { grid-template-columns: repeat(2, 1fr); }
  .cm-audio-student { flex-wrap: wrap; }
  .cm-audio-student__name { width: auto; }
}

/* ========================================================================
   Videos (design/comparaison/creas_video_5a_5b)

   The teacher tool "Vidéos" and the student's watching screen. Both are the audio tool ported, so
   they wear the .cm-audio-* components above wherever the component is the same one - list table,
   pills, stepper, form, summary tiles, bars. Only what a video has and a recording has not is
   declared here: an upload button instead of a microphone, the retention map, and the player's
   three-zone track.
   ======================================================================== */

/* Step 2's file picker: the same shape as the audio "Enregistrer au micro" button, in the platform's
   blue rather than the recorder's gold - nothing is being recorded here. */
.cm-video-pick {
  display: inline-flex; align-items: center; gap: 8px; margin-left: 12px; cursor: pointer;
  border: 1px solid var(--cm-brand); border-radius: var(--cm-radius-sm); background: var(--cm-surface);
  padding: 7px 14px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong);
}
.cm-video-pick:disabled { opacity: .45; cursor: default; }
.cm-video-file__icon {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--cm-surface); border: 1px solid var(--cm-border); color: var(--cm-brand-strong);
}

/* The row of a file on its way up. It sits among the finished ones and keeps their shape, so the
   card does not jump when the transfer ends and the row becomes a real file. */
.cm-video-upload__track {
  flex: 1; min-width: 80px; margin-left: auto; height: 6px; border-radius: 3px;
  background: var(--cm-border-soft); overflow: hidden;
}
.cm-video-upload__fill {
  display: block; width: 0; height: 100%; border-radius: 3px; background: var(--cm-brand);
  transition: width .15s linear;
}
.cm-video-upload__percent {
  flex: none; min-width: 44px; text-align: right;
  font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong);
}
/* No total to measure against - a proxy that hides Content-Length, typically. The bar sweeps to say
   the transfer is alive, which is the only thing that can honestly be said. */
.cm-video-upload__track.is-indeterminate .cm-video-upload__fill {
  width: 35%; transition: none; animation: cm-video-upload-sweep 1.1s ease-in-out infinite;
}
@keyframes cm-video-upload-sweep {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(300%); }
}
@media (prefers-reduced-motion: reduce) {
  .cm-video-upload__track.is-indeterminate .cm-video-upload__fill { animation: none; width: 100%; opacity: .5; }
  .cm-video-upload__fill { transition: none; }
}

/* ---- Retention map (teacher) ---- */

.cm-video-map {
  display: flex; align-items: flex-end; gap: 2px; height: 96px;
  background: var(--cm-surface-soft); border: 1px solid var(--cm-border-soft);
  border-radius: var(--cm-radius-sm); padding: 8px;
}
.cm-video-map__bar { flex: 1; min-height: 2px; border-radius: 2px 2px 0 0; background: var(--cm-brand); }
.cm-video-map__axis {
  display: flex; justify-content: space-between; margin-top: 6px;
  font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint);
}
.cm-video-map__note { margin: 10px 0 0; font: 400 12.5px var(--cm-font-ui); color: var(--cm-muted); }
.cm-video-map__note strong { color: var(--cm-ink); }

/* ---- Student watching screen ---- */

.cm-video-head { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.cm-video-head__due,
.cm-video-head__topic { font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-video-brief { margin-bottom: 16px; font: 400 13.5px var(--cm-font-ui); color: var(--cm-text); }
.cm-video-block .cm-audio-card__body { gap: 12px; }
.cm-video-player {
  width: 100%; max-height: 62vh; border-radius: var(--cm-radius-sm);
  background: #000; display: block;
}

/* The bar that IS the feature: credited, seen-after-a-jump, playhead. */
.cm-video-track {
  position: relative; height: 10px; border-radius: 5px; overflow: hidden;
  background: #e8eef3;
}
.cm-video-track__fill { position: absolute; inset: 0 auto 0 0; height: 100%; background: var(--cm-brand); }
.cm-video-track__fill--done { background: #2f6b45; }
/* Hatched rather than tinted: it is not a second kind of progress, it is progress that does not
   count. */
.cm-video-track__skipped {
  position: absolute; top: 0; height: 100%;
  background: repeating-linear-gradient(45deg, #c8d3dc, #c8d3dc 3px, #e8eef3 3px, #e8eef3 6px);
}
.cm-video-track__cursor { position: absolute; top: 0; width: 2px; height: 100%; background: var(--cm-navy); }

.cm-video-legend { display: flex; flex-wrap: wrap; gap: 16px; }
.cm-video-legend__item {
  display: inline-flex; align-items: center; gap: 7px;
  font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint);
}
.cm-video-legend__item::before { content: ''; width: 12px; height: 6px; border-radius: 3px; }
.cm-video-legend__item--credited::before { background: var(--cm-brand); }
.cm-video-legend__item--skipped::before {
  background: repeating-linear-gradient(45deg, #c8d3dc, #c8d3dc 3px, #e8eef3 3px, #e8eef3 6px);
}
.cm-video-legend__item--cursor::before { width: 2px; height: 12px; border-radius: 0; background: var(--cm-navy); }

.cm-video-state { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.cm-video-state__percent { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-video-state__note { font: 400 12.5px var(--cm-font-ui); color: var(--cm-muted); }

.cm-video-total { flex-direction: row; align-items: center; gap: 14px; }
.cm-video-total__label {
  font: 700 11px var(--cm-font-ui); letter-spacing: .5px; text-transform: uppercase; color: var(--cm-faint);
}
.cm-video-total__percent { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }

/* ------------------------------------------------------------------------
   Interactive video (créas 5B): the markers, the teacher's timeline editor,
   and the question laid over the student's player.

   Same rule as the rest of this section - the audio handoff's pixels are already declared once
   above, so what is here is only what a question posted inside a video has and a plain video has
   not: a pin on a timeline, an overlay, and a verdict.
   ------------------------------------------------------------------------ */

.cm-video-cue-lead { margin: 0 0 14px; font: 400 13px var(--cm-font-ui); color: var(--cm-muted); }
.cm-video-cue-grid { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 16px; align-items: start; }

/* ---- Teacher: the timeline a marker is posted on ---- */

.cm-video-timeline { position: relative; height: 34px; cursor: crosshair; margin-top: 4px; }
.cm-video-timeline__rule {
  position: absolute; left: 0; right: 0; top: 15px; height: 4px; border-radius: 2px; background: #e8eef3;
}
.cm-video-timeline__cursor { position: absolute; top: 8px; width: 2px; height: 18px; background: var(--cm-navy); }
.cm-video-timeline__marker {
  position: absolute; top: 0; transform: translateX(-50%); display: flex; flex-direction: column;
  align-items: center; gap: 2px; border: 0; background: none; padding: 0; cursor: pointer;
}
.cm-video-timeline__pin {
  width: 11px; height: 11px; border-radius: 50%; background: var(--cm-brand);
  border: 2px solid var(--cm-surface); box-shadow: 0 0 0 1px var(--cm-brand);
}
.cm-video-timeline__tag { font: 600 10.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-video-timeline__marker.is-selected .cm-video-timeline__pin { background: var(--cm-gold-tx); box-shadow: 0 0 0 1px var(--cm-gold-tx); }
.cm-video-timeline__marker.is-selected .cm-video-timeline__tag { color: var(--cm-ink); }
.cm-video-timeline__axis {
  display: flex; justify-content: space-between; font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint);
}

/* ---- Teacher: the list of markers, and the panel of the selected one ---- */

.cm-video-cue-rows { border-top: 1px solid var(--cm-border-soft); }
.cm-video-cue-row {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; cursor: pointer;
  border: 0; border-bottom: 1px solid var(--cm-border-soft); background: none; padding: 11px 20px;
}
.cm-video-cue-row:last-child { border-bottom: 0; }
.cm-video-cue-row:hover { background: var(--cm-surface-soft); }
.cm-video-cue-row.is-selected { background: var(--cm-blue-bg-soft); }
.cm-video-cue-row--empty { cursor: default; font: 400 13px var(--cm-font-ui); color: var(--cm-faint); }
.cm-video-cue-row--empty:hover { background: none; }
.cm-video-cue-row__time {
  flex: none; width: 52px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong);
}
.cm-video-cue-row__label {
  flex: 1; min-width: 0; font: 400 13px var(--cm-font-ui); color: var(--cm-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cm-video-cue-row__type { flex: none; }
/* Both settings are always drawn - off is a state, not an absence, so that "is this one blocking?"
   is answered by looking rather than by clicking. */
.cm-video-cue-row__flag {
  flex: none; border: 1px solid var(--cm-border); border-radius: 999px; padding: 2px 9px;
  font: 600 10.5px var(--cm-font-ui); color: var(--cm-border); text-transform: uppercase; letter-spacing: .4px;
}
.cm-video-cue-row__flag.is-on { border-color: var(--cm-brand); color: var(--cm-brand-strong); }

.cm-video-cue-panel { gap: 16px; }
.cm-video-cue-hint { font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-video-cue-toggle { display: flex; align-items: center; gap: 9px; font: 400 13px var(--cm-font-ui); color: var(--cm-text); }
.cm-video-cue-panel__footer {
  display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end;
  border-top: 1px solid var(--cm-border-soft); padding-top: 14px;
}
.cm-video-cue-prompt {
  width: 100%; max-height: 420px; overflow: auto; margin: 0;
  background: var(--cm-surface-alt); border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius);
  padding: 12px 14px; font-size: 11.5px; line-height: 1.65; white-space: pre-wrap;
}
.cm-video-cue-error { font: 400 12.5px var(--cm-font-ui); color: var(--cm-red-tx); }
.cm-video-cue-warning { font: 400 12.5px var(--cm-font-ui); color: var(--cm-gold-tx); }

/* ---- Teacher: the results screen ---- */

.cm-video-cue-results__timecode { width: 84px; font: 600 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); }
.cm-video-cue-results__type { width: 120px; }
.cm-video-cue-results__count { width: 90px; }
.cm-video-cue-results__rate { width: 190px; }
.cm-video-cue-results__row--weak { background: var(--cm-red-bg); }
.cm-video-cue-rate { display: flex; align-items: center; gap: 10px; }
.cm-video-cue-rate__percent { flex: none; font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-video-cue-rate__percent--weak { color: var(--cm-red-tx); }

/* ---- Student: the question laid over the player ---- */

/* The floor keeps a question readable even when the player itself is short - a file whose poster
   never loaded collapses the <video> to a sliver, and the card would be squeezed into it. */
.cm-video-stage { position: relative; min-height: 300px; }
/* Scrolls as a whole rather than clipping: a légende or an appariement carrying an image is taller
   than a 12-minute lecture's frame, and its "Valider" button has to be reachable. */
.cm-video-overlay {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  padding: 16px; overflow: auto;
}
.cm-video-overlay__veil { position: absolute; inset: 0; background: rgba(18, 52, 77, .82); border-radius: var(--cm-radius-sm); }
.cm-video-overlay__panel { position: relative; width: min(620px, 100%); max-height: 100%; overflow: auto; }

.cm-video-cue-card { background: var(--cm-surface); border-radius: var(--cm-radius); overflow: hidden; }
.cm-video-cue-card__head {
  display: flex; align-items: center; gap: 10px;
  border-bottom: 1px solid var(--cm-border-soft); padding: 11px 18px;
}
.cm-video-cue-card__time { margin-left: auto; font: 600 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-video-cue-card__body { display: flex; flex-direction: column; gap: 14px; padding: 16px 18px; }
.cm-video-cue-card__label { font: 600 15px var(--cm-font-title); color: var(--cm-ink); line-height: 1.4; }
.cm-video-cue-card__image { max-height: 190px; border-radius: var(--cm-radius-sm); align-self: flex-start; }
.cm-video-cue-card__footer {
  display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end;
  border-top: 1px solid var(--cm-border-soft); padding-top: 13px;
}
.cm-video-cue-explanation { font: 400 13px var(--cm-font-ui); color: var(--cm-text); line-height: 1.55; }
.cm-video-cue-verdict { font: 700 13.5px var(--cm-font-ui); }
.cm-video-cue-verdict--right { color: #2f6b45; }
.cm-video-cue-verdict--wrong { color: var(--cm-red-tx); }

/* The markers on the watching bar: a question one can see coming rather than be interrupted by. */
.cm-video-track__cue {
  position: absolute; top: 0; width: 3px; height: 100%; transform: translateX(-1px);
  background: var(--cm-gold-tx);
}
.cm-video-track__cue--done { background: #2f6b45; }
.cm-video-legend__item--cue::before { width: 3px; height: 12px; border-radius: 0; background: var(--cm-gold-tx); }

@media (max-width: 900px) {
  .cm-video-cue-grid { grid-template-columns: 1fr; }
}

/* The class picker of the tools opened from the top bar (App\Controller\ToolsController). */
.cm-tools-picker { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; }
.cm-tools-picker__item {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); padding: 10px 18px;
  font: 600 13px var(--cm-font-ui); color: var(--cm-ink); text-decoration: none;
  background: var(--cm-surface);
}
.cm-tools-picker__item:hover { border-color: var(--cm-brand); color: var(--cm-brand-strong); background: var(--cm-blue-bg-soft); }

/* One audio file inside the student's brief, for a Listening assignment. */
.cm-tw-listen {
  display: flex; flex-direction: column; gap: 7px;
  background: var(--cm-surface-soft); border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm);
  padding: 11px 13px;
}
.cm-tw-listen__head { display: flex; align-items: baseline; gap: 10px; }
.cm-tw-listen__name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tw-listen__duration { font: 400 12px var(--cm-font-ui); color: var(--cm-faint); }
.cm-tw-listen__state { margin-left: auto; font: 600 11.5px var(--cm-font-ui); color: var(--cm-muted); }
.cm-tw-listen__player { width: 100%; height: 34px; }

/* An empty file card would otherwise show a padded strip under its header, which reads as a broken
   row rather than as "no file yet". */
.cm-audio-card__body:empty { display: none; }

/* ==========================================================================
   Rubrique d'aide (design_handoff_aide, écrans 1/2/3 — itération 2)
   Its own component family rather than Tabler cards: the handoff's cards, filter pills and result
   rows have no Tabler equivalent, and the article page is a two-column reading layout, not a form.

   The rule that drives the whole iteration: on the grey page background, every block of content
   sits on a bordered white card. No content text rests directly on the grey. The page also uses the
   application's full width - the handoff dropped its own max-width:1140px centring in favour of the
   shell's container.
   ========================================================================== */

/* The search field, on the title line of all three screens (page_actions block, right-aligned). */
.cm-help-search { position: relative; display: inline-block; width: min(300px, 60vw); }
.cm-help-search__icon {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  color: var(--cm-faint); pointer-events: none;
}
.cm-help-search__input {
  width: 100%; padding: 9px 14px 9px 36px;
  border: 1px solid var(--cm-border); border-radius: 9px;
  background: var(--cm-surface); color: var(--cm-ink);
  font: 400 13.5px var(--cm-font-ui);
}
.cm-help-search__input:focus { outline: none; border-color: var(--cm-brand); }
.cm-help-search.is-filled .cm-help-search__input { border-color: var(--cm-brand); }

/* Écran 1 - the category grid: 4 across, no article counter (the handoff says so explicitly). */
.cm-help-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.cm-help-card {
  display: flex; flex-direction: column; gap: 6px;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  background: var(--cm-surface); padding: 15px 17px; text-decoration: none;
}
.cm-help-card:hover { border-color: var(--cm-brand); box-shadow: 0 2px 6px rgba(18, 52, 77, .07); }
.cm-help-card__title { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); }
.cm-help-card__desc { font: 400 12.5px var(--cm-font-ui); color: var(--cm-muted); }

.cm-help-columns { display: grid; grid-template-columns: 1fr 320px; gap: 24px; margin-top: 28px; }
.cm-help-heading { font: 600 16px var(--cm-font-title); color: var(--cm-ink); margin: 0 0 12px; }
.cm-help-lede { font: 400 14px/1.7 var(--cm-font-ui); color: var(--cm-text); }
.cm-help-empty { font: 400 14px var(--cm-font-ui); color: var(--cm-muted); }

/* The FAQ accordion: plain <details>, so opening an answer needs no controller. */
.cm-help-faq {
  border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
  overflow: hidden;
}
.cm-help-faq details + details { border-top: 1px solid var(--cm-border-soft); }
.cm-help-faq summary {
  list-style: none; cursor: pointer; padding: 14px 42px 14px 17px; position: relative;
  font: 600 14px var(--cm-font-ui); color: var(--cm-ink);
}
.cm-help-faq summary::-webkit-details-marker { display: none; }
/* The +/- of the mockup, drawn from the open state rather than from an icon font. */
.cm-help-faq summary::after {
  content: '+'; position: absolute; right: 17px; top: 50%; transform: translateY(-50%);
  font: 400 17px var(--cm-font-ui); color: var(--cm-faint);
}
.cm-help-faq details[open] summary::after { content: '–'; }
.cm-help-faq details[open] summary { padding-bottom: 4px; }
.cm-help-faq p { margin: 0; padding: 0 17px 15px; font: 400 13.5px/1.7 var(--cm-font-ui); color: var(--cm-text); }

.cm-help-links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.cm-help-links a { font: 400 13.5px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-help-links a:hover { text-decoration: underline; }
/* "Les plus consultés" is a block of content, so it gets the white card like everything else. */
.cm-help-card-list {
  border: 1px solid var(--cm-border); border-radius: 12px;
  background: var(--cm-surface); padding: 16px 18px;
}

/* A section's article list - one white card per article. */
.cm-help-list { list-style: none; margin: 0 0 26px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.cm-help-list li {
  display: flex; flex-direction: column; gap: 3px;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  background: var(--cm-surface); padding: 14px 17px;
}
.cm-help-list li:hover { border-color: var(--cm-brand); box-shadow: 0 2px 6px rgba(18, 52, 77, .07); }
.cm-help-list a { font: 600 14px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-help-list a:hover { text-decoration: underline; }
.cm-help-list span { font: 400 13px var(--cm-font-ui); color: var(--cm-muted); }

.cm-help-glossary {
  margin: 0; border: 1px solid var(--cm-border); border-radius: 12px;
  background: var(--cm-surface); padding: 6px 18px 18px;
}
.cm-help-glossary dt { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); margin-top: 14px; }
.cm-help-glossary dd { margin: 3px 0 0; font: 400 13.5px/1.7 var(--cm-font-ui); color: var(--cm-text); }

/* Écran 2 - the article: reading card plus a sticky sommaire card, both white. */
.cm-help-article { display: grid; grid-template-columns: minmax(0, 1fr) 264px; gap: 18px; }
.cm-help-article__main {
  border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
  padding: 26px 34px 32px;
}
.cm-help-article__aside {
  position: sticky; top: 20px; align-self: start;
  border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
  padding: 18px 20px;
}
.cm-help-aside__title {
  font: 700 11px var(--cm-font-ui); letter-spacing: .08em; text-transform: uppercase;
  color: var(--cm-gold-deep); margin: 0 0 10px;
}
.cm-help-aside__title + .cm-help-links { margin-bottom: 22px; }
.cm-help-article__main .cm-help-lede { margin: 0 0 22px; font-size: 14.5px; }

/* The article's own body, written in HugeRTE: h2 are the numbered steps of the mockup. */
.cm-help-body { font: 400 14.5px/1.75 var(--cm-font-ui); color: var(--cm-text); }
.cm-help-body h2 {
  font: 600 16px var(--cm-font-title); color: var(--cm-ink);
  margin: 22px 0 8px; scroll-margin-top: 20px;
}
.cm-help-body h3 { font: 600 14.5px var(--cm-font-title); color: var(--cm-ink); margin: 20px 0 6px; }
.cm-help-body p { margin: 0 0 12px; }
.cm-help-body ul, .cm-help-body ol { margin: 0 0 12px; padding-left: 22px; }
.cm-help-body li { margin-bottom: 5px; }
.cm-help-body strong { color: var(--cm-ink); }
.cm-help-body blockquote {
  margin: 0 0 14px; padding: 11px 15px; border-left: 3px solid var(--cm-gold);
  background: var(--cm-surface-alt); color: var(--cm-text);
}
.cm-help-body table { width: 100%; border-collapse: collapse; margin: 0 0 14px; }
.cm-help-body th, .cm-help-body td { border: 1px solid var(--cm-border); padding: 7px 10px; text-align: left; }
/* A screenshot inside an article gets the handoff's own frame (écran 2), whether it is dropped in
   as a plain <img> or wrapped in a <figure>. */
.cm-help-body img, .cm-help-body figure {
  display: block; max-width: 100%; margin: 0 0 14px;
  border: 1px solid #e4ebf1; border-radius: 10px; background: var(--cm-surface-alt);
}

/* Écran 3 - filter pills and the unified result list. */
.cm-help-filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.cm-help-filter {
  border: 1px solid var(--cm-border); border-radius: 999px; padding: 5px 14px;
  background: var(--cm-surface); color: var(--cm-muted); text-decoration: none;
  font: 600 12.5px var(--cm-font-ui);
}
.cm-help-filter span { color: var(--cm-faint); font-weight: 400; }
.cm-help-filter:hover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-help-filter.is-active { background: var(--cm-blue-bg-soft); border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-help-filter.is-active span { color: var(--cm-brand-strong); }

.cm-help-results {
  border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
  overflow: hidden;
}
.cm-help-result { display: flex; flex-direction: column; gap: 3px; padding: 14px 17px; text-decoration: none; }
.cm-help-result + .cm-help-result { border-top: 1px solid var(--cm-border-soft); }
.cm-help-result:hover { background: var(--cm-surface-alt); }
.cm-help-result__kicker {
  font: 700 10.5px var(--cm-font-ui); letter-spacing: .07em; text-transform: uppercase;
  color: var(--cm-gold-deep);
}
.cm-help-result__title { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); }
.cm-help-result__excerpt { font: 400 13px/1.6 var(--cm-font-ui); color: var(--cm-muted); }
.cm-help-result mark, .cm-help-body mark {
  background: var(--cm-highlight-bg); color: var(--cm-highlight-tx); padding: 0 1px; border-radius: 2px;
}

/* Help administration (App\Controller\HelpAdminController) - admin only, deliberately plainer than
   the reading screens: it is a working list, not a page anybody browses. */
.cm-help-manage {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  background: var(--cm-surface); margin-bottom: 14px;
}
.cm-help-manage__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 13px 16px; border-bottom: 1px solid var(--cm-border-soft);
}
.cm-help-manage__title { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); text-decoration: none; }
.cm-help-manage__meta, .cm-help-manage__audiences {
  display: block; margin-top: 4px; font: 400 12px var(--cm-font-ui); color: var(--cm-faint);
}
.cm-help-manage__audiences .cm-badge + .cm-badge { margin-left: 5px; }
.cm-help-manage__actions { display: flex; align-items: center; gap: 14px; white-space: nowrap; }
.cm-help-manage__empty { margin: 0; padding: 13px 16px; font: 400 13px var(--cm-font-ui); color: var(--cm-muted); }
.cm-help-manage__table { width: 100%; border-collapse: collapse; }
.cm-help-manage__table th {
  text-align: left; padding: 9px 16px; border-bottom: 1px solid var(--cm-border-soft);
  font: 600 11.5px var(--cm-font-ui); color: var(--cm-faint); text-transform: uppercase; letter-spacing: .05em;
}
.cm-help-manage__table td { padding: 9px 16px; border-bottom: 1px solid var(--cm-border-faint); font: 400 13px var(--cm-font-ui); color: var(--cm-text); }
.cm-help-manage__table tr:last-child td { border-bottom: 0; }

@media (max-width: 1200px) {
  .cm-help-grid { grid-template-columns: repeat(2, 1fr); }
  .cm-help-columns { grid-template-columns: 1fr; gap: 26px; }
  .cm-help-article { grid-template-columns: 1fr; gap: 18px; }
  .cm-help-article__aside { position: static; }
}
@media (max-width: 700px) {
  .cm-help-grid { grid-template-columns: 1fr; }
  .cm-help-search { width: 100%; }
  .cm-help-article__main { padding: 20px 18px 24px; }
}

/* ==========================================================================
   Changelog (App\Controller\ChangelogController)
   A reading page, not a table: one block per release, the summary first, then one line per entry
   with its type as a badge. Technical entries sit in a <details> so a teacher meets the product
   changes first without the record being hidden.
   ========================================================================== */

/* Two columns: the log reads on the left, the filters and the figures sit on the right. The reading
   column keeps a cap - a 1300px-wide paragraph is scanned, not read - and the aside uses the width
   that was previously left empty beside it. */
.cm-changelog-layout { display: grid; grid-template-columns: minmax(0, 860px) 300px; gap: 26px; align-items: start; }
.cm-changelog { display: flex; flex-direction: column; gap: 14px; }
.cm-changelog__aside {
  position: sticky; top: 20px; align-self: start;
  border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
  padding: 18px 20px;
}
.cm-changelog__aside-title { margin-top: 22px !important; }
.cm-changelog__aside-note { margin: 10px 0 0; font: 400 12px/1.6 var(--cm-font-ui); color: var(--cm-faint); }

.cm-changelog__filters { display: flex; flex-direction: column; gap: 6px; }
.cm-changelog__filter {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
  border: 1px solid var(--cm-border); border-radius: 999px; padding: 6px 14px;
  background: var(--cm-surface); color: var(--cm-muted); cursor: pointer; text-align: left;
  font: 600 12.5px var(--cm-font-ui);
}
.cm-changelog__filter span { font-weight: 400; color: var(--cm-faint); font-variant-numeric: tabular-nums; }
.cm-changelog__filter:hover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-changelog__filter.is-active { background: var(--cm-blue-bg-soft); border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-changelog__filter.is-active span { color: var(--cm-brand-strong); }

/* One fact per row: the figure in Spectral, what it means underneath in small grey. */
.cm-changelog__stats { margin: 0; display: flex; flex-direction: column; gap: 12px; }
.cm-changelog__stats dt { font: 600 15px var(--cm-font-title); color: var(--cm-brand-strong); }
.cm-changelog__stats dd { margin: 1px 0 0; font: 400 12px/1.55 var(--cm-font-ui); color: var(--cm-faint); }
.cm-changelog__release {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
  background: var(--cm-surface); padding: 16px 18px;
}
.cm-changelog__head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.cm-changelog__version { margin: 0; font: 600 17px var(--cm-font-title); color: var(--cm-ink); }
.cm-changelog__date { font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-changelog__summary { margin: 8px 0 0; font: 400 14px/1.7 var(--cm-font-ui); color: var(--cm-text); }

.cm-changelog__entries { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.cm-changelog__entries li {
  display: grid; grid-template-columns: 108px minmax(0, 1fr); gap: 10px; align-items: baseline;
}
.cm-changelog__entries .cm-badge { justify-self: start; }
.cm-changelog__title { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
/* The optional second sentence, on its own line under the title rather than beside it: entry
   titles stay scannable in a column, details do not have to be. */
.cm-changelog__detail {
  grid-column: 2; display: block; margin-top: 2px;
  font: 400 13px/1.6 var(--cm-font-ui); color: var(--cm-muted);
}

.cm-changelog__internal { margin-top: 14px; border-top: 1px solid var(--cm-border-soft); padding-top: 10px; }
.cm-changelog__internal summary {
  list-style: none; cursor: pointer; font: 600 12.5px var(--cm-font-ui); color: var(--cm-muted);
}
.cm-changelog__internal summary::-webkit-details-marker { display: none; }
.cm-changelog__internal summary::before { content: '▸ '; color: var(--cm-faint); }
.cm-changelog__internal[open] summary::before { content: '▾ '; }

@media (max-width: 1100px) {
  .cm-changelog-layout { grid-template-columns: minmax(0, 1fr); }
  .cm-changelog__aside { position: static; }
  .cm-changelog__filters { flex-direction: row; flex-wrap: wrap; }
  .cm-changelog__filter { width: auto; }
  .cm-changelog__stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
}
@media (max-width: 700px) {
  .cm-changelog__entries li { grid-template-columns: minmax(0, 1fr); }
  .cm-changelog__detail { grid-column: 1; }
}

/* ==========================================================================
   Description technique (design_handoff_description_technique)
   Full application width, every block of content on a bordered white card.

   The layout rule that fixes the previous version: a section is split into columns only when its
   blocks are of comparable height. As soon as one is markedly longer than its neighbour (design
   patterns, external services) it goes full width with an internal grid. Two independent columns
   scrolling at their own pace left half the page empty and made the eye jump.

   Spacing, from the handoff: 16px between the cards of one section, 34px between two sections.
   ========================================================================== */

.cm-tech { --cm-tech-card-gap: 16px; --cm-tech-section-gap: 34px; }
.cm-tech p { font: 400 14.5px/1.75 var(--cm-font-ui); color: var(--cm-text); margin: 0 0 12px; }
.cm-tech h3 { font: 600 17px var(--cm-font-title); color: var(--cm-ink); margin: 0 0 14px; }

.cm-tech__card {
  border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
  padding: 24px 28px 26px; margin-bottom: var(--cm-tech-card-gap);
}
/* The last card of a section carries the wider gap - this is what was missing under the Tests
   block, which sat flush against the banner below it. */
.cm-tech__card--section-end, .cm-tech__row--section-end { margin-bottom: var(--cm-tech-section-gap); }
.cm-tech__card-head { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; margin-bottom: 16px; }
.cm-tech__card-head h3 { margin: 0; }
.cm-tech__card-head span { font: 400 13.5px/1.6 var(--cm-font-ui); color: var(--cm-muted); flex: 1 1 260px; }
.cm-tech__card-title { display: flex; align-items: center; gap: 11px; margin-bottom: 14px; }
.cm-tech__card-title h3 { margin: 0; }
.cm-tech__card-title img { width: 26px; height: 26px; object-fit: contain; }

/* ---- Intro: chapeau on the left, the note about the figures on the right ---- */
.cm-tech__intro { display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 40px; align-items: start; padding: 26px 30px 28px; }
.cm-tech__lede { font-size: 16px !important; line-height: 1.8 !important; margin: 0 !important; text-wrap: pretty; }
.cm-tech__aside-note { border-left: 3px solid var(--cm-gold); padding: 2px 0 2px 16px; }
.cm-tech__aside-note p { margin: 0; font-size: 13.5px; line-height: 1.75; color: var(--cm-muted); }

.cm-tech__kicker {
  display: block; font: 600 10.5px var(--cm-font-ui); letter-spacing: 1.6px;
  text-transform: uppercase; color: var(--cm-gold-deep); margin-bottom: 7px;
}
.cm-tech__kicker--gold { color: var(--cm-gold); }
.cm-tech__kicker--blue { color: var(--cm-brand-strong); }

/* ---- Section rules: a gold small-caps label, or an h2, followed by a hairline ---- */
.cm-tech__rule { display: flex; align-items: baseline; gap: 14px; margin: 0 0 14px; scroll-margin-top: 16px; }
.cm-tech__rule h2 { margin: 0; font: 600 19px var(--cm-font-title); color: var(--cm-ink); }
.cm-tech__rule > span:first-child {
  font: 600 11px var(--cm-font-ui); letter-spacing: 2.4px; text-transform: uppercase; color: var(--cm-gold-deep);
}
.cm-tech__rule::after { content: ''; flex: 1; height: 1px; background: var(--cm-border); }
.cm-tech__rule--gold::after { background: #e3d3ab; }
.cm-tech__section-lede { font-size: 14px !important; color: var(--cm-muted) !important; margin-bottom: 22px !important; }
.cm-tech__note { font-size: 13.5px !important; color: var(--cm-muted) !important; }
.cm-tech__note--section { margin-bottom: var(--cm-tech-section-gap) !important; }

/* ---- Anchor pills + the outbound link to the repository ---- */
.cm-tech__anchors { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 30px; }
.cm-tech__anchor {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--cm-border); border-radius: 999px; padding: 5px 14px;
  background: var(--cm-surface); color: var(--cm-text); text-decoration: none;
  font: 600 12.5px var(--cm-font-ui);
}
.cm-tech__anchor:hover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-tech__anchor.is-active { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong); }
.cm-tech__anchor--code { margin-left: auto; color: var(--cm-muted); }
.cm-tech__gh { flex: none; }

/* ---- Volumetry ---- */
.cm-tech__metrics { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; margin-bottom: 12px; }
.cm-tech__metric {
  border: 1px solid var(--cm-border); border-radius: 10px; background: var(--cm-surface);
  padding: 14px 16px 13px;
}
.cm-tech__metric-value { display: block; font: 600 26px var(--cm-font-title); color: var(--cm-brand-strong); line-height: 1.1; }
.cm-tech__metric-label { display: block; font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); margin-top: 6px; }
.cm-tech__metric-hint { display: block; font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint); margin-top: 2px; }

/* ---- Rows. Each variant exists for one height relationship, never "because two things fit". ---- */
.cm-tech__row { display: grid; gap: var(--cm-tech-card-gap); margin-bottom: var(--cm-tech-card-gap); align-items: start; }
.cm-tech__row--main-aside { grid-template-columns: minmax(0, 1fr) 420px; align-items: stretch; }
.cm-tech__row--aside-main { grid-template-columns: 420px minmax(0, 1fr); }
.cm-tech__row--half { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cm-tech__row > .cm-tech__card, .cm-tech__stackcol > .cm-tech__card:last-child { margin-bottom: 0; }
.cm-tech__stackcol { display: flex; flex-direction: column; gap: var(--cm-tech-card-gap); }
.cm-tech__two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px; }

/* ---- The navy diagram: one base, two presentations ---- */
.cm-tech__diagram {
  background: var(--cm-navy); border-radius: 12px; padding: 24px 26px; color: #cfdde9;
  display: flex; flex-direction: column; justify-content: center; gap: 14px;
}
.cm-tech__diagram-row { display: flex; gap: 10px; }
.cm-tech__diagram-box {
  flex: 1; border: 1px solid rgba(255, 255, 255, .22); border-radius: 9px; padding: 11px 12px;
  text-align: center; font: 600 13px var(--cm-font-ui); color: #fff;
}
.cm-tech__diagram-box--gold { border-color: rgba(201, 160, 78, .55); background: rgba(201, 160, 78, .12); color: #f0e2c4; font-size: 13.5px; padding: 13px; }
.cm-tech__diagram-arrows { display: flex; justify-content: center; gap: 120px; color: rgba(255, 255, 255, .4); font-size: 15px; line-height: 1; }
.cm-tech__diagram-arrows--one { gap: 0; }

/* ---- Technical stack: 5 across, two rows ---- */
.cm-tech__stack-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; }
.cm-tech__stack-item { border: 1px solid #e4ebf1; border-radius: 10px; padding: 14px 16px; display: flex; flex-direction: column; gap: 8px; }
.cm-tech__stack-head { display: flex; align-items: center; gap: 10px; }
.cm-tech__stack-logo {
  flex: none; width: 38px; height: 38px; border-radius: 8px; background: var(--cm-surface);
  border: 1px solid var(--cm-border); display: grid; place-items: center; overflow: hidden;
}
.cm-tech__stack-logo--inline { width: 30px; height: 30px; }
.cm-tech__stack-logo img { width: 24px; height: 24px; object-fit: contain; }
/* Twig, OpenLDAP and FrankenPHP exist in neither icon collection - a text monogram, as the handoff
   provides for. */
.cm-tech__stack-mono { font: 500 9.5px ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--cm-brand-strong); }
.cm-tech__stack-id { display: flex; flex-direction: column; min-width: 0; }
.cm-tech__stack-id .cm-tech__kicker { margin-bottom: 0; letter-spacing: 1.4px; font-size: 10px; }
.cm-tech__stack-name { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tech__stack-desc { font-size: 13px !important; line-height: 1.6 !important; color: var(--cm-muted) !important; margin: 0 !important; }

/* ---- Numbered steps: request journey and deployment, 5 columns divided by hairlines ---- */
.cm-tech__steps-grid { display: grid; grid-template-columns: repeat(5, 1fr); }
.cm-tech__step { display: flex; flex-direction: column; gap: 9px; padding: 0 18px; border-left: 1px solid var(--cm-border-soft); }
.cm-tech__step:first-child { padding-left: 0; border-left: 0; }
.cm-tech__step-head { display: flex; align-items: center; gap: 9px; }
.cm-tech__step-n {
  flex: none; width: 24px; height: 24px; border-radius: 50%; background: var(--cm-brand-strong);
  color: #fff; display: grid; place-items: center; font: 600 12px var(--cm-font-ui);
}
.cm-tech__step-n--gold { background: var(--cm-gold); color: var(--cm-navy); font-weight: 700; }
.cm-tech__step-title { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tech__step-text { font-size: 13px !important; line-height: 1.65 !important; color: var(--cm-muted) !important; margin: 0 !important; }

/* A file path, everywhere it appears: journey step, design pattern, branch name. */
.cm-tech__path {
  display: block; font: 400 11.5px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--cm-faint); background: var(--cm-surface-alt);
  border: 1px solid var(--cm-border-soft); border-radius: 6px; padding: 5px 8px;
}

/* ---- Branch legend ---- */
.cm-tech__branches { display: flex; flex-direction: column; margin-bottom: 16px; }
.cm-tech__branch { display: flex; align-items: center; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--cm-border-soft); }
.cm-tech__branch-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--cm-gold); }
.cm-tech__branch-name { flex: none; width: 74px; font: 500 12.5px ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--cm-brand-strong); }
.cm-tech__branch-desc { font: 400 13px/1.5 var(--cm-font-ui); color: var(--cm-muted); }

/* ---- Security: 8 measures, two columns, a hairline on the left of each ---- */
.cm-tech__security { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px 22px; }
.cm-tech__security-item { display: flex; flex-direction: column; gap: 3px; padding-left: 12px; border-left: 2px solid #e4ebf1; }
.cm-tech__security-term { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tech__security-text { font: 400 13px/1.6 var(--cm-font-ui); color: var(--cm-muted); }

/* ---- External services: full width, 3 columns - it is far longer than any neighbour ---- */
.cm-tech__services { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px 26px; }
.cm-tech__service { display: flex; flex-direction: column; gap: 3px; }
.cm-tech__service-term { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tech__service-text { font: 400 13.5px/1.65 var(--cm-font-ui); color: var(--cm-muted); }

.cm-tech__pipeline-foot {
  border-top: 1px solid var(--cm-border-soft); margin-top: 20px; padding-top: 16px;
  display: grid; grid-template-columns: minmax(0, 1fr) 420px; gap: 26px; align-items: start;
}
.cm-tech__pipeline-foot p { font-size: 14px !important; }
.cm-tech__pills { display: flex; flex-wrap: wrap; gap: 6px; }
.cm-tech__pill {
  border: 1px solid var(--cm-border); background: var(--cm-surface-alt); border-radius: 999px;
  padding: 4px 11px; font: 500 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--cm-muted);
}

/* ---- Layers ---- */
.cm-tech__layers { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.cm-tech__layer { display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: 16px; align-items: center; border: 1px solid #e4ebf1; border-radius: 9px; padding: 12px 14px; }
.cm-tech__layer-term { font: 600 13px var(--cm-font-ui); color: var(--cm-brand-strong); }
.cm-tech__layer-text { font: 400 13.5px/1.65 var(--cm-font-ui); color: var(--cm-muted); }

/* ---- Design patterns: full width, 3 columns, ten entries ---- */
.cm-tech__patterns { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.cm-tech__pattern { border: 1px solid #e4ebf1; border-radius: 9px; padding: 12px 14px; background: var(--cm-surface-soft); display: flex; flex-direction: column; gap: 3px; }
.cm-tech__pattern-name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tech__pattern-text { font: 400 13px/1.6 var(--cm-font-ui); color: var(--cm-muted); margin-bottom: 4px; }
.cm-tech__pattern .cm-tech__path { background: var(--cm-surface); }

/* ---- Tests: five cards, then the conclusion across the full width ---- */
.cm-tech__tests { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; margin-bottom: 16px; }
.cm-tech__test { border: 1px solid #e4ebf1; border-radius: 10px; padding: 14px 16px; background: var(--cm-surface-soft); display: flex; flex-direction: column; gap: 5px; }
.cm-tech__test-term { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tech__test-text { font: 400 13px/1.65 var(--cm-font-ui); color: var(--cm-muted); }

/* ---- Closing navy banner ---- */
.cm-tech__banner {
  background: var(--cm-navy); border-radius: 12px; padding: 26px 30px;
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 30px; align-items: center;
}
.cm-tech__banner p { font-size: 14.5px !important; line-height: 1.75 !important; color: #cfdde9 !important; }
.cm-tech__banner-btn {
  display: inline-flex; align-items: center; gap: 10px; white-space: nowrap;
  border: 1px solid rgba(201, 160, 78, .6); background: rgba(201, 160, 78, .14); color: #f0e2c4;
  border-radius: 9px; padding: 12px 20px; font: 600 13.5px var(--cm-font-ui); text-decoration: none;
}
.cm-tech__banner-btn:hover { background: rgba(201, 160, 78, .24); color: #f0e2c4; }

@media (max-width: 1400px) {
  .cm-tech__metrics { grid-template-columns: repeat(4, 1fr); }
  .cm-tech__patterns, .cm-tech__services { grid-template-columns: repeat(2, 1fr); }
  .cm-tech__stack-grid, .cm-tech__tests { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 1200px) {
  .cm-tech__intro, .cm-tech__row--main-aside, .cm-tech__row--aside-main,
  .cm-tech__row--half, .cm-tech__pipeline-foot { grid-template-columns: minmax(0, 1fr); }
  .cm-tech__steps-grid { grid-template-columns: repeat(2, 1fr); gap: 18px 0; }
  .cm-tech__step:nth-child(odd) { padding-left: 0; border-left: 0; }
}
@media (max-width: 900px) {
  .cm-tech__metrics, .cm-tech__stack-grid, .cm-tech__tests,
  .cm-tech__security, .cm-tech__services, .cm-tech__two,
  .cm-tech__patterns { grid-template-columns: minmax(0, 1fr); }
  .cm-tech__steps-grid { grid-template-columns: minmax(0, 1fr); }
  .cm-tech__step { padding-left: 0; border-left: 0; }
  .cm-tech__card { padding: 18px 18px 20px; }
  .cm-tech__banner { grid-template-columns: minmax(0, 1fr); }
}

/* -------------------------------------------------------------------------
   Data model screen (/technical/data-model) - four notations of the same
   schema, Mermaid-rendered client side, plus the MLD in its textual form.
   ------------------------------------------------------------------------- */
.cm-datamodel__toolbar {
  display: flex; flex-wrap: wrap; gap: 14px 24px;
  align-items: flex-end; justify-content: space-between; margin-bottom: 18px;
}
.cm-datamodel__domain { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.cm-datamodel__domain-label { font-weight: 600; color: var(--cm-ink); margin: 0; }
.cm-datamodel__domain .form-select { width: auto; min-width: 240px; }
.cm-datamodel__count { font-size: 13px; color: var(--cm-muted); }
.cm-datamodel__exports { display: flex; flex-direction: column; gap: 6px; }
.cm-datamodel__export-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
/* The notation tabs are buttons (client-side switch), not links: same skin as .cm-tabs links. */
button.cm-tabs__tab { border: 0; background: none; font: inherit; cursor: pointer; }
.cm-datamodel__panel[hidden] { display: none; }
.cm-datamodel__explain { max-width: 70ch; }
.cm-datamodel__scroll { overflow: auto; max-height: 78vh; border: 1px solid var(--cm-border); border-radius: 8px; padding: 12px; background: var(--cm-surface); }
.cm-datamodel__diagram svg { max-width: none; }
.cm-datamodel__mld {
  font-family: var(--tblr-font-monospace, monospace); font-size: 13px; line-height: 1.9;
  overflow-x: auto; border: 1px solid var(--cm-border); border-radius: 8px; padding: 14px 16px; background: var(--cm-surface);
}
.cm-datamodel__table { font-weight: 700; color: var(--cm-ink); }
.cm-datamodel__pk { text-decoration: underline; }
.cm-datamodel__fk { color: var(--cm-brand-strong); }
.cm-datamodel__open-btn { display: inline-flex; margin-top: 14px; }
/* ==========================================================================
   Zone / Légende questions (étude 2026-08-11) - the clickable support shared
   by passation, correction, the "Tester" tab and the import preview
   (templates/quiz/_zone_support.html.twig).
   ========================================================================== */

/* A zone is always a real <button>, inline with its surrounding text. */
.cm-zone {
    display: inline; border: 0; border-radius: 4px; padding: 0 2px; margin: 0;
    font: inherit; color: var(--cm-blue-tx); background: transparent; cursor: pointer;
    box-shadow: inset 0 0 0 1px transparent;
}
.cm-zone:hover:not(:disabled) { background: var(--cm-blue-bg-soft); box-shadow: inset 0 0 0 1px var(--cm-brand); }
.cm-zone:focus-visible { outline: 2px solid var(--cm-brand); outline-offset: 1px; }
.cm-zone:disabled { cursor: default; }
.cm-zone.is-selected { background: var(--cm-blue-bg-soft); box-shadow: 0 0 0 2px var(--cm-brand); font-weight: 700; }
/* Hint mode (entraînement): everything but the same-level candidates fades out. */
.cm-zone--dimmed { opacity: .25; }
/* Correction states - same palette as the blanks correction one block up. */
.cm-zone--good { background: #eaf3ee; box-shadow: 0 0 0 2px var(--cm-positive-tx); color: #25603d; font-weight: 700; }
.cm-zone--bad { background: var(--cm-red-bg); box-shadow: 0 0 0 2px var(--cm-red-tx); color: var(--cm-red-tx); }

/* A placed label rides inside its zone as a small chip (légende). */
.cm-zone__placed {
    display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: 10px;
    font: 600 11.5px var(--cm-font-ui); background: var(--cm-blue-bg-soft); color: var(--cm-blue-tx);
    border: 1px solid var(--cm-brand); vertical-align: 1px; white-space: nowrap;
}
.cm-zone.is-filled { box-shadow: inset 0 0 0 1px var(--cm-brand); }
.cm-zone--good .cm-zone__placed { background: #eaf3ee; color: #25603d; border-color: var(--cm-positive-tx); }
.cm-zone--bad .cm-zone__placed { background: var(--cm-red-bg); color: var(--cm-red-tx); border-color: var(--cm-red-tx); }

/* Prose support: comfortable reading rhythm, zones inherit it. */
.cm-zone-text { font: 400 17px/2 var(--cm-font-title); color: var(--cm-ink); }

/* Code support: numbered lines, monospace, horizontal scroll on its own. */
.cm-zone-code {
    background: var(--cm-surface-alt); border: 1px solid var(--cm-border-soft); border-radius: 10px;
    padding: 10px 0; overflow-x: auto;
}
.cm-zone-code ol { list-style: none; counter-reset: cm-zone-line; margin: 0; padding: 0; }
.cm-zone-code li {
    counter-increment: cm-zone-line; white-space: pre; padding: 0 14px 0 0;
    font: 400 13px/1.9 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--cm-ink);
}
.cm-zone-code li::before {
    content: counter(cm-zone-line); display: inline-block; width: 34px; margin-right: 12px;
    text-align: right; font-size: 11px; color: var(--cm-disabled); user-select: none;
}
.cm-zone-code .cm-zone { font: inherit; }

/* Image support: the drawn rectangles sit over the image, percent-positioned. */
.cm-zone-image { position: relative; display: inline-block; max-width: 100%; }
.cm-zone-image img { display: block; max-width: 100%; border-radius: 10px; }
.cm-zone-image .cm-zone--area {
    position: absolute; display: flex; align-items: center; justify-content: center;
    border: 2px dashed var(--cm-brand); border-radius: 8px; padding: 0;
    background: color-mix(in srgb, var(--cm-brand) 8%, transparent);
}
.cm-zone-image .cm-zone--area:hover:not(:disabled) { background: color-mix(in srgb, var(--cm-brand) 18%, transparent); box-shadow: none; }
.cm-zone-image .cm-zone--area.is-selected { border-style: solid; background: color-mix(in srgb, var(--cm-brand) 22%, transparent); box-shadow: none; }
.cm-zone-image .cm-zone--area .cm-zone__placed { margin-left: 0; }
.cm-zone-image .cm-zone--area.cm-zone--good { border: 2px solid var(--cm-positive-tx); background: color-mix(in srgb, var(--cm-positive-tx) 15%, transparent); }
.cm-zone-image .cm-zone--area.cm-zone--bad { border: 2px solid var(--cm-red-tx); background: color-mix(in srgb, var(--cm-red-tx) 15%, transparent); }

/* ---------- Editor (question bank): zones table + live preview ---------- */
.cm-zone-editor__rows { display: flex; flex-direction: column; border: 1px solid var(--cm-border-soft); border-radius: 10px; overflow: hidden; }
.cm-zone-editor__row {
    display: flex; align-items: center; gap: 10px; padding: 8px 12px;
    border-bottom: 1px solid var(--cm-border-soft); background: var(--cm-surface);
}
.cm-zone-editor__row:last-child { border-bottom: 0; }
.cm-zone-editor__id {
    font: 600 12px ui-monospace, Menlo, Consolas, monospace; background: var(--cm-surface-alt);
    border-radius: 6px; padding: 2px 8px; color: var(--cm-faint); flex: none;
}
.cm-zone-editor__text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px; }

/* ==========================================================================
   Apparier: two columns to relate, one row per pair
   The passation board and its review rendering share the same markup
   (templates/quiz/_matching_board.html.twig), which is why the states below
   are modifiers rather than separate blocks.
   ========================================================================== */
.cm-match { display: flex; flex-direction: column; gap: 8px; }
.cm-match__headers {
    display: grid; grid-template-columns: 1fr 24px 1fr; gap: 10px; padding: 0 2px;
    font: 600 11.5px var(--cm-font-ui); color: var(--cm-faint);
    text-transform: uppercase; letter-spacing: .5px;
}
/* The arrow column sits between the two, so the headers line up with what they name. */
.cm-match__headers span:last-child { grid-column: 3; }
.cm-match__row {
    display: grid; grid-template-columns: 1fr 24px 1fr; gap: 10px; align-items: center;
    padding: 6px 8px; border: 1px solid transparent; border-radius: 10px;
}
.cm-match__row--good { border-color: color-mix(in srgb, var(--cm-positive-tx) 45%, var(--cm-border)); background: var(--cm-green-bg); }
.cm-match__row--bad { border-color: color-mix(in srgb, var(--cm-red-tx) 45%, var(--cm-border)); background: var(--cm-red-bg); }
.cm-match__left {
    font: 400 14.5px var(--cm-font-ui); color: var(--cm-ink);
    padding: 8px 12px; background: var(--cm-surface-alt);
    border: 1px solid var(--cm-border-soft); border-radius: 8px;
}
.cm-match__link { text-align: center; color: var(--cm-disabled); font-size: 15px; }
.cm-match__slot {
    display: block; width: 100%; text-align: left; min-height: 40px;
    padding: 8px 12px; background: var(--cm-surface); color: var(--cm-ink);
    border: 1px dashed var(--cm-border); border-radius: 8px;
    font: 400 14.5px var(--cm-font-ui); cursor: pointer;
}
.cm-match__slot:hover:not(:disabled) { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); }
.cm-match__slot.is-filled { border-style: solid; border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); color: var(--cm-blue-tx); font-weight: 600; }
/* An empty slot has to keep its height, or the rows jump as the student fills them in. */
.cm-match__slot-text:empty::before { content: '\00a0'; }
.cm-match__row--good .cm-match__slot { border-color: var(--cm-positive-tx); background: transparent; color: var(--cm-positive-tx); }
.cm-match__row--bad .cm-match__slot { border-color: var(--cm-red-tx); background: transparent; color: var(--cm-red-tx); }
/* Only strike what the student actually wrote: an unanswered slot is empty apart from its spacer,
   and a line-through over that renders as a stray dash. */
.cm-match__row--bad .cm-match__slot.is-filled { text-decoration: line-through; }
/* The expected item rides under the slot rather than in a fourth column: on a phone the row is
   already two texts wide, and a third one would shrink all of them below readable. */
.cm-match__expected {
    grid-column: 3; font: 600 12.5px var(--cm-font-ui); color: var(--cm-positive-tx);
    padding-left: 12px;
}
.cm-match__row--good .cm-match__expected { display: none; }

@media (max-width: 575.98px) {
    .cm-match__headers { display: none; }
    .cm-match__row { grid-template-columns: 1fr; gap: 4px; }
    .cm-match__link { display: none; }
    .cm-match__expected { grid-column: 1; padding-left: 0; }
}

/* An image item, on either column and in the choice pool. Capped rather than sized: the pictures a
   teacher uploads are whatever their phone produced, and a row has to stay a row. */
.cm-match__img { display: block; max-width: 100%; max-height: 92px; border-radius: 6px; object-fit: contain; }
.cm-match--left-image .cm-match__left { background: transparent; padding: 4px; }
.cm-match__slot .cm-match__img { max-height: 72px; }
.cm-wordbank__word--image { padding: 6px; }
.cm-wordbank__word--image .cm-match__img { max-height: 72px; }
.cm-match__expected .cm-match__img { max-height: 64px; margin-top: 2px; }

/* ==========================================================================
   Numérique / Calculée: a statement carrying drawn values, and one number field
   ========================================================================== */
/* The statement reads as prose, with the drawn values standing out enough that a student sees
   at a glance which numbers are theirs - two of them side by side get different ones. */
.cm-numeric-statement { font: 400 17px/1.9 var(--cm-font-title); color: var(--cm-ink); }
.cm-numeric-statement--review { font-size: 14.5px; line-height: 1.7; }
.cm-numeric-value {
    font-weight: 700; padding: 1px 7px; border-radius: 6px;
    background: var(--cm-blue-bg-soft); color: var(--cm-blue-tx);
}

.cm-numeric-field { display: flex; align-items: center; gap: 10px; max-width: 420px; }
.cm-numeric-field input { font: 600 18px var(--cm-font-ui); text-align: right; }

/* Réponse courte: one field, sized like the answer it expects - a word or a short phrase, not a
   paragraph. Left-aligned unlike the numeric one, since what goes in it is text. */
.cm-shortanswer { max-width: 480px; }
.cm-shortanswer input { font: 500 17px var(--cm-font-ui); }
/* A fixed unit sits beside the field rather than inside it: the student types only the number,
   and the unit is not something they can accidentally delete. */
.cm-numeric-field__unit {
    font: 600 15px var(--cm-font-ui); color: var(--cm-faint); flex: none;
    padding: 8px 12px; background: var(--cm-surface-alt);
    border: 1px solid var(--cm-border-soft); border-radius: 8px;
}
.cm-numeric-field__hint { font-size: 12.5px; color: var(--cm-faint); flex: none; }

.cm-numeric-badge--good { background: var(--cm-green-bg); color: var(--cm-positive-tx); border: 1px solid var(--cm-positive-tx); font-weight: 600; padding: 6px 12px; }
.cm-numeric-badge--bad { background: var(--cm-red-bg); color: var(--cm-red-tx); border: 1px solid var(--cm-red-tx); font-weight: 600; padding: 6px 12px; }
.cm-numeric-badge--expected { background: var(--cm-surface-alt); color: var(--cm-ink); border: 1px solid var(--cm-border); font-weight: 600; padding: 6px 12px; }

/* ---------- Editor (question bank): the variable table of a calculée ---------- */
.cm-numeric-editor__field { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--cm-faint); }
.cm-numeric-editor__field input { width: 84px; }
.cm-numeric-editor__warning { color: var(--cm-red-tx); font-weight: 600; }

/* ---------- Editor (question bank): the pair rows ---------- */
.cm-match-editor { display: flex; flex-direction: column; gap: 10px; }
.cm-match-editor__row { display: flex; align-items: flex-start; gap: 8px; }
.cm-match-editor__pair { flex: 1; min-width: 0; display: flex; align-items: flex-start; gap: 8px; }
.cm-match-editor__side { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.cm-match-editor__link { color: var(--cm-disabled); flex: none; padding-top: 6px; }
.cm-match-editor__feedback { flex: 1.2; min-width: 0; }
.cm-match-editor__actions { display: flex; align-items: center; gap: 2px; flex: none; }
/* The image widget of a side only exists while that column is an image one - both are always in
   the DOM so switching a column back and forth never loses what was typed. */
.cm-match-editor__image { display: none; flex-direction: column; gap: 6px; }
.cm-match-editor__side.is-image .cm-match-editor__image { display: flex; }
.cm-match-editor__image img { max-height: 90px; align-self: flex-start; border-radius: 6px; border: 1px solid var(--cm-border-soft); }
.cm-match-editor__thumbs { display: flex; flex-wrap: wrap; gap: 10px; }
.cm-match-editor__thumb { position: relative; }
.cm-match-editor__thumb img { max-height: 80px; border-radius: 6px; border: 1px solid var(--cm-border-soft); display: block; }
.cm-match-editor__thumb .cm-answer-remove { position: absolute; top: -6px; right: -6px; background: var(--cm-surface); border-radius: 50%; }

/* ==========================================================================
   Access conditions (.cm-condition, .cm-lockwhy) - design/comparaison/
   creas_espace_cours_conditions.html, écrans 4 and 5.

   Two sides of one feature: the teacher's list of conditions, and the single line a student reads
   on a row they cannot open yet. The segmented "toutes / au moins une" toggle and the two display
   cards reuse .cm-segment and .cm-radiocard as they stand - the créa draws them identically, and
   the pixels of this design system exist once.
   ========================================================================== */
/* The card's own heading, as the créa draws it: a label, not a title. */
.cm-condition-heading { font: 700 11px var(--cm-font-ui); letter-spacing: .5px; text-transform: uppercase; color: var(--cm-faint); margin: 0; }
.cm-condition-list { display: flex; flex-direction: column; gap: 9px; margin-bottom: 12px; }
.cm-condition {
    display: flex; align-items: center; gap: 10px; padding: 12px 14px; font-size: 13px;
    border: 1px solid var(--cm-border-soft); border-radius: 8px; background: var(--cm-surface-soft);
}
.cm-condition__icon {
    flex: none; width: 22px; height: 22px; border-radius: 6px; display: grid; place-items: center;
    background: var(--cm-blue-bg); color: var(--cm-blue-tx); font: 700 11px var(--cm-font-ui);
}
.cm-condition__text { flex: 1; min-width: 0; color: var(--cm-ink); }
/* The resolved date under a séance: what the condition is worth today, next to what the teacher
   chose. Its whole point is to make visible that the condition follows the slot. */
.cm-condition__note { display: block; font-size: 12px; color: var(--cm-muted); margin-top: 4px; }
.cm-condition__note b { color: var(--cm-ink); font-weight: 600; }
/* Why the builder refused the row. Sits where the note sits, in the danger colour so it reads as a
   refusal rather than as the note's neighbour. */
.cm-condition__error { display: block; font-size: 12px; color: var(--cm-red-tx); font-weight: 600; }
.cm-condition__error[hidden] { display: none; }
/* "Ajouter" is disabled when the chosen type has nothing to point at - it has to look it, or the
   click that does nothing is the same silence the empty select used to be. */
.cm-condition-builder .cm-btn:disabled { opacity: .5; cursor: not-allowed; }
.cm-condition__remove {
    flex: none; border: 0; background: none; padding: 0 2px; color: var(--cm-disabled); font-size: 15px; line-height: 1;
}
.cm-condition__remove:hover { color: var(--cm-red-tx); }
.cm-condition-empty { font-size: 13px; color: var(--cm-faint); margin-bottom: 12px; }

/* The row being built: a type, then whatever that type needs. Fields that do not apply are hidden
   rather than disabled - an empty percentage box next to a date says nothing useful. */
.cm-condition-builder { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.cm-condition-builder select, .cm-condition-builder input { max-width: 260px; }
.cm-condition-builder input[type="number"] { max-width: 130px; }
.cm-condition-builder [hidden] { display: none; }

/* Student side: the sentence on a greyed row. Never an error style - it is the way out, not a
   refusal. */
.cm-lockwhy {
    /* align-self keeps the chip hugging its sentence: inside a column flex row body it would
       otherwise stretch the full width and stop reading as a chip. */
    display: inline-flex; align-self: flex-start; align-items: center; gap: 6px; margin-top: 7px; padding: 5px 10px;
    border: 1px solid var(--cm-border-soft); border-radius: 7px;
    background: var(--cm-surface-alt); color: var(--cm-muted); font-size: 12px;
}
.cm-lockwhy svg { flex: none; opacity: .7; }
/* A locked line on "Travail à faire": greyed, still there, and carrying its way out. */
.cm-tw-row--locked { background: var(--cm-surface-soft); }
.cm-tw-row--locked .cm-tw-row__title, .cm-tw-row--locked .cm-tw-row__meta { color: var(--cm-faint); }

/* ---------- Import de questions (.cm-qimport-*) - the deposit, and the built prompt ----------
   The rest of this screen is the audio handoff's own components (.cm-audio-card, .cm-audio-option,
   .cm-audio-mode): same pixels, declared once. Only what that handoff has no equivalent for is
   here. */

.cm-qimport-images { display: flex; flex-wrap: wrap; gap: 12px; }
.cm-qimport-image {
    position: relative; margin: 0; width: 132px;
    border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm);
    background: var(--cm-surface-soft); overflow: hidden;
}
.cm-qimport-image img { display: block; width: 100%; height: 84px; object-fit: cover; background: var(--cm-surface); }
.cm-qimport-image figcaption { display: flex; align-items: center; gap: 6px; padding: 7px 9px; min-width: 0; }
/* The reference is the only thing about this image that reaches the model, so it is what the eye
   lands on - the file name is context, not identity. */
.cm-qimport-image__ref {
    flex: none; border-radius: 999px; padding: 2px 8px;
    background: var(--cm-blue-bg); color: var(--cm-blue-tx);
    font: 700 11px var(--cm-font-ui); letter-spacing: .3px;
}
.cm-qimport-image__name {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font: 400 12px var(--cm-font-ui); color: var(--cm-faint);
}
.cm-qimport-image form { position: absolute; top: 4px; right: 4px; }
.cm-qimport-image form button { background: var(--cm-surface); border-radius: 999px; line-height: 1; }

/* Same treatment as .cm-radiocard: on an expanded choice, Symfony's Bootstrap layout renders the
   whole .form-check block (input + its own label) rather than a bare <input>, and that block sits
   over the card - a click on the card lands on the wrapper, not on the radio. The destination cards
   write their own label, so Symfony's is hidden rather than left in duplicate; the enclosing
   <label> keeps ticking the input natively, with no `for` attribute. */
.cm-audio-mode .form-check { display: contents; }
.cm-audio-mode .form-check-label { position: absolute; opacity: 0; width: 1px; height: 1px; }

.cm-qimport-prompt {
    max-height: 460px; overflow: auto; width: 100%; margin: 0;
    background: var(--cm-surface-alt); border: 1px solid var(--cm-border-soft); border-radius: 10px;
    padding: 14px 16px; font-size: 11.5px; line-height: 1.6; white-space: pre-wrap;
    /* Explicit, and not redundant: Tabler paints <pre> in the near-white it uses for code on a dark
       block, which on this card's own light surface is white on white. */
    color: var(--cm-ink);
}

/* The character counter of the course block the teacher attaches to a generation prompt. Neutral
   while it fits, red past the stated cap - and never a truncation either way: the cap is a number
   this application states and cannot know (QuizPromptCatalog::MAX_CONTEXT_CHARACTERS), so going over
   is a sentence addressed to the teacher, naming the two levers, not a limit that cuts. */
.cm-qimport-context-count { font: 400 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-qimport-context-count.is-over { color: var(--cm-red-tx); font-weight: 600; }

/* A chip the course has nothing to put in stays visible and disabled rather than disappearing: both
   levers have to be readable for the sentence about them to make sense. No extra class - the disabled
   input is the state. */
.cm-audio-option:has(input:disabled) { opacity: .55; cursor: not-allowed; }

/* --- Séquence import assistant (templates/library/sequence_import_*.html.twig) --- */

/* Step 1's two cards. A whole card is the click target, so the arrow is decoration and the <a>
   carries the affordance. */
.cm-import-choice { display: block; color: inherit; text-decoration: none; transition: border-color .15s ease; }
.cm-import-choice:hover { border-color: var(--cm-brand); }
/* On the quiz import screen the same two cards are a *state* rather than a departure - the mode lives
   in the query string, so the current one has to say it is the current one. */
.cm-import-choice.is-active { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); }
.cm-import-choice.is-active .cm-import-choice__arrow { color: var(--cm-brand-strong); }
.cm-import-choice__meta { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.cm-import-choice__arrow { margin-left: auto; color: var(--cm-muted); font-size: 18px; }

/* Step 2 is a set of instructions, and reads as one: numbered, roomy, one action per line. */
.cm-import-howto { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 8px; font-size: 13px; }

/* Step 4's séance rows - closed by default, because the check is "four séances, the right titles,
   the right durations", and the déroulé is what one opens when a row looks wrong. */
.cm-import-seance { border: 1px solid var(--cm-border-soft); border-radius: 10px; background: var(--cm-surface-alt); }
.cm-import-seance > summary { display: flex; align-items: center; gap: 10px; padding: 10px 14px; cursor: pointer; }
.cm-import-seance__rank {
    width: 22px; height: 22px; border-radius: 50%; flex: none; display: grid; place-items: center;
    background: var(--cm-bg); color: var(--cm-muted); font: 700 11px var(--cm-font-ui);
}
.cm-import-seance__title { font-weight: 600; margin-right: auto; }
.cm-import-phases { margin: 0; padding: 0 14px 12px 34px; display: flex; flex-direction: column; gap: 6px; font-size: 12.5px; }
.cm-import-phases li { display: flex; align-items: center; gap: 8px; }
.cm-import-phases__name { color: var(--cm-ink); }

/* An unplaced block: its name and the one decision it asks for on the same line, its text below so
   the teacher decides on what they can see rather than on a label. */
.cm-import-unplaced { border: 1px solid var(--cm-border-soft); border-radius: 10px; padding: 10px 14px; background: var(--cm-surface-alt); }
.cm-import-unplaced__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cm-import-unplaced__title { font-weight: 600; margin-right: auto; }
.cm-import-unplaced__head .form-select { width: auto; min-width: 220px; }
.cm-import-unplaced__body {
    margin-top: 8px; font-size: 12.5px; line-height: 1.5; white-space: pre-wrap;
    max-height: 160px; overflow: auto; color: var(--cm-muted);
}

/* ==========================================================================
   Base documentaire (design_handoff_base_documentaire, écrans 2a - 2h)
   Two families: the browsing screens (rail + card grids) and the article, which is a reading
   column with a side panel. Everything is written in --cm-* tokens, so the dark theme follows
   without a rule of its own - the handoff's own hex values are exactly this file's tokens
   (#1B6BA8 = --cm-brand, #9a7729 = --cm-gold-deep, #dde5ec = --cm-border, ...).

   The one deliberate departure from the mockups: the perimeter rail indents less than the
   22/36px of the handoff, which was drawn on a 1440px canvas with a narrower shell.
   ========================================================================== */

/* The destructive button of the dashboard and of the editor - brick outline, filled on hover,
   like the .cm-action--danger row actions. */
.cm-btn--danger { background: var(--cm-surface); border-color: var(--cm-red-tx); color: var(--cm-red-tx); }
.cm-btn--danger:hover { background: var(--cm-red-tx); color: #fff; }

/* ---- Kickers and rules, the same idiom as the Description technique screen ---- */
.cm-doc__kicker {
    display: block; font: 600 10.5px var(--cm-font-ui); letter-spacing: 1.8px;
    text-transform: uppercase; color: var(--cm-faint); margin-bottom: 8px;
}
.cm-doc__kicker--inline { margin-top: 14px; }
.cm-doc__rule { display: flex; align-items: baseline; gap: 14px; margin: 0 0 14px; }
.cm-doc__rule > span:first-child {
    font: 600 11px var(--cm-font-ui); letter-spacing: 2.2px; text-transform: uppercase; color: var(--cm-muted);
}
.cm-doc__rule--gold > span:first-child { color: var(--cm-gold-deep); }
.cm-doc__rule::after { content: ''; flex: 1; height: 1px; background: var(--cm-border); }
.cm-doc__rule--gold::after { background: var(--cm-gold-bg); }
.cm-doc__empty { font: 400 14px var(--cm-font-ui); color: var(--cm-muted); }

/* ---- 2a / 2b: navigation rail + card grids ---- */
.cm-doc { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 34px; align-items: start; }
.cm-doc__rail { position: sticky; top: 16px; }
.cm-doc__rail-sep { border: 0; border-top: 1px solid var(--cm-border); margin: 22px 0 18px; }
.cm-doc__tree { display: flex; flex-direction: column; gap: 2px; }
.cm-doc__tree-item {
    display: block; padding: 6px 10px; border: 1px solid transparent; border-radius: 8px;
    font: 400 13px var(--cm-font-ui); color: var(--cm-muted); text-decoration: none;
}
.cm-doc__tree-item:hover { background: var(--cm-surface); color: var(--cm-brand-strong); }
.cm-doc__tree-item.is-active {
    background: var(--cm-surface); border-color: var(--cm-border);
    color: var(--cm-ink); font-weight: 600;
}
.cm-doc__grid { display: grid; gap: 18px; margin-bottom: 26px; }
.cm-doc__grid--3 { grid-template-columns: repeat(3, 1fr); }
.cm-doc__grid--4 { grid-template-columns: repeat(4, 1fr); }

.cm-doc-card {
    display: flex; flex-direction: column; gap: 9px; text-decoration: none;
    border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
    padding: 16px 18px 15px;
}
.cm-doc-card:hover { border-color: var(--cm-brand); box-shadow: 0 4px 14px rgba(18, 52, 77, .09); }
.cm-doc-card__tag { font: 600 10.5px var(--cm-font-ui); letter-spacing: 1.4px; text-transform: uppercase; color: var(--cm-gold-deep); }
.cm-doc-card__title { font: 600 15.5px/1.35 var(--cm-font-title); color: var(--cm-ink); }
.cm-doc-card__excerpt { font: 400 12.5px/1.5 var(--cm-font-ui); color: var(--cm-muted); }
.cm-doc-card__foot { display: flex; align-items: center; gap: 10px; margin-top: auto; padding-top: 6px; }
.cm-doc-card__author { font: 600 11.5px var(--cm-font-ui); color: var(--cm-muted); }
.cm-doc-card__date { margin-left: auto; font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-doc-card__status {
    display: inline-block; margin-right: 6px; padding: 1px 8px; border-radius: 999px;
    font: 600 10.5px var(--cm-font-ui); background: var(--cm-border-soft); color: var(--cm-muted);
}
.cm-doc-card__status--draft { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }

.cm-doc-search { position: relative; display: inline-block; width: min(250px, 50vw); }
.cm-doc-search__input {
    width: 100%; padding: 8px 14px; border: 1px solid var(--cm-border); border-radius: 9px;
    background: var(--cm-surface); color: var(--cm-ink); font: 400 13.5px var(--cm-font-ui);
}
.cm-doc-search__input:focus { outline: none; border-color: var(--cm-brand); }

/* ---- 2c / 2e: the article and its right-hand column ---- */
.cm-doc-article { display: grid; grid-template-columns: minmax(0, 1fr) 296px; gap: 22px; align-items: start; }
.cm-doc-article__body {
    border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
    padding: 30px 44px 38px;
}
.cm-doc-article__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 18px; }
.cm-doc-article__meta { font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-doc-article__excerpt { font: 400 16px/1.7 var(--cm-font-ui); color: var(--cm-muted); margin: 0 0 18px; }
.cm-doc-article__content { font: 400 16.5px/1.7 var(--cm-font-ui); color: var(--cm-text); }
.cm-doc-article__content h2 { font: 600 19px var(--cm-font-title); color: var(--cm-ink); margin: 26px 0 12px; }
.cm-doc-article__content h3 { font: 600 16.5px var(--cm-font-title); color: var(--cm-ink); margin: 22px 0 10px; }
.cm-doc-article__content p { margin: 0 0 14px; }
/* An image pasted into the body is sized by the author in the editor; the page only guarantees it
   never pushes the reading column wider than itself. */
.cm-doc-article__content img { max-width: 100%; height: auto; border-radius: 8px; }
.cm-doc-article__content table { width: 100%; border-collapse: collapse; margin: 8px 0 18px; }
.cm-doc-article__content th {
    text-align: left; background: var(--cm-surface-alt); padding: 9px 12px;
    font: 600 10.5px var(--cm-font-ui); letter-spacing: 1.2px; text-transform: uppercase; color: var(--cm-faint);
}
.cm-doc-article__content td { padding: 10px 12px; border-top: 1px solid var(--cm-border-faint); font-size: 14px; }

.cm-doc-aside { display: flex; flex-direction: column; gap: 14px; }
.cm-doc-panel { display: flex; flex-direction: column; gap: 14px; }
/* A teacher who does not own the article: the panel is there, it does not operate. */
.cm-doc-panel--inert { opacity: .45; pointer-events: none; }
.cm-doc-panel__action { width: 100%; margin-top: 12px; }
.cm-doc-card-block {
    border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
    padding: 16px 16px 18px;
}
.cm-doc-perimeter { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.cm-doc-perimeter li, .cm-doc-perimeter__row {
    display: flex; align-items: center; gap: 9px;
    font: 400 13px var(--cm-font-ui); color: var(--cm-text);
}
.cm-doc-perimeter--editable { display: flex; flex-direction: column; gap: 8px; }
.cm-doc-perimeter__row { margin: 0; }
.cm-doc-check {
    width: 16px; height: 16px; flex: none; border-radius: 4px; border: 1px solid var(--cm-disabled);
    display: grid; place-items: center; font-size: 11px; color: #fff; background: var(--cm-surface);
}
.cm-doc-check.is-checked { background: var(--cm-brand); border-color: var(--cm-brand); }

.cm-doc-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.cm-doc-pill {
    display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px; border-radius: 999px;
    border: 1px solid var(--cm-border); background: var(--cm-surface);
    font: 600 12px var(--cm-font-ui); color: var(--cm-muted); text-decoration: none;
}
.cm-doc-pill--gold { border-color: var(--cm-gold-bg); background: var(--cm-highlight-bg); color: var(--cm-gold-deep); }
.cm-doc-pill--on { border-color: #a9c6dd; background: var(--cm-blue-bg-soft); color: var(--cm-blue-tx); }
.cm-doc-pill--off { border-color: #e6b8b3; background: var(--cm-red-bg); color: var(--cm-red-tx); }
.cm-doc-pill--link:hover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-doc-pill--checkable { cursor: pointer; }
.cm-doc-pill--checkable input { margin: 0; }
.cm-doc-pill--checkable:has(input:checked) { border-color: #a9c6dd; background: var(--cm-blue-bg-soft); color: var(--cm-blue-tx); }
.cm-doc-pill--checkable label { margin: 0; cursor: pointer; }
.cm-doc-pill--removable { padding-right: 6px; }
.cm-doc-pill__remove {
    border: 0; background: transparent; color: inherit; cursor: pointer;
    font-size: 14px; line-height: 1; padding: 0 2px;
}
.cm-doc-figure { font: 600 26px var(--cm-font-title); color: var(--cm-ink); margin: 0 0 4px; }
.cm-doc-figure span { font: 400 13px var(--cm-font-ui); color: var(--cm-muted); }
.cm-doc-date { font: 600 15px var(--cm-font-title); color: var(--cm-ink); margin: 0; }
.cm-doc-note { font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); margin: 10px 0 0; }
.cm-doc-author { display: flex; align-items: center; gap: 11px; }
.cm-doc-author__avatar {
    width: 38px; height: 38px; border-radius: 50%; flex: none; display: grid; place-items: center;
    background: var(--cm-blue-bg-soft); color: var(--cm-blue-tx); font: 700 13px var(--cm-font-ui);
}
.cm-doc-author__name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }

.cm-doc-attachments { margin-top: 26px; border: 1px solid var(--cm-gold-bg); border-radius: 10px; background: var(--cm-highlight-bg); padding: 16px 18px 18px; }
.cm-doc-attachments__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.cm-doc-attachment {
    display: flex; align-items: center; gap: 11px; text-decoration: none;
    border: 1px solid var(--cm-border); border-radius: 9px; background: var(--cm-surface); padding: 11px 13px;
}
.cm-doc-attachment:hover { border-color: var(--cm-brand); background: var(--cm-surface-alt); }
.cm-doc-attachment__icon {
    width: 34px; height: 34px; flex: none; border-radius: 8px; display: grid; place-items: center;
    background: var(--cm-highlight-bg); color: var(--cm-gold-deep);
}
.cm-doc-attachment__text { display: flex; flex-direction: column; min-width: 0; }
.cm-doc-attachment__name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); overflow-wrap: anywhere; }
.cm-doc-attachment__meta { font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-doc-attachment--editable { justify-content: space-between; }
.cm-doc-attachment__remove { border: 0; background: transparent; color: var(--cm-faint); font-size: 18px; cursor: pointer; }
.cm-doc-attachment__remove:hover { color: var(--cm-red-tx); }

/* ---- 2d: the editor ---- */
.cm-doc-editor { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 22px; align-items: start; }
.cm-doc-editor__main {
    display: flex; flex-direction: column; gap: 20px;
    border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface);
    padding: 30px 44px 36px;
}
.cm-doc-editor__side { display: flex; flex-direction: column; gap: 14px; }
.cm-doc-editor__actions { display: flex; gap: 10px; justify-content: flex-end; }
.cm-doc-editor__delete { margin-top: 18px; display: flex; justify-content: flex-end; }
.cm-doc-field { display: flex; flex-direction: column; }
.cm-doc-input {
    width: 100%; padding: 9px 13px; border: 1px solid var(--cm-disabled); border-radius: 8px;
    background: var(--cm-surface); color: var(--cm-ink); font: 400 14px var(--cm-font-ui);
}
.cm-doc-input:focus { outline: none; border-color: var(--cm-brand); }
.cm-doc-input--title { font: 600 20px var(--cm-font-title); }
.cm-doc-select {
    width: 100%; padding: 8px 12px; border: 1px solid var(--cm-disabled); border-radius: 8px;
    background: var(--cm-surface); color: var(--cm-ink); font: 400 13.5px var(--cm-font-ui);
}
.cm-doc-radios { display: flex; flex-direction: column; gap: 8px; }
.cm-doc-radio { display: flex; align-items: center; gap: 9px; font: 400 13.5px var(--cm-font-ui); color: var(--cm-text); }
.cm-doc-radio label { margin: 0; }
.cm-doc-dates { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 14px; }
.cm-doc-dropzone {
    display: flex; align-items: center; justify-content: center; gap: 8px; cursor: pointer;
    border: 1px dashed var(--cm-disabled); border-radius: 10px; padding: 16px;
    font: 600 13px var(--cm-font-ui); color: var(--cm-muted); margin-top: 10px;
}
.cm-doc-dropzone.is-dragover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-doc-tags__input { position: relative; margin-top: 10px; }
.cm-doc-tags__suggestions {
    list-style: none; margin: 4px 0 0; padding: 4px; position: absolute; z-index: 5; left: 0; right: 0;
    border: 1px solid var(--cm-border); border-radius: 9px; background: var(--cm-surface);
    box-shadow: 0 10px 24px rgba(18, 52, 77, .2);
}
.cm-doc-tags__suggestion {
    display: flex; align-items: center; justify-content: space-between; gap: 10px; cursor: pointer;
    padding: 7px 10px; border-radius: 7px; font: 400 13px var(--cm-font-ui); color: var(--cm-text);
}
.cm-doc-tags__suggestion:hover { background: var(--cm-surface-alt); }
.cm-doc-tags__suggestion--create { font-weight: 600; color: var(--cm-brand-strong); }
.cm-doc-tags__count { font-size: 12px; color: var(--cm-faint); }

/* ---- 2f / 2g / 2h: dashboard and lists ---- */
.cm-doc-kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; margin-bottom: 22px; }
.cm-doc-kpi {
    display: flex; flex-direction: column; gap: 6px; text-decoration: none;
    border: 1px solid var(--cm-border); border-radius: 12px; background: var(--cm-surface); padding: 20px 22px;
}
.cm-doc-kpi__value { font: 600 30px var(--cm-font-title); color: var(--cm-ink); }
.cm-doc-kpi__label { font: 400 13px var(--cm-font-ui); color: var(--cm-muted); }
.cm-doc-kpi--link .cm-doc-kpi__label { color: var(--cm-brand-strong); font-weight: 600; }
.cm-doc-kpi--link:hover { border-color: var(--cm-brand); box-shadow: 0 4px 14px rgba(18, 52, 77, .09); }
.cm-doc-dashboard { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 18px; align-items: start; }
.cm-doc-dashboard__side { display: flex; flex-direction: column; gap: 18px; }
.cm-doc-toggle { display: inline-flex; border: 1px solid var(--cm-border); border-radius: 8px; overflow: hidden; }
.cm-doc-toggle a {
    padding: 5px 12px; font: 600 12px var(--cm-font-ui); color: var(--cm-muted);
    text-decoration: none; background: var(--cm-surface);
}
.cm-doc-toggle a.is-active { background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong); }
.cm-doc-table { width: 100%; border-collapse: collapse; }
.cm-doc-table th {
    text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--cm-border);
    font: 600 10.5px var(--cm-font-ui); letter-spacing: 1.2px; text-transform: uppercase; color: var(--cm-faint);
}
.cm-doc-table td { padding: 12px; border-bottom: 1px solid var(--cm-border-faint); font: 400 14px var(--cm-font-ui); color: var(--cm-text); }
.cm-doc-table td a { color: var(--cm-ink); font-weight: 600; text-decoration: none; }
.cm-doc-table td a:hover { color: var(--cm-brand-strong); }
.cm-doc-table__num { text-align: right; }
.cm-doc-table__rank { width: 28px; color: var(--cm-faint); }
.cm-doc-table__quiet { color: var(--cm-disabled); }
.cm-doc-table__sorted { color: var(--cm-brand-strong); }
.cm-doc-seeall { display: inline-block; margin-top: 12px; font: 600 13px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-doc-bar { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; margin-bottom: 14px; }
.cm-doc-bar__label { font: 400 13px var(--cm-font-ui); color: var(--cm-text); }
.cm-doc-bar__value { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-doc-bar__track { grid-column: 1 / -1; height: 5px; border-radius: 3px; background: var(--cm-border-soft); }
.cm-doc-bar__fill { display: block; height: 100%; border-radius: 3px; background: var(--cm-brand); }
.cm-doc-filters { display: flex; align-items: flex-end; gap: 18px; flex-wrap: wrap; margin-bottom: 16px; }
.cm-doc-filters__field { display: flex; flex-direction: column; min-width: 240px; }
.cm-doc-pager { display: flex; align-items: center; gap: 14px; padding-top: 14px; }
.cm-doc-pager a { font: 600 13px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-doc-pager__count { margin-left: auto; font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-doc-inline-form { display: flex; align-items: center; gap: 8px; }
.cm-doc-inline-form .cm-doc-input, .cm-doc-inline-form .cm-doc-select { min-width: 160px; }

@media (max-width: 1200px) {
    .cm-doc__grid--4 { grid-template-columns: repeat(2, 1fr); }
    .cm-doc__grid--3 { grid-template-columns: repeat(2, 1fr); }
    .cm-doc-kpis { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 992px) {
    .cm-doc, .cm-doc-article, .cm-doc-editor, .cm-doc-dashboard { grid-template-columns: minmax(0, 1fr); }
    .cm-doc__rail { position: static; }
    .cm-doc__grid--4, .cm-doc__grid--3 { grid-template-columns: 1fr; }
    .cm-doc-editor__main, .cm-doc-article__body { padding: 22px 20px 26px; }
    .cm-doc-attachments__grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Wiki (design/validated/wiki.md)
   A rail on the left holding the whole tree, the page on the right. The rail is the feature:
   without a permanent, complete tree a wiki is a pile of pages, so it stays visible on every
   screen of the area - reading, editing, and the trash alike.
   ========================================================================== */

.cm-wiki { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 22px; align-items: start; }

/* The sticky box is bounded, and the tree scrolls inside it. Without the max-height, a tree taller
   than the viewport is not merely cut off at the fold - it is unreachable: the rail sticks at 84px
   and scrolling no longer moves it, so the lower branches and the foot's Réglages/Corbeille links
   only reappear once the *page* has been scrolled to its very end and the rail finally meets the
   bottom of its containing block. Bounding it turns the overflow into an ordinary scroll.
   The 104px is the 84px offset plus a gutter, so the rail never touches the bottom edge. */
.cm-wiki__rail { position: sticky; top: 84px; max-height: calc(100vh - 104px); display: flex; flex-direction: column; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 14px 12px; }
.cm-wiki__rail-head { display: flex; align-items: center; gap: 8px; padding: 0 4px 10px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-wiki__rail-title { flex: 1; min-width: 0; font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-wiki__rail-add { flex: none; }
.cm-wiki__rail-empty { margin: 12px 4px; font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
/* margin-top: auto rather than 10px - the rail is a flex column now, so the foot sits on its bottom
   edge instead of wherever the tree happens to stop. On a three-page wiki that is the difference
   between two links halfway up the screen and two links where they are looked for. */
.cm-wiki__rail-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 10px; margin-top: auto; border-top: 1px solid var(--cm-border-soft); }
/* Pushed to the right edge even when it is the only link left, so its place never moves. */
.cm-wiki__rail-link--end { margin-left: auto; }
.cm-wiki__rail-link { font: 500 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-wiki__rail-link:hover { text-decoration: underline; }

.cm-wiki__tree { list-style: none; margin: 10px 0 0; padding: 0; }
/* The root list is the one part of the rail that scrolls - `:not(--nested)` because the two share a
   class and a nested list must not become a scroll container of its own: setting one axis to
   something other than `visible` computes the other to `auto`, which would hand every branch its own
   stray horizontal scrollbar. `min-height: 0` is what makes it actually shrink: a flex item's
   default `min-height: auto` sizes it to its content, and the overflow would then be pushed outside
   the bounded rail instead of scrolling inside it. */
.cm-wiki__tree:not(.cm-wiki__tree--nested) { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.cm-wiki__tree--nested { margin: 2px 0 2px 14px; border-left: 1px solid var(--cm-border-soft); padding-left: 6px; }
.cm-wiki__node { margin: 1px 0; }
.cm-wiki__node.is-dragging { opacity: 0.45; }
.cm-wiki__node.is-drop-target > .cm-wiki__node-row { background: var(--cm-blue-bg-soft); box-shadow: inset 0 0 0 1px var(--cm-brand); border-radius: 6px; }
/* The two edge zones: a rule where the page would land, drawn on the row rather than on the whole
   <li> - a folder's <li> is as tall as its subtree. */
.cm-wiki__node.is-drop-before > .cm-wiki__node-row { box-shadow: inset 0 2px 0 0 var(--cm-brand); }
.cm-wiki__node.is-drop-after > .cm-wiki__node-row { box-shadow: inset 0 -2px 0 0 var(--cm-brand); }
.cm-wiki__node-row { display: flex; align-items: center; gap: 1px; min-width: 0; }
.cm-wiki__node-link { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 6px; font: 400 13px var(--cm-font-ui); color: var(--cm-text); text-decoration: none; }
.cm-wiki__node-link:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }
.cm-wiki__node-link.is-current { background: var(--cm-blue-bg); color: var(--cm-blue-tx); font-weight: 600; }
/* The rail is 264px wide and nests; without the ellipsis a long title pushes the + and the bin off
   the panel. Every flex ancestor of this needs min-width:0 or the text refuses to shrink. */
.cm-wiki__node-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-wiki__node--folder > .cm-wiki__node-row .cm-wiki__node-link .cm-wiki__node-title { font-weight: 600; }

/* One triangle, rotated: collapsed points right, expanded points down, so the two states cannot
   drift apart the way two glyphs would. */
.cm-wiki__node-toggle { flex: none; display: grid; place-items: center; width: 14px; height: 18px; padding: 0; border: 0; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-wiki__node-toggle svg { transform: rotate(90deg); transition: transform 0.12s ease; }
.cm-wiki__node.is-collapsed > .cm-wiki__node-row .cm-wiki__node-toggle svg { transform: rotate(0deg); }
.cm-wiki__node-toggle:hover { color: var(--cm-brand-strong); }
.cm-wiki__node-toggle--empty { cursor: default; }
.cm-wiki__node.is-collapsed > .cm-wiki__tree--nested { display: none; }

/* The grip says the row can be dragged. It is an affordance only - the whole <li> stays draggable. */
.cm-wiki__node-grip { flex: none; display: grid; place-items: center; width: 12px; color: var(--cm-disabled); cursor: grab; }
.cm-wiki__node-row:hover .cm-wiki__node-grip { color: var(--cm-faint); }
.cm-wiki__node.is-dragging .cm-wiki__node-grip { cursor: grabbing; }
/* Always visible rather than hover-revealed: a rail is also read on a touch screen, where hover
   never happens and a hidden action is an absent one. */
.cm-wiki__node-actions { flex: none; display: flex; align-items: center; gap: 1px; }
.cm-wiki__node-action { display: grid; place-items: center; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 5px; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-wiki__node-action:hover { background: var(--cm-surface-alt); color: var(--cm-brand-strong); }
.cm-wiki__node-action--danger:hover { background: var(--cm-red-bg); color: var(--cm-red-tx); }

.cm-wiki__content { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 24px 26px 26px; }
/* `overflow-wrap` is not cosmetic here: a wiki page is where a URL, a token, a stack trace or a
   command line gets pasted, and an unbroken run of characters is wider than the column it sits in.
   Without this the text simply left the white panel and ran under the outline. `anywhere` rather
   than `break-word` so the break is taken into account when the browser measures the line, which
   is what a table cell holding a long URL needs. */
.cm-wiki__body { font: 400 15px/1.65 var(--cm-font-ui); color: var(--cm-text); overflow-wrap: anywhere; }
.cm-wiki__body h1, .cm-wiki__body h2, .cm-wiki__body h3, .cm-wiki__body h4 { font-family: var(--cm-font-title); color: var(--cm-ink); margin: 1.4em 0 0.5em; }
.cm-wiki__body h1:first-child, .cm-wiki__body h2:first-child { margin-top: 0; }
.cm-wiki__body table { width: 100%; border-collapse: collapse; margin: 1em 0; }
.cm-wiki__body td, .cm-wiki__body th { border: 1px solid var(--cm-border); padding: 7px 10px; }
.cm-wiki__body img { max-width: 100%; height: auto; }
.cm-wiki__placeholder { font: 400 13.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-wiki__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 16px; margin: 20px 0 0; padding-top: 12px; border-top: 1px solid var(--cm-border-soft); font: 400 12px var(--cm-font-ui); color: var(--cm-faint); }
.cm-wiki__meta-link { color: var(--cm-brand-strong); text-decoration: none; }
.cm-wiki__meta-link:hover { text-decoration: underline; }
.cm-wiki-history-cap { margin: 0 0 14px; font: 400 12.5px var(--cm-font-ui); color: var(--cm-muted); }
.cm-wiki__inline-form { display: inline-block; }
.cm-wiki__editor-actions { display: flex; gap: 10px; align-items: center; }

.cm-wiki-invite { max-width: 720px; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 26px 28px; }
.cm-wiki-invite__lead { font: 400 15px/1.6 var(--cm-font-ui); color: var(--cm-text); }
.cm-wiki-invite__list { margin: 0 0 22px; padding-left: 20px; font: 400 14px/1.7 var(--cm-font-ui); color: var(--cm-text); }
.cm-wiki-invite__reader { color: var(--cm-ink); font-weight: 600; }

.cm-wiki-filter { display: flex; align-items: center; gap: 10px; }
.cm-wiki-filter__input, .cm-wiki-filter__select { min-width: 200px; padding: 7px 11px; border: 1px solid var(--cm-border); border-radius: 8px; background: var(--cm-surface); font: 400 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-wiki-filter__check { display: flex; align-items: center; gap: 6px; font: 400 12.5px var(--cm-font-ui); color: var(--cm-muted); white-space: nowrap; }

.cm-wiki-group { margin-bottom: 18px; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 14px 18px; }
.cm-wiki-group__head { display: flex; align-items: baseline; gap: 12px; cursor: pointer; }
.cm-wiki-group__name { font: 600 12px var(--cm-font-ui); letter-spacing: 0.06em; text-transform: uppercase; color: var(--cm-muted); }
.cm-wiki-group__year, .cm-wiki-group__count { margin-left: auto; font: 400 12px var(--cm-font-ui); color: var(--cm-faint); }
.cm-wiki-group__idle { margin-top: 8px; font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }

.cm-wiki-list { list-style: none; margin: 10px 0 0; padding: 0; }
.cm-wiki-list__row { display: flex; align-items: center; gap: 12px; padding: 9px 2px; border-top: 1px solid var(--cm-border-faint); }
.cm-wiki-list__row:first-child { border-top: 0; }
.cm-wiki-list__row--muted { color: var(--cm-faint); }
.cm-wiki-list__title { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); text-decoration: none; }
a.cm-wiki-list__title:hover { color: var(--cm-brand-strong); text-decoration: underline; }
.cm-wiki-list__subtitle { flex: 1; min-width: 0; font: 400 12.5px var(--cm-font-ui); color: var(--cm-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-wiki-list__date { flex: none; font: 400 12px var(--cm-font-ui); color: var(--cm-faint); }
.cm-wiki-list--muted .cm-wiki-list__row { border-top: 0; padding: 3px 2px; }

.cm-wiki-regime { margin-bottom: 18px; padding: 11px 14px; border-radius: 8px; background: var(--cm-surface-alt); border: 1px solid var(--cm-border-soft); font: 400 13px var(--cm-font-ui); color: var(--cm-muted); }
.cm-wiki-regime--supervised { background: var(--cm-gold-bg); border-color: var(--cm-gold); color: var(--cm-gold-tx); }
/* The banner announces what the whole screen is, so it is as wide as the whole screen - which is
   the two-column layout below it, not the form's own column. It follows that layout back down to
   900px exactly where the layout does, single-column screens included. */
.cm-wiki-regime--narrow { max-width: 900px; }

/* What the wiki *is* on the left, what one does to it as a whole on the right. The right column
   collapses under the form on a narrow screen rather than squeezing both. */
.cm-wiki-settings-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 22px; align-items: start; }
.cm-wiki-settings-layout--single { grid-template-columns: minmax(0, 1fr); max-width: 900px; }
.cm-wiki-settings-layout__side { display: flex; flex-direction: column; gap: 16px; }

.cm-wiki-settings { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 22px 24px; }
.cm-wiki-settings__checks { display: flex; flex-wrap: wrap; gap: 10px 18px; margin-top: 6px; }
.cm-wiki-settings__check { display: flex; align-items: center; gap: 7px; font: 400 13px var(--cm-font-ui); color: var(--cm-text); }
.cm-wiki-settings__block { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 18px 24px 20px; }
.cm-wiki-settings__block-title { display: block; font: 600 11.5px var(--cm-font-ui); letter-spacing: 0.06em; text-transform: uppercase; color: var(--cm-faint); }
.cm-wiki-settings__block-hint { margin: 6px 0 14px; font: 400 13px var(--cm-font-ui); color: var(--cm-muted); }
.cm-wiki-settings__block-actions { display: flex; flex-wrap: wrap; gap: 10px; }
/* In the side column the buttons stack and fill it: three of them side by side would wrap into a
   ragged block at 340px. */
.cm-wiki-settings-layout__side .cm-wiki-settings__block-actions { flex-direction: column; align-items: stretch; gap: 8px; }
.cm-wiki-settings-layout__side .cm-wiki-settings__block-actions .cm-btn { justify-content: center; width: 100%; }
/* The irreversible pair, said out loud rather than merely placed last. */
.cm-wiki-settings__block--danger { border-color: var(--cm-red-bg); }
.cm-wiki-settings__block--danger .cm-wiki-settings__block-title { color: var(--cm-red-tx); }

@media (max-width: 992px) {
    .cm-wiki { grid-template-columns: minmax(0, 1fr); }
    .cm-wiki__rail { position: static; }
}

/* --- Wiki: content lot (attachments, outline, lock, history, link picker) ----------------- */

/* The sommaire is closed at load and opened from the action bar. Two classes rather than one, and
   the distinction is the whole point: `--with-outline` says the page *has* headings (so the toggle
   was rendered), `is-outline-open` says somebody asked to see them. The column is 208px plus a 22px
   gutter, so leaving it open by default took 230px - about a quarter of the text - from every page
   whether or not it was read. Nothing remembers the choice: each page opens closed, the same rule
   the rail's folding follows. */
.cm-wiki--with-outline.is-outline-open { grid-template-columns: 264px minmax(0, 1fr) 208px; }
.cm-wiki__outline { display: none; position: sticky; top: 84px; padding: 4px 0 0 14px; border-left: 1px solid var(--cm-border-soft); }
.cm-wiki--with-outline.is-outline-open .cm-wiki__outline { display: block; }
.cm-wiki__outline-toggle.is-active { background: var(--cm-blue-bg); border-color: var(--cm-brand); color: var(--cm-blue-tx); }
.cm-wiki__outline-title { display: block; margin-bottom: 8px; font: 600 11.5px var(--cm-font-ui); letter-spacing: 0.06em; text-transform: uppercase; color: var(--cm-faint); }
.cm-wiki__outline ul { list-style: none; margin: 0; padding: 0; }
.cm-wiki__outline-entry a { display: block; padding: 3px 0; font: 400 12.5px var(--cm-font-ui); color: var(--cm-muted); text-decoration: none; }
.cm-wiki__outline-entry a:hover { color: var(--cm-brand-strong); }
.cm-wiki__outline-entry--h3 { padding-left: 12px; }

/* The search field in the action bar, sized to sit next to a cm-btn without towering over it. */
.cm-wiki-search { margin: 0; }
.cm-wiki-search__input { width: 230px; max-width: 100%; padding: 7px 11px; border: 1px solid var(--cm-border); border-radius: 8px; background: var(--cm-surface); font: 400 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-wiki-search__input:focus { outline: none; border-color: var(--cm-brand); }

/* The Base documentaire's attachment block, worn by the wiki: same panel, same cards, same drop
   zone - the two features attach the same kind of file for the same reason, and a reader who has
   seen one should not have to learn the other. */
.cm-wiki-attachments { margin-top: 26px; border: 1px solid var(--cm-gold-bg); border-radius: 10px; background: var(--cm-highlight-bg); padding: 16px 18px 18px; }
.cm-wiki-attachments__rule { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; font: 600 11.5px var(--cm-font-ui); letter-spacing: 0.06em; text-transform: uppercase; color: var(--cm-gold-deep); }
.cm-wiki-attachments__rule::after { content: ''; flex: 1; height: 1px; background: var(--cm-gold-bg); }
.cm-wiki-attachments__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.cm-wiki-attachments__grid:empty { display: none; }
.cm-wiki-attachment {
    display: flex; align-items: center; gap: 6px;
    border: 1px solid var(--cm-border); border-radius: 9px; background: var(--cm-surface); padding: 11px 13px;
}
.cm-wiki-attachment:hover { border-color: var(--cm-brand); background: var(--cm-surface-alt); }
.cm-wiki-attachment__open { flex: 1; min-width: 0; display: flex; align-items: center; gap: 11px; text-decoration: none; }
.cm-wiki-attachment__icon {
    width: 34px; height: 34px; flex: none; border-radius: 8px; display: grid; place-items: center;
    background: var(--cm-highlight-bg); color: var(--cm-gold-deep);
}
.cm-wiki-attachment__text { display: flex; flex-direction: column; min-width: 0; }
.cm-wiki-attachment__name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); overflow-wrap: anywhere; }
.cm-wiki-attachment__meta { font: 400 11.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-wiki-attachment__remove { flex: none; border: 0; background: transparent; color: var(--cm-faint); font-size: 18px; line-height: 1; cursor: pointer; }
.cm-wiki-attachment__remove:hover { color: var(--cm-red-tx); }
.cm-wiki-attachments__dropzone {
    display: flex; align-items: center; justify-content: center; gap: 8px; cursor: pointer;
    border: 1px dashed var(--cm-disabled); border-radius: 10px; padding: 16px;
    font: 600 13px var(--cm-font-ui); color: var(--cm-muted); margin-top: 12px;
}
.cm-wiki-attachments__dropzone.is-dragover { border-color: var(--cm-brand); color: var(--cm-brand-strong); }
.cm-wiki-attachments__add > .cm-wiki-attachments__grid { margin-top: 10px; }
.cm-wiki-attachments__submit { display: flex; justify-content: flex-end; margin-top: 12px; }

/* The folded adding zone. Closed it is one line of small text with a paper-clip; open it is exactly
   the panel that used to be there, unchanged. `list-style: none` plus the ::-webkit- rule is what it
   takes to be rid of the browser's own triangle - the chevron is drawn in the markup so it can turn
   with the state and match the rest of the app's disclosures. */
.cm-wiki-attachments__adder > summary::-webkit-details-marker { display: none; }
.cm-wiki-attachments__toggle {
    display: inline-flex; align-items: center; gap: 7px; list-style: none; cursor: pointer;
    padding: 4px 2px; font: 500 12.5px var(--cm-font-ui); color: var(--cm-faint);
}
.cm-wiki-attachments__toggle:hover { color: var(--cm-brand-strong); }
.cm-wiki-attachments__toggle:focus-visible { outline: 2px solid var(--cm-brand); outline-offset: 3px; border-radius: 6px; }
.cm-wiki-attachments__clip, .cm-wiki-attachments__chevron { display: inline-flex; line-height: 0; }
.cm-wiki-attachments__chevron { transition: transform .15s ease; }
.cm-wiki-attachments__adder[open] .cm-wiki-attachments__chevron { transform: rotate(180deg); }
.cm-wiki-attachments__adder[open] .cm-wiki-attachments__toggle { color: var(--cm-muted); margin-bottom: 10px; }
/* With attachments above it, the line needs the separation the panel's own rule gives the heading. */
.cm-wiki-attachments__grid + .cm-wiki-attachments__adder { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--cm-gold-bg); }

/* Nothing to list: the section is that one line, so the gold panel around it goes away. */
.cm-wiki-attachments--bare { margin-top: 18px; border: 0; background: none; padding: 0; }

.cm-wiki-lock { margin-bottom: 16px; padding: 10px 14px; border-radius: 8px; background: var(--cm-gold-bg); border: 1px solid var(--cm-gold); font: 400 13px var(--cm-font-ui); color: var(--cm-gold-tx); }

.cm-wiki__editor-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
/* The "Lien wiki" field inside HugeRTE's dialog.

   Tom Select's own stylesheet is loaded on the page but does not win inside that dialog - measured,
   the list computes `position: static` there and lands beside the control instead of under it - so
   the geometry is pinned here. None of these declarations is a preference.

   The list itself is rendered on <body> (dropdownParent, in wiki_editor_controller.js) rather than
   inside the field: the dialog clips and stacks its own content, and every attempt to make it give
   the list room from the inside failed. Escaping the dialog is what actually works, which is why
   the z-index has to clear HugeRTE's modal rather than merely the page. */
.cm-wiki-linkfield__label { display: block; margin-bottom: 6px; font: 600 12px var(--cm-font-ui); color: var(--cm-ink); }
.cm-wiki-linkfield { position: relative; width: 420px; max-width: 100%; }
.cm-wiki-linkfield .ts-wrapper { display: block; width: 100%; }
.cm-wiki-linkfield .ts-control { display: flex; flex-wrap: wrap; align-items: center; width: 100%; min-height: 38px; height: auto; }
.cm-wiki-linkfield__dropdown { z-index: 1500; }

.cm-wiki-linkpicker { position: relative; }
.cm-wiki-linkpicker__panel { position: absolute; right: 0; z-index: 20; min-width: 240px; max-height: 320px; overflow-y: auto; margin-top: 6px; padding: 8px; border: 1px solid var(--cm-border); border-radius: 8px; background: var(--cm-surface); box-shadow: 0 8px 24px rgba(18, 52, 77, 0.12); }
.cm-wiki-linkpicker__panel ul { list-style: none; margin: 0; padding: 0; }
.cm-wiki-linkpicker__panel button { display: block; width: 100%; padding: 5px 8px; border: 0; border-radius: 6px; background: none; font: 400 13px var(--cm-font-ui); color: var(--cm-text); text-align: left; cursor: pointer; }
.cm-wiki-linkpicker__panel button:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }

.cm-wiki-compare { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--cm-border-soft); }
.cm-wiki-compare__pane { min-width: 0; }
.cm-wiki-compare__title { display: block; margin-bottom: 8px; font: 600 11.5px var(--cm-font-ui); letter-spacing: 0.06em; text-transform: uppercase; color: var(--cm-faint); }

@media (max-width: 1200px) {
    /* Below 1200px there is no room for a third column, so the sommaire cannot open at all - and
       the toggle goes with it. A button that visibly does nothing is worse than no button, which
       is the same reason it is not rendered on a page without headings. */
    .cm-wiki--with-outline.is-outline-open { grid-template-columns: 264px minmax(0, 1fr); }
    .cm-wiki--with-outline.is-outline-open .cm-wiki__outline { display: none; }
    .cm-wiki__outline-toggle { display: none; }
    .cm-wiki-settings-layout { grid-template-columns: minmax(0, 1fr); max-width: 900px; }
    .cm-wiki-regime { max-width: 900px; }
    .cm-wiki-settings-layout__side .cm-wiki-settings__block-actions { flex-direction: row; align-items: center; }
    .cm-wiki-settings-layout__side .cm-wiki-settings__block-actions .cm-btn { width: auto; }
}

@media (max-width: 992px) {
    .cm-wiki-compare { grid-template-columns: minmax(0, 1fr); }
    .cm-wiki-attachments__grid { grid-template-columns: 1fr; }
}

/* --- Wiki: editor lot (callouts, diagrams, formulas, code, accordion, page break) ---------
   These style what the editor STORES, so the same rules have to hold in three places: the read
   screen, the editor's own content area, and the PDF. That is why they are built on --cm-* tokens
   and on nothing else - no Tabler class, no editor-only selector.
   ------------------------------------------------------------------------------------------ */

.cm-callout { margin: 1.1em 0; padding: 12px 16px; border-left: 3px solid var(--cm-brand); border-radius: 0 8px 8px 0; background: var(--cm-blue-bg-soft); color: var(--cm-text); }
.cm-callout > p:last-child { margin-bottom: 0; }
.cm-callout--info { border-left-color: var(--cm-brand); background: var(--cm-blue-bg-soft); }
.cm-callout--warning { border-left-color: var(--cm-gold); background: var(--cm-gold-bg); color: var(--cm-gold-tx); }
.cm-callout--tip { border-left-color: var(--cm-green-tx); background: var(--cm-green-bg); color: var(--cm-green-tx); }

/* A stored diagram carries its own appearance, in `style` attributes written when it was inserted
   (assets/wiki/mermaid_svg.js). There is deliberately **nothing about colour or type here**: the
   sanitizer removes the <style> block Mermaid emits, so restyling by its class names is what this
   used to attempt, and it only ever covered flowcharts - every other kind came out black, shifted
   or truncated. Inline styles would win over these rules anyway.

   Layout stays, because it is about the page rather than the picture. */
.cm-mermaid { margin: 1.2em 0; text-align: center; }
.cm-mermaid svg { max-width: 100%; height: auto; }

/* The diagram is drawn once, in the light palette, and those colours are inside the file - the way a
   photograph's are. So dark mode gives it a light surface to sit on rather than trying to repaint
   it, which is also what keeps screen and PDF showing the same picture. Without this the labels
   Mermaid draws straight on the background - a sequence diagram's messages, a flowchart's edge
   labels - are dark ink on a dark page, which is to say gone.

   The surface goes on the <svg> and not on `.cm-mermaid`, which is a full-width block: painting the
   block would draw a white band across the page instead of a card behind the picture. */
[data-bs-theme="dark"] .cm-mermaid svg { background: #ffffff; border-radius: 10px; padding: 10px; }

.cm-katex { display: inline-block; }

/* `color` is not decoration here: Tabler styles <pre> for a dark code panel and paints its text
   near-white, so a code sample on this light surface was invisible - the block rendered as an empty
   box. Measured in a browser, which is the only way that kind of bug shows up. */
.cm-wiki__body pre {
    padding: 12px 14px; border: 1px solid var(--cm-border); border-radius: 8px;
    background: var(--cm-surface-alt); color: var(--cm-ink); overflow-x: auto;
    /* The editor face, not the document face: a code sample is read column by column, so it gets
       the monospace stack, a tab that is four columns wide rather than the browser's eight, and no
       ligature - `!=` and `=>` must stay two glyphs in a command somebody is about to retype. */
    font: 400 13px/1.55 var(--cm-font-mono); tab-size: 4; font-variant-ligatures: none;
}
.cm-wiki__body pre code { color: inherit; background: none; font: inherit; }

/* A code block and its copy button, put together at read time by wiki_code_copy_controller.js. The
   button rides in the corner of the block. It used to appear only under the pointer, which made a
   feature nobody could see: an affordance that has to be discovered by hovering is one that gets
   reported as missing. It is drawn at rest instead, faint enough that a page of commands still
   reads as text, and comes up to full contrast on hover or keyboard focus. */
.cm-codeblock { position: relative; }
/* Room kept for the button, so the first line of code never runs underneath it. */
.cm-codeblock > pre { padding-right: 42px; }
/* Written as a descendant rather than as the bare class on purpose: .cm-copybtn is declared further
   down this file with the same specificity, so it was winning on source order and the button ended
   up 20px, transparent and borderless - the plated 26px square described here never reached the
   screen. Two classes beat one, whatever the order. */
.cm-codeblock > .cm-codeblock__copy {
    position: absolute; top: 7px; right: 7px;
    width: 26px; height: 26px; border-radius: 7px;
    background: var(--cm-surface); border: 1px solid var(--cm-border);
    color: var(--cm-muted); opacity: .55; transition: opacity .12s ease, color .12s ease;
}
.cm-codeblock:hover > .cm-codeblock__copy,
.cm-codeblock > .cm-codeblock__copy:hover,
.cm-codeblock > .cm-codeblock__copy:focus-visible,
.cm-codeblock > .cm-codeblock__copy.cm-copybtn--done { opacity: 1; }
.cm-codeblock > .cm-codeblock__copy:hover { color: var(--cm-ink); background: var(--cm-surface-soft); }
/* The button hangs off the wrapper and not off the <pre>, which scrolls sideways when a line is
   long - anchored to the block itself it would slide out of sight with the first wide command. The
   wrapper carries no border, padding or height, so the <pre>'s own margins still collapse through it
   and the page keeps the spacing it had before the buttons existed. */
/* Inline code: same face as the blocks, on a soft plate so a class name inside a sentence is
   picked out without the sentence breaking up. 0.92em keeps it on the paragraph's baseline grid -
   a monospace face at the body's own size reads a notch too large. */
.cm-wiki__body code {
    font-family: var(--cm-font-mono); font-size: 0.92em; color: var(--cm-ink);
    background: var(--cm-surface-alt); border: 1px solid var(--cm-border-soft);
    border-radius: 4px; padding: 1px 5px; font-variant-ligatures: none;
}
.cm-wiki__body blockquote { margin: 1.1em 0; padding: 4px 0 4px 16px; border-left: 3px solid var(--cm-border); color: var(--cm-muted); }

.cm-wiki__body details { margin: 1em 0; padding: 10px 14px; border: 1px solid var(--cm-border); border-radius: 8px; background: var(--cm-surface-alt); }
.cm-wiki__body summary { cursor: pointer; font-weight: 600; color: var(--cm-ink); }

/* pagebreak's marker: invisible furniture on screen, a real page break in the PDF (the export's
   own stylesheet turns it into `break-after: page`). */
.cm-wiki-pagebreak { height: 0; margin: 1.4em 0; border-top: 1px dashed var(--cm-border); }

/* --- Wiki: Markdown archive (the import assistant's analysis step) ------------------------ */

.cm-wiki-import { max-width: 760px; margin-top: 16px; }
.cm-wiki-import .form-label { display: block; margin-bottom: 6px; }
.cm-wiki-import__summary { margin: 14px 0 4px; font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-wiki-import__blocking, .cm-wiki-import__warnings { list-style: none; margin: 0 0 14px; padding: 12px 16px; border-radius: 8px; font: 400 13px var(--cm-font-ui); }
.cm-wiki-import__blocking { background: var(--cm-red-bg); color: var(--cm-red-tx); border: 1px solid var(--cm-red-tx); }
.cm-wiki-import__warnings { background: var(--cm-gold-bg); color: var(--cm-gold-tx); border: 1px solid var(--cm-gold); }
.cm-wiki-import__blocking li, .cm-wiki-import__warnings li { margin: 2px 0; }
.cm-wiki-import__reset { max-width: 760px; margin-top: 10px; }

/* --- The upload component, everywhere (App\Form\FilePickerType) --------------------------- */
/* Mockup 5 of design_handoff_bibliotheque_fichiers, which draws four states on one capture:
   finished, in flight, linked from the library (lot 4) and refused. The refusal arrives while the
   teacher is still filling the form in, which is the whole point of staging the upload. */

.cm-filepick { border: 1px solid var(--cm-border); border-radius: 11px; background: var(--cm-surface); overflow: hidden; }
.cm-filepick__tabs { display: flex; border-bottom: 1px solid var(--cm-border-soft); background: var(--cm-surface-alt); }
.cm-filepick__tab { font: 600 12.5px/1 var(--cm-font-ui); padding: 11px 15px; color: var(--cm-muted); border-bottom: 2px solid transparent; display: flex; align-items: center; gap: 7px; }
.cm-filepick__tab--on { color: var(--cm-brand-strong); border-bottom-color: var(--cm-brand); background: var(--cm-surface); }

/* A <button>, not a <div>: it opens the file dialog, so the keyboard has to reach it. Reset to look
   like the dashed zone the mockup draws rather than like a button. */
.cm-filepick__drop { display: block; width: calc(100% - 28px); margin: 14px; padding: 26px 18px; text-align: center; border: 1.5px dashed var(--cm-border); border-radius: 10px; background: var(--cm-surface-alt); cursor: pointer; }
.cm-filepick__drop:hover, .cm-filepick__drop:focus-visible, .cm-filepick__drop.is-hot { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); }
.cm-filepick__drop-main { display: block; font: 600 14px/1.4 var(--cm-font-ui); color: var(--cm-ink); }
.cm-filepick__drop-sub { display: block; font-size: 12.5px; color: var(--cm-faint); margin-top: 5px; }

.cm-filepick__row { display: flex; align-items: center; gap: 11px; padding: 10px 14px; border-top: 1px solid var(--cm-border-soft); }
.cm-filepick__grow { flex: 1 1 auto; min-width: 0; }
.cm-filepick__name { display: block; font: 600 13px/1.3 var(--cm-font-ui); color: var(--cm-ink); overflow-wrap: anywhere; }
.cm-filepick__meta { display: block; font-size: 11.5px; color: var(--cm-faint); font-variant-numeric: tabular-nums; }
.cm-filepick__remove { border: 0; background: none; color: var(--cm-faint); font-weight: 700; line-height: 1; padding: 4px 6px; border-radius: 6px; cursor: pointer; }
.cm-filepick__remove:hover { color: var(--cm-action-danger); background: var(--cm-red-bg); }

.cm-filepick__track { display: block; height: 5px; border-radius: 999px; background: var(--cm-blue-bg); overflow: hidden; margin-top: 6px; }
.cm-filepick__fill { display: block; height: 100%; width: 0; background: var(--cm-brand); border-radius: 999px; transition: width .15s linear; }
/* A total the server never announced - a proxy that strips Content-Length. The bar sweeps rather
   than fills, and no percentage is claimed. */
.cm-filepick__track.is-indeterminate .cm-filepick__fill { width: 35%; animation: cm-filepick-sweep 1.1s ease-in-out infinite; }
@keyframes cm-filepick-sweep { 0% { margin-left: -35%; } 100% { margin-left: 100%; } }

.cm-filepick__state { font: 600 11.5px/1 var(--cm-font-ui); padding: 4px 8px; border-radius: 999px; white-space: nowrap; }
.cm-filepick__state--ok { background: var(--cm-green-bg); color: var(--cm-green-tx); }
.cm-filepick__state--go { background: var(--cm-blue-bg); color: var(--cm-blue-tx); font-variant-numeric: tabular-nums; }
.cm-filepick__state--ko { background: var(--cm-red-bg); color: var(--cm-red-tx); }
/* The library pill is gold on purpose: a linked file is visibly not the same thing as an uploaded
   one, because deleting it from the library later will remove it from here (lot 4). */
.cm-filepick__state--lib { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }

.cm-filepick__ext { font: 700 9.5px/1 var(--cm-font-ui); letter-spacing: .04em; padding: 5px 6px; border-radius: 5px; min-width: 34px; text-align: center; flex: none; background: var(--cm-surface-alt); color: var(--cm-muted); border: 1px solid var(--cm-border-soft); }
.cm-filepick__ext--pdf { background: var(--cm-red-bg); color: var(--cm-red-tx); border-color: transparent; }
.cm-filepick__ext--img { background: var(--cm-green-bg); color: var(--cm-green-tx); border-color: transparent; }
.cm-filepick__ext--vid { background: var(--cm-teal-bg); color: var(--cm-teal-tx); border-color: transparent; }
.cm-filepick__ext--doc { background: var(--cm-blue-bg); color: var(--cm-blue-tx); border-color: transparent; }
.cm-filepick__ext--zip { background: var(--cm-purple-bg); color: var(--cm-purple-tx); border-color: transparent; }

/* --- Bibliothèque de fichiers (design/validated/file-library.md, maquettes 1 à 3 et 9) ----- */
/* The rail derives from `.cm-wiki__rail` deliberately - same gestures, same states, same class
   shapes - so there are not two vocabularies for one tree. The bounded sticky box is the rule fixed
   for the wiki on 2026-08-16 and copied here rather than rediscovered: without the max-height a tree
   taller than the viewport is not merely cut off, it is unreachable. */

.cm-flib { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 22px; align-items: start; }
.cm-flib__rail { position: sticky; top: 84px; max-height: calc(100vh - 104px); display: flex; flex-direction: column; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 14px 12px; }

/* The quota bar: two lines, a 4px track, no heading. An ambient indicator answering "can I still
   drop something here", not a figure somebody comes to consult - which is why it is here and neither
   in the action bar nor in the upload component. */
.cm-flib__quota { padding: 0 4px 10px; }
.cm-flib__quota-line { display: flex; justify-content: space-between; gap: 8px; font: 500 11.5px var(--cm-font-ui); color: var(--cm-muted); }
.cm-flib__quota-percent { font-variant-numeric: tabular-nums; }
.cm-flib__quota-track { height: 4px; margin-top: 5px; border-radius: 999px; background: var(--cm-border-soft); overflow: hidden; }
.cm-flib__quota-fill { display: block; height: 100%; border-radius: 999px; background: var(--cm-positive-tx); }
.cm-flib__quota--amber .cm-flib__quota-fill { background: var(--cm-gold); }
.cm-flib__quota--red .cm-flib__quota-fill { background: var(--cm-action-danger); }
.cm-flib__quota--red .cm-flib__quota-percent { color: var(--cm-red-tx); font-weight: 700; }

.cm-flib__rail-head { display: flex; align-items: center; gap: 8px; padding: 0 4px 10px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-flib__rail-title { flex: 1; min-width: 0; font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-flib__rail-empty { margin: 12px 4px; font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }
/* Pinned to the bottom of the block rather than laid under the last folder: its two entries act on
   the *whole* library. A button on the left, a link on the right - creating a folder is daily
   housekeeping, the corbeille is a rarely-opened back door, and the two must not weigh the same. */
.cm-flib__rail-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 10px; margin-top: auto; border-top: 1px solid var(--cm-border-soft); }
.cm-flib__rail-link { font: 500 12.5px var(--cm-font-ui); color: var(--cm-brand-strong); text-decoration: none; }
.cm-flib__rail-link:hover { text-decoration: underline; }
.cm-flib__rail-link--end { margin-left: auto; }

.cm-flib__tree { list-style: none; margin: 10px 0 0; padding: 0; }
/* `:not(--nested)` for the reason the wiki records: the two lists share a class, and setting one
   overflow axis to anything but `visible` computes the other to `auto` - every branch would get a
   stray horizontal scrollbar. `min-height: 0` is what makes a flex item actually shrink. */
.cm-flib__tree:not(.cm-flib__tree--nested) { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.cm-flib__tree--nested { margin: 2px 0 2px 14px; border-left: 1px solid var(--cm-border-soft); padding-left: 6px; }
.cm-flib__node { margin: 1px 0; }
.cm-flib__node.is-dragging { opacity: 0.45; }
/* One drop state, not the wiki's three: there is no re-order here, so "between two folders" means
   nothing - a drop is always *into*. */
.cm-flib__node.is-drop-target > .cm-flib__node-row { background: var(--cm-blue-bg-soft); box-shadow: inset 0 0 0 1px var(--cm-brand); border-radius: 6px; }
.cm-flib__node-row { display: flex; align-items: center; gap: 1px; min-width: 0; }
.cm-flib__node-link { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 6px; font: 400 13px var(--cm-font-ui); color: var(--cm-text); text-decoration: none; }
.cm-flib__node-link:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }
.cm-flib__node-link.is-current { background: var(--cm-blue-bg); color: var(--cm-blue-tx); font-weight: 600; }
.cm-flib__node-ico { flex: none; display: grid; place-items: center; color: var(--cm-faint); }
.cm-flib__node-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-flib__node-toggle { flex: none; display: grid; place-items: center; width: 14px; height: 18px; padding: 0; border: 0; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-flib__node-toggle svg { transform: rotate(90deg); transition: transform 0.12s ease; }
.cm-flib__node.is-collapsed > .cm-flib__node-row .cm-flib__node-toggle svg { transform: rotate(0deg); }
.cm-flib__node-toggle--empty { cursor: default; }
.cm-flib__node.is-collapsed > .cm-flib__tree--nested { display: none; }
.cm-flib__node-grip { flex: none; display: grid; place-items: center; width: 12px; color: var(--cm-disabled); cursor: grab; }
.cm-flib__node-row:hover .cm-flib__node-grip { color: var(--cm-faint); }
.cm-flib__node-actions { flex: none; display: flex; align-items: center; gap: 1px; }
.cm-flib__node-action { display: grid; place-items: center; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 5px; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-flib__node-action:hover { background: var(--cm-surface-alt); color: var(--cm-brand-strong); }
.cm-flib__node-action--danger:hover { background: var(--cm-red-bg); color: var(--cm-red-tx); }

.cm-flib__content { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 18px 20px 20px; }
.cm-flib__empty { margin: 8px 2px; font: 400 13.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-flib__search { min-width: 220px; }
.cm-flib__trash-lead { margin: 0 0 14px; font: 400 13px var(--cm-font-ui); color: var(--cm-muted); }

/* Not a DataTable, and there is no grid view: this table hosts drag-and-drop and rows a Stimulus
   controller acts on, and the rule here is never to bind a controller to an element a DOM-rewriting
   library moves. */
.cm-flib__table { width: 100%; border-collapse: collapse; font: 400 13px var(--cm-font-ui); }
.cm-flib__table th { text-align: left; padding: 6px 10px; font: 600 11px var(--cm-font-ui); letter-spacing: 0.05em; text-transform: uppercase; color: var(--cm-faint); border-bottom: 1px solid var(--cm-border-soft); }
.cm-flib__table td { padding: 9px 10px; border-bottom: 1px solid var(--cm-border-faint); color: var(--cm-text); vertical-align: middle; }
.cm-flib__row:hover td { background: var(--cm-surface-alt); }
.cm-flib__row.is-dragging td { opacity: 0.45; }
.cm-flib__name { display: flex; align-items: center; gap: 9px; min-width: 0; font-weight: 600; color: var(--cm-ink); }
.cm-flib__name a, .cm-flib__namelink { color: inherit; text-decoration: none; border: 0; background: none; padding: 0; font: inherit; cursor: pointer; text-align: left; }
.cm-flib__name a:hover, .cm-flib__namelink:hover { text-decoration: underline; }
.cm-flib__row.is-folder .cm-flib__name a { color: var(--cm-brand-strong); }
.cm-flib__num { font-variant-numeric: tabular-nums; color: var(--cm-muted); white-space: nowrap; }
.cm-flib__use { font: 600 11.5px var(--cm-font-ui); padding: 4px 8px; border-radius: 999px; background: var(--cm-surface-alt); color: var(--cm-muted); border: 1px solid var(--cm-border-soft); white-space: nowrap; }
.cm-flib__days { color: var(--cm-warn-tx); }
.cm-flib__menu { width: 1%; white-space: nowrap; text-align: right; }
.cm-flib__kebab { border: 0; background: none; color: var(--cm-faint); font-weight: 700; letter-spacing: 1px; padding: 2px 6px; border-radius: 6px; cursor: pointer; }
.cm-flib__kebab:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }

/* The same coloured tile as the upload component's, on purpose: an extension reads the same whether
   it is in a picker or in the library. */
.cm-flib__ext { font: 700 9.5px/1 var(--cm-font-ui); letter-spacing: .04em; padding: 5px 6px; border-radius: 5px; min-width: 34px; text-align: center; flex: none; background: var(--cm-surface-alt); color: var(--cm-muted); border: 1px solid var(--cm-border-soft); display: grid; place-items: center; }
.cm-flib__ext--pdf { background: var(--cm-red-bg); color: var(--cm-red-tx); border-color: transparent; }
.cm-flib__ext--img { background: var(--cm-green-bg); color: var(--cm-green-tx); border-color: transparent; }
.cm-flib__ext--vid { background: var(--cm-teal-bg); color: var(--cm-teal-tx); border-color: transparent; }
.cm-flib__ext--aud { background: var(--cm-gold-bg); color: var(--cm-gold-tx); border-color: transparent; }
.cm-flib__ext--doc { background: var(--cm-blue-bg); color: var(--cm-blue-tx); border-color: transparent; }
.cm-flib__ext--zip { background: var(--cm-purple-bg); color: var(--cm-purple-tx); border-color: transparent; }
.cm-flib__ext--dir { background: transparent; color: var(--cm-brand); border-color: var(--cm-border); }

/* The upload strip. The table draws no progress bar of its own - one place shows a transfer. */
.cm-flib__uploads { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.cm-flib__upload { display: flex; align-items: center; gap: 11px; padding: 8px 10px; border: 1px solid var(--cm-border-soft); border-radius: 8px; background: var(--cm-surface-alt); }
.cm-flib__upload.is-failed { background: var(--cm-red-bg); border-color: transparent; }
.cm-flib__upload-name { flex: 1; min-width: 0; font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-flib__upload-track { flex: 0 0 160px; height: 5px; border-radius: 999px; background: var(--cm-blue-bg); overflow: hidden; }
.cm-flib__upload-fill { display: block; height: 100%; width: 0; background: var(--cm-brand); border-radius: 999px; transition: width .15s linear; }
.cm-flib__upload-state { font: 600 11.5px var(--cm-font-ui); color: var(--cm-muted); white-space: nowrap; }

/* A full-frame viewer, not a side panel: with no grid view this is the only place a document is
   actually looked at, and a PDF in a 240px column is not looked at. */
.cm-flib__viewer { position: fixed; inset: 0; z-index: 1080; display: flex; flex-direction: column; background: rgba(18, 52, 77, .92); }
.cm-flib__viewer-head { display: flex; align-items: center; gap: 14px; padding: 14px 20px; color: #fff; }
.cm-flib__viewer-name { flex: 1; min-width: 0; font: 600 14px var(--cm-font-ui); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-flib__viewer-count { font: 500 12.5px var(--cm-font-ui); opacity: .8; font-variant-numeric: tabular-nums; }
.cm-flib__viewer-close { border: 0; background: none; color: #fff; font-size: 26px; line-height: 1; cursor: pointer; }
.cm-flib__viewer-body { flex: 1; min-height: 0; display: grid; place-items: center; padding: 0 20px; }
.cm-flib__viewer-body img { max-width: 100%; max-height: 100%; object-fit: contain; }
.cm-flib__viewer-body iframe, .cm-flib__viewer-body video { width: 100%; height: 100%; border: 0; background: #fff; }
.cm-flib__viewer-body audio { width: min(560px, 90%); }
/* The footer repeats the menu's actions minus the ones that make no sense while looking at the file
   (renommer, supprimer). */
.cm-flib__viewer-foot { display: flex; justify-content: center; gap: 10px; padding: 14px 20px; }

@media (max-width: 991.98px) {
    .cm-flib { grid-template-columns: minmax(0, 1fr); }
    .cm-flib__rail { position: static; max-height: none; }
}

/* --- Bibliothèque de quiz : le classement par dossiers ------------------------------------- */
/* Derived from `.cm-flib` just above, which is itself derived from `.cm-wiki__rail` - same rail,
   same listing, same row menu, because a teacher who has learnt to file a document should not have
   to learn a second gesture to file a quiz. What is dropped here is the file library's own three
   blocks that a quiz has no use for: no quota bar (a folder holds no bytes), no upload strip, no
   full-frame viewer. What is added is « Déplacer vers… », a list of folders that does what the drag
   does for a target that is collapsed, on a touch screen, or from the keyboard. */

.cm-qlib { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 22px; align-items: start; }
.cm-qlib__rail { position: sticky; top: 84px; max-height: calc(100vh - 104px); display: flex; flex-direction: column; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 14px 12px; }

.cm-qlib__rail-head { display: flex; align-items: center; gap: 8px; padding: 0 4px 10px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-qlib__rail-title { flex: 1; min-width: 0; font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Pinned to the bottom of the block rather than laid under the last folder: it acts on the *whole*
   library. One entry and not the file library's two - there is no corbeille on this side. */
.cm-qlib__rail-foot { display: flex; align-items: center; padding-top: 10px; margin-top: auto; border-top: 1px solid var(--cm-border-soft); }

.cm-qlib__tree { list-style: none; margin: 10px 0 0; padding: 0; }
/* `:not(--nested)` for the reason the wiki records: the two lists share a class, and setting one
   overflow axis to anything but `visible` computes the other to `auto` - every branch would get a
   stray horizontal scrollbar. `min-height: 0` is what makes a flex item actually shrink. */
.cm-qlib__tree:not(.cm-qlib__tree--nested) { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.cm-qlib__tree--nested { margin: 2px 0 2px 14px; border-left: 1px solid var(--cm-border-soft); padding-left: 6px; }
.cm-qlib__node { margin: 1px 0; }
.cm-qlib__node.is-dragging { opacity: 0.45; }
/* One drop state, not the wiki's three: there is no re-order here, so "between two folders" means
   nothing - a drop is always *into*. */
.cm-qlib__node.is-drop-target > .cm-qlib__node-row { background: var(--cm-blue-bg-soft); box-shadow: inset 0 0 0 1px var(--cm-brand); border-radius: 6px; }
.cm-qlib__node-row { display: flex; align-items: center; gap: 1px; min-width: 0; }
.cm-qlib__node-link { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 6px; font: 400 13px var(--cm-font-ui); color: var(--cm-text); text-decoration: none; }
.cm-qlib__node-link:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }
.cm-qlib__node-link.is-current { background: var(--cm-blue-bg); color: var(--cm-blue-tx); font-weight: 600; }
.cm-qlib__node-ico { flex: none; display: grid; place-items: center; color: var(--cm-faint); }
.cm-qlib__node-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-qlib__node-toggle { flex: none; display: grid; place-items: center; width: 14px; height: 18px; padding: 0; border: 0; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-qlib__node-toggle svg { transform: rotate(90deg); transition: transform 0.12s ease; }
.cm-qlib__node.is-collapsed > .cm-qlib__node-row .cm-qlib__node-toggle svg { transform: rotate(0deg); }
.cm-qlib__node-toggle--empty { cursor: default; }
.cm-qlib__node.is-collapsed > .cm-qlib__tree--nested { display: none; }
.cm-qlib__node-grip { flex: none; display: grid; place-items: center; width: 12px; color: var(--cm-disabled); cursor: grab; }
.cm-qlib__node-row:hover .cm-qlib__node-grip { color: var(--cm-faint); }
/* The root entry keeps the grip's width so its label lines up with every folder below it, and loses
   the grab cursor: it receives a drop and is never itself dragged. */
.cm-qlib__node-grip--fixed { cursor: default; }
.cm-qlib__node-actions { flex: none; display: flex; align-items: center; gap: 1px; }
.cm-qlib__node-action { display: grid; place-items: center; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 5px; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-qlib__node-action:hover { background: var(--cm-surface-alt); color: var(--cm-brand-strong); }
.cm-qlib__node-action--danger:hover { background: var(--cm-red-bg); color: var(--cm-red-tx); }

.cm-qlib__content { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 18px 20px 20px; }
.cm-qlib__empty { margin: 8px 2px; font: 400 13.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-qlib__search { min-width: 220px; }

.cm-qlib__table { width: 100%; border-collapse: collapse; font: 400 13px var(--cm-font-ui); }
.cm-qlib__table th { text-align: left; padding: 6px 10px; font: 600 11px var(--cm-font-ui); letter-spacing: 0.05em; text-transform: uppercase; color: var(--cm-faint); border-bottom: 1px solid var(--cm-border-soft); }
.cm-qlib__table td { padding: 9px 10px; border-bottom: 1px solid var(--cm-border-faint); color: var(--cm-text); vertical-align: middle; }
.cm-qlib__row:hover td { background: var(--cm-surface-alt); }
.cm-qlib__row.is-dragging td { opacity: 0.45; }
.cm-qlib__name { display: flex; align-items: center; gap: 9px; min-width: 0; font-weight: 600; color: var(--cm-ink); }
.cm-qlib__name a { color: inherit; text-decoration: none; }
.cm-qlib__name a:hover { text-decoration: underline; }
.cm-qlib__row.is-folder .cm-qlib__name a { color: var(--cm-brand-strong); }
.cm-qlib__num { font-variant-numeric: tabular-nums; color: var(--cm-muted); white-space: nowrap; }
.cm-qlib__muted { color: var(--cm-muted); }
.cm-qlib__folderlink { color: var(--cm-brand-strong); text-decoration: none; }
.cm-qlib__folderlink:hover { text-decoration: underline; }
.cm-qlib__menu { width: 1%; white-space: nowrap; text-align: right; }
.cm-qlib__kebab { border: 0; background: none; color: var(--cm-faint); font-weight: 700; letter-spacing: 1px; padding: 2px 6px; border-radius: 6px; cursor: pointer; }
.cm-qlib__kebab:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }

/* The same tile as the file library's, so a row reads the same in both libraries - two cases here
   rather than seven: a folder, and a quiz. */
.cm-qlib__ext { font: 700 9.5px/1 var(--cm-font-ui); letter-spacing: .04em; padding: 5px 6px; border-radius: 5px; min-width: 34px; text-align: center; flex: none; background: var(--cm-surface-alt); color: var(--cm-muted); border: 1px solid var(--cm-border-soft); display: grid; place-items: center; }
.cm-qlib__ext--dir { background: transparent; color: var(--cm-brand); border-color: var(--cm-border); }
.cm-qlib__ext--quiz { background: var(--cm-purple-bg); color: var(--cm-purple-tx); border-color: transparent; }

/* « Déplacer vers… » : the folder tree, flattened and indented, over the screen. A dialog rather than
   a submenu because the list is as deep as the library is, and a dropdown inside a dropdown inside a
   `.table-responsive` is the clipping trap the row menu already had to be taken out of. */
.cm-qlib__move { position: fixed; inset: 0; z-index: 1080; display: grid; place-items: center; background: rgba(18, 52, 77, .55); padding: 20px; }
.cm-qlib__move-box { width: min(420px, 100%); max-height: min(70vh, 560px); display: flex; flex-direction: column; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); box-shadow: 0 18px 48px rgba(18, 52, 77, .28); }
.cm-qlib__move-head { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-qlib__move-title { flex: 1; min-width: 0; margin: 0; font: 600 14px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-qlib__move-close { border: 0; background: none; color: var(--cm-faint); font-size: 22px; line-height: 1; cursor: pointer; }
.cm-qlib__move-list { flex: 1; min-height: 0; overflow-y: auto; list-style: none; margin: 0; padding: 8px 0; }
.cm-qlib__move-item { width: 100%; display: flex; align-items: center; gap: 8px; padding: 7px 10px; border: 0; background: none; font: 400 13px var(--cm-font-ui); color: var(--cm-text); text-align: left; cursor: pointer; }
.cm-qlib__move-item:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }

@media (max-width: 991.98px) {
    .cm-qlib { grid-template-columns: minmax(0, 1fr); }
    .cm-qlib__rail { position: static; max-height: none; }
}

/* --- Bibliothèque de sondages : le classement par dossiers --------------------------------- */
/* The quiz library's `.cm-qlib` block just above, under its own prefix. Duplicated rather than
   shared, which is what `.cm-flib` -> `.cm-qlib` already did: the two libraries look alike today and
   their columns are not the same set, so a single block would have to be split again the first time
   one of them grows a column the other has no use for. What changes here is the row tile - a model
   is not a quiz - and nothing else. */

.cm-slib { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 22px; align-items: start; }
.cm-slib__rail { position: sticky; top: 84px; max-height: calc(100vh - 104px); display: flex; flex-direction: column; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 14px 12px; }

.cm-slib__rail-head { display: flex; align-items: center; gap: 8px; padding: 0 4px 10px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-slib__rail-title { flex: 1; min-width: 0; font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Pinned to the bottom of the block rather than laid under the last folder: it acts on the *whole*
   library. One entry and not the file library's two - there is no corbeille on this side. */
.cm-slib__rail-foot { display: flex; align-items: center; padding-top: 10px; margin-top: auto; border-top: 1px solid var(--cm-border-soft); }

.cm-slib__tree { list-style: none; margin: 10px 0 0; padding: 0; }
/* `:not(--nested)` for the reason the wiki records: the two lists share a class, and setting one
   overflow axis to anything but `visible` computes the other to `auto` - every branch would get a
   stray horizontal scrollbar. `min-height: 0` is what makes a flex item actually shrink. */
.cm-slib__tree:not(.cm-slib__tree--nested) { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.cm-slib__tree--nested { margin: 2px 0 2px 14px; border-left: 1px solid var(--cm-border-soft); padding-left: 6px; }
.cm-slib__node { margin: 1px 0; }
.cm-slib__node.is-dragging { opacity: 0.45; }
/* One drop state, not the wiki's three: there is no re-order here, so "between two folders" means
   nothing - a drop is always *into*. */
.cm-slib__node.is-drop-target > .cm-slib__node-row { background: var(--cm-blue-bg-soft); box-shadow: inset 0 0 0 1px var(--cm-brand); border-radius: 6px; }
.cm-slib__node-row { display: flex; align-items: center; gap: 1px; min-width: 0; }
.cm-slib__node-link { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 6px; font: 400 13px var(--cm-font-ui); color: var(--cm-text); text-decoration: none; }
.cm-slib__node-link:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }
.cm-slib__node-link.is-current { background: var(--cm-blue-bg); color: var(--cm-blue-tx); font-weight: 600; }
.cm-slib__node-ico { flex: none; display: grid; place-items: center; color: var(--cm-faint); }
.cm-slib__node-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-slib__node-toggle { flex: none; display: grid; place-items: center; width: 14px; height: 18px; padding: 0; border: 0; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-slib__node-toggle svg { transform: rotate(90deg); transition: transform 0.12s ease; }
.cm-slib__node.is-collapsed > .cm-slib__node-row .cm-slib__node-toggle svg { transform: rotate(0deg); }
.cm-slib__node-toggle--empty { cursor: default; }
.cm-slib__node.is-collapsed > .cm-slib__tree--nested { display: none; }
.cm-slib__node-grip { flex: none; display: grid; place-items: center; width: 12px; color: var(--cm-disabled); cursor: grab; }
.cm-slib__node-row:hover .cm-slib__node-grip { color: var(--cm-faint); }
/* The root entry keeps the grip's width so its label lines up with every folder below it, and loses
   the grab cursor: it receives a drop and is never itself dragged. */
.cm-slib__node-grip--fixed { cursor: default; }
.cm-slib__node-actions { flex: none; display: flex; align-items: center; gap: 1px; }
.cm-slib__node-action { display: grid; place-items: center; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 5px; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-slib__node-action:hover { background: var(--cm-surface-alt); color: var(--cm-brand-strong); }
.cm-slib__node-action--danger:hover { background: var(--cm-red-bg); color: var(--cm-red-tx); }

.cm-slib__content { background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 18px 20px 20px; }
.cm-slib__empty { margin: 8px 2px; font: 400 13.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-slib__search { min-width: 220px; }

.cm-slib__table { width: 100%; border-collapse: collapse; font: 400 13px var(--cm-font-ui); }
.cm-slib__table th { text-align: left; padding: 6px 10px; font: 600 11px var(--cm-font-ui); letter-spacing: 0.05em; text-transform: uppercase; color: var(--cm-faint); border-bottom: 1px solid var(--cm-border-soft); }
.cm-slib__table td { padding: 9px 10px; border-bottom: 1px solid var(--cm-border-faint); color: var(--cm-text); vertical-align: middle; }
.cm-slib__row:hover td { background: var(--cm-surface-alt); }
.cm-slib__row.is-dragging td { opacity: 0.45; }
.cm-slib__name { display: flex; align-items: center; gap: 9px; min-width: 0; font-weight: 600; color: var(--cm-ink); }
.cm-slib__name a { color: inherit; text-decoration: none; }
.cm-slib__name a:hover { text-decoration: underline; }
.cm-slib__row.is-folder .cm-slib__name a { color: var(--cm-brand-strong); }
.cm-slib__num { font-variant-numeric: tabular-nums; color: var(--cm-muted); white-space: nowrap; }
.cm-slib__muted { color: var(--cm-muted); }
.cm-slib__folderlink { color: var(--cm-brand-strong); text-decoration: none; }
.cm-slib__folderlink:hover { text-decoration: underline; }
.cm-slib__menu { width: 1%; white-space: nowrap; text-align: right; }
.cm-slib__kebab { border: 0; background: none; color: var(--cm-faint); font-weight: 700; letter-spacing: 1px; padding: 2px 6px; border-radius: 6px; cursor: pointer; }
.cm-slib__kebab:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }

/* The same tile as the file library's, so a row reads the same in both libraries - two cases here
   rather than seven: a folder, and a quiz. */
.cm-slib__ext { font: 700 9.5px/1 var(--cm-font-ui); letter-spacing: .04em; padding: 5px 6px; border-radius: 5px; min-width: 34px; text-align: center; flex: none; background: var(--cm-surface-alt); color: var(--cm-muted); border: 1px solid var(--cm-border-soft); display: grid; place-items: center; }
.cm-slib__ext--dir { background: transparent; color: var(--cm-brand); border-color: var(--cm-border); }
.cm-slib__ext--survey { background: var(--cm-blue-bg); color: var(--cm-blue-tx); border-color: transparent; }

/* « Déplacer vers… » : the folder tree, flattened and indented, over the screen. A dialog rather than
   a submenu because the list is as deep as the library is, and a dropdown inside a dropdown inside a
   `.table-responsive` is the clipping trap the row menu already had to be taken out of. */
.cm-slib__move { position: fixed; inset: 0; z-index: 1080; display: grid; place-items: center; background: rgba(18, 52, 77, .55); padding: 20px; }
.cm-slib__move-box { width: min(420px, 100%); max-height: min(70vh, 560px); display: flex; flex-direction: column; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); box-shadow: 0 18px 48px rgba(18, 52, 77, .28); }
.cm-slib__move-head { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-slib__move-title { flex: 1; min-width: 0; margin: 0; font: 600 14px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-slib__move-close { border: 0; background: none; color: var(--cm-faint); font-size: 22px; line-height: 1; cursor: pointer; }
.cm-slib__move-list { flex: 1; min-height: 0; overflow-y: auto; list-style: none; margin: 0; padding: 8px 0; }
.cm-slib__move-item { width: 100%; display: flex; align-items: center; gap: 8px; padding: 7px 10px; border: 0; background: none; font: 400 13px var(--cm-font-ui); color: var(--cm-text); text-align: left; cursor: pointer; }
.cm-slib__move-item:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }

@media (max-width: 991.98px) {
    .cm-slib { grid-template-columns: minmax(0, 1fr); }
    .cm-slib__rail { position: static; max-height: none; }
}

/* --- Bibliothèque de séquences : le classement par dossiers -------------------------------- */
/* The quiz library's `.cm-qlib` rail under its own prefix, and **only the rail**: this screen keeps
   its Tabler card and table, because the classement was asked to add a tree, not to redraw the rows.
   So there is no `__table`, no `__ext` tile and no `__kebab` here - the listing's row is the one the
   teacher already knows, minus the « Blocs » column. What is duplicated is the tree, the foot and
   « Déplacer vers… », for the reason `.cm-flib` -> `.cm-qlib` -> `.cm-slib` already recorded: the
   libraries look alike today and their listings are not the same table, so a single block would have
   to be split again the first time one of them grows something the others have no use for. */

.cm-seqlib { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 22px; align-items: start; }
.cm-seqlib__rail { position: sticky; top: 84px; max-height: calc(100vh - 104px); display: flex; flex-direction: column; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 14px 12px; }

.cm-seqlib__rail-head { display: flex; align-items: center; gap: 8px; padding: 0 4px 10px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-seqlib__rail-title { flex: 1; min-width: 0; font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Pinned to the bottom of the block rather than laid under the last folder: it acts on the *whole*
   library. One entry and not the file library's two - there is no corbeille on this side. */
.cm-seqlib__rail-foot { display: flex; align-items: center; padding-top: 10px; margin-top: auto; border-top: 1px solid var(--cm-border-soft); }

.cm-seqlib__tree { list-style: none; margin: 10px 0 0; padding: 0; }
/* `:not(--nested)` for the reason the wiki records: the two lists share a class, and setting one
   overflow axis to anything but `visible` computes the other to `auto` - every branch would get a
   stray horizontal scrollbar. `min-height: 0` is what makes a flex item actually shrink. */
.cm-seqlib__tree:not(.cm-seqlib__tree--nested) { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.cm-seqlib__tree--nested { margin: 2px 0 2px 14px; border-left: 1px solid var(--cm-border-soft); padding-left: 6px; }
.cm-seqlib__node { margin: 1px 0; }
.cm-seqlib__node.is-dragging { opacity: 0.45; }
/* One drop state, not the wiki's three: there is no re-order in this tree, so "between two folders"
   means nothing - a drop is always *into*. */
.cm-seqlib__node.is-drop-target > .cm-seqlib__node-row { background: var(--cm-blue-bg-soft); box-shadow: inset 0 0 0 1px var(--cm-brand); border-radius: 6px; }
.cm-seqlib__node-row { display: flex; align-items: center; gap: 1px; min-width: 0; }
.cm-seqlib__node-link { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 6px; font: 400 13px var(--cm-font-ui); color: var(--cm-text); text-decoration: none; }
.cm-seqlib__node-link:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }
.cm-seqlib__node-link.is-current { background: var(--cm-blue-bg); color: var(--cm-blue-tx); font-weight: 600; }
.cm-seqlib__node-ico { flex: none; display: grid; place-items: center; color: var(--cm-faint); }
.cm-seqlib__node-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-seqlib__node-toggle { flex: none; display: grid; place-items: center; width: 14px; height: 18px; padding: 0; border: 0; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-seqlib__node-toggle svg { transform: rotate(90deg); transition: transform 0.12s ease; }
.cm-seqlib__node.is-collapsed > .cm-seqlib__node-row .cm-seqlib__node-toggle svg { transform: rotate(0deg); }
.cm-seqlib__node-toggle--empty { cursor: default; }
.cm-seqlib__node.is-collapsed > .cm-seqlib__tree--nested { display: none; }
.cm-seqlib__node-grip { flex: none; display: grid; place-items: center; width: 12px; color: var(--cm-disabled); cursor: grab; }
.cm-seqlib__node-row:hover .cm-seqlib__node-grip { color: var(--cm-faint); }
/* The root entry keeps the grip's width so its label lines up with every folder below it, and loses
   the grab cursor: it receives a drop and is never itself dragged. */
.cm-seqlib__node-grip--fixed { cursor: default; }
.cm-seqlib__node-actions { flex: none; display: flex; align-items: center; gap: 1px; }
.cm-seqlib__node-action { display: grid; place-items: center; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 5px; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-seqlib__node-action:hover { background: var(--cm-surface-alt); color: var(--cm-brand-strong); }
.cm-seqlib__node-action--danger:hover { background: var(--cm-red-bg); color: var(--cm-red-tx); }

/* The content column carries the screen's own card, so it needs no surface of its own - only the
   `min-width: 0` that keeps a wide table inside the grid track instead of stretching it. */
.cm-seqlib__content { min-width: 0; }
.cm-seqlib__empty { margin: 14px 16px; font: 400 13.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-seqlib__search { min-width: 220px; }
/* A folder row of the listing: the folder pictogram beside the name, nothing else - the row keeps the
   table's own typography so it reads as one listing and not two. */
.cm-seqlib__foldername { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }

/* « Déplacer vers… » : the folder tree, flattened and indented, over the screen. A dialog rather than
   a submenu because the list is as deep as the library is. */
.cm-seqlib__move { position: fixed; inset: 0; z-index: 1080; display: grid; place-items: center; background: rgba(18, 52, 77, .55); padding: 20px; }
.cm-seqlib__move-box { width: min(420px, 100%); max-height: min(70vh, 560px); display: flex; flex-direction: column; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius); box-shadow: 0 18px 48px rgba(18, 52, 77, .28); }
.cm-seqlib__move-head { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-seqlib__move-title { flex: 1; min-width: 0; margin: 0; font: 600 14px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-seqlib__move-close { border: 0; background: none; color: var(--cm-faint); font-size: 22px; line-height: 1; cursor: pointer; }
.cm-seqlib__move-list { flex: 1; min-height: 0; overflow-y: auto; list-style: none; margin: 0; padding: 8px 0; }
.cm-seqlib__move-item { width: 100%; display: flex; align-items: center; gap: 8px; padding: 7px 10px; border: 0; background: none; font: 400 13px var(--cm-font-ui); color: var(--cm-text); text-align: left; cursor: pointer; }
.cm-seqlib__move-item:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }

@media (max-width: 991.98px) {
    .cm-seqlib { grid-template-columns: minmax(0, 1fr); }
    .cm-seqlib__rail { position: static; max-height: none; }
}

/* --- The link: the picker's Bibliothèque tab, the usage panel, the deletion modal -------- */

/* The tab strip's buttons are buttons now that they switch panes - reset to the tab's look rather
   than the browser's. The underline is restated here: the `border: 0` this rule used to carry alone
   also wiped the 2px transparent bottom border declared with the tab, so the active tab lost the
   only mark that said which pane was showing. */
.cm-filepick__tab { border: 0; border-bottom: 2px solid transparent; background: none; cursor: pointer; }
.cm-filepick__library { padding: 14px; border-top: 1px solid var(--cm-border-soft); }
.cm-filepick__library-search { margin-bottom: 10px; }
/* **The height belongs to the two columns, not to the grid that holds them.** It used to sit on this
   container, and a grid row is sized by its content: the tree grew to its natural 900-odd pixels,
   the row grew with it, and the `max-height` only clipped what stuck out - so the lower half of the
   tree was drawn outside the component and reachable by nothing, the `overflow-y: auto` never having
   anything to scroll. Capping each column instead gives the overflow something to work on, and a
   short library still takes only the room it needs. */
.cm-filepick__library-body { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 12px; }
.cm-filepick__library-tree { max-height: 260px; overflow-y: auto; border-right: 1px solid var(--cm-border-soft); padding-right: 8px; }
.cm-filepick__library-col { max-height: 260px; min-height: 0; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.cm-filepick__library-list { list-style: none; margin: 0; padding: 0; }
/* The folded branch, exactly as the library's own rail draws it: one triangle rotated by CSS, so the
   two states cannot drift apart the way two glyphs would. */
.cm-filepick__library-node.is-collapsed > .cm-filepick__library-list--nested { display: none; }
.cm-filepick__library-row { display: flex; align-items: center; gap: 2px; }
.cm-filepick__library-toggle { flex: none; display: grid; place-items: center; width: 14px; height: 18px; padding: 0; border: 0; background: none; color: var(--cm-faint); cursor: pointer; }
.cm-filepick__library-toggle svg { transform: rotate(90deg); transition: transform 0.12s ease; }
.cm-filepick__library-node.is-collapsed > .cm-filepick__library-row .cm-filepick__library-toggle svg { transform: rotate(0deg); }
.cm-filepick__library-toggle--empty { cursor: default; }
.cm-filepick__library-folder { flex: 1; min-width: 0; display: block; text-align: left; border: 0; background: none; padding: 5px 6px; border-radius: 6px; font: 400 12.5px var(--cm-font-ui); color: var(--cm-text); cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-filepick__library-folder:hover { background: var(--cm-surface-alt); color: var(--cm-ink); }
.cm-filepick__library-folder.is-current { background: var(--cm-surface-alt); color: var(--cm-brand-strong); font-weight: 600; }
/* The selection bar sits above the scrolling list, never inside it: a « Tout sélectionner » that
   scrolls out of sight is one nobody finds. */
.cm-filepick__library-bar { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cm-filepick__library-selectall { border: 0; background: none; padding: 0; font: 600 12px var(--cm-font-ui); color: var(--cm-brand-strong); cursor: pointer; }
.cm-filepick__library-selectall:hover { text-decoration: underline; }
.cm-filepick__library-bar .cm-btn:disabled { opacity: .5; cursor: not-allowed; }
.cm-filepick__library-files { min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
.cm-filepick__library-file { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; border: 1px solid transparent; background: none; padding: 6px 8px; border-radius: 8px; cursor: pointer; }
.cm-filepick__library-file:hover { background: var(--cm-surface-alt); border-color: var(--cm-border-soft); }
.cm-filepick__library-file--pick { margin: 0; }
.cm-filepick__library-check { flex: none; margin: 0; cursor: pointer; }
.cm-filepick__library-file-name { flex: 1; min-width: 0; font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-filepick__library-empty { margin: 8px 2px; font: 400 12.5px var(--cm-font-ui); color: var(--cm-faint); }

/* The « Lien externe » pane: one field, and the sentence that says what a link costs. */
.cm-filepick__link { padding: 14px; border-top: 1px solid var(--cm-border-soft); }
.cm-filepick__link-help { margin: 8px 2px 0; font: 400 12px var(--cm-font-ui); color: var(--cm-faint); }

/* The usage count is a button now: it opens the panel. It keeps the pill's look, because what it
   shows is still a count. */
.cm-flib__use { border: 1px solid var(--cm-border-soft); cursor: default; }
button.cm-flib__use { cursor: pointer; }
.cm-flib__use--n { background: var(--cm-blue-bg); color: var(--cm-blue-tx); border-color: transparent; }
button.cm-flib__use--n:hover { background: var(--cm-brand); color: #fff; }

.cm-flib__usages { position: fixed; inset: 0; z-index: 1075; display: grid; place-items: center; background: rgba(18, 52, 77, .45); padding: 20px; }
.cm-flib__usages-box { width: min(560px, 100%); max-height: 80vh; display: flex; flex-direction: column; background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 14px; box-shadow: var(--cm-shadow-pop); overflow: hidden; }
.cm-flib__usages-head { display: flex; align-items: center; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-flib__usages-title { flex: 1; min-width: 0; margin: 0; font: 600 18px/1.25 var(--cm-font-title); color: var(--cm-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-flib__usages-head .cm-flib__viewer-close { color: var(--cm-faint); }
.cm-flib__usages-body { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 20px; font: 400 13.5px var(--cm-font-ui); color: var(--cm-text); }
.cm-flib__usages-foot { display: flex; gap: 9px; justify-content: flex-end; padding: 14px 20px; border-top: 1px solid var(--cm-border-soft); background: var(--cm-surface-alt); }

.cm-flib__uselist { list-style: none; margin: 0; padding: 0; }
.cm-flib__uselist li { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--cm-border-soft); }
.cm-flib__uselist li:last-child { border-bottom: 0; }
.cm-flib__uselist .where { font: 600 10.5px/1 var(--cm-font-ui); letter-spacing: 0.08em; text-transform: uppercase; color: var(--cm-faint); min-width: 128px; }
.cm-flib__uselist .what { color: var(--cm-ink); font-weight: 600; }
.cm-flib__uselist a.what { color: var(--cm-brand-strong); text-decoration: none; }
.cm-flib__uselist a.what:hover { text-decoration: underline; }
/* One line, and only one - see the controller: the modal says the links go now and the bytes go in
   thirty days, and anything more turns a confirmation into a lecture. */
.cm-flib__usenotice { margin: 14px 0 0; padding: 10px 12px; border-radius: 8px; background: var(--cm-gold-bg); color: var(--cm-gold-tx); font-size: 12.5px; }

/* The chip of a file the wizard was opened from: gold, like the picker's library pill, because it
   is a link and not a copy - deleting the file from the library will remove it from the work too. */
.cm-chip--library { background: var(--cm-gold-bg); color: var(--cm-gold-tx); border-color: transparent; }
.cm-chip__note { margin-left: 8px; font-size: 11px; opacity: .85; }

/* ---------- Lot 7 · Partager un document a une classe ---------- */
/* The share form is a column of fields, not a card grid: each decision narrows the previous one,
   and reading it top to bottom is reading the audience being built. The per-class blocks are
   revealed by shared_document_controller.js, which also enables their fields - a hidden block must
   not submit, so `hidden` alone would not do. */
.cm-share { display: flex; flex-direction: column; gap: 20px; max-width: 620px; margin-bottom: 32px; }
.cm-share__field { border: 0; padding: 0; margin: 0; }
.cm-share__field .form-label { display: block; margin-bottom: 4px; font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
/* One block per class, revealed by the `shared-document` controller. Named after the program and
   not after a « scope »: the content-sharing modal further down owns `.cm-share__scope`, and the
   two used to collide - the block inherited that card's two-column grid, which stretched the
   options and squeezed the modalities into a narrow tower. */
.cm-share__program { display: flex; flex-direction: column; gap: 20px; }
.cm-share__program[hidden] { display: none; }
.cm-share__help { margin: 0 0 8px; font-size: 12px; color: var(--cm-faint); }
/* What sharing a folder means, above the form. Gold rather than grey: it is not help text about
   a field, it is the scope of what the teacher is about to do. */
.cm-share__notice { margin: 0 0 18px; padding: 10px 14px; border-radius: 8px; background: var(--cm-gold-bg); border: 1px solid var(--cm-gold); font: 400 13px var(--cm-font-ui); color: var(--cm-gold-tx); }
.cm-share__checks { display: flex; flex-wrap: wrap; gap: 6px 20px; }
.cm-share__window { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; }
.cm-share__window[hidden] { display: none; }
.cm-share__window > div { flex: 1; min-width: 190px; }
.cm-share__actions { display: flex; gap: 10px; }
.cm-share__listtitle { margin: 0 0 12px; font: 600 15px var(--cm-font-title); color: var(--cm-ink); }

/* The student list's headings - a matiere or a teacher, above the table of its own documents. The
   flat list draws none, which is what makes it flat. */
.cm-shared__group { margin: 24px 0 10px; font: 600 15px var(--cm-font-title); color: var(--cm-ink); }
.cm-shared__group:first-of-type { margin-top: 0; }

/* ============================================================================================
   Console Proxmox (/infrastructure)
   ============================================================================================
   Reference: the fourteen mockups of the "Console Proxmox" handoff, rendered in these very
   tokens. Everything below is area-local furniture that had no equivalent yet - the buttons,
   badges, notes, radio cards, steppers and action bars all come from the shared cm-* blocks
   above and are not restated here.

   Navigation between the screens of the area used to be a shape of its own (.cm-localnav), on the
   argument that tabs switch views of one screen while that strip moved between screens. The
   application makes no such distinction anywhere else - Paramètres, Partages and UFA all tab
   between separate routes - so the area now uses .cm-tabs like everything else, and that block is
   gone. */


/* ---------- Hub tiles ---------- */

.cm-tilegrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
@media (max-width: 991.98px) { .cm-tilegrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 575.98px) { .cm-tilegrid { grid-template-columns: 1fr; } }

.cm-tile {
    display: flex; flex-direction: column; gap: 3px;
    padding: 15px 17px; border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
    background: var(--cm-surface); text-decoration: none;
}
.cm-tile:hover { border-color: var(--cm-brand); text-decoration: none; }
.cm-tile__title { font: 700 14px var(--cm-font-ui); color: var(--cm-ink); }
.cm-tile__desc { font-size: 12.5px; color: var(--cm-muted); line-height: 1.45; }
.cm-tile__go { margin-top: auto; padding-top: 8px; font: 600 13px var(--cm-font-ui); color: var(--cm-brand-strong); }

/* ---------- Host cards ---------- */
/* Cards rather than a table, because a host carries a live state a row cannot show: a status
   dot, a version, its credential mode, three counters and the age of the last check. That age
   is not decoration - it is the admission that the dot reports the last known state and not the
   current one, and it is why nothing here probes anything as the page renders. */

.cm-hostgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
/* The dot sits on the first line of the name rather than on the block's centre. */
.cm-host .cm-statusdot { margin-top: 7px; }
.cm-host__ident { flex: 1; min-width: 0; }
/* Inside a card the note is the block, so it carries no margin of its own. */
.cm-host__alert { margin: 0; }
@media (max-width: 1199.98px) { .cm-hostgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 767.98px) { .cm-hostgrid { grid-template-columns: 1fr; } }

.cm-host {
    display: flex; flex-direction: column; overflow: hidden;
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: var(--cm-radius);
}
.cm-host--inactive { opacity: .62; }
.cm-host__top { display: flex; align-items: flex-start; gap: 10px; padding: 14px 16px 10px; }
.cm-host__name { font: 500 16px var(--cm-font-title); color: var(--cm-ink); line-height: 1.2; }
.cm-host__url { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 11.5px; color: var(--cm-faint); margin-top: 3px; word-break: break-all; }
.cm-host__body { padding: 0 16px 12px; }
.cm-host__badges { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 9px; }
.cm-host__meta { margin-top: 11px; font-size: 11.5px; color: var(--cm-faint); }
.cm-host__meta b { color: var(--cm-muted); font-weight: 600; }
/* The footer is a .cm-actions row, so the spacing between actions comes from there; this only
   places the row and gives it its band. */
.cm-host__foot {
    margin-top: auto; display: flex; align-items: center; flex-wrap: wrap;
    padding: 9px 16px; background: var(--cm-surface-soft); border-top: 1px solid var(--cm-border-soft);
}
/* Two classes rather than one, to beat .cm-actions > *:not(:first-child)'s own margin-left - which
   would otherwise pin the last action next to its neighbour instead of at the far end. */
.cm-host__foot .cm-host__spacer { margin-left: auto; }

.cm-host__kpis { display: flex; gap: 0; border-top: 1px solid var(--cm-border-faint); padding-top: 10px; margin-top: 4px; }
.cm-host__kpi { flex: 1; }
.cm-host__kpi-value { font: 600 19px var(--cm-font-ui); color: var(--cm-ink); font-variant-numeric: tabular-nums; line-height: 1.1; }
.cm-host__kpi-label { font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--cm-faint); margin-top: 2px; }

/* ---------- Status dot ---------- */

.cm-statusdot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none; }
.cm-statusdot--ok { background: var(--cm-positive-tx); }
.cm-statusdot--down { background: var(--cm-red-tx); }
.cm-statusdot--unknown { background: var(--cm-disabled); }
.cm-statusdot--warn { background: var(--cm-warn-tx); }

/* ---------- Inline text actions inside a card foot ---------- */


/* ---------- Host form ---------- */
/* One card, fieldsets separated by a rule, each headed by its own small caps legend. The four
   sections are read in the order the decisions are made: who it is, how we log in, how we check
   its certificate, and what we are allowed to do on it. */

.cm-fieldset { border: 0; padding: 16px 18px; margin: 0; border-top: 1px solid var(--cm-border-faint); }
.cm-fieldset:first-of-type { border-top: 0; }
.cm-fieldset__legend {
    float: none; width: auto; padding: 0; margin: 0 0 12px;
    font: 700 11px var(--cm-font-ui); letter-spacing: .1em; text-transform: uppercase; color: var(--cm-brand);
}
.cm-fieldset__legend span { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--cm-faint); }
.cm-fieldset[hidden] { display: none; }

/* The two certificate digests, shown together and named apart - the only remedy to pasting a
   certificate fingerprint into a field that wants a public-key pin. Monospaced and wrapping,
   because both are long and both get compared character by character against a terminal. */
.cm-fingerprint {
    font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 11.5px; line-height: 1.6;
    background: var(--cm-surface-alt); border: 1px solid var(--cm-border); border-radius: 7px;
    padding: 9px 11px; color: var(--cm-text); word-break: break-all;
}
.cm-fingerprint dt { font-weight: 600; color: var(--cm-ink); }
.cm-fingerprint dd { margin: 0 0 8px; }
.cm-fingerprint dd:last-child { margin-bottom: 0; }

/* The permission that will never be a permission. Drawn disabled rather than omitted so the
   question "and if I do want to delete one?" is answered where it is asked. */
.cm-never { display: flex; gap: 8px; align-items: flex-start; color: var(--cm-disabled); font-size: 13px; }
.cm-never i { font-style: italic; }

/* ---------- Console Proxmox · lot 2 : la liste des machines et le journal ---------- */

/* A plain table rather than DataTables, and on purpose: the rows come from the hypervisor at
   render time rather than from an AJAX endpoint, and DataTables rewrites the DOM it is given -
   which is exactly what must not happen to rows carrying a Stimulus target that a poller writes
   into. See the repository-wide rule about never binding a controller to an element DataTables
   moves. */


/* Out of perimeter: dimmed, still legible, still counted. Never hidden - a machine that
   disappears from the list reads as a machine that disappeared from the hypervisor. */
.cm-row--out td { background: var(--cm-surface-alt); }
.cm-row--out .cm-guest__name { color: var(--cm-muted); font-weight: 500; }

.cm-mono { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12.5px; color: var(--cm-muted); }
.cm-guest__name { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-guest__sub { font-size: 11.5px; color: var(--cm-faint); }
.cm-guest__actions { display: flex; gap: 12px; align-items: center; justify-content: flex-end; flex-wrap: wrap; }

/* The address of a machine, with its copy button. The button keeps the row's height: it is sized
   on the sub-line rather than on a control, and only its icon reacts. */
.cm-guest__ip { display: inline-flex; align-items: center; gap: 4px; }
.cm-guest__ip .cm-mono { font-size: 12px; }
.cm-copybtn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 20px; height: 20px; padding: 0;
    border: 0; border-radius: 5px; background: transparent;
    color: var(--cm-faint); cursor: pointer; line-height: 0;
}
.cm-copybtn:hover { background: var(--cm-surface-soft); color: var(--cm-ink); }
.cm-copybtn__icon--done,
.cm-copybtn--done .cm-copybtn__icon { display: none; }
.cm-copybtn--done .cm-copybtn__icon--done { display: block; color: var(--cm-green-tx); }

/* The resource bars. Deliberately unlabelled beyond their own caption: the number underneath is
   the fact, the bar is only what makes a column of them scannable. */
.cm-meter { height: 5px; border-radius: 3px; background: var(--cm-border-soft); overflow: hidden; width: 78px; }
.cm-meter__fill { display: block; height: 100%; border-radius: 3px; background: var(--cm-brand); }
.cm-meter__fill--high { background: var(--cm-warn-tx); }
.cm-meter__text { font-size: 11px; color: var(--cm-faint); margin-top: 3px; font-variant-numeric: tabular-nums; }

/* ---------- Console Proxmox · lot 3 : images, adresses, assistant de création ---------- */

/* The two kinds of image, told apart by a badge rather than by their position in a list: a
   template can be cloned in a second and an ISO means a manual installation, and that difference
   decides what the creation wizard is able to do. */
.cm-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 7px; margin-right: 8px; vertical-align: middle;
    font: 700 10px var(--cm-font-ui); letter-spacing: .02em;
}
.cm-ico--template { background: var(--cm-teal-bg); color: var(--cm-teal-tx); }
.cm-ico--iso { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }

/* The summary of what is about to be created, or of what just was: a label column and a value
   column, because every line is one fact and the eye should be able to run down either side. */
.cm-sum { display: grid; grid-template-columns: 160px 1fr; gap: 7px 14px; font-size: 13px; margin: 0; }
.cm-sum dt { color: var(--cm-muted); font-weight: 400; }
.cm-sum dd { margin: 0; color: var(--cm-ink); font-weight: 600; }
@media (max-width: 575.98px) { .cm-sum { grid-template-columns: 1fr; gap: 2px 0; } }

/* ---------- Import d'une classe (Annuaire > Utilisateurs) ---------- */

/* The three moves of the import, on all three screens. A wizard that does not say how far along it
   is turns "vérification" into a step somebody might take for the end of the road. */
.cm-steps {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    font-size: 12.5px; color: var(--cm-faint);
}
.cm-steps__step { display: inline-flex; align-items: center; gap: 6px; }
.cm-steps__step b { color: var(--cm-faint); font-weight: 700; }
.cm-steps__step--on { color: var(--cm-brand-strong); font-weight: 600; }
.cm-steps__step--on b { color: var(--cm-brand-strong); }
.cm-steps__step--done { color: var(--cm-green-tx); }
.cm-steps__step--done b { color: var(--cm-green-tx); }
.cm-steps__arrow { color: var(--cm-disabled); }

/* A value the screen shows but nobody may change - here the account type, which a class import
   never chooses. Drawn as a field so the eye reads it in the column of fields, dashed so the hand
   does not try to type in it. */
.cm-locked-value {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    border: 1px dashed var(--cm-border); border-radius: var(--cm-radius-sm);
    background: var(--cm-surface-alt); color: var(--cm-muted);
    padding: 9px 12px; font-size: 14px;
}

/* One answer among several, with room for the two or three lines that let an operator tell two
   namesakes apart - a plain radio list has nowhere to put them. */
.cm-choice {
    display: flex; align-items: flex-start; gap: 10px;
    border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
    background: var(--cm-surface); padding: 11px 13px; cursor: pointer;
}
.cm-choice:hover { border-color: var(--cm-brand); }
.cm-choice > span { display: flex; flex-direction: column; gap: 2px; }
.cm-choice__title { color: var(--cm-ink); font-weight: 600; font-size: 13.5px; }
.cm-choice__detail { color: var(--cm-muted); font-size: 12.5px; }

/* ==========================================================================
   Partage de contenu entre enseignants
   design/validated/content-sharing-between-teachers.md
   ========================================================================== */

/* The line under a title or a badge that says what the item weighs in its own units - « 6 séances
   · 9 supports », « 24 questions ». Small and muted on purpose: it answers a question the reader
   has not asked yet. */
.cm-share__detail {
    color: var(--cm-faint);
    font-size: 12.5px;
    margin-top: 3px;
}

/* « la version refaite après le retour de janvier » - the author's own word, and the only free text
   of the whole feature. */
.cm-share__note {
    color: var(--cm-muted);
    font-size: 12.5px;
    font-style: italic;
    margin-top: 3px;
}

/* A withdrawn share does NOT leave the list: it greys and names itself. Vanishing without a word
   from a list a colleague was working from is the behaviour this avoids - and what they had already
   duplicated is still theirs. */
.cm-share__row--revoked { background: var(--cm-border-faint); }
.cm-share__row--revoked td:first-child { color: var(--cm-muted); }

/* --- the modal ---------------------------------------------------------- */

.cm-share__scopes { display: flex; gap: 10px; flex-wrap: wrap; }

.cm-share__scope {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: start;
    gap: 3px 9px;
    flex: 1 1 160px;
    border: 1.5px solid var(--cm-border);
    border-radius: var(--cm-radius-sm);
    background: var(--cm-surface);
    padding: 11px 13px;
    cursor: pointer;
}
.cm-share__scope:hover { border-color: var(--cm-brand); }
.cm-share__scope:has(input:checked) { border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); }
.cm-share__scope input { grid-row: 1 / span 2; margin-top: 3px; }
.cm-share__scope-title { color: var(--cm-ink); font-weight: 600; font-size: 13px; }
.cm-share__scope-hint { color: var(--cm-muted); font-size: 11.5px; }

/* The group tree is a checkbox list, per this app's form rule - never a <select multiple>. The
   indentation is what makes « BTS SIO » read as the parent of the classes it will also reach. */
.cm-share__grouptree {
    max-height: 220px;
    overflow-y: auto;
    border: 1px solid var(--cm-border);
    border-radius: var(--cm-radius-sm);
    background: var(--cm-surface);
    padding: 8px 10px;
}
.cm-share__grouprow {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 3px 0;
    font-size: 13px;
    cursor: pointer;
}

/* « Ce partage sera visible de 87 personnes » - measured, and said before the submit. */
.cm-share__count {
    margin: 8px 0 0;
    color: var(--cm-blue-tx);
    background: var(--cm-blue-bg);
    border-radius: var(--cm-radius-sm);
    padding: 8px 11px;
    font-size: 12.5px;
}

/* The two sentences the whole feature turns on, stated where the decision is made. */
.cm-share__rule {
    margin: 0;
    color: var(--cm-muted);
    font-size: 12.5px;
    line-height: 1.5;
}

.cm-share__subtitle {
    margin: 22px 0 8px;
    font-size: 11px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--cm-faint);
    font-weight: 700;
}

.cm-share__list { list-style: none; margin: 0; padding: 0; }

.cm-share__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    border-top: 1px solid var(--cm-border-soft);
    padding: 9px 0;
    font-size: 13px;
}
.cm-share__item form { margin: 0; }
.cm-share__item button {
    background: none;
    border: 0;
    padding: 0;
    font-size: 13px;
    font-weight: 600;
}

/* The folder tree the confirmation screen draws before writing anything - the one place a reader
   sees the « <séquence>/<séance>/ » rule as a shape rather than as a sentence. */
.cm-share__tree {
    background: var(--cm-surface-alt);
    border: 1px solid var(--cm-border);
    border-radius: var(--cm-radius-sm);
    padding: 12px 14px;
    margin: 0 0 12px;
    font-size: 12.5px;
    line-height: 1.7;
    color: var(--cm-ink);
    overflow-x: auto;
}

/* The recipient's quota as it will stand after the copy. Drawn even when it fits: the honest cost of
   a file that now weighs twice is a number, not a feeling. */
.cm-share__quota { max-width: 420px; }
.cm-share__quota-track {
    height: 6px;
    border-radius: 3px;
    background: var(--cm-border-soft);
    overflow: hidden;
}
.cm-share__quota-fill {
    height: 100%;
    border-radius: 3px;
    background: var(--cm-brand);
}

/* The catalogue's filter row: a free-text search, a type, an author. Deliberately no facet select
   built on Niveau/Option/Bloc - those are a private vocabulary per teacher, and a select built on
   the reader's own would hide every colleague's séquence whose label differs by a space. */
.cm-share__filters {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}
.cm-share__filters input[type="search"] { flex: 1 1 240px; min-width: 200px; }
.cm-share__filters select { flex: 0 0 auto; width: auto; }

/* A shared quiz read flat: the questions, their answers, and which ones are right. */
.cm-share__answers {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
    font-size: 13px;
}
.cm-share__answers li {
    padding: 2px 0 2px 18px;
    position: relative;
    color: var(--cm-muted);
}
.cm-share__answers li::before {
    content: '·';
    position: absolute;
    left: 6px;
    color: var(--cm-faint);
}
.cm-share__answer--right { color: var(--cm-positive-tx); font-weight: 600; }
.cm-share__answer--right::before { content: '✓'; color: var(--cm-positive-tx); font-size: 11px; }

/* The one sentence a screen that asks a question opens on. */
.cm-share__notice {
    background: var(--cm-blue-bg-soft);
    border: 1px solid var(--cm-blue-bg);
    border-radius: var(--cm-radius-sm);
    color: var(--cm-blue-tx);
    padding: 11px 13px;
    font-size: 12.5px;
    margin-bottom: 14px;
}

/* Installation log of one machine in a batch: a folded narrative under its row. Read top to bottom
   when a machine will not let anybody in, so the step and its detail sit on one line and a refusal
   is coloured rather than merely worded. */
.cm-installlog__row > td {
    padding-top: 0;
    background: var(--cm-surface-soft);
}

.cm-installlog > summary {
    cursor: pointer;
    color: var(--cm-muted);
    font-size: 0.85rem;
    padding: 4px 0;
}

.cm-installlog__list {
    list-style: none;
    margin: 4px 0 8px;
    padding: 0;
}

.cm-installlog__entry {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: baseline;
    padding: 3px 0;
    border-bottom: 1px solid var(--cm-border-soft);
    font-size: 0.85rem;
}

.cm-installlog__entry:last-child {
    border-bottom: 0;
}

.cm-installlog__at {
    color: var(--cm-faint);
    flex: 0 0 auto;
}

.cm-installlog__step {
    flex: 0 0 auto;
    font-weight: 500;
}

/* The detail is the part that gets long - an SSH refusal, a hypervisor's own words - so it takes
   the remaining width and wraps rather than stretching the table. */
.cm-installlog__detail {
    flex: 1 1 240px;
    color: var(--cm-muted);
    word-break: break-word;
}

.cm-installlog__entry--ko .cm-installlog__step,
.cm-installlog__entry--ko .cm-installlog__detail {
    color: var(--cm-red-tx);
}

/* ==========================================================================
   Sondages (.cm-survey-*) - design/validated/surveys.md, écran « éditeur ».
   ========================================================================== */

/* The ordered list of a survey's lines. A line is a question, a free-text comment or an
   intertitle; the intertitle carries no number and is drawn as a section rather than as a row,
   because it is not something one answers. */
.cm-survey-lines { display: flex; flex-direction: column; gap: 6px; }

.cm-survey-line {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 10px 12px;
    border: 1px solid var(--cm-border);
    border-radius: 10px;
    background: var(--cm-surface);
}
.cm-survey-line:hover { border-color: color-mix(in srgb, var(--cm-brand) 40%, var(--cm-border)); }
.cm-survey-line.is-selected {
    border-color: var(--cm-brand);
    box-shadow: inset 3px 0 0 var(--cm-brand);
}
/* An intertitle structures the reading of the survey, so it reads as a heading in the list too. */
.cm-survey-line--titre { background: color-mix(in srgb, var(--cm-brand) 5%, var(--cm-surface)); }
.cm-survey-line--titre .cm-survey-line__label { font-family: var(--cm-font-title); font-size: 14.5px; }

.cm-survey-line__body { flex: 1; min-width: 0; text-decoration: none; color: inherit; }
.cm-survey-line__body:hover { color: inherit; }
.cm-survey-line__head { display: flex; align-items: baseline; gap: 8px; }
.cm-survey-line__number { flex: none; font: 600 12px var(--cm-font-ui); color: var(--cm-faint); }
.cm-survey-line__label { font-size: 13.5px; }
.cm-survey-line__meta { display: flex; align-items: center; gap: 6px; margin-top: 4px; font-size: 12px; }
.cm-survey-line__controls { flex: none; display: flex; align-items: center; gap: 2px; }
.cm-survey-line__controls form { display: inline; }

/* The proposed answers of the editor's right-hand panel. The rank is shown rather than left to be
   counted: on a question flagged « échelle » it IS the value, 0 being the low pole. */
.cm-survey-answers { display: flex; flex-direction: column; gap: 6px; }
.cm-survey-answers .cm-answer-row { display: flex; align-items: center; gap: 8px; }
.cm-survey-answers__rank { flex: none; width: 52px; font: 500 11.5px var(--cm-font-ui); color: var(--cm-faint); }

/* ---------- The respondent's screen ---------- */

/* An intertitle structures the reading of the survey - and of its results - without ever being
   answered or counted. */
.cm-survey-section {
    margin: 8px 0 0;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--cm-border);
    font-family: var(--cm-font-title);
    font-size: 16px;
}

.cm-survey-question { display: flex; flex-direction: column; gap: 10px; }
.cm-survey-question.is-missing { border-left: 3px solid var(--cm-red-tx); padding-left: 10px; }
.cm-survey-question__head { display: flex; gap: 10px; align-items: baseline; }
.cm-survey-question__number { flex: none; font: 600 12px var(--cm-font-ui); color: var(--cm-faint); }
.cm-survey-question__label { font-size: 14px; }
.cm-survey-choices { display: flex; flex-direction: column; gap: 4px; padding-left: 30px; }

/* The ranking question. Two arrows per row *beside* the drag handle, never instead of it: the
   ranking has to be doable from the keyboard as much as with the mouse. */
.cm-survey-order { display: flex; flex-direction: column; gap: 6px; padding-left: 30px; }
.cm-survey-order__row {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 10px;
    border: 1px solid var(--cm-border); border-radius: 8px;
    background: var(--cm-surface);
    cursor: grab;
}
.cm-survey-order__row.is-dragging { opacity: .5; }
.cm-survey-order__rank { flex: none; width: 18px; font: 600 12px var(--cm-font-ui); color: var(--cm-faint); }
.cm-survey-order__label { flex: 1; font-size: 13.5px; }
.cm-survey-order__handle { flex: none; color: var(--cm-border); letter-spacing: -2px; }

/* ---------- The results screen ---------- */

.cm-result { display: flex; flex-direction: column; gap: 10px; }
.cm-result__head { display: flex; gap: 10px; align-items: baseline; }
.cm-result__number { flex: none; font: 600 12px var(--cm-font-ui); color: var(--cm-faint); }
.cm-result__label { font-size: 14px; font-weight: 600; }
.cm-result__bars { display: flex; flex-direction: column; gap: 6px; padding-left: 30px; }
.cm-result__row { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.cm-result__answer { flex: 0 0 40%; }
.cm-result__bar { flex: 1; height: 10px; border-radius: 5px; background: var(--cm-bg); overflow: hidden; }
.cm-result__fill { display: block; height: 100%; background: var(--cm-brand); border-radius: 5px; }
.cm-result__value { flex: 0 0 92px; text-align: right; color: var(--cm-muted); font-size: 12.5px; }

/* « Lecture en pôles » - the diverging bar of a scale question. One hue in five steps of
   lightness, the low pole at the left end and the high at the right: a categorical palette would
   present an ordered scale as five unordered categories, which is exactly what it is not. */
.cm-result__poles { padding-left: 30px; }
.cm-result__poles-title { font: 600 10.5px var(--cm-font-ui); letter-spacing: .06em; text-transform: uppercase; color: var(--cm-faint); margin-bottom: 5px; }
.cm-diverging { display: flex; height: 16px; border-radius: 4px; overflow: hidden; background: var(--cm-bg); }
.cm-diverging__seg { display: block; height: 100%; }
.cm-diverging__legend { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 6px; font-size: 11.5px; color: var(--cm-muted); }
.cm-diverging__key { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 4px; vertical-align: 0; }
.cm-diverging__seg--0 { background: #b8342f; }
.cm-diverging__seg--1 { background: #d98b6a; }
.cm-diverging__seg--2 { background: #b9bcc0; }
.cm-diverging__seg--3 { background: #6fa3c4; }
.cm-diverging__seg--4 { background: #12507e; }

/* The verbatims. Given to be read, and nothing else - no word cloud, no sentiment, no summary. */
.cm-verbatims { display: flex; flex-direction: column; gap: 10px; padding-left: 30px; }
.cm-verbatim { border-left: 2px solid var(--cm-border); padding-left: 10px; }
.cm-verbatim__key { font: 600 11px var(--cm-font-ui); color: var(--cm-faint); }
.cm-verbatim__text { margin: 3px 0 0; font-size: 13px; line-height: 1.6; }

/* ---------- Comparing the waves of a series ----------
   One hue in three steps of lightness - light = the oldest wave, dark = the most recent. Time is an
   ordered quantity: a categorical palette would present three waves as three categories with no
   order between them, which is precisely what they are not. Ramp validated in surveys.md §9
   (monotonic in lightness, steps ≥ 0.06, single hue within 7°). */
.cm-wave--0 { background: #8bb8d8; }
.cm-wave--1 { background: #3d84b8; }
.cm-wave--2 { background: #12507e; }

.cm-wave-legend { display: flex; gap: 12px; font-size: 11.5px; color: var(--cm-muted); }
.cm-wave-key { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 4px; }

.cm-comparison__answer { font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.cm-comparison__wave { flex: 0 0 30px; font: 600 11.5px var(--cm-font-ui); color: var(--cm-faint); }

/* A question that changed between two waves is shown, greyed, saying so - never silently dropped
   and never aligned against a different question. */
.cm-comparison--off { opacity: .72; }
.cm-comparison--off .card-title { color: var(--cm-muted); }

/* ==========================================================================
   « Mes machines virtuelles » (.cm-vm*) - design/design_handoff_infrastructure
   The card is the reference's, to the pixel: every length, radius and colour
   below is read off Infrastructure.dc.html rather than approximated from the
   app's own scale. Where a value happens to equal an existing --cm-* token it
   uses the token; where it does not, it gets a --cm-vm-* of its own, declared
   twice like every other token here so the screen still has a dark face.
   The page chrome around it - navbar, breadcrumb, title - stays the app's, as
   the project's own navigation and breadcrumb rules outrank a créa.
   ========================================================================== */
.cm-vmgrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(430px, 1fr));
    gap: 14px;
    align-items: start;
}

.cm-vmcard {
    background: var(--cm-surface);
    border: 1px solid var(--cm-border);
    border-radius: 12px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

/* 1. Header ------------------------------------------------------------- */
.cm-vmcard__head {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 13px 18px;
    border-bottom: 1px solid var(--cm-border-soft);
}
.cm-vmcard__icon {
    width: 32px; height: 32px; border-radius: 9px;
    display: grid; place-items: center; flex: none;
    background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong);
}
.cm-vmcard--off .cm-vmcard__icon { background: var(--cm-bg); color: var(--cm-faint); }
.cm-vmcard--busy .cm-vmcard__icon { background: var(--cm-vm-gold-bg); color: var(--cm-vm-gold-tx); }
.cm-vmcard__name { font: 600 15px var(--cm-font-title); color: var(--cm-ink); }

.cm-vmcard__status {
    margin-left: auto;
    display: inline-flex; align-items: center; gap: 7px;
    padding: 4px 11px 4px 9px;
    border-radius: 999px;
    font: 600 11.5px var(--cm-font-ui);
    background: var(--cm-vm-ok-bg); color: var(--cm-vm-ok-tx);
}
.cm-vmcard__status::before {
    content: ''; width: 7px; height: 7px; border-radius: 50%;
    background: currentColor; flex: none;
}
.cm-vmcard__status--off { background: var(--cm-bg); color: var(--cm-muted); }
/* The dot of the stopped pill is lighter than its text, the one place the two
   part company - hence the override rather than currentColor. */
.cm-vmcard__status--off::before { background: var(--cm-vm-dim); }
.cm-vmcard__status--busy { background: var(--cm-vm-gold-bg); color: var(--cm-vm-gold-tx); }
.cm-vmcard__status--busy::before { background: var(--cm-gold); }
.cm-vmcard__status--unknown { background: var(--cm-bg); color: var(--cm-muted); }
.cm-vmcard__status--unknown::before { background: var(--cm-vm-dim); }

/* 2. Specifications ------------------------------------------------------ */
.cm-vmcard__specs {
    padding: 15px 18px;
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 9px 22px;
    align-items: center;
    border-bottom: 1px solid var(--cm-border-soft);
}
.cm-vmcard__label { font: 600 11.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-vmcard__value { font-size: 13px; color: var(--cm-text); }
.cm-vmcard__value--mono { font: 500 13px ui-monospace, Menlo, monospace; color: var(--cm-ink); }
/* A machine that is off has nothing to say about itself: the reference dims
   every value at once rather than marking them one by one. */
.cm-vmcard--off .cm-vmcard__value,
.cm-vmcard--off .cm-vmcard__value--mono { color: var(--cm-faint); }

/* 3. Password ------------------------------------------------------------ */
.cm-vmcard__pw { padding: 15px 18px; display: flex; flex-direction: column; gap: 9px; }
.cm-vmcard__pw--off { opacity: .55; }
.cm-vmcard__pwlabel { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-vmcard__pwrow { display: flex; gap: 9px; }
.cm-vmcard__input {
    flex: 1; min-width: 0;
    border: 1px solid var(--cm-border); border-radius: 8px;
    padding: 9px 12px;
    background: var(--cm-vm-soft); color: var(--cm-ink);
    font: 400 13px var(--cm-font-ui); outline: none;
}
.cm-vmcard__input::placeholder { color: var(--cm-vm-dim); }
.cm-vmcard__input:focus { border-color: var(--cm-brand); }
.cm-vmcard__input:disabled { border-color: var(--cm-vm-off-bd); background: var(--cm-vm-off-bg); }

.cm-vmcard__btn {
    flex: none;
    border: 1px solid var(--cm-border); background: var(--cm-surface);
    color: var(--cm-brand-strong); border-radius: 8px;
    padding: 9px 16px; font: 600 12.5px var(--cm-font-ui); cursor: pointer;
}
.cm-vmcard__btn:hover:not(:disabled) { background: var(--cm-bg); }
.cm-vmcard__btn:disabled {
    border-color: var(--cm-vm-off-bd); background: var(--cm-vm-off-bg);
    color: var(--cm-vm-dim); cursor: default;
}

/* 4. Footer -------------------------------------------------------------- */
.cm-vmcard__foot {
    margin-top: auto;
    padding: 12px 18px;
    border-top: 1px solid var(--cm-border-soft);
    background: var(--cm-vm-soft);
    display: flex; align-items: center; gap: 10px;
}
.cm-vmcard__note {
    display: inline-flex; align-items: center; gap: 7px;
    font: 600 12px var(--cm-font-ui); color: var(--cm-vm-ok-tx);
}
.cm-vmcard__note--ko { color: var(--cm-vm-stop-tx); }
.cm-vmcard__note--wait { color: var(--cm-muted); }
.cm-vmcard__note:empty { display: none; }
.cm-vmcard__actions { margin-left: auto; display: flex; gap: 8px; }

.cm-vmcard__stop {
    border: 1px solid var(--cm-vm-stop-bd); background: var(--cm-surface);
    color: var(--cm-vm-stop-tx); border-radius: 8px;
    padding: 8px 14px; font: 600 12.5px var(--cm-font-ui); cursor: pointer;
}
.cm-vmcard__stop:hover:not(:disabled) { background: var(--cm-vm-stop-bg); }
.cm-vmcard__start {
    border: 0; background: var(--cm-brand); color: #fff; border-radius: 8px;
    padding: 9px 16px; font: 600 12.5px var(--cm-font-ui); cursor: pointer;
}
.cm-vmcard__start:hover:not(:disabled) { background: var(--cm-brand-strong); }
.cm-vmcard__stop:disabled, .cm-vmcard__start:disabled { opacity: .55; cursor: default; }

/* ============================================================================
   Console des machines (design/validated/guest-console.md)

   Le terminal est sombre quel que soit le thème du lecteur, et c'est la seule
   exception au thème de toute l'application. Les couleurs ANSI sont choisies
   par les programmes qui tournent dans la machine en supposant un fond sombre :
   un panneau clair en rend la moitié illisible. Les jetons --cm-console-* sont
   donc déclarés une seule fois, sans variante claire, et c'est voulu.
   ============================================================================ */

:root {
    --cm-console-bg:    #0e1620;
    --cm-console-pane:  #0a121a;
    --cm-console-rule:  #22303d;
    --cm-console-tx:    #c9d6e1;
    --cm-console-dim:   #7b8d9c;
    --cm-console-fade:  #5a6b78;
    --cm-console-green: #74c69d;
    --cm-console-yel:   #d9b26a;
    --cm-console-copper:#d89a5c;
}

/* L'écran sort de la colonne de contenu : toute la fenêtre moins une gouttière
   de 24 px. Sous 1 024 px on défile horizontalement plutôt que de comprimer un
   terminal jusqu'à l'illisible. */
.cm-console-bleed {
    padding: 0 24px 24px;
    min-width: 1024px;
}

.cm-console {
    border: 1.5px solid var(--cm-console-rule);
    border-radius: 10px;
    overflow: hidden;
    background: var(--cm-console-bg);
    display: flex;
    flex-direction: column;
}

.cm-console__status {
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    padding: 9px 14px;
    background: #131f2b;
    border-bottom: 1px solid var(--cm-console-rule);
    font: 400 12.5px var(--cm-font-ui);
    color: var(--cm-console-dim);
}
.cm-console__name { color: #eef4f9; font-weight: 700; font-size: 13.5px; }
.cm-console__id { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11.5px; color: var(--cm-console-green); }
.cm-console__id--other { color: var(--cm-console-yel); }
.cm-console__sep { color: #35485a; }
.cm-console__dim { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11.5px; color: var(--cm-console-fade); }
.cm-console__right { margin-left: auto; display: flex; align-items: center; gap: 12px; }

.cm-console__live {
    display: inline-flex; align-items: center; gap: 6px;
    color: var(--cm-console-green); font-weight: 600;
}
.cm-console__live::before {
    content: ''; width: 7px; height: 7px; border-radius: 50%;
    background: currentColor; display: inline-block; flex: none;
}
.cm-console__live[data-state="opening"],
.cm-console__live[data-state="preparing"],
.cm-console__live[data-state="booting"] { color: var(--cm-console-yel); }
.cm-console__live[data-state="off"],
.cm-console__live[data-state="lost"],
.cm-console__live[data-state="noDoor"],
.cm-console__live[data-state="noConsole"] { color: var(--cm-console-fade); }

.cm-console__with {
    display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px;
    background: #1d2c3a; border: 1px solid #2b3d4e; color: #a8bccd;
    border-radius: 20px; padding: 2px 9px 2px 4px;
}
.cm-console__ava {
    width: 17px; height: 17px; border-radius: 50%; background: #2f4a63; color: #cfe2f2;
    font-size: 9.5px; font-weight: 700;
    display: inline-flex; align-items: center; justify-content: center;
}

.cm-console__resume {
    padding: 7px 14px; background: #12263a;
    border-bottom: 1px solid var(--cm-console-rule);
    font: 400 12.5px var(--cm-font-ui); color: #9dc0dd;
}
.cm-console__resume b { color: #dceaf5; font-weight: 700; }

.cm-console__pane {
    position: relative;
    background: var(--cm-console-pane);
    padding: 10px 12px;
    min-height: 420px;
    flex: 1;
}
.cm-console__screen { height: 100%; min-height: 400px; }
.cm-console__screen .xterm { height: 100%; }

.cm-console__overlay {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
    background: rgba(10, 18, 26, .92);
    color: #eef4f9;
    font: 400 14px var(--cm-font-ui);
    text-align: center;
    padding: 24px;
}
.cm-console__overlay[hidden] { display: none; }
.cm-console__overlaytext { margin: 0; max-width: 52ch; line-height: 1.55; }

.cm-console__acts {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding: 9px 12px; background: #131f2b;
    border-top: 1px solid var(--cm-console-rule);
}
.cm-console__chip {
    display: inline-flex; align-items: center; gap: 6px;
    border: 1px solid #2d3f50; background: #1a2734; color: #b9cad8;
    border-radius: 7px; padding: 5px 11px;
    font: 600 12.5px var(--cm-font-ui);
    cursor: pointer; text-decoration: none;
}
.cm-console__chip:hover { background: #223141; color: #dbe6ef; }
.cm-console__chip--on { border-color: var(--cm-console-copper); background: #33240f; color: #f0c088; }
.cm-console__chip--pri { border-color: #2f6d99; background: #16324a; color: #a9d1ee; }
.cm-console__chip[disabled] { opacity: .42; cursor: default; }
.cm-console__key {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 10.5px; color: var(--cm-console-fade);
    border: 1px solid #33465a; border-radius: 4px; padding: 0 4px;
}

.cm-console__zoom {
    display: inline-flex; align-items: center;
    border: 1px solid #2d3f50; border-radius: 7px; overflow: hidden; background: #1a2734;
}
.cm-console__zoom button {
    border: 0; background: none; padding: 4px 9px;
    font: 600 12.5px var(--cm-font-ui); color: #b9cad8; cursor: pointer;
}
.cm-console__zoom button + button { border-left: 1px solid #2d3f50; }
.cm-console__zoom button:hover { background: #223141; }

.cm-console__holders {
    margin: 0 0 16px; padding-left: 20px;
    font-size: 14px; color: var(--cm-text);
}

/* Le bouton « Console » du pied de carte, à l'opposé d'« Arrêter ». */
.cm-vmcard__console {
    display: inline-flex; align-items: center; gap: 6px;
    border: 1.5px solid var(--cm-navy); background: var(--cm-navy); color: #fff;
    border-radius: 7px; padding: 5px 11px;
    font: 700 12.5px var(--cm-font-ui); text-decoration: none;
}
.cm-vmcard__console:hover { color: #fff; filter: brightness(1.15); }
.cm-vmcard__console--off { opacity: .4; pointer-events: none; }

/* La transcription d'une session : du texte, lu dans un champ, jamais des couleurs. */
.cm-console__transcript {
    margin: 0;
    padding: 16px 22px;
    max-height: 60vh;
    overflow: auto;
    background: var(--cm-console-pane);
    color: var(--cm-console-tx);
    font: 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    white-space: pre-wrap;
    word-break: break-word;
    border-top: 1px solid var(--cm-border-soft);
}

/* ---- palette de commandes, menus et recherche de la console ---- */

.cm-console__notice {
    padding: 7px 14px;
    background: #22190f;
    border-bottom: 1px solid #55401f;
    color: var(--cm-console-copper);
    font: 600 12.5px var(--cm-font-ui);
}
.cm-console__notice[hidden] { display: none; }

.cm-console.is-dropping { border-color: var(--cm-console-copper); box-shadow: 0 0 0 3px rgba(216, 154, 92, .16); }

.cm-console__palette {
    position: absolute; inset: 0;
    background: rgba(4, 10, 16, .62);
    padding: 26px 0 46px;
    overflow: auto;
}
.cm-console__palette[hidden] { display: none; }
.cm-console__palbox {
    width: min(620px, 92%); margin: 0 auto;
    background: #16222e; border: 1px solid #2d3f50; border-radius: 12px; overflow: hidden;
    box-shadow: 0 18px 48px rgba(0, 0, 0, .5);
}
.cm-console__palquery {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 15px; border-bottom: 1px solid #2d3f50; color: var(--cm-console-fade);
}
.cm-console__palquery input {
    flex: 1; border: 0; background: none; outline: none;
    color: #eef4f9; font: 400 14.5px var(--cm-font-ui);
}
.cm-console__pallist { max-height: 46vh; overflow: auto; }
.cm-console__palgroup {
    padding: 8px 15px 3px;
    font: 700 10.5px var(--cm-font-ui); letter-spacing: .1em; text-transform: uppercase;
    color: var(--cm-console-fade);
}
.cm-console__palitem {
    display: flex; align-items: baseline; gap: 11px; width: 100%;
    padding: 7px 15px; border: 0; background: none; text-align: left; cursor: pointer;
}
.cm-console__palitem.is-on, .cm-console__palitem:hover { background: #1e3145; }
.cm-console__pallabel { color: #dbe6ef; font: 600 13.5px var(--cm-font-ui); flex: none; width: 168px; }
.cm-console__palcmd {
    flex: 1; color: #7fb3dc;
    font: 11.5px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cm-console__palmeta { color: var(--cm-console-fade); font: 11px ui-monospace, Menlo, monospace; }
.cm-console__palfoot {
    display: flex; gap: 18px; padding: 9px 15px; border-top: 1px solid #2d3f50;
    background: #121d28; font: 11.5px var(--cm-font-ui); color: var(--cm-console-dim);
}
.cm-console__palfoot kbd {
    font: 10.5px ui-monospace, Menlo, monospace; border: 1px solid #33465a;
    border-radius: 4px; padding: 0 4px; color: #a8bccd; background: none;
}

.cm-console__menuwrap { position: relative; display: inline-flex; }
.cm-console__menu {
    position: absolute; bottom: calc(100% + 8px); left: 0; z-index: 5; width: 300px;
    background: #16222e; border: 1px solid #2d3f50; border-radius: 10px;
    box-shadow: 0 10px 24px rgba(0, 0, 0, .45); padding: 7px 0;
}
.cm-console__menu[hidden] { display: none; }
.cm-console__menuhead {
    padding: 9px 14px 5px; font: 700 10.5px var(--cm-font-ui);
    letter-spacing: .1em; text-transform: uppercase; color: var(--cm-console-fade);
}
.cm-console__menuitem {
    display: block; width: 100%; text-align: left; border: 0; background: none; cursor: pointer;
    padding: 8px 14px; font: 400 13px var(--cm-font-ui); color: #dbe6ef;
}
.cm-console__menuitem:hover { background: #1e3145; }
.cm-console__menuitem span { display: block; font-size: 11.5px; color: var(--cm-console-fade); }
.cm-console__menusep { height: 1px; background: #2d3f50; margin: 6px 0; }
.cm-console__menufetch { display: flex; gap: 7px; padding: 4px 14px 10px; }
.cm-console__menufetch input {
    flex: 1; min-width: 0; border: 1px solid #2d3f50; background: #101b25; border-radius: 7px;
    padding: 5px 9px; color: #dbe6ef; font: 11.5px ui-monospace, Menlo, monospace;
}

.cm-console__search {
    position: absolute; top: 12px; right: 16px; z-index: 4; width: 380px;
    background: #16222e; border: 1px solid #2d3f50; border-radius: 10px;
    box-shadow: 0 10px 24px rgba(0, 0, 0, .45); padding: 9px;
}
.cm-console__search[hidden] { display: none; }
.cm-console__search input {
    width: 100%; border: 1px solid #2d3f50; background: #101b25; border-radius: 7px;
    padding: 6px 10px; color: #eef4f9; font: 13px var(--cm-font-ui);
}
.cm-console__searchlist { max-height: 40vh; overflow: auto; margin-top: 8px; }
.cm-console__searchrow {
    padding: 3px 4px; color: var(--cm-console-dim);
    font: 11.5px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    white-space: pre-wrap; word-break: break-word;
}

/* ---- « Mes extraits de commande » ---- */

.cm-hint { font: 12px var(--cm-font-ui); color: var(--cm-faint); margin: 8px 0 0; }
.cm-snippet {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 9px 0; border-top: 1px solid var(--cm-border-faint);
}
.cm-snippet:first-of-type { border-top: 0; }
.cm-snippet__label { width: 230px; flex: none; }
.cm-snippet__command { flex: 1; min-width: 240px; font: 12.5px ui-monospace, SFMono-Regular, Menlo, monospace; }
.cm-snippet__check { display: inline-flex; align-items: center; gap: 6px; font: 12.5px var(--cm-font-ui); color: var(--cm-text); margin: 10px 0 0; }
.cm-snippet .cm-snippet__check { margin: 0; }
.cm-snippet__uses { margin: 0; width: 44px; text-align: right; }
.cm-snippet__delete { border-color: var(--cm-red-tx); color: var(--cm-red-tx); background: transparent; }

/* ---- diffusion armée : le seul écran de l'application dont le cadre change de couleur ---- */

.cm-console.is-armed { border-color: var(--cm-console-copper); box-shadow: 0 0 0 3px rgba(216, 154, 92, .16); }
.cm-console.is-armed .cm-console__status,
.cm-console.is-armed .cm-console__acts { background: #24190e; border-color: #55401f; }

.cm-console__armband {
    display: inline-flex; align-items: center; gap: 7px;
    font: 700 12px var(--cm-font-ui); letter-spacing: .03em; text-transform: uppercase;
    color: #f0c088;
}
.cm-console__armband[hidden] { display: none; }
.cm-console__armband::before {
    content: ''; width: 8px; height: 8px; border-radius: 2px;
    background: var(--cm-console-copper); display: inline-block;
}

.cm-console__bcast {
    display: flex; align-items: center; gap: 10px;
    padding: 9px 14px; background: #24190e; border-top: 1px solid #55401f;
    font: 600 12.5px var(--cm-font-ui); color: #f0c088;
}
.cm-console__bcast[hidden] { display: none; }
.cm-console__bcast input {
    flex: 1; min-width: 0;
    border: 1px solid #55401f; background: #16100a; border-radius: 7px;
    padding: 6px 10px; color: #f4e2cb;
    font: 12.5px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.cm-console__bresult {
    background: #22190f; border-top: 1px solid #55401f; padding: 10px 14px;
    font: 13px var(--cm-font-ui); color: #d8c3a4;
}
.cm-console__bresult[hidden] { display: none; }
.cm-console__bhead { display: flex; align-items: center; gap: 10px; }
.cm-console__bhead b { color: #f0c088; font-weight: 700; }
.cm-console__bcmd {
    font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; color: #b0c6d6;
    background: #10202c; border: 1px solid #24384a; border-radius: 5px; padding: 1px 7px;
}
.cm-console__blist {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 4px 16px;
    margin-top: 9px; font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace;
}
.cm-console__blist span { display: flex; align-items: center; gap: 6px; color: #90a5b5; }
.cm-console__blist span::before {
    content: ''; width: 6px; height: 6px; border-radius: 50%; display: inline-block; flex: none;
    background: var(--cm-console-green);
}
.cm-console__blist .is-ko { color: #dba393; }
.cm-console__blist .is-ko::before { background: #e09384; }

/* ---- le mur de consoles ---- */

.cm-console__wall {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px;
    padding: 12px; background: var(--cm-console-bg);
}
.cm-console__tile {
    display: block; text-decoration: none;
    border: 1px solid var(--cm-console-rule); border-radius: 8px; overflow: hidden;
    background: var(--cm-console-pane);
}
.cm-console__tile:hover { border-color: #7fb3dc; box-shadow: 0 0 0 2px rgba(127, 179, 220, .2); }
.cm-console__tile.is-off { border-color: #4a2b24; }
.cm-console__tilehead {
    display: flex; align-items: center; gap: 7px; padding: 5px 9px;
    background: #131f2b; border-bottom: 1px solid var(--cm-console-rule);
    font: 11.5px var(--cm-font-ui);
}
.cm-console__tiledot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--cm-console-fade); }
.cm-console__tiledot[data-state="running"] { background: var(--cm-console-green); }
.cm-console__tiledot[data-state="off"] { background: var(--cm-console-fade); }
.cm-console__tiledot[data-state="idle"] { background: var(--cm-console-yel); }
.cm-console__tiledot[data-state="unknown"] { background: #a43e2e; }
.cm-console__tilename { color: #dbe6ef; font-weight: 700; }
.cm-console__tileage { color: var(--cm-console-fade); margin-left: auto; font-size: 10.5px; }
.cm-console__tilebody {
    display: block; height: 96px; overflow: hidden; padding: 7px 9px;
    color: var(--cm-console-dim); white-space: pre-wrap; word-break: break-word;
    font: 9.8px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.cm-console__tile.is-off .cm-console__tilebody {
    display: flex; align-items: center; justify-content: center;
    color: var(--cm-console-fade); font-style: italic; font-size: 11.5px;
}

/* --- Opérations de compte (annuaire) --------------------------------------------------------
   The banner at the top of a user's fiche, in its five states, and the consequence list the
   deactivation and rename modals both use.

   Four colours and not three: « réussi » is two states, verified (green) and unverified (gold).
   That distinction is the whole point of the second proof - a row the directory did not confirm
   must never read as a success, and gold is what makes an administrator look twice. */
.cm-accountband {
    display: flex; align-items: center; gap: 12px;
    margin-bottom: 16px; padding: 12px 16px;
    border: 1px solid transparent; border-left-width: 3px; border-radius: 0 10px 10px 0;
    font: 13px var(--cm-font-ui);
}
.cm-accountband--wait { background: var(--cm-blue-bg-soft); border-left-color: var(--cm-brand); color: var(--cm-text); }
.cm-accountband--ok   { background: var(--cm-green-bg); border-left-color: var(--cm-green-tx); color: var(--cm-green-tx); }
.cm-accountband--warn { background: var(--cm-gold-bg); border-left-color: var(--cm-gold); color: var(--cm-gold-tx); }
.cm-accountband--crit { background: var(--cm-red-bg); border-left-color: var(--cm-red-tx); color: var(--cm-red-tx); align-items: flex-start; }
.cm-accountband__mark { flex: none; font-size: 16px; line-height: 1; }
.cm-accountband__text { flex: 1; min-width: 0; }
.cm-accountband__title { font-weight: 600; color: var(--cm-ink); }
.cm-accountband--ok .cm-accountband__title { color: var(--cm-green-tx); }
.cm-accountband--warn .cm-accountband__title { color: var(--cm-gold-tx); }
.cm-accountband--crit .cm-accountband__title { color: var(--cm-red-tx); }
.cm-accountband__detail { margin-top: 2px; font-size: 12.5px; opacity: .9; }
/* The script's own output, kept scrollable inside the banner: a stack trace from the domain
   controller must never be what makes the page scroll sideways. */
.cm-accountband__log {
    margin: 8px 0 0; padding: 8px 10px; max-height: 160px; overflow: auto;
    background: var(--cm-surface); border-radius: 6px;
    font: 11.5px/1.5 var(--cm-font-mono); color: var(--cm-text); white-space: pre-wrap; word-break: break-word;
}
.cm-accountband__side { flex: none; display: flex; align-items: center; gap: 10px; }
.cm-accountband--crit .cm-accountband__side { align-self: flex-start; }

/* « Ce que ce changement entraîne » - the list both account modals open with. */
.cm-conseq { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.cm-conseq li { display: flex; gap: 9px; font: 13px/1.5 var(--cm-font-ui); color: var(--cm-text); }
.cm-conseq__mark { flex: none; color: var(--cm-brand); font-weight: 700; }
.cm-conseq b { color: var(--cm-ink); }
.cm-conseq code, .cm-mono { font-family: var(--cm-font-mono); font-size: .94em; }

/* The live availability line under « Nouveau login ». Its three answers are three colours, and the
   fourth state - nothing typed yet - says nothing at all rather than "invalid". */
.cm-loginhint { min-height: 18px; margin-top: 5px; font: 12px var(--cm-font-ui); color: var(--cm-muted); }
.cm-loginhint[data-state="available"] { color: var(--cm-green-tx); }
.cm-loginhint[data-state="taken"], .cm-loginhint[data-state="invalid"] { color: var(--cm-red-tx); }
.cm-loginhint[data-state="current"] { color: var(--cm-gold-tx); }
.cm-loginhint[data-state]:not([data-state=""])::before { content: "● "; }

/* A button that reads as a link - « Proposer d'après le nom » sits inside a sentence, and a real
   <a href> would be a lie: it fills a field, it does not go anywhere. */
.cm-linkbtn {
    padding: 0; border: 0; background: none;
    color: var(--cm-brand); font: inherit; text-decoration: underline; cursor: pointer;
}
.cm-linkbtn:hover { color: var(--cm-brand-strong); }

/* ============================================================================
   Accès aux fonctionnalités (design/validated/feature-access.md §9)
   Two screens, one vocabulary: the role matrix in Paramètres > Fonctionnalités,
   and the per-person derogations on the annuaire card.
   ============================================================================ */

.cm-featmatrix { padding: 8px 0 20px; }
.cm-featmatrix__family {
    margin: 18px 20px 6px; font: 600 15px var(--cm-font-heading);
    color: var(--cm-ink); letter-spacing: .2px;
}
.cm-featmatrix__table th { white-space: nowrap; }
/* Eight role columns on one row: the header reads sideways-narrow rather than wrapping the table
   into a horizontal scroll on a laptop. */
.cm-featmatrix__role { text-align: center; font-size: 11.5px; letter-spacing: .4px; color: var(--cm-faint); }
.cm-featmatrix__cell { text-align: center; }
.cm-featmatrix__overrides { text-align: center; }
/* The enum's own key, under the label. It is what the attribute in the code names, and it is what
   an admin reads back to whoever is asking - worth showing rather than hiding as an implementation
   detail. */
.cm-featmatrix__key { font: 11px var(--cm-font-mono); color: var(--cm-faint); }
.cm-featmatrix__note { font: 11.5px var(--cm-font-ui); color: var(--cm-gold-tx); margin-top: 2px; }

.cm-featovr__family td {
    background: var(--cm-bg); font: 600 12.5px var(--cm-font-ui);
    text-transform: uppercase; letter-spacing: .6px; color: var(--cm-faint);
}
.cm-featovr__key { font: 11px var(--cm-font-mono); color: var(--cm-faint); }
.cm-featovr__choices { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; }
.cm-featovr__choice { display: flex; align-items: center; gap: 6px; font: 13px var(--cm-font-ui); cursor: pointer; }
/* What « Par défaut » gives this person today, in grey next to the button. Without it the three
   buttons say nothing about what choosing the first one means. */
.cm-featovr__hint { font: 11.5px var(--cm-font-ui); color: var(--cm-faint); }

/* ===========================================================================
   Mode contrôle (quiz) - the frise of one copy, design/validated/quiz-supervised-mode.html.
   One row per question: the bar is the time the question was on screen, the notches are the exits
   that fell inside it. Everything in --cm-* tokens, so dark mode follows with no rule of its own.
   =========================================================================== */

.cm-tl { border-top: 1px solid var(--cm-border-faint); }
.cm-tl__row {
  display: grid; grid-template-columns: 118px 1fr 118px 88px; align-items: center;
  gap: 12px; padding: 7px 10px; border-bottom: 1px solid var(--cm-border-faint);
  font: 12.5px var(--cm-font-ui);
}
.cm-tl__row--flagged { background: var(--cm-red-bg); }
.cm-tl__q { color: var(--cm-ink); font-weight: 600; }
.cm-tl__diff { color: var(--cm-faint); font-weight: 500; font-size: 11px; }
.cm-tl__track {
  position: relative; height: 15px; background: var(--cm-surface-alt);
  border: 1px solid var(--cm-border); border-radius: 4px;
}
.cm-tl__bar { position: absolute; inset: 0 auto 0 0; background: var(--cm-blue-bg); border-radius: 3px; }
/* Deliberately taller than the bar: an absence is a notch across it, not a segment of it. */
.cm-tl__exit { position: absolute; top: -3px; bottom: -3px; background: var(--cm-red-tx); border-radius: 2px; opacity: .82; }
.cm-tl__time { text-align: right; font-variant-numeric: tabular-nums; color: var(--cm-ink); font-weight: 600; }
.cm-tl__count { display: block; color: var(--cm-faint); font-weight: 500; font-size: 11px; }
.cm-tl__verdict { font-size: 11px; letter-spacing: .05em; text-transform: uppercase; font-weight: 700; }
.cm-tl__verdict--right { color: var(--cm-positive-tx); }
.cm-tl__verdict--wrong { color: var(--cm-faint); }
.cm-tl__verdict--flagged { color: var(--cm-red-tx); }
.cm-tl__rest > summary { padding: 9px 10px; color: var(--cm-muted); font: 12.5px var(--cm-font-ui); cursor: pointer; }
.cm-tl__legend {
  display: flex; gap: 18px; align-items: center; padding: 9px 10px 0;
  font: 11.5px var(--cm-font-ui); color: var(--cm-muted);
}
.cm-tl__swatch { display: inline-block; height: 9px; border-radius: 2px; margin-right: 6px; vertical-align: -1px; }
.cm-tl__swatch--bar { width: 16px; background: var(--cm-blue-bg); border: 1px solid var(--cm-border); }
.cm-tl__swatch--exit { width: 5px; background: var(--cm-red-tx); }

@media (max-width: 720px) {
  .cm-tl__row { grid-template-columns: 88px 1fr 84px; }
  .cm-tl__verdict { grid-column: 2 / -1; }
}

/* ==========================================================================
   Campus game (design/validated/gamification.md)

   Not one image anywhere, on purpose: the ring is a conic-gradient, the halo a box-shadow of
   growing opacity, the band a filled pill and the tier frame an inset shadow laid *over* the ring
   without replacing it - two independent layers, the ring saying the cursus and the frame saying
   the period. The six tints themselves are not written here: they live in
   App\Service\Game\GameLevels and reach the markup through game_level_style(), so the handoff's
   values are transcribed exactly once.
   ========================================================================== */

.cm-gm-av { display: inline-flex; flex-direction: column; align-items: center; }
.cm-gm-av__ring { padding: 4px; border-radius: 18px; display: inline-block; line-height: 0; }
.cm-gm-av__face {
    display: grid; width: 64px; height: 64px; border-radius: 13px;
    background: var(--cm-gold); color: var(--cm-navy); place-items: center;
    font: 700 20px var(--cm-font-ui); line-height: 1;
    background-size: cover; background-position: center;
}
/* Overlaps the bottom of the avatar - the band is part of the medallion, not a caption under it. */
.cm-gm-av__band {
    margin-top: -9px; position: relative; z-index: 1; border-radius: 99px;
    padding: 3px 10px; font: 700 9px var(--cm-font-ui); letter-spacing: 1.2px;
    white-space: nowrap;
}
/* The main bar's size (34 px), the only other one that exists. */
.cm-gm-av--sm .cm-gm-av__ring { padding: 2.5px; border-radius: 11px; }
.cm-gm-av--sm .cm-gm-av__face { width: 34px; height: 34px; border-radius: 8px; font-size: 13px; }
.cm-gm-av--sm .cm-gm-av__band { margin-top: -6px; padding: 2px 8px; font-size: 7px; letter-spacing: 1px; }

/* The band turned into a progress bar: same geometry, same place, but it carries the way to the
   next level rather than the level reached. */
.cm-gm-av__band--prog {
    position: relative; overflow: hidden; background: #12344d; color: #cfe3f2;
    min-width: 62px; text-align: center; border: 1px solid rgba(255, 255, 255, .18);
}
.cm-gm-av__band--prog .fill {
    position: absolute; inset: 0 auto 0 0;
    background: linear-gradient(90deg, #3f88bd, #6ea3cd);
}
.cm-gm-av__band--prog .lb { position: relative; z-index: 1; }

/* The tier frame, laid over the ring and never replacing it. */
.cm-gm-frame { position: relative; display: inline-block; padding: 5px; border-radius: 22px; }
.cm-gm-frame--bronze { box-shadow: inset 0 0 0 2px #8a5a2b; }
.cm-gm-frame--silver { box-shadow: inset 0 0 0 2px #5b6c79; }
.cm-gm-frame--gold   { box-shadow: inset 0 0 0 2px #8a6a1f; }
.cm-gm-frame__medal {
    position: absolute; right: -4px; bottom: 12px; font-size: 15px; line-height: 1;
    filter: drop-shadow(0 1px 2px rgba(18, 52, 77, .35));
}

/* ---------- The board of six levels ---------- */
.cm-gm-plate { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; }
.cm-gm-plate__cell { display: flex; flex-direction: column; align-items: center; gap: 9px; text-align: center; }
.cm-gm-plate__title { font-size: 12.5px; font-weight: 700; color: var(--cm-ink); line-height: 1.25; }
.cm-gm-plate__xp { font-size: 11.5px; font-weight: 700; color: var(--cm-brand); font-variant-numeric: tabular-nums; }
@media (max-width: 900px) { .cm-gm-plate { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 520px) { .cm-gm-plate { grid-template-columns: repeat(2, 1fr); } }

/* ---------- Level wording, filière by filière ---------- */
.cm-gm-levels { display: grid; grid-template-columns: 148px repeat(4, minmax(0, 1fr)); border: 1px solid var(--cm-border); border-radius: 12px; overflow: hidden; font-size: 12.5px; }
.cm-gm-levels__head { background: var(--cm-navy); color: #fff; padding: 11px 14px; font-weight: 600; font-size: 12.5px; }
.cm-gm-levels__head--key { color: #cfdde9; font-size: 11px; letter-spacing: .5px; text-transform: uppercase; }
.cm-gm-levels__cell { padding: 8px 10px; border-top: 1px solid var(--cm-border-soft); color: var(--cm-text); }
.cm-gm-levels__cell--key { color: var(--cm-muted); }
.cm-gm-levels__cell--key b { color: var(--cm-ink); }
.cm-gm-levels__cell input { width: 100%; border: 1px solid var(--cm-border); border-radius: 6px; padding: 5px 8px; font-size: 12.5px; background: var(--cm-surface); color: var(--cm-ink); }

/* ---------- The four families ---------- */
.cm-gm-fams { display: flex; flex-direction: column; gap: 13px; }
.cm-gm-fam { display: grid; grid-template-columns: 172px minmax(0, 1fr) 108px; align-items: center; gap: 14px; }
.cm-gm-fam__name { font-size: 13px; font-weight: 700; color: var(--cm-ink); }
/* The denominator, printed under the family's name. A rate whose possible is not shown cannot be
   checked by the person reading it, so it is not decoration. */
.cm-gm-fam__sub { display: block; font-size: 11px; font-weight: 500; color: var(--cm-faint); }
.cm-gm-fam__track { position: relative; height: 13px; border-radius: 7px; background: var(--cm-border-soft); overflow: hidden; }
.cm-gm-fam__fill { position: absolute; inset: 0 auto 0 0; border-radius: 7px; }
.cm-gm-fam__fill--attendance { background: linear-gradient(90deg, #1B6BA8, #6ea3cd); }
.cm-gm-fam__fill--work { background: linear-gradient(90deg, #12507e, #3f88bd); }
.cm-gm-fam__fill--engagement { background: linear-gradient(90deg, #1f7a54, #7cc3a3); }
.cm-gm-fam__fill--recognition { background: linear-gradient(90deg, #8a6a1f, #e0bc6d); }
.cm-gm-fam__value { text-align: right; font-size: 12.5px; color: var(--cm-ink); font-weight: 700; font-variant-numeric: tabular-nums; }
.cm-gm-fam__value .cap { color: var(--cm-faint); font-weight: 500; }
/* A family with no data at all: it left the index, and it says so rather than showing 0 %. */
.cm-gm-fam--empty .cm-gm-fam__value { color: var(--cm-faint); font-weight: 500; }
@media (max-width: 640px) { .cm-gm-fam { grid-template-columns: 1fr; gap: 6px; } .cm-gm-fam__value { text-align: left; } }

/* ---------- The one number ---------- */
.cm-gm-index { display: flex; align-items: flex-end; gap: 6px; }
.cm-gm-index__n { font: 700 40px var(--cm-font-ui); color: var(--cm-ink); line-height: .95; font-variant-numeric: tabular-nums; }
.cm-gm-index__u { font-size: 15px; color: var(--cm-faint); font-weight: 600; padding-bottom: 4px; }
.cm-gm-label { font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--cm-faint); font-weight: 700; }

/* ---------- The journal ---------- */
.cm-gm-log { border-top: 1px solid var(--cm-border-soft); }
.cm-gm-log__row { display: grid; grid-template-columns: 76px minmax(0, 1fr) 128px 66px; align-items: baseline; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--cm-border-soft); font-size: 13px; }
.cm-gm-log__row:last-child { border-bottom: 0; }
.cm-gm-log__date { font-size: 11.5px; color: var(--cm-faint); }
.cm-gm-log__rule { color: var(--cm-ink); font-weight: 600; }
.cm-gm-log__src { display: block; font-size: 11.5px; color: var(--cm-muted); font-weight: 400; }
.cm-gm-log__fam { font-size: 11px; letter-spacing: .05em; text-transform: uppercase; color: var(--cm-faint); font-weight: 700; }
.cm-gm-log__pts { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; color: #2e7d4f; }
.cm-gm-log__pts--neg { color: #b0553f; }
/* Two kinds of line are worth zero and say so - the consultation no rule pays, and the reward that
   is not points. It is the cheapest way to teach decisions 4 and 7 without writing a paragraph. */
.cm-gm-log__pts--nil { color: var(--cm-faint); font-weight: 500; }
@media (max-width: 640px) { .cm-gm-log__row { grid-template-columns: 1fr 66px; } .cm-gm-log__fam { display: none; } }

/* ---------- The shelf ---------- */
.cm-gm-shelf { display: flex; flex-direction: column; gap: 9px; }
.cm-gm-shelf__row { display: flex; align-items: center; gap: 10px; font-size: 12.5px; color: var(--cm-text); }
.cm-gm-shelf__icon { font-size: 17px; line-height: 1; flex: none; }
.cm-gm-shelf__label b { color: var(--cm-ink); }

/* ---------- Tier row ---------- */
.cm-gm-tiers { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 13px; }
.cm-gm-tier { border: 1px solid var(--cm-border); border-radius: 10px; background: var(--cm-surface); padding: 13px 15px; }
.cm-gm-tier--reached { border-color: var(--cm-gold); background: var(--cm-gold-bg); }
.cm-gm-tier__head { display: flex; align-items: baseline; gap: 8px; }
.cm-gm-tier__medal { font-size: 17px; }
.cm-gm-tier__name { font-size: 13px; font-weight: 700; color: var(--cm-ink); }
.cm-gm-tier__threshold { margin-left: auto; font-size: 11.5px; color: var(--cm-muted); font-variant-numeric: tabular-nums; }
@media (max-width: 860px) { .cm-gm-tiers { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---------- The main bar's own cell ---------- */
.cm-user__avatar--game { background: none; width: auto; height: auto; border-radius: 0; }

/* ---------- « Ma progression » page frame ---------- */
.cm-gm-context { font-size: 13px; color: var(--cm-muted); margin: -6px 0 16px; }
.cm-gm-layout { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 16px; align-items: start; }
.cm-gm-layout__side, .cm-gm-layout__main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
@media (max-width: 980px) { .cm-gm-layout { grid-template-columns: 1fr; } }
.cm-gm-identity { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
.cm-gm-identity__name { font: 700 15px var(--cm-font-ui); color: var(--cm-ink); }
.cm-gm-identity__title { font-size: 12.5px; color: var(--cm-brand-strong); font-weight: 600; }
.cm-gm-identity__xp { font-size: 11.5px; color: var(--cm-faint); margin-top: 4px; }
.cm-gm-rule { border: 0; border-top: 1px solid var(--cm-border-soft); margin: 16px 0; }

/* ---------- The relevé's grid (design's screen 5) ----------
   Cards rather than a table: the pass is « everybody is net, click the exceptions », and a table
   invites reading thirty rows where a grid invites spotting three. Nothing here carries a motive,
   a date or a counter - see App\Entity\AttendanceStatement. */
.cm-att-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.cm-att-tally { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.cm-att-nav { display: flex; align-items: center; gap: 10px; }
.cm-att-nav__label { font-size: 13px; font-weight: 600; color: var(--cm-ink); font-variant-numeric: tabular-nums; }
.cm-btn.is-disabled { opacity: .4; pointer-events: none; }

.cm-att-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; margin-top: 16px; }
.cm-att-card {
    display: grid; grid-template-columns: 26px minmax(0, 1fr); grid-template-rows: auto auto;
    align-items: center; gap: 2px 10px; text-align: left;
    padding: 10px 12px; border-radius: 9px; cursor: pointer;
    border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-ink);
    font: inherit;
}
.cm-att-card:hover { border-color: var(--cm-brand); }
.cm-att-card:disabled { cursor: default; opacity: .65; }
.cm-att-card__mark { grid-row: 1 / 3; font-size: 17px; font-weight: 700; text-align: center; }
.cm-att-card__name { font-size: 13.5px; font-weight: 600; }
.cm-att-card__state { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; font-weight: 700; color: var(--cm-faint); }
.cm-att-card--clean { border-color: var(--cm-green-tx); background: var(--cm-green-bg); }
.cm-att-card--clean .cm-att-card__mark, .cm-att-card--clean .cm-att-card__state { color: var(--cm-green-tx); }
.cm-att-card--not_clean { border-color: var(--cm-red-tx); background: var(--cm-red-bg); }
.cm-att-card--not_clean .cm-att-card__mark, .cm-att-card--not_clean .cm-att-card__state { color: var(--cm-red-tx); }
.cm-att-card--out_of_scope { border-style: dashed; background: var(--cm-surface-alt); }
.cm-att-card--out_of_scope .cm-att-card__mark, .cm-att-card--out_of_scope .cm-att-card__state { color: var(--cm-faint); }

/* ---------- Teacher gestures and the class council (screens 6 and 7) ---------- */
.cm-gm-env { display: inline-flex; align-items: center; gap: 6px; }
.cm-gm-env i { width: 15px; height: 15px; border-radius: 4px; background: var(--cm-brand); display: inline-block; }
/* A spent token, not a missing one: the envelope is six squares all period long, and what changes
   is how many of them are still yours. */
.cm-gm-env i.is-used { background: var(--cm-border-soft); box-shadow: inset 0 0 0 1px var(--cm-border); }
.cm-gm-env__count { font-size: 12px; color: var(--cm-muted); margin-left: 5px; }

.cm-gm-gestureform { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; align-items: start; }
.cm-gm-gestureform__reason { grid-column: 1 / -1; }
.cm-gm-gestureform label { display: block; }
.cm-gm-gestureform select, .cm-gm-gestureform textarea {
    width: 100%; margin-top: 6px; padding: 7px 10px; font: inherit; font-size: 13.5px;
    border: 1px solid var(--cm-border); border-radius: 8px; background: var(--cm-surface); color: var(--cm-ink);
}

/* A segmented control built from radios: the input is hidden, its label is the segment. */
.cm-gm-seg { display: inline-flex; margin-top: 6px; border: 1px solid var(--cm-border); border-radius: 8px; overflow: hidden; background: var(--cm-surface); }
.cm-gm-seg label { display: inline-flex; margin: 0; }
.cm-gm-seg input { position: absolute; opacity: 0; width: 0; height: 0; }
.cm-gm-seg span {
    display: inline-block; padding: 5px 12px; font-size: 12.5px; font-weight: 600;
    color: var(--cm-muted); border-left: 1px solid var(--cm-border); cursor: pointer; white-space: nowrap;
}
.cm-gm-seg label:first-child span { border-left: 0; }
.cm-gm-seg input:checked + span { background: var(--cm-brand); color: #fff; }
.cm-gm-seg input:focus-visible + span { outline: 2px solid var(--cm-brand); outline-offset: -2px; }
.cm-gm-seg input:disabled + span { opacity: .45; cursor: default; }

.cm-gm-gesture--cancelled td { opacity: .55; text-decoration: line-through; }
.cm-gm-gesture--cancelled .cm-actions { text-decoration: none; }

.cm-council__mentions span { padding: 4px 9px; font-size: 12px; }
.cm-council__comment { width: 100%; padding: 5px 8px; font: inherit; font-size: 12.5px; border: 1px solid var(--cm-border); border-radius: 6px; background: var(--cm-surface); color: var(--cm-ink); }
.cm-council-keys { font-size: 11.5px; color: var(--cm-muted); }
.cm-council-keys kbd {
    display: inline-block; padding: 1px 5px; border-radius: 4px; font-size: 11px; font-weight: 700;
    background: var(--cm-surface-alt); border: 1px solid var(--cm-border); color: var(--cm-ink);
}

/* ---------- Declared engagement, validation queue (screen 13) ---------- */
.cm-engagement-review { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.cm-engagement-review input[type="text"] {
    width: 190px; padding: 4px 8px; font: inherit; font-size: 12px;
    border: 1px solid var(--cm-border); border-radius: 6px; background: var(--cm-surface); color: var(--cm-ink);
}

/* ---------- The shelf's « Utiliser » ---------- */
.cm-gm-shelf__spend { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.cm-gm-shelf__spend input {
    width: 130px; padding: 3px 7px; font: inherit; font-size: 11.5px;
    border: 1px solid var(--cm-border); border-radius: 6px; background: var(--cm-surface); color: var(--cm-ink);
}
.cm-gm-shelf__row { flex-wrap: wrap; }

/* ---------- Figures, ranking and teams (screens 9 to 12) ---------- */
.cm-gm-figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; }
.cm-gm-figure {
    display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
    border: 1px solid var(--cm-border); border-radius: 10px; background: var(--cm-surface); padding: 16px;
}
.cm-gm-figure.is-chosen { border-color: var(--cm-green-tx); background: var(--cm-green-bg); }
.cm-gm-figure__initials {
    width: 40px; height: 40px; border-radius: 10px; display: grid; place-items: center;
    background: var(--cm-navy); color: var(--cm-gold); font: 700 15px var(--cm-font-ui);
}
.cm-gm-figure__surname { font: 700 16px var(--cm-font-title); color: var(--cm-ink); }
.cm-gm-figure__full { font-size: 12px; color: var(--cm-muted); }
.cm-gm-figure__notice { font-size: 12.5px; color: var(--cm-text); line-height: 1.5; }

/* The viewer's own line, in both the ranking and their team. */
.cm-gm-rank--me td { background: var(--cm-blue-bg); }
.cm-gm-rank--me td:first-child { font-weight: 700; }

/* ---------- The game's own settings screen (screen 14) ---------- */
.cm-gm-switch { display: flex; align-items: center; gap: 9px; font-size: 13.5px; color: var(--cm-ink); cursor: pointer; }
.cm-gm-switch input { width: 16px; height: 16px; accent-color: var(--cm-brand); }
.cm-gm-switch span { font-weight: 600; }

/* ---------- « Filières de cette classe », on the game's settings screen ---------- */
.cm-gm-tracks { margin-top: 14px; padding: 12px 14px; border: 1px solid var(--cm-border); border-radius: 10px; background: var(--cm-surface-alt); }
.cm-gm-tracks__list { margin: 6px 0 0; padding-left: 18px; font-size: 13px; color: var(--cm-text); line-height: 1.6; }
.cm-gm-tracks__list b { color: var(--cm-ink); }

/* ==========================================================================
   Campus game - form and section furniture

   The game's screens were built with bare `<label>` + `<input>` and inline styles, which read as a
   different application from the rest of the platform. These are the pieces that put them back on
   the charter: the same field shell everywhere, the same section rhythm, one heading style.
   ========================================================================== */

/* One field: a label, a control, a help line. Replaces the ad-hoc labels the game's forms used. */
.cm-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.cm-field__label { font: 600 12px var(--cm-font-ui); color: var(--cm-muted); text-transform: uppercase; letter-spacing: .06em; }
.cm-field__label .req { color: var(--cm-red-tx); margin-left: 2px; }
.cm-field__help { font-size: 11.5px; color: var(--cm-faint); line-height: 1.5; }
.cm-field input[type="text"],
.cm-field input[type="number"],
.cm-field input[type="date"],
.cm-field select,
.cm-field textarea {
    width: 100%; padding: 8px 11px; font: inherit; font-size: 13.5px; color: var(--cm-ink);
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 8px;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.cm-field input:focus, .cm-field select:focus, .cm-field textarea:focus {
    outline: none; border-color: var(--cm-brand);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--cm-brand) 18%, transparent);
}
.cm-field input:disabled, .cm-field select:disabled, .cm-field textarea:disabled {
    background: var(--cm-surface-alt); color: var(--cm-faint); cursor: not-allowed;
}
.cm-field textarea { resize: vertical; min-height: 72px; }

/* The grid the game's forms lay their fields on. */
.cm-formgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 18px 20px; align-items: start; }
.cm-formgrid__wide { grid-column: 1 / -1; }

/* A titled block inside a card - the rhythm the Leveling tabs and the settings screen share. */
.cm-gm-section { padding: 22px 0; border-top: 1px solid var(--cm-border-soft); }
.cm-gm-section:first-child { padding-top: 0; border-top: 0; }
.cm-gm-section__title { font: 600 15px var(--cm-font-title); color: var(--cm-ink); margin: 0 0 6px; }
.cm-gm-section__hint { font-size: 12.5px; color: var(--cm-muted); line-height: 1.6; margin: 0 0 12px; }
.cm-gm-section__hint:last-child { margin-bottom: 0; }

/* The banner every Leveling tab opens on. */
.cm-gm-hero { display: flex; align-items: center; gap: 22px; padding: 4px 0 22px; flex-wrap: wrap; }
.cm-gm-hero__body { flex: 1 1 260px; min-width: 0; }
.cm-gm-hero__name { font: 700 18px var(--cm-font-title); color: var(--cm-ink); }
.cm-gm-hero__stats { display: flex; flex-wrap: wrap; gap: 18px; margin-top: 8px; font-size: 12.5px; color: var(--cm-muted); }
.cm-gm-hero__stats b { font: 700 20px var(--cm-font-ui); color: var(--cm-ink); display: block; line-height: 1.1; font-variant-numeric: tabular-nums; }
.cm-gm-hero__next { font-size: 12.5px; color: var(--cm-faint); margin-top: 8px; }

/* The six titles, with the ones already unlocked standing out from the ones ahead. */
.cm-gm-titles { display: grid; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); gap: 10px; }
.cm-gm-title {
    display: flex; align-items: center; gap: 10px; padding: 10px 12px;
    border: 1px solid var(--cm-border); border-radius: 9px; background: var(--cm-surface); opacity: .55;
}
.cm-gm-title.is-reached { opacity: 1; border-color: var(--cm-brand); }
/* The medallion of that level, worn by the reader: it holds its own width, and level 6's outline
   sits 3 px outside the ring, so the cell leaves it that much room. */
.cm-gm-title .cm-gm-av { flex: none; margin: 3px 3px 0; }
.cm-gm-title__level { flex: none; border-radius: 99px; padding: 2px 8px; font: 700 9px var(--cm-font-ui); letter-spacing: 1px; }
.cm-gm-title__name { font-size: 13px; font-weight: 600; color: var(--cm-ink); min-width: 0; }
.cm-gm-title__xp { margin-left: auto; font-size: 11.5px; color: var(--cm-faint); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- Choosing the displayed title ---------- */
/* One column per filière: a student playing in two reads them side by side, which is the only
   reason the screen exists. */
.cm-gm-titlecols { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; margin-top: 16px; }
.cm-gm-titlepicks { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.cm-gm-titlepick {
    display: flex; align-items: center; gap: 10px; padding: 9px 12px; cursor: pointer;
    border: 1px solid var(--cm-border); border-radius: 9px; background: var(--cm-surface);
}
.cm-gm-titlepick:hover { border-color: var(--cm-brand); }
.cm-gm-titlepick.is-chosen { border-color: var(--cm-brand); box-shadow: inset 0 0 0 1px var(--cm-brand); }
/* Locked rather than hidden: the six levels are the ladder, and the rung one cannot stand on yet
   still says how far it is. */
.cm-gm-titlepick.is-locked { opacity: .5; cursor: not-allowed; }
.cm-gm-titlepick.is-locked:hover { border-color: var(--cm-border); }
.cm-gm-titlepick__name { font-size: 13.5px; font-weight: 600; color: var(--cm-ink); min-width: 0; }
.cm-gm-titlepick__hint { margin-left: auto; font-size: 11.5px; color: var(--cm-faint); white-space: nowrap; }

/* ---------- The administration's observation screen ---------- */
.cm-gm-obs-figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin-top: 16px; }
.cm-gm-obs-figure {
    display: flex; flex-direction: column; gap: 2px; padding: 14px 16px;
    border: 1px solid var(--cm-border); border-radius: 9px; background: var(--cm-surface);
}
.cm-gm-obs-figure__n { font: 700 26px var(--cm-font-title); color: var(--cm-ink); font-variant-numeric: tabular-nums; }
.cm-gm-obs-figure__l { font-size: 12px; color: var(--cm-faint); }
/* A rule that paid nothing over the window: shown, and shown as such - it is the finding, not a
   gap in the table. */
.cm-gm-obs-row--idle { opacity: .55; }

/* ---------- Ranked months, and the month bar of the ranking ---------- */
.cm-gm-months { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-top: 10px; }
.cm-gm-month {
    display: flex; align-items: center; gap: 8px; padding: 8px 11px; cursor: pointer;
    border: 1px solid var(--cm-border); border-radius: 8px; background: var(--cm-surface);
    font-size: 13px; color: var(--cm-muted);
}
.cm-gm-month.is-on { border-color: var(--cm-brand); background: var(--cm-blue-bg); color: var(--cm-blue-tx); font-weight: 600; }
.cm-gm-month input { accent-color: var(--cm-brand); }

.cm-gm-monthbar { display: flex; align-items: center; gap: 12px; padding: 16px 0 4px; flex-wrap: wrap; }
.cm-gm-monthbar__label { font: 600 16px var(--cm-font-title); color: var(--cm-ink); }
.cm-gm-monthlinks { display: flex; flex-wrap: wrap; gap: 6px; }
.cm-gm-monthlinks a { text-decoration: none; }

/* Figure catalogue - a table of editable rows. The notice takes what the three short fields do
   not, so the table stays readable rather than the fields all sharing a quarter each. */
.cm-figtable { width: 100%; table-layout: fixed; }
.cm-figtable th:nth-child(1), .cm-figtable td:nth-child(1) { width: 18%; }
.cm-figtable th:nth-child(2), .cm-figtable td:nth-child(2) { width: 24%; }
.cm-figtable th:nth-child(4), .cm-figtable td:nth-child(4) { width: 200px; }
.cm-figtable td { vertical-align: top; }
.cm-figtable input[type="text"], .cm-figtable textarea { width: 100%; }

/* ==========================================================================
   Cahier de texte - the period screen (design_handoff_cahier_de_texte_seances)
   One teacher's séances of one week, every class together: the period selector on the title line,
   the list on the left in either of two groupings, the selected séance's three parts on the right.
   Sizes are the handoff's own; the colours go through the --cm-cdt-* tokens declared at the top of
   this file so the screen follows the theme.
   ========================================================================== */

/* --- The class, shown next to the page's own name once one is picked --- */
.cm-cdt-title-class {
    color: var(--cm-muted); font-weight: 500;
    /* Tabler's .page-title is a flex container, and a flex container drops the whitespace-only text
       node between two children - so the gap before « › » has to be a margin, not a space. */
    margin-left: .3em;
}

/* --- Class picker, immediately left of the period selector and cut to the same height --- */
.cm-cdt-classpick { position: relative; display: inline-flex; font-family: var(--cm-font-ui); }
.cm-cdt-classpick__select {
    appearance: none; height: 38px; padding: 0 32px 0 14px;
    border: 1px solid var(--cm-border); border-radius: 9px;
    background: var(--cm-surface); color: var(--cm-ink);
    font: 600 13px var(--cm-font-ui); cursor: pointer;
}
.cm-cdt-classpick__select:hover { background: var(--cm-bg); }
.cm-cdt-classpick__select:focus-visible { outline: 2px solid var(--cm-brand); outline-offset: 1px; }
/* The same glyph as the period control's caret rather than a background image: a data: URI cannot
   follow the theme's own ink. */
.cm-cdt-classpick__caret {
    position: absolute; right: 12px; top: 0; height: 38px; display: grid; place-items: center;
    pointer-events: none; color: var(--cm-faint); font-size: 13px;
}

/* --- Period selector, pushed right on the title line --- */
.cm-cdt-period { position: relative; font-family: var(--cm-font-ui); }
.cm-cdt-period__group {
    display: flex; align-items: center; background: var(--cm-surface);
    border: 1px solid var(--cm-border); border-radius: 9px; overflow: hidden;
}
.cm-cdt-period.is-open .cm-cdt-period__group { border-color: var(--cm-brand); box-shadow: 0 1px 2px rgba(0, 0, 0, .05); }
.cm-cdt-period__arrow {
    display: grid; place-items: center; width: 34px; height: 38px; flex: none;
    border: 0; background: var(--cm-surface); color: var(--cm-muted); font-size: 14px;
    text-decoration: none; cursor: pointer;
}
.cm-cdt-period__arrow:first-child { border-right: 1px solid var(--cm-border-soft); }
.cm-cdt-period__arrow:last-child { border-left: 1px solid var(--cm-border-soft); }
.cm-cdt-period__arrow:hover { background: var(--cm-bg); color: var(--cm-brand-strong); }
.cm-cdt-period__label {
    display: flex; align-items: center; gap: 9px; white-space: nowrap;
    border: 0; background: var(--cm-surface); color: var(--cm-ink);
    height: 38px; padding: 0 14px; font: 600 13px var(--cm-font-ui); cursor: pointer;
}
.cm-cdt-period__label:hover { background: var(--cm-bg); }
.cm-cdt-period__label svg { flex: none; stroke: var(--cm-brand); }
.cm-cdt-period__caret { font-weight: 400; color: var(--cm-faint); }
.cm-cdt-period.is-open .cm-cdt-period__label { background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong); }
.cm-cdt-period.is-open .cm-cdt-period__caret { color: var(--cm-cdt-open-bd); }

/* --- The month popover: weeks are picked whole, never a single day --- */
.cm-cdt-pop {
    position: absolute; top: 44px; right: 0; z-index: 30; width: 288px;
    background: var(--cm-surface); border: 1px solid var(--cm-border); border-radius: 11px;
    box-shadow: 0 18px 40px rgba(18, 52, 77, .18);
    padding: 14px; display: flex; flex-direction: column; gap: 10px;
}
.cm-cdt-pop[hidden] { display: none !important; }
.cm-cdt-pop__head { display: flex; align-items: center; gap: 8px; }
.cm-cdt-pop__nav {
    border: 0; background: var(--cm-bg); color: var(--cm-muted);
    border-radius: 6px; width: 26px; height: 26px; flex: none; cursor: pointer;
}
.cm-cdt-pop__nav:hover { color: var(--cm-brand-strong); }
.cm-cdt-pop__month {
    flex: 1; text-align: center; font: 600 13px var(--cm-font-ui); color: var(--cm-ink);
    /* Intl gives « août 2026 » in French, and the calendar's heading is a title. */
    text-transform: capitalize;
}
.cm-cdt-pop__weeks { display: flex; flex-direction: column; gap: 2px; }
.cm-cdt-pop__dows {
    display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
    font: 600 10.5px var(--cm-font-ui); color: var(--cm-faint); text-align: center;
}
.cm-cdt-pop__week {
    display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
    padding: 0 4px; border: 1px solid transparent; cursor: pointer;
    text-decoration: none; color: inherit;
}
.cm-cdt-pop__week.is-current {
    background: var(--cm-blue-bg-soft); border-color: var(--cm-cdt-open-bd);
    border-radius: 8px; padding: 3px;
}
.cm-cdt-pop__day {
    height: 30px; display: grid; place-items: center; border-radius: 6px;
    font: 400 12.5px var(--cm-font-ui); color: var(--cm-text);
}
.cm-cdt-pop__day--weekend { color: var(--cm-cdt-cal-weekend); }
.cm-cdt-pop__week:not(.is-current):hover .cm-cdt-pop__day { background: var(--cm-bg); }
.cm-cdt-pop__week.is-current .cm-cdt-pop__day { font-weight: 600; color: var(--cm-brand-strong); }
.cm-cdt-pop__week.is-current .cm-cdt-pop__day--outside { color: var(--cm-faint); }
.cm-cdt-pop__week.is-current .cm-cdt-pop__day--weekend { color: var(--cm-cdt-cal-sunday); }
/* Today keeps its filled square in either row, selected or not. */
.cm-cdt-pop__day.is-today { background: var(--cm-brand); color: #fff; font-weight: 700; }
.cm-cdt-pop__shortcuts {
    display: flex; align-items: center; gap: 8px;
    border-top: 1px solid var(--cm-border-soft); padding-top: 10px;
}
.cm-cdt-pop__shortcut {
    flex: 1; text-align: center; text-decoration: none;
    border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-text);
    border-radius: 8px; padding: 7px 0; font: 600 12px var(--cm-font-ui); cursor: pointer;
}
.cm-cdt-pop__shortcut:hover { background: var(--cm-bg); color: var(--cm-brand-strong); }
.cm-cdt-pop__shortcut.is-active {
    border-color: var(--cm-cdt-open-bd); background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong);
}

/* --- Page grid: the list, then the selected séance --- */
/* min-width: 0 on the columns, not decoration: a grid track sizes itself to its content's
   min-content width by default, so any child that refuses to wrap makes the whole grid wider than
   the page. The séance preview holds a teacher's own prose, and one long line of it used to push
   « Ouvrir la séance » past the right edge behind a page-wide horizontal scrollbar. */
.cm-cdt { display: grid; grid-template-columns: 330px 1fr; gap: 16px; align-items: start; font-family: var(--cm-font-ui); }
.cm-cdt > * { min-width: 0; }
.cm-cdt-card {
    background: var(--cm-surface); border: 1px solid var(--cm-border);
    border-radius: 11px; display: flex; flex-direction: column;
}
/* Day headers go edge to edge, so the chronological card has to clip them at its own corners. */
.cm-cdt-card--flush { overflow: hidden; }
/* Both the séance blocks and the « rien de sélectionné » card are toggled through the `hidden`
   attribute, which a display:flex would otherwise win against. */
.cm-cdt-card[hidden] { display: none !important; }

/* --- Segmented control: which grouping the left column uses --- */
.cm-cdt-modes { padding: 12px; border-bottom: 1px solid var(--cm-border-soft); }
.cm-cdt-modes__track { display: flex; background: var(--cm-bg); border-radius: 8px; padding: 3px; gap: 3px; }
.cm-cdt-modes__tab {
    flex: 1; text-align: center; text-decoration: none; border: 0; background: transparent;
    color: var(--cm-muted); border-radius: 6px; padding: 6px 0;
    font: 600 12.5px var(--cm-font-ui); cursor: pointer;
}
.cm-cdt-modes__tab:hover { color: var(--cm-brand-strong); }
/* Grouping by class inside one class: the tab stays, so the control keeps saying which of the two
   groupings one is on, but it leads nowhere and its title says why. */
.cm-cdt-modes__tab.is-disabled { color: var(--cm-disabled); cursor: not-allowed; }
.cm-cdt-modes__tab.is-disabled:hover { color: var(--cm-disabled); }
.cm-cdt-modes__tab.is-active {
    background: var(--cm-surface); color: var(--cm-brand-strong); box-shadow: 0 1px 2px rgba(0, 0, 0, .08);
}

/* --- By class: an accordion, one class open at a time --- */
.cm-cdt-classes { padding: 8px; display: flex; flex-direction: column; gap: 6px; }
.cm-cdt-class { border: 1px solid var(--cm-border); border-radius: 10px; }
.cm-cdt-class.is-open { border-color: var(--cm-cdt-open-bd); overflow: hidden; }
.cm-cdt-class__head {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 11px 13px; border: 0; border-radius: 10px; background: transparent;
    text-align: left; cursor: pointer;
}
.cm-cdt-class:not(.is-open) .cm-cdt-class__head:hover { background: var(--cm-bg); }
.cm-cdt-class.is-open .cm-cdt-class__head { background: var(--cm-blue-bg-soft); border-radius: 0; }
.cm-cdt-class__name { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); }
.cm-cdt-class.is-open .cm-cdt-class__name { color: var(--cm-brand-strong); }
.cm-cdt-class__caret { margin-left: auto; font-size: 11px; color: var(--cm-faint); }
.cm-cdt-class.is-open .cm-cdt-class__caret { color: var(--cm-cdt-open-bd); }
.cm-cdt-class__body { padding: 6px; display: flex; flex-direction: column; gap: 3px; background: var(--cm-surface); }
.cm-cdt-class__body[hidden] { display: none !important; }

/* --- Chronological: days edge to edge, each folding on its own --- */
.cm-cdt-days { display: flex; flex-direction: column; }
.cm-cdt-day__head {
    display: flex; align-items: center; gap: 8px; width: 100%;
    padding: 8px 16px; border: 0; border-bottom: 1px solid var(--cm-border-soft);
    background: var(--cm-surface-soft); text-align: left; cursor: pointer;
}
.cm-cdt-day:not(.is-open) .cm-cdt-day__head:hover { background: var(--cm-bg); }
.cm-cdt-day__label {
    font: 700 11px var(--cm-font-ui); color: var(--cm-muted);
    letter-spacing: .6px; text-transform: uppercase;
}
.cm-cdt-day__caret { margin-left: auto; font-size: 11px; color: var(--cm-faint); }
.cm-cdt-day__body[hidden] { display: none !important; }
/* The list ends on a rule that would draw the card's own border twice. */
.cm-cdt-day:last-child:not(.is-open) .cm-cdt-day__head { border-bottom: 0; }
.cm-cdt-day:last-child .cm-cdt-seance--flat:last-child { border-bottom: 0; }

/* --- One séance in the list --- */
.cm-cdt-seance {
    display: flex; align-items: center; gap: 10px; width: 100%;
    border: 0; background: transparent; text-align: left; cursor: pointer;
    padding: 9px 11px; border-radius: 8px;
}
.cm-cdt-seance:hover { background: var(--cm-bg); }
/* Chronological rows sit edge to edge and are separated by a rule instead of by a gap. */
.cm-cdt-seance--flat { padding: 10px 16px; border-radius: 0; border-bottom: 1px solid var(--cm-border-faint); }
.cm-cdt-seance--flat:hover { background: var(--cm-cdt-row-hover); }
.cm-cdt-seance__body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.cm-cdt-seance__head { font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-cdt-seance__topic { font-size: 11.5px; color: var(--cm-faint); }
.cm-cdt-seance.is-selected { background: var(--cm-cdt-sel-bg); }
.cm-cdt-seance.is-selected:hover { background: var(--cm-cdt-sel-bg); }
.cm-cdt-seance.is-selected .cm-cdt-seance__head { color: var(--cm-cdt-sel-tx); }
.cm-cdt-seance.is-selected .cm-cdt-seance__topic { color: var(--cm-cdt-sel-dim); }

/* --- The state tag: three words, and a lighter set of them on a selected row --- */
.cm-cdt-tag {
    margin-left: auto; flex: none; display: inline-flex;
    padding: 3px 9px; border-radius: 999px; font: 600 11px var(--cm-font-ui);
}
.cm-cdt-tag--filled { background: var(--cm-vm-ok-bg); color: var(--cm-vm-ok-tx); }
.cm-cdt-tag--partial { background: var(--cm-vm-gold-bg); color: var(--cm-vm-gold-tx); }
.cm-cdt-tag--empty { background: var(--cm-bg); color: var(--cm-muted); }
.is-selected .cm-cdt-tag--filled { background: var(--cm-cdt-sel-ok-bg); color: var(--cm-cdt-sel-ok-tx); }
.is-selected .cm-cdt-tag--partial { background: var(--cm-cdt-sel-warn-bg); color: var(--cm-cdt-sel-warn-tx); }
.is-selected .cm-cdt-tag--empty { background: var(--cm-cdt-sel-off-bg); color: var(--cm-cdt-sel-off-tx); }

/* --- The selected séance, on the right --- */
.cm-cdt-detail { display: flex; flex-direction: column; }
.cm-cdt-detail[hidden] { display: none !important; }
.cm-cdt-detail__head {
    display: flex; align-items: center; gap: 14px;
    padding: 16px 20px; border-bottom: 1px solid var(--cm-border-soft);
}
.cm-cdt-detail__titles { display: flex; flex-direction: column; min-width: 0; }
.cm-cdt-detail__title { font: 600 16px var(--cm-font-title); color: var(--cm-ink); }
.cm-cdt-detail__meta { font-size: 12.5px; color: var(--cm-faint); }
.cm-cdt-detail__open {
    margin-left: auto; flex: none; text-decoration: none;
    background: var(--cm-brand); color: #fff; border-radius: 8px;
    padding: 9px 18px; font: 600 13px var(--cm-font-ui);
}
.cm-cdt-detail__open:hover { background: var(--cm-brand-strong); color: #fff; }
.cm-cdt-detail__body { padding: 16px 20px; display: flex; flex-direction: column; gap: 10px; }
/* Aligned to the top rather than centred: the part now carries the cahier de texte as it was
   written, so it is as tall as that content - a dot and a badge floating at its middle would no
   longer read as belonging to the line they label. */
.cm-cdt-part {
    display: flex; align-items: flex-start; gap: 12px;
    border: 1px solid var(--cm-border); border-radius: 10px;
    padding: 12px 15px; background: var(--cm-vm-soft);
}
/* The dot and the badge sit on the title's own line, which the flex box no longer centres them on. */
.cm-cdt-part__dot { flex: none; width: 9px; height: 9px; margin-top: 6px; border-radius: 50%; background: var(--cm-border); }
.cm-cdt-part__dot--filled { background: var(--cm-vm-ok-tx); }
.cm-cdt-part__dot--partial { background: var(--cm-gold); }
.cm-cdt-part__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.cm-cdt-part__title { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
/* The one-line pieces: how many travaux, how many documents, or that nothing is written yet. */
.cm-cdt-part__summary {
    font-size: 12px; color: var(--cm-faint);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The content itself, with the formatting the editor gave it. No border and no ground of its own -
   it is already inside the part's box - and a long word or a pasted URL breaks rather than widening
   the card. A table pasted from elsewhere scrolls inside this box, never taking the page with it. */
.cm-cdt-part__content {
    margin-top: 4px; border: 0; padding: 0; background: transparent;
    font-size: 12.5px; color: var(--cm-text);
    overflow-wrap: anywhere; overflow-x: auto;
}
.cm-cdt-part__content > :last-child { margin-bottom: 0; }
.cm-cdt-part .cm-badge { flex: none; margin-top: 1px; }

/* --- Nothing to show: an empty week, or a day the link named that carries no séance --- */
.cm-cdt-notice {
    padding: 11px 16px; border-bottom: 1px solid var(--cm-border-soft);
    background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong);
    font: 600 12.5px var(--cm-font-ui);
}
.cm-cdt-empty { padding: 22px 16px; text-align: center; font-size: 12.5px; color: var(--cm-muted); }

@media (max-width: 1100px) {
    .cm-cdt { grid-template-columns: 1fr; }
}

/* ===========================================================================
   Nuage de mots (design_handoff_nuage_de_mots) - the seven screens of the tool.

   The buttons, chips and tags below are a family of their own rather than the app's .cm-btn /
   .cm-badge, and that is deliberate: this handoff sizes them to the pixel (a 10px-radius lead
   button at 11px 22px, a row button at 8px 14px, an 11px-radius status tag) and the shared
   components carry different numbers. Widening .cm-btn to cover both would have moved every other
   screen in the app.

   Everything reads --cm-* tokens, so the dark theme follows without a single rule of its own -
   except the projection board, which is dark in both themes and says so where it is defined.
   =========================================================================== */

/* -- Buttons ------------------------------------------------------------- */
.cm-wc-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-muted);
  border-radius: var(--cm-radius-sm); padding: 9px 15px;
  font: 600 12.5px var(--cm-font-ui); cursor: pointer; text-decoration: none; white-space: nowrap;
}
.cm-wc-btn:hover { background: var(--cm-bg); color: var(--cm-muted); }
.cm-wc-btn--primary { border-color: transparent; background: var(--cm-brand); color: #fff; }
.cm-wc-btn--primary:hover { background: var(--cm-brand-strong); color: #fff; }
/* The lead action of a screen ("Lancer un nouveau nuage", "Envoyer"): heavier and rounder than a
   toolbar button, which is what tells them apart at a glance. */
.cm-wc-btn--lead {
  border-color: transparent; background: var(--cm-brand); color: #fff;
  border-radius: var(--cm-radius); padding: 11px 22px; font: 700 14px var(--cm-font-ui);
}
.cm-wc-btn--lead:hover { background: var(--cm-brand-strong); color: #fff; }
/* Outlined gold, not the app's filled .cm-btn--gold: on this tool the gold marks what leaves the
   screen for the wall - "Projeter", "Reprojeter" - and a filled button would out-shout the primary
   one next to it. */
.cm-wc-btn--gold { border-color: var(--cm-gold); color: var(--cm-gold-deep); }
.cm-wc-btn--gold:hover { background: var(--cm-wc-gold-hover); color: var(--cm-gold-deep); }
.cm-wc-btn--row { color: var(--cm-brand-strong); padding: 8px 14px; }
.cm-wc-btn--row:hover { background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong); }
.cm-wc-actions { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }

/* -- Status tag ---------------------------------------------------------- */
.cm-wc-tag {
  display: inline-block; flex: none;
  padding: 3px 10px; border-radius: 11px;
  font: 600 11px var(--cm-font-ui); white-space: nowrap;
}
.cm-wc-tag--open { background: var(--cm-wc-ok-bg); color: var(--cm-wc-ok-tx); }
.cm-wc-tag--scheduled { background: var(--cm-gold-bg); color: var(--cm-gold-tx); }
.cm-wc-tag--closed { background: var(--cm-border-soft); color: var(--cm-muted); }
/* « Sans réponse » on the follow-up: an alert shade rather than the neutral one, because the whole
   tab exists to make that line findable. */
.cm-wc-tag--silent { background: var(--cm-wc-no-bg); color: var(--cm-red-tx); }
/* On a title line the tag sits beside the h1 and is a shade taller, per the créa. */
.cm-wc-title { display: inline-flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cm-wc-title .cm-wc-tag { padding: 4px 11px; }

/* -- Cards and fields ---------------------------------------------------- */
.cm-wc-card {
  background: var(--cm-surface); border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius);
}
.cm-wc-card--flush { overflow: hidden; }
.cm-wc-card--pad { padding: 18px; display: flex; flex-direction: column; gap: 14px; }
.cm-wc-label {
  font: 700 11px var(--cm-font-ui); color: var(--cm-faint);
  text-transform: uppercase; letter-spacing: .5px;
}
.cm-wc-field { display: flex; flex-direction: column; gap: 8px; }
/* `legend` as well as `label`: Symfony's Bootstrap 5 theme renders a <legend> for the label of an
   expanded choice field (the targeted options, the projection mode), a <label> for everything else,
   and the handoff draws the two identically. */
.cm-wc-field > label,
.cm-wc-field > legend {
  font: 700 11px var(--cm-font-ui); color: var(--cm-faint);
  text-transform: uppercase; letter-spacing: .5px;
  margin: 0; padding: 0; float: none; width: auto;
}
/* No asterisks: the handoff marks nothing as required, and every field of this form is. A form
   where the mark is on all six lines is a form where it says nothing. */
.cm-wc-field > label.required::after,
.cm-wc-field > legend.required::after { content: none; }
.cm-wc-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.cm-wc-field > input[type="text"],
.cm-wc-field > select,
.cm-wc-field > .form-control,
.cm-wc-field > .form-select {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
  padding: 10px 12px; font: 600 13.5px var(--cm-font-ui); line-height: 1.3;
  color: var(--cm-ink); background-color: var(--cm-surface); outline: none; box-sizing: border-box;
  width: 100%; box-shadow: none;
}
.cm-wc-field > select,
.cm-wc-field > .form-select { cursor: pointer; }
.cm-wc-field > input:focus,
.cm-wc-field > select:focus { border-color: var(--cm-brand); box-shadow: none; }
/* The chip, the mode card and the switch each draw their own box, so the real input is hidden -
   still there, still checkable, and still named by the <label> it sits in. */
.cm-wc-chip input,
.cm-wc-mode input,
.cm-wc-switch input { position: absolute; opacity: 0; pointer-events: none; }

/* -- Chips (targeted options, period shortcuts, "Illimités") -------------- */
.cm-wc-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.cm-wc-chip {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  border: 1px solid var(--cm-border); background: var(--cm-surface); color: var(--cm-muted);
  border-radius: 16px; padding: 7px 16px; font: 600 13px var(--cm-font-ui);
}
.cm-wc-chip input { position: absolute; opacity: 0; pointer-events: none; }
.cm-wc-chip:hover { background: var(--cm-bg); }
.cm-wc-chip:has(input:checked),
.cm-wc-chip.is-on {
  border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong);
}
/* The tick of a selected chip is drawn in CSS, not in markup: the checked box already says it to a
   screen reader, and a second announcement of the same fact is noise. */
.cm-wc-chip:has(input:checked)::before { content: '\2713'; font-size: 11px; }
.cm-wc-chip--sm { border-radius: 14px; padding: 5px 12px; font-size: 12px; }
.cm-wc-shortcuts { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 2px; }

/* -- Availability period -------------------------------------------------- */
.cm-wc-period { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cm-wc-period__word { font-size: 13px; color: var(--cm-muted); }
.cm-wc-period input[type="date"],
.cm-wc-period input[type="time"] {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
  padding: 9px 11px; font: 600 13px var(--cm-font-ui);
  color: var(--cm-ink); background-color: var(--cm-surface); outline: none;
  /* Bootstrap's .form-control is 100% wide, which would put each of the four boxes on a line of its
     own instead of the one row the handoff draws. */
  width: auto; box-shadow: none;
}
.cm-wc-period.is-manual { opacity: .45; pointer-events: none; }

/* -- Stepper -------------------------------------------------------------- */
.cm-wc-stepper { display: flex; align-items: center; gap: 10px; }
.cm-wc-stepper__btn {
  width: 32px; height: 32px; flex: none;
  border: 1px solid var(--cm-border); background: var(--cm-surface);
  border-radius: var(--cm-radius-sm); color: var(--cm-muted); font-size: 16px; cursor: pointer;
}
.cm-wc-stepper__btn:hover { background: var(--cm-bg); }
.cm-wc-stepper__value {
  min-width: 24px; text-align: center;
  font: 700 18px var(--cm-font-title); color: var(--cm-ink);
}
.cm-wc-stepper.is-off .cm-wc-stepper__btn,
.cm-wc-stepper.is-off .cm-wc-stepper__value { opacity: .4; pointer-events: none; }

/* -- Projection mode: two radio cards ------------------------------------- */
.cm-wc-modes { display: flex; gap: 10px; flex-wrap: wrap; }
.cm-wc-mode {
  flex: 1 1 220px; display: flex; align-items: center; gap: 10px; cursor: pointer;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius); padding: 14px 16px;
  font: 600 13px var(--cm-font-ui); color: var(--cm-muted);
}
.cm-wc-mode:hover { background: var(--cm-surface-alt); }
.cm-wc-mode input { position: absolute; opacity: 0; pointer-events: none; }
.cm-wc-mode__dot {
  width: 16px; height: 16px; flex: none; box-sizing: border-box;
  border-radius: 50%; border: 1px solid var(--cm-wc-off);
}
.cm-wc-mode:has(input:checked) {
  border-color: var(--cm-brand); background: var(--cm-blue-bg-soft); color: var(--cm-brand-strong);
}
.cm-wc-mode:has(input:checked) .cm-wc-mode__dot {
  border: 5px solid var(--cm-brand); background: var(--cm-surface);
}

/* -- Switches ------------------------------------------------------------- */
.cm-wc-switches {
  display: flex; flex-direction: column; gap: 11px;
  border-top: 1px solid var(--cm-border-soft); padding-top: 18px;
}
.cm-wc-switch {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  font-size: 13.5px; color: var(--cm-ink);
}
.cm-wc-switch input { position: absolute; opacity: 0; pointer-events: none; }
.cm-wc-switch__track {
  width: 46px; height: 24px; flex: none; box-sizing: border-box;
  border-radius: 12px; background: var(--cm-wc-off);
  display: flex; align-items: center; justify-content: flex-start; padding: 0 3px;
}
.cm-wc-switch__knob {
  width: 18px; height: 18px; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
}
.cm-wc-switch:has(input:checked) .cm-wc-switch__track {
  background: var(--cm-brand); justify-content: flex-end;
}

/* -- Creation form -------------------------------------------------------- */
.cm-wc-form { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 18px; align-items: start; }
.cm-wc-form__main { padding: 26px; display: flex; flex-direction: column; gap: 20px; }
.cm-wc-formfoot {
  display: flex; justify-content: flex-end; gap: 10px;
  border-top: 1px solid var(--cm-border-soft); padding-top: 18px;
}
.cm-wc-formfoot .cm-wc-btn { padding: 10px 18px; font-size: 13px; }
.cm-wc-formfoot .cm-wc-btn--primary { padding: 10px 26px; font: 700 13.5px var(--cm-font-ui); }

/* The non-interactive "Vue étudiant" rehearsal beside the form. */
.cm-wc-preview__frame {
  border: 1px solid var(--cm-border-soft); border-radius: 12px; padding: 16px;
  background: var(--cm-wc-soft); display: flex; flex-direction: column; gap: 12px;
}
.cm-wc-preview__question { font: 600 15px var(--cm-font-title); color: var(--cm-ink); }
.cm-wc-preview__boxes { display: flex; flex-direction: column; gap: 8px; }
.cm-wc-preview__box {
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); padding: 10px 12px;
  background: var(--cm-surface); font: 600 13px var(--cm-font-ui); color: var(--cm-ink);
  /* An empty box is a field waiting to be filled, and must keep the height of one. */
  min-height: 19px;
}
.cm-wc-preview__box--empty { border-style: dashed; border-color: var(--cm-wc-off); color: var(--cm-faint); }
.cm-wc-preview .cm-wc-btn--lead { border-radius: var(--cm-radius-sm); padding: 11px; font-size: 13.5px; width: 100%; }

/* -- List of clouds ------------------------------------------------------- */
.cm-wc-list__head,
.cm-wc-list__row {
  display: grid; align-items: center; gap: 14px;
  grid-template-columns: minmax(0, 1fr) 150px 230px 150px 130px 120px;
}
.cm-wc-list__head {
  padding: 12px 18px; border-bottom: 1px solid var(--cm-border-soft);
  font: 700 11px var(--cm-font-ui); color: var(--cm-faint);
  text-transform: uppercase; letter-spacing: .5px;
}
.cm-wc-list__row { padding: 15px 18px; border-bottom: 1px solid var(--cm-border-faint); }
.cm-wc-list__row:last-child { border-bottom: 0; }
.cm-wc-list__row:hover { background: var(--cm-wc-soft); }
.cm-wc-list__name { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cm-wc-list__name b {
  font: 600 14px var(--cm-font-ui); color: var(--cm-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cm-wc-list__name span {
  font-size: 12px; color: var(--cm-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cm-wc-list__cell { font-size: 13px; color: var(--cm-muted); }
.cm-wc-list__act { display: flex; justify-content: flex-end; }
.cm-wc-empty { padding: 26px 18px; font-size: 13px; color: var(--cm-muted); }

/* -- The cloud itself ----------------------------------------------------- */
.cm-wc-cloud {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 6px 20px;
}
.cm-wc-cloud--preview {
  border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius);
  background: var(--cm-wc-soft); padding: 26px 20px; min-height: 280px;
}
/* Each word carries its size as an inline style and its rung as a class: the weighting rule lives
   in App\Service\WordCloud\WordCloudWeighting and nowhere else, so a browser can never draw a cloud
   the server would have drawn differently - while *which* palette a rung is drawn in stays here,
   with the theme it depends on. The transition is what makes a live board readable: words grow into
   their new size instead of jumping. */
.cm-wc-word {
  font-family: var(--cm-font-title); font-weight: 600; line-height: 1.05; white-space: nowrap;
  transition: font-size .3s ease-out, color .3s ease-out;
}
.cm-wc-word--top { color: var(--cm-wc-word-top); }
.cm-wc-word--strong { color: var(--cm-wc-word-strong); }
.cm-wc-word--mid { color: var(--cm-wc-word-mid); }
.cm-wc-word--faint { color: var(--cm-wc-word-faint); }
@media (prefers-reduced-motion: reduce) { .cm-wc-word { transition: none; } }

/* -- Pilot screen --------------------------------------------------------- */
.cm-wc-pilot { display: grid; grid-template-columns: minmax(0, 1fr) 330px; gap: 18px; align-items: start; }
.cm-wc-pilot__side { display: flex; flex-direction: column; gap: 14px; }
.cm-wc-pilot__main { padding: 20px; display: flex; flex-direction: column; gap: 14px; }
.cm-wc-questionline { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cm-wc-question { font: 600 15px var(--cm-font-title); color: var(--cm-ink); }
.cm-wc-meta { margin-left: auto; font: 600 12.5px var(--cm-font-ui); color: var(--cm-faint); }
.cm-wc-flux { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.cm-wc-fluxchip {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid var(--cm-border); border-radius: 14px; padding: 5px 11px;
  font: 600 12px var(--cm-font-ui); color: var(--cm-text); background: var(--cm-surface);
}
.cm-wc-fluxchip span { color: var(--cm-faint); font-weight: 400; }

.cm-wc-pending { display: flex; flex-direction: column; gap: 10px; }
.cm-wc-pending__item {
  display: flex; align-items: center; gap: 9px;
  border: 1px solid var(--cm-wc-pending-bd); background: var(--cm-wc-pending-bg);
  border-radius: var(--cm-radius-sm); padding: 9px 11px;
}
.cm-wc-pending__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cm-wc-pending__word { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-wc-pending__who { font-size: 11.5px; color: var(--cm-faint); }
.cm-wc-pending__acts { margin-left: auto; display: flex; gap: 6px; }
.cm-wc-pending__btn {
  border: 1px solid var(--cm-border); background: var(--cm-surface);
  border-radius: 7px; padding: 5px 10px; font: 600 12px var(--cm-font-ui); cursor: pointer;
}
.cm-wc-pending__btn--ok { border-color: var(--cm-wc-ok-bd); color: var(--cm-wc-ok-tx); }
.cm-wc-pending__btn--ok:hover { background: var(--cm-wc-ok-bg); }
.cm-wc-pending__btn--no { border-color: var(--cm-wc-no-bd); color: var(--cm-red-tx); }
.cm-wc-pending__btn--no:hover { background: var(--cm-wc-no-bg); }

.cm-wc-part { display: flex; align-items: baseline; gap: 8px; }
.cm-wc-part__count { font: 600 30px var(--cm-font-title); color: var(--cm-brand-strong); }
.cm-wc-part__unit { font: 600 12.5px var(--cm-font-ui); color: var(--cm-muted); }
.cm-wc-silent {
  display: flex; flex-direction: column; gap: 8px;
  border-top: 1px solid var(--cm-border-soft); padding-top: 14px;
}
.cm-wc-silent__head { display: flex; align-items: center; gap: 9px; }
.cm-wc-silent__count {
  font: 600 11px var(--cm-font-ui); padding: 3px 9px; border-radius: 10px;
  background: var(--cm-wc-no-bg); color: var(--cm-red-tx);
}
.cm-wc-silent__relaunch {
  margin-left: auto; border: 1px solid var(--cm-border); background: var(--cm-surface);
  color: var(--cm-brand-strong); border-radius: 14px; padding: 4px 11px;
  font: 600 12px var(--cm-font-ui); cursor: pointer;
}
.cm-wc-silent__relaunch:hover { background: var(--cm-blue-bg-soft); }
.cm-wc-silent__item {
  display: flex; align-items: center; gap: 9px;
  border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm);
  padding: 8px 11px; background: var(--cm-wc-soft);
}
.cm-wc-silent__name { font: 600 13px var(--cm-font-ui); color: var(--cm-ink); }
.cm-wc-silent__dash { margin-left: auto; color: var(--cm-wc-dim); font-size: 12px; }

/* The option micro-tag. Its colour is the Option's own (Option::$color), not a table of hard-coded
   filière shades: this app lets an establishment name and colour its own options, and a SLAM blue
   written into the stylesheet would be right for one school and wrong for the next. */
.cm-wc-opt {
  flex: none; font: 700 9.5px var(--cm-font-ui);
  padding: 2px 6px; border-radius: 7px; color: var(--cm-navy); white-space: nowrap;
}

/* -- Follow-up ------------------------------------------------------------ */
.cm-wc-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.cm-wc-stat { padding: 16px 18px; display: flex; flex-direction: column; gap: 4px; }
.cm-wc-stat__value { font: 600 30px var(--cm-font-title); color: var(--cm-ink); }
.cm-wc-stat__value--blue { color: var(--cm-brand-strong); }
.cm-wc-stat__value--gold { font-size: 22px; color: var(--cm-gold-deep); }
.cm-wc-stat__label { font: 600 12.5px var(--cm-font-ui); color: var(--cm-muted); }

.cm-wc-toolbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 14px 18px; border-bottom: 1px solid var(--cm-border-soft);
}
.cm-wc-seg {
  display: flex; border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm);
  overflow: hidden; font: 600 12.5px var(--cm-font-ui);
}
.cm-wc-seg a {
  padding: 8px 18px; background: var(--cm-surface); color: var(--cm-muted); text-decoration: none;
}
.cm-wc-seg a.is-on { background: var(--cm-brand); color: #fff; }
.cm-wc-search {
  margin-left: auto; width: 220px; box-sizing: border-box;
  border: 1px solid var(--cm-border); border-radius: var(--cm-radius-sm); padding: 8px 12px;
  font: 600 12.5px var(--cm-font-ui); color: var(--cm-ink); background: var(--cm-surface);
  outline: none;
}

.cm-wc-words__head,
.cm-wc-words__row {
  display: grid; align-items: center; gap: 14px;
  grid-template-columns: minmax(0, 1fr) 90px 200px 40px;
}
.cm-wc-students__head,
.cm-wc-students__row {
  display: grid; align-items: center; gap: 14px;
  grid-template-columns: 200px 130px 70px minmax(0, 1fr) 130px;
}
.cm-wc-words__head,
.cm-wc-students__head {
  padding: 11px 18px; border-bottom: 1px solid var(--cm-border-soft);
  font: 700 11px var(--cm-font-ui); color: var(--cm-faint);
  text-transform: uppercase; letter-spacing: .5px;
}
.cm-wc-words__group { border-bottom: 1px solid var(--cm-border-faint); }
.cm-wc-words__row { padding: 12px 18px; cursor: pointer; }
.cm-wc-words__row:hover { background: var(--cm-wc-soft); }
.cm-wc-words__word { font: 600 14px var(--cm-font-ui); color: var(--cm-ink); }
.cm-wc-words__count { font: 600 13.5px var(--cm-font-ui); color: var(--cm-muted); }
.cm-wc-words__chev { text-align: right; color: var(--cm-faint); font-size: 12px; }
.cm-wc-bar { display: block; height: 6px; border-radius: 3px; background: var(--cm-wc-track); overflow: hidden; }
.cm-wc-bar__fill { display: block; height: 100%; border-radius: 3px; background: var(--cm-brand); }
.cm-wc-contribs { padding: 2px 18px 16px; display: flex; flex-wrap: wrap; gap: 7px; }
.cm-wc-contrib {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--cm-border); border-radius: 14px; padding: 5px 11px;
  font: 600 12px var(--cm-font-ui); color: var(--cm-text); background: var(--cm-wc-soft);
}
.cm-wc-contrib span { color: var(--cm-faint); font-weight: 400; }

.cm-wc-students__row { padding: 12px 18px; border-bottom: 1px solid var(--cm-border-faint); }
.cm-wc-students__row:last-child { border-bottom: 0; }
.cm-wc-students__row:hover { background: var(--cm-wc-soft); }
.cm-wc-students__name { display: flex; align-items: center; gap: 8px; min-width: 0; }
.cm-wc-students__name b { font: 600 13.5px var(--cm-font-ui); color: var(--cm-ink); }
.cm-wc-students__nb { font: 600 13.5px var(--cm-font-ui); color: var(--cm-muted); }
.cm-wc-students__words { display: flex; flex-wrap: wrap; gap: 6px; }
.cm-wc-students__last { font-size: 12.5px; color: var(--cm-muted); }
.cm-wc-wordchip {
  border: 1px solid var(--cm-border); border-radius: 13px; padding: 4px 10px;
  font: 600 12px var(--cm-font-ui); color: var(--cm-text); background: var(--cm-wc-soft);
}
/* A word the teacher took out: still on its author's line, struck through so the record reads
   correctly at a glance. */
.cm-wc-wordchip--rejected { color: var(--cm-red-tx); border-color: var(--cm-wc-no-bd); text-decoration: line-through; }
.cm-wc-dash { color: var(--cm-wc-dim); font-size: 12.5px; }

/* -- Projection board ----------------------------------------------------- */
/* Dark in both themes on purpose: this is a wall at the back of a room, not a page. */
.cm-wc-board {
  position: fixed; inset: 0; z-index: 2000;
  background: radial-gradient(1200px 800px at 50% -10%, var(--cm-wc-board-top) 0%, var(--cm-wc-board-bottom) 62%);
  color: var(--cm-wc-board-tx); font-family: var(--cm-font-ui);
  /* 64px at the top rather than the handoff's 38: the two controls live in that band, and at 38 the
     mode switch sat across the « SIO-1 · Cybersécurité » kicker whenever the browser refused full
     screen. The handoff's own full-screen figure is 48px with the exit button overlapping nothing,
     so this is the same idea with room for both. */
  padding: 64px 46px 38px; box-sizing: border-box; display: flex; flex-direction: column;
}
.cm-wc-board--panel { flex-direction: row; gap: 38px; }
.cm-wc-board__left { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.cm-wc-board__topline { display: flex; align-items: baseline; gap: 14px; }
.cm-wc-board__kicker {
  font: 600 13px var(--cm-font-ui); letter-spacing: 4px;
  color: var(--cm-gold); text-transform: uppercase;
}
.cm-wc-board__meta { margin-left: auto; font: 600 14px var(--cm-font-ui); color: var(--cm-wc-board-faint); }
.cm-wc-board__question { font: 600 34px var(--cm-font-title); color: #fff; margin-top: 14px; }
.cm-wc-board--panel .cm-wc-board__question { font-size: 30px; }
.cm-wc-board__cloud { flex: 1; gap: 10px 34px; padding: 20px 0; }
/* The board keeps the dark palette whatever the app's theme: the wall at the back of the room does
   not follow the teacher's own preference. */
.cm-wc-board .cm-wc-word--top { color: #c9a04e; }
.cm-wc-board .cm-wc-word--strong { color: #fff; }
.cm-wc-board .cm-wc-word--mid { color: #cfdde9; }
.cm-wc-board .cm-wc-word--faint { color: var(--cm-wc-board-faint); }
.cm-wc-board--panel .cm-wc-board__cloud { gap: 8px 26px; padding: 16px 0; }
.cm-wc-board__foot { display: flex; align-items: center; gap: 12px; }
.cm-wc-board__live { width: 9px; height: 9px; flex: none; border-radius: 50%; background: var(--cm-wc-board-live); }
.cm-wc-board__livetx { font: 600 14px var(--cm-font-ui); color: var(--cm-wc-board-dim); }
.cm-wc-board__side {
  width: 380px; flex: none; border-left: 1px solid rgba(255, 255, 255, .14);
  padding-left: 32px; display: flex; flex-direction: column; gap: 26px;
}
.cm-wc-board__panelhead {
  font: 600 12px var(--cm-font-ui); letter-spacing: 3px;
  color: var(--cm-gold); text-transform: uppercase;
}
.cm-wc-top { display: flex; flex-direction: column; gap: 14px; }
.cm-wc-top__row { display: flex; flex-direction: column; gap: 6px; }
.cm-wc-top__line { display: flex; align-items: baseline; gap: 10px; }
.cm-wc-top__word { font: 600 20px var(--cm-font-title); color: #fff; }
.cm-wc-top__count { margin-left: auto; font: 600 15px var(--cm-font-ui); color: var(--cm-wc-board-dim); }
.cm-wc-top__bar { height: 6px; border-radius: 3px; background: rgba(255, 255, 255, .12); overflow: hidden; }
.cm-wc-top__fill { display: block; height: 100%; border-radius: 3px; background: var(--cm-gold); }
.cm-wc-board__latest { margin-top: auto; display: flex; flex-direction: column; gap: 12px; }
.cm-wc-board__latest b { font: 600 17px var(--cm-font-ui); color: var(--cm-wc-board-tx); }
/* The one control on the wall, and it hides itself while the browser is genuinely full-screen -
   there the Escape key is already the way out and the chrome is gone. */
/* One bar holding both controls, so they cannot land on top of each other or on the kicker. It
   fades back once the room is looking at the board rather than at the person driving it. */
.cm-wc-board__controls {
  position: absolute; top: 16px; left: 46px; right: 46px;
  display: flex; align-items: center; gap: 8px;
  opacity: .35; transition: opacity .2s ease-out;
}
.cm-wc-board__controls:hover,
.cm-wc-board__controls:focus-within { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .cm-wc-board__controls { transition: none; } }
.cm-wc-board__exit { margin-left: auto; }
.cm-wc-board__controls a {
  display: flex; align-items: center; gap: 7px;
  border: 1px solid rgba(255, 255, 255, .22); border-radius: var(--cm-radius-sm);
  padding: 8px 14px; font: 600 13px var(--cm-font-ui); color: var(--cm-wc-board-dim);
  text-decoration: none; cursor: pointer; background: transparent; white-space: nowrap;
}
.cm-wc-board__controls a:hover { color: #fff; border-color: #fff; }
.cm-wc-board__controls a.is-on { color: #fff; border-color: rgba(255, 255, 255, .55); }
.cm-wc-board__empty { flex: 1; display: grid; place-items: center; font: 600 18px var(--cm-font-ui); color: var(--cm-wc-board-faint); }

/* -- Student screen ------------------------------------------------------- */
.cm-wc-student { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 18px; align-items: start; }
.cm-wc-student__own { display: flex; flex-wrap: wrap; gap: 7px; }
.cm-wc-student__closed {
  border: 1px solid var(--cm-border-soft); border-radius: var(--cm-radius-sm);
  background: var(--cm-wc-soft); padding: 14px 16px; font-size: 13px; color: var(--cm-muted);
}

/* -- Narrow screens ------------------------------------------------------- */
@media (max-width: 1100px) {
  .cm-wc-form,
  .cm-wc-pilot,
  .cm-wc-student { grid-template-columns: minmax(0, 1fr); }
  .cm-wc-stats { grid-template-columns: repeat(2, 1fr); }
}
/* The tables keep their column widths and scroll inside their own card rather than squeezing a
   200px name column into 90px. */
@media (max-width: 900px) {
  .cm-wc-scroll { overflow-x: auto; }
  .cm-wc-scroll > * { min-width: 860px; }
}
