/* ============================================================================
   iCal Aggregator — app.css (feuille de style applicative)
   Réalisation du Design System v3 « Console » (docs/ui/design-system.md), acté par
   ADR-056 (langage d'interface) et ADR-057 (typographie & iconographie). Chargée via
   AssetMapper (import dans assets/app.js). Bootstrap 5 = socle ; surcharges par
   variables CSS. Source unique des tokens : docs/ui/design-system.md.
   Nommage : miroir strict du DS — un token CSS = son chemin `a.b.c` en `--cp-a-b-c`.
   Mode sombre : hors périmètre F3 (tokens fournis au DS, aucune couleur en dur ici).
   ========================================================================== */

:root {
  /* Thème (§2.6, EPIC-072) : chaque token couleur est défini UNE fois via
     light-dark(clair, sombre). color-scheme: light dark → le sombre suit
     prefers-color-scheme SANS JS ; :root[data-theme=…] le force (cookie, F4-T02).
     Valeurs sombres = §2.6 (contrastes AA mesurés). Aucune couleur en dur. */
  color-scheme: light dark;
  /* Neutres (§2.2) */
  --cp-bg: light-dark(#EEF1F5, #0D1014);
  --cp-surface: light-dark(#FFFFFF, #161B22);
  --cp-surface-sunken: light-dark(#EDF0F3, #10151B);
  --cp-surface-hover: light-dark(#E4E9F0, #1E242E);   /* survol / jour « hors mois » (DT-013) */
  --cp-border: light-dark(#E1E6EC, #232A33);          /* filet décoratif — sans seuil, jamais un contrôle */
  --cp-border-strong: light-dark(#CDD4DE, #333B47);   /* filet décoratif marqué */
  --cp-border-control: light-dark(#808B9B, #5D6875);  /* contour de contrôle — ≥ 3:1 (WCAG 1.4.11) */
  --cp-text: light-dark(#14181F, #E7EBF1);
  --cp-text-secondary: light-dark(#4A5462, #AEB7C3);
  --cp-text-muted: light-dark(#616B79, #8B94A2);
  /* Accent — indigo encre (§2.3) */
  --cp-primary: light-dark(#3B3E93, #ACAEF2);
  --cp-primary-hover: light-dark(#2F3283, #C4C6F8);
  --cp-primary-subtle: light-dark(#ECEDFB, #23264A);
  --cp-on-primary: light-dark(#FFFFFF, #14181F);
  /* Statut — plein / subtle (§2.4) */
  --cp-status-success: light-dark(#17724A, #57C089); --cp-status-success-subtle: light-dark(#E7F3EC, #10241A);
  --cp-status-danger:  light-dark(#B3271E, #F0857D); --cp-status-danger-subtle:  light-dark(#FBEAE8, #2C120F);
  --cp-status-warning: light-dark(#8A5A00, #DCA34C); --cp-status-warning-subtle: light-dark(#FAF1E1, #271D0B);
  --cp-status-info:    light-dark(#14608F, #6DB0E6); --cp-status-info-subtle:    light-dark(#E7F1F8, #0E1F2B);
  --cp-status-neutral: light-dark(#616B79, #8B94A2); --cp-status-neutral-subtle: light-dark(#EDF0F3, #10151B);
  /* Espacement — base 4, échelle resserrée (§4.1) */
  --cp-space-1: 4px; --cp-space-2: 8px; --cp-space-3: 12px;
  --cp-space-4: 16px; --cp-space-5: 24px; --cp-space-6: 32px;
  /* Rayons (§5) */
  --cp-radius-sm: 4px; --cp-radius-md: 6px; --cp-radius-lg: 10px;
  /* Typographie — deux voix (§3.1). Familles IBM Plex servies en woff2 par F3-T01b ;
     d'ici là le fallback système s'applique sans casse. */
  --cp-font-base: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --cp-font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Bootstrap overrides — alignés sur les tokens ci-dessus */
  --bs-primary: light-dark(#3B3E93, #ACAEF2);
  --bs-primary-rgb: 59, 62, 147;
  --bs-body-bg: light-dark(#EEF1F5, #0D1014);
  --bs-body-color: light-dark(#14181F, #E7EBF1);
  --bs-border-color: light-dark(#E1E6EC, #232A33);
  --bs-body-font-size: 0.875rem;      /* base 14px */
  --bs-border-radius: 6px;
  --bs-link-color: light-dark(#3B3E93, #ACAEF2);
  --bs-link-hover-color: light-dark(#2F3283, #C4C6F8);
  /* Bootstrap 5.3 calcule la couleur d'un <a> depuis les variantes -rgb ; sans elles,
     les liens restaient au bleu par défaut (DT-012). a:hover réassigne -color-rgb. */
  --bs-link-color-rgb: 59, 62, 147;
  --bs-link-hover-color-rgb: 47, 50, 131;
}

/* Choix explicite (F4-T02) : force le color-scheme → résout light-dark() et pose
   le rendu natif (contrôles, scrollbars). Sans data-theme, prefers-color-scheme
   s'applique (défaut auto, sans JS). */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

/* Les triplets -rgb (rgba(var(--…-rgb), α)) ne peuvent pas passer par light-dark() :
   surcharge dédiée en sombre — explicite, et par préférence OS quand non forcé clair. */
:root[data-theme="dark"] {
  --bs-primary-rgb: 172, 174, 242;
  --bs-link-color-rgb: 172, 174, 242;
  --bs-link-hover-color-rgb: 196, 198, 248;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bs-primary-rgb: 172, 174, 242;
    --bs-link-color-rgb: 172, 174, 242;
    --bs-link-hover-color-rgb: 196, 198, 248;
  }
}

* { font-variant-numeric: tabular-nums; }
body {
  background: var(--cp-bg);
  color: var(--cp-text);
  font-size: 14px;
  font-family: var(--cp-font-base);
}
.mono, code, .text-mono { font-family: var(--cp-font-mono); }
/* `code` inline : neutraliser le rose décoratif de Bootstrap (--bs-code-color, hors DS)
   au profit du texte principal — mono = machine (§3.1), couleur ≠ décor (règle 1).
   Ratio AA garanti clair et sombre (var(--cp-text) : 17,79/15,71 clair, 14,46+ sombre). */
code { color: var(--cp-text); }

/* ---- Accessibilité (WCAG AA — docs/ui/accessibilite.md) ---- */
/* Focus toujours visible au clavier : anneau net à la couleur de marque (jamais
   outline:none sans alternative). :focus-visible n'affiche pas l'anneau au clic souris. */
:focus-visible { outline: 2px solid var(--cp-primary); outline-offset: 2px; }
/* Lien d'évitement « Aller au contenu » : premier focusable, masqué jusqu'au focus. */
.skip-link {
  position: absolute; top: -3rem; left: .5rem; z-index: 1060;
  padding: 8px 12px; border-radius: var(--cp-radius-md);
  background: var(--cp-surface); color: var(--cp-primary);
  border: 1px solid var(--cp-border-control); text-decoration: none; font-weight: 600;
  transition: top .15s ease;
}
.skip-link:focus { top: .5rem; }
h1, .h1 { font-size: 1.5rem; font-weight: 600; }
h2, .h2 { font-size: 1.125rem; font-weight: 600; }
h3, .h3 { font-size: 0.95rem; font-weight: 600; }
.text-2 { color: var(--cp-text-secondary); }
.text-muted-2 { color: var(--cp-text-muted); }
.overline { font-size: 0.7rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--cp-text-secondary); }

/* ---- Boutons : accent sobre ---- */
.btn-primary { --bs-btn-bg: var(--cp-primary); --bs-btn-border-color: var(--cp-primary);
  --bs-btn-color: var(--cp-on-primary); --bs-btn-hover-color: var(--cp-on-primary); --bs-btn-active-color: var(--cp-on-primary);
  --bs-btn-hover-bg: var(--cp-primary-hover); --bs-btn-hover-border-color: var(--cp-primary-hover);
  --bs-btn-active-bg: var(--cp-primary-hover); }
.btn-outline-secondary { --bs-btn-color: var(--cp-text-secondary); --bs-btn-border-color: var(--cp-border-control);
  --bs-btn-hover-bg: var(--cp-surface-sunken); --bs-btn-hover-color: var(--cp-text); --bs-btn-hover-border-color: var(--cp-border-control); }
.btn-ghost { color: var(--cp-text-secondary); background: transparent; border: 0; }
.btn-ghost:hover { background: var(--cp-surface-sunken); color: var(--cp-text); }
.btn { --bs-btn-font-size: .875rem; }
/* Form help text = secondary/muted (§2.7) via a token so it adapts to the theme
   (Bootstrap's default --bs-secondary-color stays dark → invisible in dark). */
.form-text { color: var(--cp-text-muted); }

/* ============================ APP SHELL ============================ */
.app-topbar {
  height: 48px; background: var(--cp-surface);
  border-bottom: 1px solid var(--cp-border);
  position: sticky; top: 0; z-index: 1030;
}
.app-brand { font-weight: 600; color: var(--cp-text); text-decoration: none; }
.app-brand:hover { color: var(--cp-primary); }

/* Sticky footer : le shell occupe toute la hauteur, le corps prend l'espace restant,
   le footer reste en bas SANS créer de scroll quand le contenu est court. */
.app-shell { display: flex; flex-direction: column; min-height: 100vh; }
.app-shell > .app-body { flex: 1 1 auto; min-height: 0; }
.app-body { display: flex; align-items: stretch; }
.app-sidebar {
  width: 224px; flex: 0 0 224px; background: var(--cp-surface);
  border-right: 1px solid var(--cp-border); padding: 12px 10px;
}
.app-sidebar .side-group { font-size: .7rem; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--cp-text-muted); padding: 10px 10px 6px; }
.nav-side { list-style: none; padding: 0; margin: 0; }
.nav-side a {
  display: flex; align-items: center; gap: 10px; min-height: 44px; /* cible tactile */
  padding: 8px 10px; margin-bottom: 2px; border-radius: var(--cp-radius-md);
  color: var(--cp-text-secondary); text-decoration: none; font-weight: 500;
}
.nav-side a:hover { background: var(--cp-surface-sunken); color: var(--cp-text); }
.nav-side a.active {
  background: var(--cp-primary-subtle); color: var(--cp-primary);
  box-shadow: inset 3px 0 0 var(--cp-primary);
}
.nav-side a i { font-size: 1.05rem; width: 1.2em; text-align: center; }
.nav-side .nav-disabled {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; margin-bottom: 2px; border-radius: var(--cp-radius-md);
  color: var(--cp-text-muted); font-weight: 500; cursor: default;
}
.nav-side .nav-disabled i { font-size: 1.05rem; width: 1.2em; text-align: center; }
.nav-side .nav-disabled .soon { margin-left: auto; font-size: .68rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em; color: var(--cp-text-muted); }
.app-main { flex: 1 1 auto; padding: 20px 24px; min-width: 0; }

/* ============================ PAGE HEADER ============================ */
.breadcrumb { font-size: .8rem; margin-bottom: .35rem; }
.page-head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; margin-bottom: 18px; }
.page-head h1 { margin: 0; }

/* ============================ CARTES / PANNEAUX ============================ */
.card { --bs-card-border-color: var(--cp-border); --bs-card-cap-bg: var(--cp-surface);
  border-radius: var(--cp-radius-md); box-shadow: none; }
.panel { background: var(--cp-surface); border: 1px solid var(--cp-border);
  border-radius: var(--cp-radius-md); }
.panel > .panel-h { padding: 12px 16px; border-bottom: 1px solid var(--cp-border);
  display: flex; align-items: center; justify-content: space-between; }
.panel > .panel-b { padding: 16px; }
.kv { display: grid; grid-template-columns: 160px 1fr; gap: 8px 16px; margin: 0; }
.kv dt { color: var(--cp-text-secondary); font-weight: 500; }
.kv dd { margin: 0; }

/* ============================ EN-TÊTE D'ENTITÉ + ONGLETS ============================ */
.entity-head { background: var(--cp-surface); border: 1px solid var(--cp-border);
  border-radius: var(--cp-radius-md) var(--cp-radius-md) 0 0; border-bottom: 0; padding: 16px; }
.entity-head h1 { font-size: 1.25rem; }
.tabs { display: flex; gap: 4px; background: var(--cp-surface);
  border: 1px solid var(--cp-border); border-top: 0; padding: 0 8px;
  border-radius: 0 0 var(--cp-radius-md) var(--cp-radius-md);
  max-width: 100%; overflow-x: auto; overflow-y: hidden; }
.tabs a { padding: 10px 14px; color: var(--cp-text-secondary); text-decoration: none;
  font-weight: 500; border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; }
.tabs a:hover { color: var(--cp-text); }
.tabs a.active { color: var(--cp-primary); border-bottom-color: var(--cp-primary); }
.tabs .disabled { padding: 10px 14px; color: var(--cp-text-muted); font-weight: 500;
  border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; cursor: not-allowed; }
.tabs .count { color: var(--cp-text-muted); font-weight: 600; }

/* ============================ PASTILLE DE STATUT ============================ */
.status { display: inline-flex; align-items: center; gap: 6px; font-weight: 600;
  font-size: .8rem; white-space: nowrap; }
.status .glyph { font-size: .6rem; line-height: 1; }
.status.success { color: var(--cp-status-success); }
.status.danger  { color: var(--cp-status-danger); }
.status.warning { color: var(--cp-status-warning); }
.status.info    { color: var(--cp-status-info); }
.status.neutral { color: var(--cp-status-neutral); }

/* Badge sobre */
.badge-soft { font-weight: 600; border-radius: var(--cp-radius-sm); padding: .25em .5em; font-size: .72rem; }
.badge-role { background: var(--cp-primary-subtle); color: var(--cp-primary); }
.badge-user { background: var(--cp-status-neutral-subtle); color: var(--cp-status-neutral); }
.badge-obf  { background: var(--cp-status-info-subtle); color: var(--cp-status-info); }

/* ============================ TOOLBAR + TABLE ============================ */
.list-toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-bottom: 12px; }
.list-toolbar .search { max-width: 280px; }
.list-toolbar .form-select, .list-toolbar .form-control { font-size: .82rem; }
.table { --bs-table-border-color: var(--cp-border); --bs-table-border-width: 0; vertical-align: middle; margin: 0; }
.table td, .table th { padding: 10px 12px; border: 0; }
/* En-tête sur fond surface : sur la carte blanche, un fond gris (sunken) se confondait
   avec le fond de page. Le filet inférieur, plus marqué, est porté par un pseudo-élément
   en retrait de 3px des bords — les bordures de cellules HTML iraient sinon jusqu'au bord
   de la carte. thead/tr positionnés en `relative` pour ancrer ces filets. */
.table thead { position: relative; }
.table thead th { background: var(--cp-surface); color: var(--cp-text-secondary);
  font-weight: 600; font-size: .78rem; text-transform: uppercase; letter-spacing: .02em;
  white-space: nowrap; }
.table thead::after { content: ""; position: absolute; left: 3px; right: 3px; bottom: 0;
  height: 2px; background: var(--cp-border-strong); }
/* Séparateurs de lignes : filet en retrait de 3px, cohérent avec la gouttière d'état
   (`.status-gutter`, filet gauche 3px). */
.table tbody tr { position: relative; }
.table tbody tr:not(:last-child)::after { content: ""; position: absolute; left: 3px; right: 3px;
  bottom: 0; height: 1px; background: var(--cp-border); }
.table tbody tr:hover { background: var(--cp-primary-subtle); }
.table a.row-name { font-weight: 600; color: var(--cp-text); text-decoration: none; }
.table a.row-name:hover { color: var(--cp-primary); }
.col-actions { text-align: right; white-space: nowrap; }
.url-cell { color: var(--cp-text-secondary); max-width: 260px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: bottom; }

/* Barre d'actions groupées */
.bulk-bar { background: var(--cp-primary-subtle); border: 1px solid var(--cp-border);
  border-radius: var(--cp-radius-md); padding: 8px 12px; display: flex; align-items: center;
  gap: 10px; margin-top: 10px; font-size: .85rem; }

/* ============================ TRIAGE (dashboard) ============================ */
.attention { border: 1px solid var(--cp-border); border-radius: var(--cp-radius-md);
  background: var(--cp-surface); overflow: hidden; }
.attention .att-h { padding: 12px 16px; border-bottom: 1px solid var(--cp-border);
  font-weight: 600; display: flex; align-items: center; gap: 8px; }
.att-item { display: flex; align-items: center; gap: 12px; padding: 12px 16px;
  border-bottom: 1px solid var(--cp-border); }
.att-item:last-child { border-bottom: 0; }
.att-item .att-body { flex: 1 1 auto; min-width: 0; }
.att-item .att-ctx { color: var(--cp-text-secondary); font-size: .82rem; }
.att-item .glyph-lg { font-size: 1.05rem; }
.all-clear { background: var(--cp-status-success-subtle); color: var(--cp-status-success);
  border: 1px solid var(--cp-border); border-radius: var(--cp-radius-md); padding: 14px 16px; font-weight: 600; }

.metric-strip { display: flex; flex-wrap: wrap; gap: 24px; padding: 14px 16px;
  background: var(--cp-surface); border: 1px solid var(--cp-border);
  border-radius: var(--cp-radius-md); }
.metric-strip .metric { display: flex; flex-direction: column; }
.metric-strip .metric .v { font-size: 1.15rem; font-weight: 700; }
.metric-strip .metric .l { font-size: .75rem; color: var(--cp-text-secondary); }

/* ============================ LIEN PUBLIC ============================ */
/* Confinement partagé par le lien public et l'extrait d'intégration (iframe) : un
   token/URL insécable défile à l'intérieur du champ au lieu d'élargir la page
   (min-width:0 permet à l'item flex de rétrécir sous sa largeur naturelle). */
.public-link .link-field, .embed-panel .link-field { background: var(--cp-surface-sunken); border: 1px solid var(--cp-border);
  border-radius: var(--cp-radius-sm); padding: 8px 10px; font-family: var(--cp-font-mono);
  min-width: 0; overflow-x: auto; white-space: nowrap; }
.public-link.disabled .link-field { color: var(--cp-text-muted); }

/* ============================ ALERTES / BANDEAUX ============================ */
/* Fond en ton *subtle*, contour hairline neutre ; le sens est porté par l'icône et le
   texte (DS §8 : « icône + ton subtle »), jamais par la seule couleur. */
.banner { border-radius: var(--cp-radius-md); padding: 10px 14px; display: flex;
  align-items: center; gap: 10px; }
.banner.warning { background: var(--cp-status-warning-subtle); color: var(--cp-status-warning); border: 1px solid var(--cp-border); }
.banner.danger  { background: var(--cp-status-danger-subtle);  color: var(--cp-status-danger);  border: 1px solid var(--cp-border); }
.banner.info    { background: var(--cp-status-info-subtle);    color: var(--cp-status-info);    border: 1px solid var(--cp-border); }

/* Zone de danger (DangerZone) — fond danger-subtle + contour hairline ; le sens est porté
   par le titre et l'icône (règle DS : pas la couleur seule). */
.danger-zone { border: 1px solid var(--cp-border); border-radius: var(--cp-radius-md);
  background: var(--cp-status-danger-subtle); padding: 14px 16px; }

/* ============================ ÉTAT VIDE ============================ */
.empty-state { text-align: center; padding: 40px 20px; color: var(--cp-text-secondary); }
/* Seule l'icône illustrative (enfant direct) est agrandie/atténuée — pas les icônes
   des boutons d'action du slot (ex. le « + » de « Créer un calendrier »). */
.empty-state > .bi { font-size: 2rem; color: var(--cp-text-muted); }

/* ============================ AUTH / ERREUR (centré) ============================ */
.auth-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center;
  background: var(--cp-bg); padding: 24px; }
/* Empile le flash au-dessus de la carte (sinon, enfant direct du .auth-wrap flex,
   le flash se plaçait à CÔTÉ du formulaire). */
.auth-stack { width: 100%; max-width: 400px; display: flex; flex-direction: column; gap: 12px; }
.auth-card { width: 100%; max-width: 400px; background: var(--cp-surface);
  border: 1px solid var(--cp-border); border-radius: var(--cp-radius-lg); padding: 28px; }
/* Pages légales (mentions, confidentialité, CGU, notices) : colonne de lecture élargie
   (≈ le double de la carte d'auth) et alignée en haut, le texte étant long. Ciblé via
   :has() pour ne pas élargir le login qui partage le shell public. */
.auth-wrap:has(.legal-page) { align-items: flex-start; }
.auth-stack:has(.legal-page) { max-width: 800px; }

/* Formulaires */
.form-control, .form-select { font-size: .875rem; border-color: var(--cp-border-control); }
.form-label { font-weight: 600; font-size: .82rem; }
.form-help { font-size: .78rem; color: var(--cp-text-secondary); }
.form-narrow { max-width: 640px; }
fieldset.section { border: 1px solid var(--cp-border); border-radius: var(--cp-radius-md);
  padding: 16px; margin-bottom: 16px; }
fieldset.section > legend { float: none; width: auto; padding: 0 6px; font-size: .8rem;
  font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--cp-text-secondary); }

/* ============================ FILTRAGE : RÈGLES D'UNE SOURCE (S13-T07) ============ */
/* Liste ordonnée des règles : rang | corps | actions. */
.rule-list { display: flex; flex-direction: column; gap: 10px; }
.rule-item { display: flex; align-items: flex-start; gap: 12px;
  border: 1px solid var(--cp-border); border-radius: var(--cp-radius-md);
  background: var(--cp-surface); padding: 12px 14px; }
.rule-rank { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.6rem; height: 1.6rem; border-radius: var(--cp-radius-sm);
  background: var(--cp-surface-sunken); color: var(--cp-text-secondary);
  font-weight: 700; font-size: .8rem; }
.rule-body { flex: 1 1 auto; min-width: 0; }
.rule-heading { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.rule-conditions-summary { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 2px; font-size: .85rem; }
.rule-conditions-summary .rc-key { font-weight: 600; }
.rule-conditions-summary .rc-op { color: var(--cp-text-secondary); }
.rule-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Bouton d'ordre désactivé : signal visuel clair, non focusable via aria-disabled. */
.rule-actions .btn:disabled, .rule-actions .btn[aria-disabled="true"] { cursor: not-allowed; opacity: .5; }
/* Mobile (issue #20) : sinon le rang, le corps et les actions se disputent une seule
   rangée — le corps est écrasé et chevauche les boutons. On laisse l'item wrapper et
   on pousse les actions en pleine largeur, sous le corps. */
@media (max-width: 575.98px) {
  .rule-item { flex-wrap: wrap; }
  .rule-actions { flex-basis: 100%; justify-content: flex-start; margin-top: 8px; }
}

/* Formulaire : lignes de condition alignées « champ | opérateur | valeur | retirer ». */
.rule-conditions { display: flex; flex-direction: column; gap: 10px; }
.rule-condition-row { display: flex; align-items: flex-end; gap: 10px;
  border: 1px solid var(--cp-border); border-radius: var(--cp-radius-sm);
  background: var(--cp-surface-sunken); padding: 10px 12px; }
.rule-condition-row .rc-fields { display: flex; flex-wrap: wrap; align-items: flex-end;
  gap: 10px; flex: 1 1 auto; min-width: 0; }
.rule-condition-row .rc-field, .rule-condition-row .rc-operator { flex: 1 1 160px; min-width: 150px; }
.rule-condition-row .rc-value { flex: 2 1 200px; min-width: 160px; }
/* Neutralise la marge basse des groupes de champ Bootstrap pour aligner la ligne. */
.rule-condition-row .rc-fields > div > .mb-3,
.rule-condition-row .rc-fields .mb-3:last-child { margin-bottom: 0 !important; }
.rule-condition-row .rc-remove { flex: 0 0 auto; }

/* Divers */
.text-danger-2 { color: var(--cp-status-danger); }

/* ============================ SIGNATURES (CSS pur, sans JS/asset — ADR-030) ============ */
/* Gouttière d'état (B-13) : filet de 3 px porté par la 1re cellule de la ligne, coloré
   par le ton d'état. Toujours doublée d'une pastille de statut (StatusBadge). */
.status-gutter { box-shadow: inset 3px 0 0 var(--cp-status-neutral); }
.status-gutter.is-success { box-shadow: inset 3px 0 0 var(--cp-status-success); }
.status-gutter.is-danger  { box-shadow: inset 3px 0 0 var(--cp-status-danger); }
.status-gutter.is-warning { box-shadow: inset 3px 0 0 var(--cp-status-warning); }
.status-gutter.is-info    { box-shadow: inset 3px 0 0 var(--cp-status-info); }
.status-gutter.is-neutral { box-shadow: inset 3px 0 0 var(--cp-status-neutral); }
.status-gutter.is-muted   { box-shadow: inset 3px 0 0 var(--cp-border); } /* ligne en retrait */

/* Le voile (B-14) : trame diagonale à très faible contraste recouvrant le contenu masqué
   au public. Non interactif (pointer-events: none) ; TOUJOURS doublé d'un texte lisible
   (≥ 4,5:1). La trame dérive du token de texte (aucune valeur littérale). */
.veil { position: relative; }
.veil::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(-45deg,
    transparent 0, transparent 5px,
    color-mix(in srgb, var(--cp-text) 6%, transparent) 5px,
    color-mix(in srgb, var(--cp-text) 6%, transparent) 6px);
}

/* En desktop, la colonne latérale garde son fond et son filet sur toute la hauteur
   (flex stretch), mais le menu lui-même reste collé sous la topbar (48px) au scroll —
   sans jamais décrocher au passage du footer (qui vit hors de `.app-body`). En < 992px
   la navigation devient un offcanvas `fixed` (ci-dessous), où ce sticky ne s'applique pas. */
@media (min-width: 992px) {
  .sidebar-sticky { position: sticky; top: 48px; }
}

/* ============================ RESPONSIVE (mobile) ============================ */
/* Sous lg (992px), la navigation latérale passe en **offcanvas** glissant, ouvert par le
   hamburger de la topbar. Implémentation **CSS pure `:target`** : aucun JS requis
   (ADR-030) ; le contenu prend toute la largeur, les tables larges défilent dans leur
   .table-responsive (anti-débordement, §4 de responsive.md). Bascule tables → cartes :
   raffinement de confort tracé (F3), non requis pour l'anti-débordement. */
.offcanvas-close { display: none; }
@media (max-width: 991.98px) {
  .app-sidebar {
    position: fixed; top: 48px; bottom: 0; left: 0; z-index: 1035;
    width: 260px; max-width: 84vw; flex: none;
    transform: translateX(-100%); transition: transform .2s ease;
    overflow-y: auto;
    box-shadow: 2px 0 12px color-mix(in srgb, var(--cp-text) 14%, transparent);
  }
  .app-sidebar:target { transform: none; }
  .offcanvas-close { display: flex; justify-content: flex-end;
    padding: 4px 6px; color: var(--cp-text-secondary); font-size: 1.1rem; text-decoration: none; }
  .app-main { padding: 16px; }
  .page-head { flex-direction: column; align-items: stretch; }
}

/* =============================== APERÇU ================================== */
/* La section d'aperçu vit dans une colonne Bootstrap (`.row > .col-12`), donc un
   flex item : par défaut `min-width: auto` l'empêche de rétrécir sous la largeur
   de son contenu, et les grilles à `min-width` (semaine/mois, 720px) débordaient la
   page au lieu de laisser leur conteneur `overflow-x` défiler. `min-width: 0` rend la
   colonne compressible → l'ascenseur horizontal s'active dans chaque vue (issue #3). */
.preview-col { min-width: 0; }

/* Vue Liste (S11-T05) : par défaut Bootstrap pose le fond des items sur
   `--bs-body-bg` (= --cp-bg, gris), qui se confond avec le fond de page. On le
   ramène à la surface claire pour détacher la liste du décor. */
.preview-list { --bs-list-group-bg: var(--cp-surface); }
/* Date / heure / titre alignés en colonnes : les deux premières colonnes prennent
   la largeur de leur contenu (dates au même format + heures en police mono à chiffres
   de largeur fixe), donc les titres démarrent tous au même axe. */
.preview-list-row { display: grid; grid-template-columns: auto 11ch 1fr;
  gap: var(--cp-space-3); align-items: baseline; }
.preview-list-date, .preview-list-time { font-family: var(--cp-font-mono);
  color: var(--cp-text-secondary); white-space: nowrap; }
.preview-list-time { text-align: left; }
.preview-list-title { overflow: hidden; text-overflow: ellipsis; }

/* Occurrence annulée (ADR-065) : republiée dans le flux en STATUS:CANCELLED, elle
   apparaît aussi dans l'aperçu, barrée et atténuée, avec une pastille « Annulé ».
   Marquage server-rendered, sans JavaScript (ADR-030). */
.is-canceled { text-decoration: line-through; opacity: .6; }
.preview-canceled { display: inline-block; margin-left: var(--cp-space-2);
  padding: 0 var(--cp-space-2); border-radius: var(--cp-radius-sm, 4px);
  background: var(--cp-danger-subtle, #f8d7da); color: var(--cp-danger, #842029);
  font-size: .7em; font-weight: 600; text-transform: uppercase; text-decoration: none;
  vertical-align: middle; letter-spacing: .02em; }

/* Aperçu des règles par source (S13-T08, ADR-066) : chaque occurrence porte son
   issue (Détail / Masqué / Caché) via <twig:StatusBadge>, uniquement rendue quand
   `occurrence.outcome` est non nul — l'aperçu calendrier reste donc inchangé.
   « Caché » = ne sera pas publié : on le barre et l'atténue comme une occurrence
   annulée. Le badge (icône + texte) porte toujours le sens : jamais la couleur seule
   (ADR-063). Server-rendered, aucun JavaScript (ADR-030). */
.is-hidden { text-decoration: line-through; opacity: .55; }
.preview-outcome { display: inline-flex; align-items: center; vertical-align: middle; }
.preview-list-row .preview-outcome { grid-column: 3; justify-self: start;
  margin-top: 2px; }
/* Cases denses (jour/semaine/mois) : badge compact, sur sa propre ligne. */
.preview-outcome--compact { margin-top: 1px; }
.preview-outcome--compact .status { font-size: .62rem; font-weight: 600; gap: 3px; }
.preview-outcome--compact .status .bi { font-size: .62rem; }

/* ============================ APERÇU — VUE JOUR ============================ */
/* Grille horaire 24 h de l'aperçu (S11-T06). Positions calculées côté serveur
   par DayLayout (top/height = minutes, left/width = colonne/columnCount) et
   posées en CSS inline : aucun JavaScript (ADR-030). Échelle : 48px/heure. */
.day-nav { display: flex; flex-wrap: wrap; gap: var(--cp-space-2); }
.day-title { font-size: 1.05rem; font-weight: 600; text-transform: capitalize;
  color: var(--cp-text); }

.day-allday { display: flex; align-items: flex-start; gap: var(--cp-space-3);
  padding: var(--cp-space-2) var(--cp-space-3); background: var(--cp-surface);
  border: 1px solid var(--cp-border); border-radius: var(--cp-radius-md); }
.day-allday-label { flex: 0 0 auto; font-weight: 600; color: var(--cp-text-secondary);
  font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.02em; padding-top: 2px; }
.day-allday-list { list-style: none; margin: 0; padding: 0; display: flex;
  flex-wrap: wrap; gap: var(--cp-space-2); }
.day-allday-item { padding: 2px var(--cp-space-2); background: var(--cp-primary-subtle);
  color: var(--cp-primary); border-radius: var(--cp-radius-sm); font-size: 0.8rem;
  font-weight: 500; }

.day-grid { display: grid; grid-template-columns: 56px 1fr; gap: 0;
  border: 1px solid var(--cp-border); border-radius: var(--cp-radius-md);
  background: var(--cp-surface); overflow: hidden; }
.day-grid-hours { display: flex; flex-direction: column;
  border-right: 1px solid var(--cp-border); }
.day-hour { height: 48px; box-sizing: border-box; padding: 2px 6px 0 0;
  text-align: right; font-family: var(--cp-font-mono); font-size: 0.72rem;
  color: var(--cp-text-muted); border-bottom: 1px solid var(--cp-border); }
.day-hour:last-child { border-bottom: 0; }
.day-grid-events { position: relative; }
.day-hour-line { position: absolute; left: 0; right: 0; height: 0;
  border-top: 1px solid var(--cp-border); pointer-events: none; }
.day-event { position: absolute; box-sizing: border-box; overflow: hidden;
  padding: 2px var(--cp-space-2); background: var(--cp-primary-subtle);
  border-left: 3px solid var(--cp-primary); border-radius: var(--cp-radius-sm);
  font-size: 0.75rem; line-height: 1.2; color: var(--cp-text); }
.day-event-time { display: block; font-family: var(--cp-font-mono);
  font-size: 0.68rem; color: var(--cp-text-secondary); }
.day-event-title { display: block; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; }

/* ========================== APERÇU — VUE SEMAINE ========================== */
/* Grille horaire 7 jours (lun→dim) de l'aperçu (S11-T07). Réutilise l'échelle
   verticale de la vue Jour (48px/heure) ; chaque jour est positionné par
   DayLayout (colonnes de chevauchement). Positions posées en CSS inline : aucun
   JavaScript (ADR-030). Sur petit écran, la grille défile horizontalement dans
   son conteneur (overflow-x) — jamais le body (issue #3). */
.week-scroll { overflow-x: auto; border: 1px solid var(--cp-border);
  border-radius: var(--cp-radius-md); background: var(--cp-surface); }
.week-grid { display: grid; grid-template-columns: 64px repeat(7, minmax(96px, 1fr));
  min-width: 720px; }

.week-corner { display: flex; align-items: center; justify-content: center;
  padding: 2px 4px; font-size: 0.62rem; text-transform: uppercase;
  overflow: hidden; color: var(--cp-text-secondary);
  border-right: 1px solid var(--cp-border);
  border-bottom: 1px solid var(--cp-border); }
.week-dayhead { display: flex; flex-direction: column; align-items: center;
  gap: 1px; padding: var(--cp-space-1) var(--cp-space-2); text-decoration: none;
  color: var(--cp-text); border-bottom: 1px solid var(--cp-border);
  border-left: 1px solid var(--cp-border); }
.week-dayhead:hover { background: var(--cp-surface-hover); }
.week-dayhead.is-today { background: var(--cp-primary-subtle); }
.week-dayhead-name { font-weight: 600; text-transform: capitalize;
  font-size: 0.8rem; }
.week-dayhead-date { font-family: var(--cp-font-mono); font-size: 0.72rem;
  color: var(--cp-text-muted); }

.week-allday-spacer { border-right: 1px solid var(--cp-border);
  border-bottom: 1px solid var(--cp-border); }
.week-allday-cell { display: flex; flex-direction: column; gap: 2px;
  padding: 2px; min-height: 22px; border-left: 1px solid var(--cp-border);
  border-bottom: 1px solid var(--cp-border); }
.week-allday-cell.is-today { background: var(--cp-primary-subtle); }
.week-allday-item { display: block; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; padding: 1px var(--cp-space-2);
  background: var(--cp-primary-subtle); color: var(--cp-primary);
  border-radius: var(--cp-radius-sm); font-size: 0.72rem; font-weight: 500; }

.week-hours { display: flex; flex-direction: column;
  border-right: 1px solid var(--cp-border); }
.week-hour { height: 30px; box-sizing: border-box; padding: 2px 6px 0 0;
  text-align: right; font-family: var(--cp-font-mono); font-size: 0.68rem;
  color: var(--cp-text-muted); }
.week-daycol { position: relative; box-sizing: border-box;
  border-left: 1px solid var(--cp-border); }
.week-daycol.is-today { background: var(--cp-primary-subtle); }
.week-hour-line { position: absolute; left: 0; right: 0; height: 0;
  border-top: 1px solid var(--cp-border); pointer-events: none; }
.week-event { position: absolute; box-sizing: border-box; overflow: hidden;
  min-width: 28px; padding: 1px 4px; background: var(--cp-primary-subtle);
  border-left: 3px solid var(--cp-primary); border-radius: var(--cp-radius-sm);
  font-size: 0.7rem; line-height: 1.15; color: var(--cp-text); }
.week-event-time { display: block; font-family: var(--cp-font-mono);
  font-size: 0.64rem; color: var(--cp-text-secondary); }
.week-event-title { display: block; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Aperçu — vue Mois (S11-T08).
   Grille de dates : une colonne « Sem. » + sept jours (lun→dim), une ligne par
   semaine. Server-rendered, aucun JS (ADR-030). Sur petit écran la grille défile
   horizontalement dans son conteneur (overflow-x) — jamais le body (issue #3). */
.month-scroll { overflow-x: auto; border: 1px solid var(--cp-border);
  border-radius: var(--cp-radius-md); background: var(--cp-surface); }
.month-grid { display: grid; grid-template-columns: 44px repeat(7, minmax(96px, 1fr));
  min-width: 720px; }

.month-weekhead { display: flex; align-items: center; justify-content: center;
  padding: var(--cp-space-2); font-size: 0.68rem; text-transform: uppercase;
  letter-spacing: 0.02em; color: var(--cp-text-secondary);
  border-right: 1px solid var(--cp-border); border-bottom: 1px solid var(--cp-border); }
.month-dayhead { padding: var(--cp-space-2); text-align: center; font-weight: 600;
  text-transform: capitalize; font-size: 0.8rem; color: var(--cp-text);
  border-left: 1px solid var(--cp-border); border-bottom: 1px solid var(--cp-border); }

.month-weeknum { display: flex; align-items: center; justify-content: center;
  padding: var(--cp-space-2); font-family: var(--cp-font-mono); font-size: 0.72rem;
  font-weight: 600; color: var(--cp-text-secondary); text-decoration: none;
  border-right: 1px solid var(--cp-border); border-bottom: 1px solid var(--cp-border); }
.month-weeknum:hover { background: var(--cp-surface-hover); color: var(--cp-primary); }

.month-cell { display: flex; flex-direction: column; gap: 2px; min-height: 92px;
  padding: 3px; box-sizing: border-box; border-left: 1px solid var(--cp-border);
  border-bottom: 1px solid var(--cp-border); overflow: hidden; }
.month-cell.is-outside { background: var(--cp-surface-hover);
  color: var(--cp-text-muted); }
.month-cell.is-today { background: var(--cp-primary-subtle); }

.month-daynum { align-self: flex-start; padding: 0 4px; font-family: var(--cp-font-mono);
  font-size: 0.74rem; font-weight: 600; color: var(--cp-text);
  text-decoration: none; border-radius: var(--cp-radius-sm); }
.month-daynum:hover { background: var(--cp-surface-hover); color: var(--cp-primary); }
.month-cell.is-outside .month-daynum { color: var(--cp-text-muted); }
.month-cell.is-today .month-daynum { color: var(--cp-primary); }

.month-event { display: flex; gap: 4px; overflow: hidden; padding: 0 4px;
  background: var(--cp-primary-subtle); border-left: 3px solid var(--cp-primary);
  border-radius: var(--cp-radius-sm); font-size: 0.68rem; line-height: 1.3;
  color: var(--cp-text); }
.month-event-time { flex: 0 0 auto; font-family: var(--cp-font-mono);
  font-size: 0.64rem; color: var(--cp-text-secondary); }
.month-event-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.month-more { align-self: flex-start; padding: 0 4px; font-size: 0.68rem;
  font-weight: 600; color: var(--cp-primary); text-decoration: none; }
.month-more:hover { text-decoration: underline; }

/* Aperçu — vue Année (S11-T09).
   Douze mini-mois en grille responsive : chaque tuile a un en-tête (nom du mois,
   lien → vue Mois) et une mini-grille de dates lun→dim (initiales en tête, une
   ligne par semaine). Un jour à événements porte une pastille (title = nombre) ;
   son numéro est un lien → vue Jour. Server-rendered, aucun JS (ADR-030). Les
   tuiles s'enroulent (auto-fill) et ne débordent jamais du body (issue #3). */
.year-grid { display: grid; gap: var(--cp-space-3);
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }

.year-month { border: 1px solid var(--cp-border); border-radius: var(--cp-radius-md);
  background: var(--cp-surface); overflow: hidden; }
.year-month-head { display: block; padding: var(--cp-space-2);
  font-weight: 600; text-transform: capitalize; font-size: 0.82rem;
  color: var(--cp-text); text-decoration: none; text-align: center;
  border-bottom: 1px solid var(--cp-border); }
.year-month-head:hover { background: var(--cp-surface-hover); color: var(--cp-primary); }

.year-mini { display: grid; grid-template-columns: repeat(7, 1fr);
  padding: var(--cp-space-2); gap: 1px; }
.year-mini-dayhead { text-align: center; font-size: 0.6rem; font-weight: 600;
  text-transform: uppercase; color: var(--cp-text-secondary); padding-bottom: 2px; }

.year-mini-cell { position: relative; display: flex; align-items: center;
  justify-content: center; min-height: 22px; font-family: var(--cp-font-mono);
  font-size: 0.66rem; color: var(--cp-text); }
.year-mini-cell.is-outside { color: var(--cp-text-muted); opacity: 0.5; }
.year-mini-cell.is-today { background: var(--cp-primary-subtle);
  border-radius: var(--cp-radius-sm); }
.year-mini-cell.is-today .year-mini-daynum { color: var(--cp-primary);
  font-weight: 700; }

.year-mini-daynum { display: block; padding: 1px 3px; color: inherit;
  text-decoration: none; border-radius: var(--cp-radius-sm); }
.year-mini-daynum:hover { background: var(--cp-surface-hover); color: var(--cp-primary); }

.year-dot { position: absolute; bottom: 1px; left: 50%; transform: translateX(-50%);
  width: 4px; height: 4px; border-radius: 50%; background: var(--cp-primary); }
/* Aperçu par source (S13-T08) : la pastille de présence reprend le ton de l'issue la
   plus restrictive du jour ; le sens reste porté par title/aria-label (ADR-063). */
.year-dot--masked { background: var(--cp-status-warning); }
.year-dot--hidden { background: var(--cp-status-danger); opacity: .6; }
.year-dot--detail { background: var(--cp-status-neutral); }

/* ===================== APERÇU — MODALE D'ÉVÉNEMENT ======================= */
/* Détails d'un événement au clic (S11-T10). Implémentation CSS pure `:target`
   (comme l'offcanvas `.app-sidebar:target`) : la modale est masquée par défaut et
   s'affiche quand son `#id` devient la cible de l'URL. Aucun JavaScript (ADR-030).
   La carte défile en interne (max-height + overflow) — jamais le body (issue #3).
   Le focus n'est pas piégé : limite assumée d'une modale sans JS. */
.event-modal { position: fixed; inset: 0; z-index: 1050; display: none; }
.event-modal:target { display: flex; align-items: center; justify-content: center;
  padding: var(--cp-space-4); }
.event-modal-overlay { position: absolute; inset: 0;
  background: color-mix(in srgb, var(--cp-text) 45%, transparent); }
.event-modal-card { position: relative; z-index: 1; background: var(--cp-surface);
  border-radius: var(--cp-radius-lg); width: 100%; max-width: 32rem; max-height: 85vh;
  overflow-y: auto; padding: var(--cp-space-5);
  box-shadow: 0 12px 40px color-mix(in srgb, var(--cp-text) 22%, transparent); }
.event-modal-close { position: absolute; top: var(--cp-space-3); right: var(--cp-space-3);
  font-size: 1.4rem; line-height: 1; color: var(--cp-text-secondary);
  text-decoration: none; }
.event-modal-close:hover { color: var(--cp-text); }
.event-modal-title { margin: 0 var(--cp-space-5) var(--cp-space-3) 0;
  font-size: 1.15rem; font-weight: 600; color: var(--cp-text); }
.event-modal-meta { margin: 0 0 var(--cp-space-2); color: var(--cp-text-secondary); }
.event-modal-time { font-family: var(--cp-font-mono); }
.event-modal-location, .event-modal-description { margin: 0 0 var(--cp-space-2);
  color: var(--cp-text); }
/* Contenu de description assaini (HTML de client mail rendu en lecture seule). */
.event-modal-description, .event-modal-original-description { overflow-wrap: anywhere; }
.event-modal-description :where(p, ul, ol, blockquote, table),
.event-modal-original-description :where(p, ul, ol, blockquote, table) { margin: 0 0 var(--cp-space-2); }
.event-modal-description :where(p, ul, ol, blockquote, table):last-child,
.event-modal-original-description :where(p, ul, ol, blockquote, table):last-child { margin-bottom: 0; }
.event-modal-description a, .event-modal-original-description a { color: var(--cp-primary); }
.event-modal-description table { border-collapse: collapse; max-width: 100%; }
.event-modal-description :where(td, th) { padding: 2px 6px; vertical-align: top; text-align: left; }
.event-modal-description blockquote, .event-modal-original-description blockquote {
  padding-left: var(--cp-space-3); border-left: 2px solid var(--cp-border); }
.event-modal-private { margin-top: var(--cp-space-4);
  background: var(--cp-surface-sunken); border: 1px solid var(--cp-border);
  border-radius: var(--cp-radius-md); padding: var(--cp-space-3);
  color: var(--cp-text-secondary); font-size: 0.85rem; }
.event-modal-private-line { margin: 0 0 var(--cp-space-1); }
.event-modal-private p:last-child { margin-bottom: 0; }
.event-modal-note { color: var(--cp-text-muted); font-size: 0.78rem; }

/* Ligne « Issue : … » de la modale (S13-T08) : rappel de la décision de filtrage
   appliquée à l'occurrence sur l'aperçu par source (rien sur l'aperçu calendrier). */
.event-modal-outcome { display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--cp-space-2); margin: 0 0 var(--cp-space-3); color: var(--cp-text); }
.event-modal-outcome-note { color: var(--cp-text-muted); font-size: 0.82rem; }

/* Légende des trois issues, en tête de la section d'aperçu de la page source. */
.preview-legend { list-style: none; margin: 0 0 var(--cp-space-3); padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--cp-space-2) var(--cp-space-4); }
.preview-legend li { display: inline-flex; align-items: center; gap: var(--cp-space-2); }
.preview-legend-text { color: var(--cp-text-secondary); font-size: 0.85rem; }

/* Événements cliquables : les anciens `div`/`span`/`li` deviennent des `<a>`
   ancrant leur modale. On neutralise le style de lien par défaut pour préserver
   l'apparence des grilles (le positionnement absolu et les styles inline restent
   portés par ces mêmes classes). */
.preview-list-row, .day-event, .week-event, .month-event,
.day-allday-item, .week-allday-item {
  text-decoration: none; color: inherit; cursor: pointer; }
.preview-list-item { padding: 0; }
.preview-list-row { padding: var(--cp-space-2) var(--cp-space-3); }
.preview-list-row:hover { background: var(--cp-surface-hover); }
/* Les pastilles colorées conservent leur teinte : on signale le survol par une
   nuance plus soutenue de leur propre couleur, sans virer au gris. */
.day-event:hover, .week-event:hover, .month-event:hover,
.day-allday-item:hover, .week-allday-item:hover {
  background: color-mix(in srgb, var(--cp-primary) 22%, var(--cp-surface)); }

/* Password strength meter (EPIC-069) — progressive enhancement (ADR-030).
   The textual label carries the strength (not colour alone, ADR-063/WCAG); the
   coloured bar is a secondary, decorative cue. */
.password-strength { margin-top: var(--cp-space-2, 0.5rem); }
.password-strength__track {
  height: 6px;
  border-radius: var(--cp-radius-sm, 4px);
  background: var(--cp-border);
  overflow: hidden; }
.password-strength__fill {
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--cp-status-danger);
  transition: width .2s ease, background-color .2s ease; }
.password-strength__fill[data-level="1"] { background: var(--cp-status-danger); }
.password-strength__fill[data-level="2"] { background: var(--cp-status-warning); }
.password-strength__fill[data-level="3"] { background: var(--cp-status-success); }
.password-strength__fill[data-level="4"] { background: var(--cp-status-success); }
.password-strength__label { margin: var(--cp-space-1, 0.25rem) 0 0; font-weight: 600; }
.password-strength__hints { margin: var(--cp-space-1, 0.25rem) 0 0; padding-left: 1.1rem; }
.password-strength__hints li { margin: 0; }

/* Show / hide password toggle (EPIC-070) — the eye button is added by JS and sits
   at the right of the field (progressive enhancement, ADR-030). */
.password-reveal { position: relative; }
.password-reveal > input { padding-right: 2.75rem; }
.password-reveal__toggle {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  display: inline-flex;
  align-items: center;
  padding: 0 0.75rem;
  border: 0;
  background: transparent;
  color: var(--cp-text-secondary);
  cursor: pointer; }
.password-reveal__toggle:hover { color: var(--cp-text, inherit); }
.password-reveal__toggle:focus-visible { outline: 2px solid var(--cp-primary); outline-offset: -2px; }

/* ============================ PAGE PUBLIQUE (S13-T09, EPIC-059) ============================
   Vue HTML anonyme d'un calendrier agrégé, intégrable en iframe. Autonome : aucun
   chrome propriétaire (base.html.twig est un squelette nu, réutilisé tel quel). Sobre,
   centré, largeur plafonnée pour rester lisible dans une iframe étroite ; les grilles
   de l'aperçu gèrent leur propre défilement interne (issue #3), le corps ne défile
   jamais horizontalement. Classes additives `.public-*`, sans impact sur l'appli. */
.public-shell {
  max-width: 960px; margin: 0 auto; min-height: 100vh;
  padding: var(--cp-space-4);
  display: flex; flex-direction: column;
  overflow-x: hidden;
}
/* Pas de défilement horizontal du corps quand la page est encadrée. */
body:has(.public-shell) { overflow-x: hidden; }

/* En-tête sobre : réutilise `.entity-head` (carte à coins hauts arrondis) qui se
   raccorde visuellement au sélecteur de paliers `.tabs` rendu juste dessous. */
.public-head { border-radius: var(--cp-radius-md) var(--cp-radius-md) 0 0; }
.public-head h1 { margin: 0; }
.public-head-desc { margin: var(--cp-space-2) 0 0; }

/* Le bloc aperçu occupe l'espace disponible ; le pied reste discret, poussé en bas. */
.public-preview { flex: 0 0 auto; min-width: 0; }
.public-foot {
  margin-top: auto; padding: var(--cp-space-4) 0 var(--cp-space-1);
  text-align: center; color: var(--cp-text-muted); font-size: 0.78rem;
}
.public-foot p { margin: 0; }

/* Iframe étroite / mobile : on resserre les marges et le titre. */
@media (max-width: 480px) {
  .public-shell { padding: var(--cp-space-2); }
  .public-head { padding: var(--cp-space-3); }
  .public-head h1 { font-size: 1.05rem; }
}

/* Theme switcher (EPIC-072, F4-T02) — compact width in the topbar. */
.theme-toggle select { width: auto; min-width: 5.5rem; }
