/* ════════════════════════════════════════════════════════════════════
   index-main.css — main application stylesheet
   Extracted from index.html inline <style> block (Phase 1.VVVVVV).
   ~10 840 lines of CSS previously shipped inline in the HTML payload.
   Loading via <link> means the browser can cache + compress it
   independently and the HTML response is dramatically lighter.
   ════════════════════════════════════════════════════════════════════ */

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

  /* Design tokens (--bg, --orchid, --r-md, etc.) now live in
     public/styles/tokens.css, linked above in <head>. Single source of
     truth — see that file for the full palette + spacing + typography. */

  html, body {
    height: 100%;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    background: var(--bg);
    color: var(--fg);
    font-size: 13px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    letter-spacing: -0.005em;
  }
  body {
    /* Removed dotted grid for a cleaner, lighter Stack look */
    background-image: none;
    overflow: hidden;
  }

  /* ═══════════ TOP BAR with TABS ═══════════ */
  .topbar {
    height: 64px;
    background: var(--bg);
    border-bottom: 1px solid var(--line-soft);
    display: flex;
    align-items: stretch;
    padding: 0 2rem;
    position: sticky;
    top: 0;
    z-index: 60;
  }
  /* Badge « bêta » — centré horizontalement ET verticalement dans la topbar
     (la topbar est position:sticky → contexte de positionnement). Décoratif,
     ne capte pas les clics. Masqué sur petit écran (topbar à l'étroit). */
  .topbar-beta {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--ink-56, #231f2391);
    background: var(--bg);
    border: 1px solid var(--line-soft);
    border-radius: 999px;
    padding: 3px 11px;
    pointer-events: none;
    white-space: nowrap;
    z-index: 1;
  }
  @media (max-width: 760px) {
    .topbar-beta { display: none; }
  }
  .topbar-brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding-right: 1.5rem;
    margin-right: 1.5rem;
  }
  .topbar-brand .mark {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1.4rem;
    line-height: 1;
    letter-spacing: -0.02em;
  }
  .topbar-brand .ey {
    font-size: 0.62rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    font-weight: 400;
  }
  /* ROAS Club logo — replaces the legacy "Ads · Architect / unified"
     wordmark. Pure type-set in red Didone-style serif: ROAS upright +
     Club italic. Inherits font fallbacks (Didot → Bodoni → Georgia)
     so it renders cleanly without shipping a custom font file. */
  .rc-logo {
    font-family: 'Didot', 'Bodoni Moda', 'Playfair Display', 'GFS Didot',
      Georgia, 'Times New Roman', serif;
    font-weight: 700;
    color: #E11A1B;
    letter-spacing: -0.015em;
    line-height: 1;
    white-space: nowrap;
  }
  .rc-logo em {
    font-style: italic;
    font-weight: 700;
  }
  .topbar-brand .rc-logo {
    font-size: 3.5rem;
  }
  /* Sidebar logo : reste sur UNE seule ligne dans la colonne 96 px
     (encart légèrement débordant via padding négatif horizontal pour
     gagner ~14 px de plus sans toucher à la largeur du sidebar). Le
     letter-spacing serré + font compacte permettent à "ROAS Club" de
     tenir à ~13.5 px sans déformer le rendu. */
  .sidebar-brand {
    /* Centré dans la colonne 96px : la marge latérale du logo = (96 - largeur)/2,
       donc on ne joue PAS sur un margin négatif (qui le collait au bord) — on
       laisse le logo centré et un poil plus petit (cf. height inline). */
    padding: 18px 8px 14px;
    display: flex;
    justify-content: center;
  }
  .sidebar-brand .rc-logo {
    font-size: 0.85rem;
    text-align: center;
    overflow: visible;
  }

  /* Logo image (remplace le wordmark texte « ROAS Club ») — ratio ~5:1.
     Dimensionné par contexte ; max-width = garde-fou anti-débordement. */
  .rc-logo img { display: block; width: auto; max-width: 100%; }
  .topbar-brand  .rc-logo img { height: 36px; }
  .sidebar-brand .rc-logo img { height: 18px; margin-inline: auto; }

  /* Logo d'agence optionnel — dans la topbar, à gauche du sélecteur projet
     (à droite du logo ROAS Club du rail). PNG fond transparent, jamais
     déformé. Quand aucun logo n'est défini, l'<img> est display:none → rien
     affiché, aucun espace réservé. */
  .agency-logo {
    /* Plafonné à un poil SOUS le logo ROAS Club du rail (~80×14px) pour qu'il
       ne le domine jamais : boîte ≤ 78×13px, object-fit:contain garde le ratio. */
    height: 13px;
    width: auto;
    max-width: 78px;
    object-fit: contain;
    display: block;
    flex-shrink: 0;
    align-self: center;
    margin-right: 0.5rem;
  }

  /* Copywriting Bank — bouton ✨ « régénérer (IA) » révélé au survol d'une case. */
  .cb-cell-regen {
    position: absolute;
    top: 3px;
    right: 3px;
    border: none;
    background: var(--surface, #fff);
    color: var(--ink-48, #231f237a);
    cursor: pointer;
    font-size: 11px;
    line-height: 1;
    padding: 1px 3px;
    border-radius: 4px;
    opacity: 0;
    transition: opacity 0.12s;
    box-shadow: 0 0 0 1px var(--ink-16, #231f2329);
  }
  .cb-cell:hover .cb-cell-regen { opacity: 0.8; }
  .cb-cell-regen:hover { opacity: 1; }

  .tabs {
    display: flex;
    align-items: stretch;
    gap: 0.25rem;
  }
  .tab {
    font-family: inherit;
    background: transparent;
    border: none;
    padding: 0 1rem;
    font-size: 0.82rem;
    letter-spacing: -0.005em;
    font-weight: 500;
    color: var(--muted);
    cursor: pointer;
    position: relative;
    transition: color 0.15s;
    display: flex;
    align-items: center;
    gap: 0.45rem;
  }
  .tab:hover { color: var(--fg); }
  .tab.active { color: var(--fg); }
  /* Disabled = "bientôt disponible" — visually muted, no hover effect, no click */
  .tab:disabled,
  .tab[aria-disabled="true"] {
    opacity: 0.4;
    cursor: not-allowed;
    color: var(--faint, var(--muted));
  }
  .tab:disabled:hover,
  .tab[aria-disabled="true"]:hover {
    color: var(--faint, var(--muted));
    background: transparent;
  }
  .tab.active::after {
    content: '';
    position: absolute;
    bottom: -1px;
    left: 0.85rem;
    right: 0.85rem;
    height: 2px;
    background: var(--fg);
    border-radius: 2px 2px 0 0;
  }
  .tab .tab-count {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1rem;
    color: var(--muted);
    letter-spacing: 0;
  }
  .tab.active .tab-count { color: var(--fg); }

  /* External link tabs — same shape as internal tabs but visually
     marked: dashed underline (instead of the solid one on hover/active),
     small ↗ arrow, slight muted text. They never get the .active state
     since clicking them navigates the parent window away. */
  .tab.tab-ext {
    text-decoration: none;
    border-bottom: 1px dashed transparent;
    cursor: pointer;
  }
  .tab.tab-ext:hover {
    color: var(--p-test);
    border-bottom-color: var(--p-test);
  }
  .tab.tab-ext .tab-ext-arrow {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.7rem;
    color: var(--muted);
    margin-left: 0.2rem;
    transition: transform 0.15s, color 0.15s;
  }
  .tab.tab-ext:hover .tab-ext-arrow {
    color: var(--p-test);
    transform: translate(1px, -1px);
  }

  .topbar-right {
    /* Sits between the utility icons (order:2, which carry the right anchor)
       and the profile chip (order:4). No own margin-left:auto — the icons
       already anchor the whole right cluster. */
    order: 3;
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }
  .tb-btn {
    background: var(--surface);
    border: 1px solid var(--line);
    color: var(--fg);
    font-family: inherit;
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: -0.005em;
    padding: 0.5rem 0.85rem;
    cursor: pointer;
    transition: all 0.12s;
    border-radius: var(--r-md);
  }
  .tb-btn:hover:not(:disabled) {
    background: var(--bg);
    border-color: var(--faint);
  }
  .tb-btn:disabled { opacity: 0.35; cursor: not-allowed; }
  /* Auth pill — Phase 3.A.3. Lives in .topbar-tools (visible) alongside
     the Reset button. data-state drives the visual :
     · anon     = "Se connecter" CTA, pastel orchid background
     · authed   = logged-in user, pastel mint + dot indicator
     · disabled = Supabase env vars missing, muted */
  .tb-btn-auth {
    flex-shrink: 0;
    white-space: nowrap;
  }
  .tb-btn-auth[data-state="anon"] {
    background: var(--orchid-soft, #E8E0FC);
    border-color: var(--orchid, #CEBFFA);
    color: var(--p-test, #5A4ECC);
  }
  .tb-btn-auth[data-state="anon"]:hover:not(:disabled) {
    background: var(--orchid, #CEBFFA);
    color: var(--surface);
  }
  .tb-btn-auth[data-state="authed"] {
    background: var(--surface, #fff);
    border-color: rgba(0, 0, 0, 0.12);
    color: var(--fg, #231F23);
    /* Authed = juste la photo (le label/nom est vidé en JS). Padding
       symétrique pour que l'avatar soit centré dans une pastille ronde. */
    padding-left: 4px;
    padding-right: 4px;
    gap: 8px;
  }
  /* Plan badge (Pro / Agency) — pill coloré à gauche du nom dans le chip
     auth, masqué sur Free pour ne pas polluer visuellement. Couleur de
     fond passée inline via PLAN_META.accent (mint pour Pro, orchid pour
     Agency). */
  .auth-plan-badge {
    display: inline-block;
    padding: 2px 7px;
    border-radius: 10px;
    background: #2A6B30;
    color: #fff;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 9px;
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    line-height: 1.4;
    vertical-align: middle;
    margin-right: 6px;
  }
  .auth-name {
    vertical-align: middle;
  }
  .tb-btn-auth[data-state="authed"]:hover {
    background: var(--depth, #ECECEC);
    border-color: rgba(0, 0, 0, 0.2);
  }
  .tb-btn-auth[data-state="disabled"] {
    background: var(--depth, #ECECEC);
    color: var(--muted);
    cursor: default;
  }
  /* Avatar slot — round 22px chip on the right side of the auth button.
     - anon state : just shows the user-circle SVG centered, no bg.
     - authed state : either an <img> filling the disc, or initials
       centered on a gradient background.
     - disabled state : muted SVG. */
  .auth-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    flex-shrink: 0;
    overflow: hidden;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    font-weight: 600;
    font-size: 10px;
    letter-spacing: 0.02em;
    color: var(--fg, #231F23);
    line-height: 1;
    text-transform: uppercase;
  }
  .auth-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .tb-btn-auth[data-state="authed"] .auth-avatar {
    background: linear-gradient(135deg, var(--mint, #B6DAA8), var(--orchid, #CEBFFA));
    color: var(--fg, #231F23);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08) inset;
  }
  /* ─── Profile dropdown menu (Modifier nom / photo / Paramètres / Déconnexion)
     Anchored to .tb-btn-auth ; positioned via JS using getBoundingClientRect.
     Hidden by default ; .open class toggles visibility. */
  .profile-menu {
    position: fixed;
    z-index: 100;
    min-width: 220px;
    background: var(--surface, #fff);
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    padding: 6px 0;
    display: none;
    font-family: inherit;
  }
  .profile-menu.open { display: block; }
  .profile-menu .pm-header {
    padding: 10px 14px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
    margin-bottom: 4px;
  }
  .profile-menu .pm-header-name {
    font-size: 13px;
    font-weight: 500;
    color: var(--fg, #231F23);
    line-height: 1.2;
  }
  .profile-menu .pm-header-email {
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 10px;
    color: var(--muted, #231F2399);
    margin-top: 3px;
    word-break: break-all;
  }
  .profile-menu button {
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    padding: 8px 14px;
    font-family: inherit;
    font-size: 12px;
    color: var(--fg, #231F23);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .profile-menu button:hover {
    background: var(--depth, #ECECEC);
  }
  .profile-menu button.pm-danger {
    color: #c53d2c;
  }
  .profile-menu button.pm-danger:hover {
    background: #fdecea;
  }
  .profile-menu hr {
    border: none;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
    margin: 4px 0;
  }
  /* Menu connecteur (topbar) : items « bientôt » désactivés + tag. */
  .tb-conn-menu button:disabled,
  .tb-conn-menu button.tb-conn-soon-item {
    opacity: 0.5;
    cursor: default;
  }
  .tb-conn-menu button.tb-conn-soon-item:hover { background: transparent; }
  /* Rappel du projet actif dans l'en-tête du menu connecteur. */
  .tb-conn-menu .tb-conn-proj {
    font-size: 10px;
    color: var(--muted, #231F2399);
    margin-top: 5px;
    line-height: 1.3;
  }
  .tb-conn-menu .tb-conn-proj strong {
    color: var(--fg, #231F23);
    font-weight: 600;
    word-break: break-word;
  }
  .tb-conn-menu .tb-conn-soon {
    font-size: 8.5px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    font-weight: 500;
    color: var(--muted, #231F2399);
    background: var(--depth, #ECECEC);
    padding: 2px 6px;
    border-radius: 999px;
    flex: 0 0 auto;
  }
  .tb-btn.primary {
    background: var(--fg);
    color: var(--surface);
    border-color: var(--fg);
    font-weight: 500;
    padding: 0.55rem 1rem;
    box-shadow: 0 2px 6px rgba(35, 31, 35, 0.12);
    transition: background 0.15s, border-color 0.15s, transform 0.15s, box-shadow 0.15s;
  }
  .tb-btn.primary:hover {
    background: var(--p-cold);
    border-color: var(--p-cold);
    transform: translateY(-1px);
    box-shadow: 0 4px 10px rgba(42, 107, 48, 0.18);
  }
  /* Pulse the + Test button when there are zero tests yet — visual
     hint that this is the next thing to do. The class is toggled
     in renderAll() based on state.tests.length. */
  .tb-btn.primary.empty-pulse {
    animation: tbPulse 2.4s ease-in-out infinite;
  }
  @keyframes tbPulse {
    0%, 100% { box-shadow: 0 2px 6px rgba(35, 31, 35, 0.12), 0 0 0 0 rgba(42, 107, 48, 0.5); }
    50%      { box-shadow: 0 2px 6px rgba(35, 31, 35, 0.12), 0 0 0 8px rgba(42, 107, 48, 0); }
  }

  /* ═══════════ APP LAYOUT ═══════════ */
  .app {
    display: flex;
    flex-direction: column;
    height: 100vh;
  }
  .main-area {
    flex: 1;
    overflow: hidden;
    position: relative;
  }
  /* Footer global © — slim, en bas de l'app, visible sur TOUTES les pages
     (enfant flex de .app → réduit légèrement .main-area sans chevaucher le
     contenu ; texte centré, ne touche pas le rail-todos à droite). */
  .app-footer {
    flex: 0 0 auto;
    padding: 7px 16px;
    text-align: center;
    font-family: var(--ns-font-mono, ui-monospace, monospace);
    font-size: 10.5px;
    letter-spacing: 0.04em;
    color: var(--ink-48, #231f237a);
    background: var(--bg);
  }
  .view {
    display: none;
    height: 100%;
  }
  .view.active { display: grid; }
  /* Override for non-grid views (full-width content with their own layout) */
  .view.view-create.active,
  .view.view-bulk.active,
  .view.view-profil.active { display: block; }

  /* Setup view layout: a full-width header bar on top
     ("Campaign architecture" + stats) spans both columns, then a
     sidebar + canvas row below — the canvas is the focal point so
     the sidebar stays narrow. */
  .view-setup {
    grid-template-columns: minmax(280px, 22%) 1fr;
    grid-template-rows: auto 1fr;
  }
  .view-setup-head {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
    padding: 0.75rem 1.75rem;
    border-bottom: 1px solid var(--line-soft);
    background: var(--bg);
    flex-wrap: wrap;
  }
  .view-setup-head-title { min-width: 0; }
  .view-setup-head h2 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.4rem;
    letter-spacing: -0.015em;
    line-height: 1;
    margin: 0;
  }
  .view-setup-head h2 em { font-style: italic; color: var(--muted); }
  .view-setup-head .sub {
    font-size: 0.58rem;
    letter-spacing: 0;
    color: var(--muted);
    margin-top: 0.35rem;
  }
  /* Stats inside the head bar — display inline (no top border, no
     own padding) so they read as a single right-aligned strip
     across from the title. */
  .view-setup-head .stats {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    padding: 0;
    margin: 0;
    border: none;
    flex-shrink: 0;
  }
  .view-setup-head .stat {
    padding: 0;
    border: none;
    text-align: right;
    min-width: 64px;
  }
  /* Tests view layout — same shape as Setup: a full-width head bar
     ("Experiment timeline" + stats), then a sidebar + canvas row. */
  .view-tests {
    grid-template-columns: minmax(280px, 22%) 1fr;
    grid-template-rows: auto 1fr;
  }
  .view-tests-head {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
    padding: 0.75rem 1.75rem;
    border-bottom: 1px solid var(--line-soft);
    background: var(--bg);
    flex-wrap: wrap;
  }
  .view-tests-head-title { min-width: 0; }
  .view-tests-head h2 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.4rem;
    letter-spacing: -0.015em;
    line-height: 1;
    margin: 0;
  }
  .view-tests-head h2 em { font-style: italic; color: var(--muted); }
  .view-tests-head .sub {
    font-size: 0.58rem;
    letter-spacing: 0;
    color: var(--muted);
    margin-top: 0.35rem;
  }
  .view-tests-head .stats {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    padding: 0;
    margin: 0;
    border: none;
    flex-shrink: 0;
  }
  .view-tests-head .stat {
    padding: 0;
    border: none;
    text-align: right;
    min-width: 64px;
  }

  .pane-side {
    background: var(--bg);
    border-right: 1px solid var(--line-soft);
    overflow-y: auto;
    padding: 0 0 4rem;
    position: relative;
  }
  .pane-main {
    overflow: hidden;
    display: flex;
    flex-direction: column;
    position: relative;
    background: var(--bg);
  }

  /* Sidebar header harmonisé : un titre principal + une mini
     description en dessous, packés en haut. Style aligné sur
     React (Ads Economics .ae-head, Inference .ce-head, Matrix
     .matrix-head). h1 prend la taille équivalente à un h2 (1.5rem)
     pour être bien visible sans dominer la sidebar. */
  .pane-head {
    padding: 1rem 2.75rem 0 1rem;
    margin-bottom: 1rem;
  }
  .pane-head .eyebrow {
    /* Eyebrow conservé en CSS pour compat mais NON UTILISÉ dans
       le markup à partir de cette session — chaque sidebar a
       uniquement title + sub maintenant. */
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    margin-bottom: 0.3rem;
    font-weight: 500;
  }
  .pane-head h1 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.5rem;
    letter-spacing: -0.02em;
    line-height: 1.1;
    margin: 0;
  }
  .pane-head h1 em {
    font-style: italic;
    color: var(--muted);
    font-weight: 400;
  }
  .pane-head .pane-sub {
    font-size: 0.78rem;
    color: var(--muted);
    line-height: 1.4;
    margin-top: 0.5rem;
    padding-right: 0.5rem;
  }

  /* ═══════════ PROJECT VIEW: hub showing all projects + activity feed ═══════════
     Layout A — GitHub-like: collapsable sidebar of projects + main detail
     pane for the active project. Reuses .pane-side / .pane-main shells from
     the Structure / Tests views so the visual rhythm stays consistent. */
  .view-project {
    /* Harmonisation : même plancher (280px) et même transition de repli
       que les autres sidebars d'outils (Setup/Tests/Matrice/Bulk). */
    grid-template-columns: minmax(280px, 22%) 1fr;
    grid-template-rows: 1fr;
    transition: grid-template-columns 0.25s ease;
  }
  /* When the user collapses the sidebar via the same chevron as Structure,
     the column shrinks to a thin rail (44px = enough to show the toggle
     button) — the canvas takes the rest. The shared `.pane-side.collapsed`
     rules in the topbar CSS handle fading out the inner content without
     removing the toggle, so the user can re-expand. */
  .view-project.sidebar-collapsed {
    grid-template-columns: 44px 1fr !important;
  }

  .project-sidebar {
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  .project-sidebar-toolbar {
    padding: 0.5rem 1rem 0.4rem;
    display: flex;
    gap: 0.4rem;
    flex-shrink: 0;
  }
  .project-sidebar-add {
    flex: 1;
    font-family: inherit;
    font-size: 0.7rem;
    padding: 0.5rem 0.7rem;
    background: transparent;
    border: 1.5px dashed var(--line);
    border-radius: var(--r-sm);
    color: var(--muted);
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
    text-align: left;
  }
  .project-sidebar-add:hover {
    border-color: var(--p-cold);
    color: var(--fg);
    background: var(--mint-soft);
  }
  #project-sidebar-body {
    flex: 1;
    overflow-y: auto;
    padding: 0.4rem 0.6rem 1rem;
  }
  /* Each row in the sidebar = one project. Active project gets a phase-
     coloured left stripe (same convention as the Structure phase-toggles)
     and a bolder background so the user always knows which project the
     detail pane is showing. */
  .project-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.55rem 0.6rem;
    margin-bottom: 4px;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-left: 3px solid var(--line);
    border-radius: var(--r-sm);
    cursor: pointer;
    user-select: none;
    transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
  }
  .project-row:hover {
    background: var(--bg);
    border-color: var(--line);
    border-left-color: var(--fg);
    box-shadow: 0 1px 4px rgba(35, 31, 35, 0.06);
  }
  .project-row.is-active {
    background: var(--bg);
    border-color: var(--fg);
    border-left-color: var(--p-cold);
    border-left-width: 4px;
  }
  .project-row-icon {
    font-size: 0.8rem;
    line-height: 1;
    color: var(--muted);
    flex-shrink: 0;
  }
  .project-row.is-active .project-row-icon { color: var(--p-cold); }
  .project-row-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
  }
  .project-row-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.78rem;
    color: var(--fg);
    line-height: 1.1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .project-row-meta {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted);
    margin-top: 2px;
    line-height: 1.2;
  }
  .project-row-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--p-cold);
    flex-shrink: 0;
  }
  .project-row.is-stale .project-row-dot { background: var(--line); }

  /* ═══ Project detail pane (right side) ═══ */
  .project-detail-scroll {
    overflow-y: auto;
    padding: 1.2rem 1.75rem 2rem;
    height: 100%;
    background: var(--bg);
  }
  .project-detail {
    max-width: 920px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 1.4rem;
  }
  .pdetail-header {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--line-soft);
    position: relative; /* Phase 3.B.5 — anchor for the interval selector */
  }
  /* Phase 3.B.5 — time interval selector top-right of project hub */
  .pdetail-interval {
    position: absolute;
    top: 0;
    right: 0;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.7rem;
    color: var(--muted);
  }
  .pdetail-interval-label { letter-spacing: 0.04em; }
  .pdetail-interval-select {
    font: inherit;
    padding: 4px 8px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    cursor: pointer;
  }
  .pdetail-interval-select:hover { border-color: var(--p-cold); }
  .pdetail-eyebrow {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .pdetail-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.85rem;
    line-height: 1;
    margin: 0;
    color: var(--fg);
    letter-spacing: -0.015em;
  }
  .pdetail-name em { font-style: italic; color: var(--p-test); }
  .pdetail-name-input {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.85rem;
    line-height: 1;
    color: var(--fg);
    letter-spacing: -0.015em;
    background: transparent;
    border: 1px solid var(--p-cold);
    border-radius: var(--r-sm);
    padding: 0.2rem 0.5rem;
    outline: none;
    width: 100%;
    max-width: 540px;
  }
  .pdetail-sub {
    font-size: 0.7rem;
    color: var(--muted);
    line-height: 1.5;
  }
  .pdetail-actions {
    display: flex;
    gap: 0.4rem;
    margin-top: 0.5rem;
    flex-wrap: wrap;
  }
  .pdetail-action {
    font-family: inherit;
    font-size: 0.7rem;
    padding: 0.4rem 0.75rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    color: var(--fg);
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
  .pdetail-action:hover {
    background: var(--bg);
    border-color: var(--fg);
  }
  .pdetail-action.danger { color: var(--s-lost); border-color: var(--coral-soft); }
  .pdetail-action.danger:hover { background: var(--coral-soft); border-color: var(--s-lost); }
  /* Zone danger en bas de la vue projet — sépare visuellement le bouton
     « Supprimer ce projet » du reste du contenu (déplacé depuis le haut). */
  .pdetail-danger-zone {
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--line-soft);
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
    flex-wrap: wrap;
  }

  /* Stats strip (5 mini stats — Phase 3.B.5 added ROAS) */
  .pdetail-stats {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 0.6rem;
  }
  /* Phase 3.B.5 — ROAS color-coding (green > 1, red < 1, muted if no data) */
  .pdetail-stat-num.profit { color: #2A6B30; }
  .pdetail-stat-num.loss { color: #b04040; }
  .pdetail-stat {
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-md);
    padding: 0.85rem 0.95rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
  }
  .pdetail-stat-num {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-size: 1.6rem;
    font-weight: 400;
    line-height: 1;
    color: var(--fg);
    font-feature-settings: 'tnum';
  }
  .pdetail-stat-num.dim { color: var(--muted); }
  .pdetail-stat-label {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
  }

  /* Mini-preview cards: Structure + Tests */
  .pdetail-cards {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.8rem;
  }
  /* When only one card is rendered (e.g. just Tests Results since
     Structure was promoted to the full-width tree), constrain it to
     half-width so it doesn't look stretched. */
  .pdetail-cards:has(> :only-child) {
    grid-template-columns: 1fr;
    max-width: 540px;
  }
  .pdetail-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: 1rem 1.1rem;
    cursor: pointer;
    transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
  }
  .pdetail-card:hover {
    border-color: var(--fg);
    box-shadow: 0 4px 12px rgba(35, 31, 35, 0.08);
    transform: translateY(-1px);
  }
  .pdc-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.4rem;
  }
  .pdc-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 1.05rem;
    color: var(--fg);
  }
  .pdc-cta {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .pdetail-card:hover .pdc-cta { color: var(--p-cold); }
  .pdc-rows {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
  }
  .pdc-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    font-size: 0.72rem;
    color: var(--fg);
  }
  .pdc-swatch {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
  }
  .pdc-row-label { flex: 1; min-width: 0; }
  .pdc-row-meta {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    color: var(--muted);
    letter-spacing: 0.04em;
  }
  /* Visual bar showing test outcomes (won/lost/incon/live) as proportions */
  .pdc-results-bar {
    display: flex;
    height: 6px;
    border-radius: 3px;
    overflow: hidden;
    background: var(--depth);
  }
  .pdc-results-bar > span { display: block; height: 100%; }
  .pdc-results-bar .seg-won  { background: var(--s-won); }
  .pdc-results-bar .seg-lost { background: var(--s-lost); }
  .pdc-results-bar .seg-incon{ background: var(--s-incon); }
  .pdc-results-bar .seg-live { background: var(--s-live); }
  .pdc-results-bar .seg-plan { background: var(--p-test); }
  .pdc-empty {
    font-size: 0.66rem;
    color: var(--muted);
    font-style: italic;
    line-height: 1.5;
  }

  /* Activity feed */
  .pdetail-activity {
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-md);
    padding: 1rem 1.1rem 0.6rem;
  }
  .pdetail-activity-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
    padding-bottom: 0.7rem;
    border-bottom: 1px solid var(--line-soft);
    margin-bottom: 0.55rem;
  }
  .pdetail-activity-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 1.05rem;
    color: var(--fg);
  }
  .pdetail-activity-count {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .activity-list {
    display: flex;
    flex-direction: column;
  }
  .activity-day-head {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    padding: 0.6rem 0 0.3rem;
    border-bottom: 1px dashed var(--line-soft);
    margin-bottom: 0.4rem;
  }
  .activity-day-head:first-child { padding-top: 0; }
  .activity-item {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.45rem 0;
  }
  .activity-icon {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.65rem;
    line-height: 1;
    background: var(--bg);
    border: 1px solid var(--line);
  }
  .activity-icon.k-camp-add    { background: var(--mint-soft); border-color: var(--p-cold); color: var(--p-cold); }
  .activity-icon.k-camp-remove { background: var(--coral-soft); border-color: var(--s-lost); color: var(--s-lost); }
  .activity-icon.k-net-add     { background: var(--sky-soft); border-color: var(--s-live); color: var(--s-live); }
  .activity-icon.k-net-remove  { background: var(--depth); border-color: var(--line); color: var(--muted); }
  .activity-icon.k-test-create { background: var(--orchid-soft); border-color: var(--p-test); color: var(--p-test); }
  .activity-icon.k-test-status { background: var(--sand-soft); border-color: var(--p-ret); color: var(--p-ret); }
  .activity-icon.k-test-won    { background: var(--mint-soft); border-color: var(--s-won); color: var(--s-won); }
  .activity-icon.k-test-lost   { background: var(--coral-soft); border-color: var(--s-lost); color: var(--s-lost); }
  .activity-icon.k-test-incon  { background: var(--sand-soft); border-color: var(--s-incon); color: var(--s-incon); }
  .activity-icon.k-test-live   { background: var(--sky-soft); border-color: var(--s-live); color: var(--s-live); }
  .activity-icon.k-project     { background: var(--bg); border-color: var(--fg); color: var(--fg); }
  .activity-icon.k-action      { background: var(--sand-soft); border-color: var(--p-ret); color: var(--p-ret); }
  .activity-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
  }
  .activity-summary {
    font-size: 0.72rem;
    color: var(--fg);
    line-height: 1.4;
  }
  .activity-summary .a-author { font-weight: 500; }
  .activity-summary .a-target { color: var(--p-cold); }
  .activity-summary code {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.65rem;
    color: var(--p-cold);
    background: var(--mint-soft);
    padding: 1px 4px;
    border-radius: 3px;
  }
  .activity-time {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    margin-top: 1px;
  }
  .activity-empty {
    padding: 1.2rem 0.4rem;
    text-align: center;
    color: var(--muted);
    font-size: 0.7rem;
    font-style: italic;
  }

  /* Project view empty state — shown when there's no active project at all
     (extremely rare since initProjectsAtBoot creates one). */
  .pdetail-empty {
    text-align: center;
    padding: 4rem 1rem;
    color: var(--muted);
    font-size: 0.85rem;
  }

  /* ═══════════════════════════════════════════════════════════════════
     LIFECYCLE SUBWAY MAP — "Statut · cycle de vie"
     One horizontal line with 5 stations (Plan / Stratégie / Création /
     Validation / Live). Each in-flight test = a dot positioned at its
     current station. Dot fill colour = campaign target colour, halo =
     phase colour, size = urgency. Hover reveals a popover with full
     test details (ref, name, assignee, dates).
     ═══════════════════════════════════════════════════════════════════ */
  .lifecycle-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: 1rem 1.25rem 1.4rem;
    position: relative;
  }
  .lifecycle-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.1rem;
  }
  .lifecycle-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 1.05rem;
    color: var(--fg);
  }
  .lifecycle-title-meta {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    margin-left: 0.5rem;
  }
  .lifecycle-deadline {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted);
    text-align: right;
  }
  .lifecycle-deadline strong { color: var(--fg); font-weight: 500; }
  .lifecycle-deadline.is-urgent strong { color: var(--s-lost); }
  .lifecycle-deadline.is-urgent::before {
    content: '⚠ ';
    color: var(--s-lost);
    margin-right: 2px;
  }
  /* The actual track. Uses a 5-column grid for stations + a track line
     drawn behind the station labels via ::before. */
  .lc-track {
    position: relative;
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    margin-top: 0.4rem;
    padding-top: 1.4rem; /* room for dots above stations */
    padding-bottom: 0.4rem;
  }
  /* Horizontal track line connecting all 5 stations */
  .lc-track::before {
    content: '';
    position: absolute;
    left: 10%;   /* aligns with the centre of station 1 */
    right: 10%;  /* aligns with the centre of station 5 */
    top: calc(1.4rem + 14px); /* same vertical centre as dots */
    height: 2px;
    background: var(--line);
    border-radius: 1px;
    z-index: 0;
  }
  .lc-station {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
    z-index: 1;
  }
  /* Station marker — small circle on the track */
  .lc-station-marker {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--surface);
    border: 2px solid var(--line);
    flex-shrink: 0;
    transition: border-color 0.15s, background 0.15s;
    z-index: 2;
  }
  .lc-station.has-tests .lc-station-marker {
    border-color: var(--fg);
  }
  .lc-station.is-plan.has-tests    .lc-station-marker { border-color: var(--p-test); background: var(--p-test-soft); }
  .lc-station.is-draft.has-tests   .lc-station-marker { border-color: var(--muted); }
  .lc-station.is-review.has-tests  .lc-station-marker { border-color: var(--p-test); background: var(--p-test-soft); }
  .lc-station.is-approved.has-tests .lc-station-marker { border-color: var(--p-cold); background: var(--mint-soft); }
  .lc-station.is-live.has-tests    .lc-station-marker { border-color: var(--s-live); background: var(--sky-soft); }

  /* Station label below */
  .lc-station-label {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    text-align: center;
    line-height: 1.2;
  }
  .lc-station.has-tests .lc-station-label { color: var(--fg); font-weight: 500; }
  .lc-station-count {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.62rem;
    color: var(--muted);
    margin-top: 1px;
  }
  .lc-station.has-tests .lc-station-count { color: var(--fg); }

  /* Dots stack — appears ABOVE the station marker, vertically stacked
     so multiple dots at the same station don't overlap. */
  .lc-dots-stack {
    position: absolute;
    bottom: calc(100% - 6px); /* sit just above the marker */
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column-reverse; /* most-recent / most-urgent at bottom */
    align-items: center;
    gap: 3px;
    pointer-events: none; /* let individual dots handle hover */
  }
  .lc-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: 2px solid var(--surface);
    background: var(--p-cold);
    cursor: pointer;
    pointer-events: auto;
    transition: transform 0.15s, box-shadow 0.15s;
    box-shadow: 0 0 0 1px var(--line);
  }
  .lc-dot:hover {
    transform: scale(1.3);
    box-shadow: 0 0 0 2px var(--fg), 0 2px 6px rgba(35, 31, 35, 0.2);
    z-index: 5;
  }
  /* Urgent dots — deadline within 7 days. Bigger + warm halo. */
  .lc-dot.is-urgent {
    width: 16px;
    height: 16px;
    box-shadow: 0 0 0 2px var(--s-lost), 0 0 0 4px var(--coral-soft);
  }
  .lc-dot.is-urgent:hover {
    box-shadow: 0 0 0 2px var(--s-lost), 0 0 0 5px var(--coral-soft), 0 2px 8px rgba(35, 31, 35, 0.25);
  }
  /* Empty-station styling — show an "add" hint that's barely visible */
  .lc-dots-empty {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 1px dashed var(--line);
    opacity: 0.4;
  }

  /* Hover popover */
  .lc-popover {
    position: absolute;
    background: var(--surface);
    border: 1px solid var(--fg);
    border-radius: var(--r-md);
    padding: 0.6rem 0.85rem;
    box-shadow: 0 6px 20px rgba(35, 31, 35, 0.18);
    z-index: 30;
    min-width: 220px;
    max-width: 320px;
    pointer-events: none;
    font-size: 0.7rem;
    line-height: 1.45;
    color: var(--fg);
  }
  .lc-popover-ref {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    margin-bottom: 2px;
  }
  .lc-popover-name {
    font-family: 'StackSans headline Variable', system-ui, sans-serif;
    font-style: italic;
    font-size: 0.85rem;
    color: var(--fg);
    margin-bottom: 0.5rem;
    line-height: 1.2;
  }
  .lc-popover-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 3px;
    font-size: 0.66rem;
    color: var(--muted);
  }
  .lc-popover-row strong { color: var(--fg); font-weight: 500; }
  .lc-popover-swatch {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
  }
  .lc-popover-phase-tag {
    display: inline-block;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 1px 5px;
    border-radius: 3px;
    border: 1px solid var(--line);
    background: var(--bg);
    color: var(--fg);
    margin-top: 4px;
  }
  .lc-popover-phase-tag.is-plan     { color: var(--p-test); border-color: var(--p-test); background: var(--p-test-soft); }
  .lc-popover-phase-tag.is-review   { color: var(--p-test); border-color: var(--p-test); background: var(--p-test-soft); }
  .lc-popover-phase-tag.is-approved { color: var(--p-cold); border-color: var(--p-cold); background: var(--mint-soft); }
  .lc-popover-phase-tag.is-live     { color: var(--s-live); border-color: var(--s-live); background: var(--sky-soft); }
  .lc-popover-phase-tag.is-urgent {
    color: var(--s-lost) !important;
    border-color: var(--s-lost) !important;
    background: var(--coral-soft) !important;
  }

  /* Empty state inside the lifecycle card */
  .lifecycle-empty {
    padding: 1.2rem 0.4rem;
    text-align: center;
    color: var(--muted);
    font-size: 0.7rem;
    font-style: italic;
  }

  /* ═══════════════════════════════════════════════════════════════════
     STRUCTURE × TESTS TREE — collapsible hierarchy on the project page
     Network → Campaign → Section → Test. Each row shows counters; the
     test rows are leaves the user can click to open the modal. Mirrors
     the design system's left-stripe pattern so it visually echoes the
     campaign-toggle in the Structure sidebar.
     ═══════════════════════════════════════════════════════════════════ */
  .struct-tree {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: 0.7rem 0.85rem;
    overflow: hidden; /* contains rounded corners during expand */
  }
  .struct-tree-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px solid var(--line-soft);
    margin-bottom: 0.45rem;
  }
  .struct-tree-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 1.05rem;
    color: var(--fg);
  }
  .struct-tree-meta {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
  }
  /* Embedded results summary (was previously its own card). Lives just
     after the head, before the actual tree rows, to give a quick visual
     read on won/lost/incon/live without leaving the tree card. */
  .struct-tree-results {
    margin: 0.35rem 0 0.85rem;
    padding-bottom: 0.85rem;
    border-bottom: 1px solid var(--line-soft);
  }
  .struct-tree-actions {
    display: flex;
    gap: 0.3rem;
    margin-top: 0.35rem;
  }
  .struct-tree-actbtn {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 0.25rem 0.5rem;
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 3px;
    color: var(--muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
  }
  .struct-tree-actbtn:hover {
    background: var(--bg);
    color: var(--fg);
    border-color: var(--fg);
  }

  /* A single tree row — used at all levels (network/campaign/section/
     test). Indentation is applied via inline padding-left. */
  .st-row {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.4rem 0.4rem 0.4rem 0.2rem;
    border-radius: var(--r-sm);
    cursor: pointer;
    user-select: none;
    transition: background 0.12s;
    min-height: 28px;
  }
  .st-row:hover { background: var(--bg); }
  .st-row.is-leaf { cursor: pointer; } /* test rows */
  .st-row.is-untested { opacity: 0.65; }
  .st-row.is-untested:hover { opacity: 1; }
  /* Expand/collapse caret. Same visual language as the campaign-toggle
     chevron in the Structure sidebar. */
  .st-caret {
    width: 14px;
    height: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.65rem;
    color: var(--muted);
    flex-shrink: 0;
    transition: transform 0.15s, color 0.15s;
  }
  .st-row.is-expanded .st-caret { transform: rotate(90deg); color: var(--fg); }
  .st-row.is-leaf .st-caret { visibility: hidden; }
  /* Coloured dot — for networks shows the network colour; for tests
     shows the status colour. */
  .st-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex-shrink: 0;
    background: var(--muted);
  }
  .st-icon {
    font-size: 0.78rem;
    color: var(--muted);
    flex-shrink: 0;
    width: 14px;
    text-align: center;
  }
  .st-label {
    flex: 1;
    min-width: 0;
    font-size: 0.74rem;
    color: var(--fg);
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .st-label-em {
    font-style: italic;
    color: var(--muted);
    margin-left: 0.3rem;
  }
  .st-meta {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    flex-shrink: 0;
  }
  .st-meta strong { color: var(--fg); font-weight: 500; }
  .st-meta.has-tests strong { color: var(--p-cold); }
  /* Mini ratio bar showing tested-vs-untested for a campaign or section.
     Built as a tiny coloured block + grey block. */
  .st-bar {
    display: inline-flex;
    align-items: center;
    gap: 1px;
    margin-left: 0.4rem;
  }
  .st-bar-cell {
    width: 5px;
    height: 8px;
    border-radius: 1px;
    background: var(--depth);
  }
  .st-bar-cell.is-on { background: var(--p-cold); }
  .st-bar-cell.is-on.is-warn { background: var(--s-incon); }
  /* Network-level emphasis */
  .st-row.lvl-network {
    border-left: 3px solid var(--line);
    padding-left: 0.35rem;
    margin-top: 0.25rem;
    margin-bottom: 1px;
    background: var(--bg);
  }
  .st-row.lvl-network .st-label {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.85rem;
    color: var(--fg);
  }
  .st-row.lvl-network .st-dot {
    width: 11px;
    height: 11px;
  }
  /* Campaign-level emphasis — phase stripe on the left */
  .st-row.lvl-campaign {
    margin-left: 0.4rem;
    border-left: 3px solid var(--line);
    padding-left: 0.4rem;
  }
  .st-row.lvl-campaign.phase-test     { border-left-color: var(--p-test); }
  .st-row.lvl-campaign.phase-cold     { border-left-color: var(--p-cold); }
  .st-row.lvl-campaign.phase-warm     { border-left-color: var(--p-warm); }
  .st-row.lvl-campaign.phase-ret      { border-left-color: var(--p-ret); }
  .st-row.lvl-campaign .st-label-em { font-size: 0.6rem; }
  /* Section-level — slightly indented, lighter font */
  .st-row.lvl-section { margin-left: 1rem; }
  .st-row.lvl-section .st-label { font-size: 0.7rem; color: var(--fg); }
  /* Test-leaf rows — narrowest, shifted right, use a return arrow */
  .st-row.lvl-test {
    margin-left: 1.7rem;
    padding-top: 0.3rem;
    padding-bottom: 0.3rem;
    min-height: 24px;
  }
  .st-row.lvl-test .st-label {
    font-size: 0.68rem;
  }
  .st-row.lvl-test .st-icon {
    font-size: 0.6rem;
    color: var(--muted);
  }
  .st-row.lvl-test .st-ref {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.58rem;
    color: var(--p-cold);
    margin-right: 0.3rem;
  }
  /* Status pill on test leaf rows */
  .st-status {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 1px 5px;
    border-radius: 3px;
    border: 1px solid var(--line);
    background: var(--bg);
    color: var(--fg);
    flex-shrink: 0;
  }
  .st-status.s-won    { color: var(--s-won);   border-color: var(--s-won);   background: var(--s-won-soft); }
  .st-status.s-lost   { color: var(--s-lost);  border-color: var(--s-lost);  background: var(--s-lost-soft); }
  .st-status.s-incon  { color: var(--s-incon); border-color: var(--s-incon); background: var(--s-incon-soft); }
  .st-status.s-live   { color: var(--s-live);  border-color: var(--s-live);  background: var(--sky-soft); }
  .st-status.s-plan,
  .st-status.s-draft,
  .st-status.s-review,
  .st-status.s-approved { color: var(--p-test); border-color: var(--p-test); background: var(--p-test-soft); }

  /* Children container — animated open/close via max-height. */
  .st-children {
    overflow: hidden;
    max-height: 0;
    transition: max-height 0.25s ease;
  }
  .st-children.is-open { max-height: 9999px; }
  /* Empty hint when a node has 0 tests under it */
  .st-empty-hint {
    margin-left: 1.7rem;
    padding: 0.3rem 0.4rem;
    font-size: 0.62rem;
    color: var(--muted);
    font-style: italic;
  }

  /* ═══════════ SETUP VIEW: sidebar-specific (networks, campaigns, sections) ═══════════ */
  /* The sidebar body now hosts network groups directly (no enclosing
     channel wrapper). A bit of side padding so the groups breathe. */
  #setup-sidebar-body { padding: 0.5rem 1rem 1rem; }

  .campaign-block { margin-bottom: 1.3rem; }
  .campaign-block.collapsed { margin-bottom: 0.55rem; }

  /* The header is the primary affordance for "this section is
     expandable" — make it look unambiguously like a toggle button:
     - left phase-color stripe = visual anchor + reinforces phase identity
     - clear chevron in its own circle slot (not just a glyph)
     - subtle background when collapsed → expanded gives elevation feedback
     - hover state feels like a button (background + slight inset)
     - mini content preview "3 audiences · 5 créas" when collapsed so the
       user knows what's behind the click without expanding */
  .phase-marker.campaign-toggle {
    cursor: pointer;
    user-select: none;
    padding: 0.45rem 0.55rem 0.45rem 0.4rem;
    border-radius: var(--r-sm);
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-left: 3px solid var(--line);
    transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
    position: relative;
  }
  .phase-marker.campaign-toggle:hover {
    background: var(--bg);
    border-color: var(--line);
    border-left-color: var(--fg);
    box-shadow: 0 1px 4px rgba(35, 31, 35, 0.06);
  }
  .phase-marker.campaign-toggle:focus-visible {
    outline: 2px solid var(--p-cold);
    outline-offset: 2px;
  }
  .campaign-block.expanded .phase-marker.campaign-toggle {
    background: var(--bg);
    border-color: var(--line);
  }
  /* Phase-colored left stripe — picks up the campaign's phase so the
     user can scan the sidebar and immediately see which campaigns are
     Test / Cold / Warm / Ret. */
  .phase-marker.campaign-toggle.p-test  { border-left-color: var(--p-test); }
  .phase-marker.campaign-toggle.p-cold  { border-left-color: var(--p-cold); }
  .phase-marker.campaign-toggle.p-warm  { border-left-color: var(--p-warm); }
  .phase-marker.campaign-toggle.p-ret   { border-left-color: var(--p-ret);  }
  .campaign-block.expanded .phase-marker.campaign-toggle.p-test { border-left-width: 4px; }
  .campaign-block.expanded .phase-marker.campaign-toggle.p-cold { border-left-width: 4px; }
  .campaign-block.expanded .phase-marker.campaign-toggle.p-warm { border-left-width: 4px; }
  .campaign-block.expanded .phase-marker.campaign-toggle.p-ret  { border-left-width: 4px; }

  /* Chevron in its own circle slot — much more readable than a bare ▸
     and signals "this is a button" at a glance. The circle has a
     real background by default (not transparent) so the affordance
     is unmistakable, and a faint phase-tinted ring on hover. */
  .campaign-chevron {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--bg);
    border: 1px solid var(--line);
    color: var(--fg);
    font-size: 0.85rem;
    font-weight: 600;
    line-height: 1;
    flex-shrink: 0;
    transition: transform 0.18s ease, color 0.15s, background 0.15s, border-color 0.15s, box-shadow 0.15s;
  }
  .phase-marker.campaign-toggle:hover .campaign-chevron {
    color: var(--surface);
    background: var(--fg);
    border-color: var(--fg);
    box-shadow: 0 1px 4px rgba(35, 31, 35, 0.15);
  }
  /* Phase-tinted chevron when expanded — picks up the campaign's phase
     color so it ties together with the left stripe. */
  .campaign-block.expanded .campaign-chevron {
    transform: rotate(90deg);
    color: var(--surface);
    background: var(--fg);
    border-color: var(--fg);
  }
  .campaign-block.expanded .phase-marker.campaign-toggle.p-test .campaign-chevron { background: var(--p-test); border-color: var(--p-test); }
  .campaign-block.expanded .phase-marker.campaign-toggle.p-cold .campaign-chevron { background: var(--p-cold); border-color: var(--p-cold); }
  .campaign-block.expanded .phase-marker.campaign-toggle.p-warm .campaign-chevron { background: var(--p-warm); border-color: var(--p-warm); }
  .campaign-block.expanded .phase-marker.campaign-toggle.p-ret  .campaign-chevron { background: var(--p-ret);  border-color: var(--p-ret); }

  /* Pill showing how many items are checked when the campaign is collapsed */
  .campaign-checked-pill {
    background: var(--p-cold);
    color: var(--surface);
    font-size: 0.55rem;
    font-weight: 600;
    padding: 1px 5px;
    border-radius: 3px;
    letter-spacing: 0.04em;
  }

  /* Mini preview line shown UNDER the campaign name when collapsed —
     gives the user a quick read of what's inside without needing to
     expand. Hidden when expanded since the body shows the real
     content. */
  .campaign-preview {
    display: block;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    margin-top: 2px;
    line-height: 1.3;
    text-transform: uppercase;
  }
  .campaign-block.expanded .campaign-preview { display: none; }
  .campaign-preview-sep {
    margin: 0 0.35rem;
    opacity: 0.5;
  }

  /* The clickable affordance hints — appear on hover only so they
     don't add noise to the resting state. */
  .campaign-toggle-hint {
    position: absolute;
    right: 0.55rem;
    top: 50%;
    transform: translateY(-50%) translateX(4px);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s, transform 0.15s;
    white-space: nowrap;
  }
  /* Only show the hint when the row has space (no badges crowding it).
     We use the simpler approach: only show on the network-badge-less
     hover, but since badges are usually present we'll keep this off
     for now. Kept the rule for future use. */
  /* .phase-marker.campaign-toggle:hover .campaign-toggle-hint {
    opacity: 1;
    transform: translateY(-50%) translateX(0);
  } */

  /* Inline hint shown inside an expanded campaign when the next cascade
     step is locked (no audience checked → no creatives, etc.) */
  .cascade-hint {
    margin: 0.5rem 0 0.75rem 1.25rem;
    padding: 0.45rem 0.65rem;
    border-left: 2px dashed var(--line);
    font-size: 0.66rem;
    color: var(--muted);
    font-style: italic;
    line-height: 1.4;
  }

  /* Hide the body when collapsed. We keep the DOM for the search filter
     but display:none means it won't paint or take space. */
  .campaign-block.collapsed .campaign-body { display: none; }
  /* Subtle slide-down animation when expanding so the user gets clear
     feedback that their click did something. */
  .campaign-block.expanded .campaign-body {
    animation: campaignBodyExpand 0.22s ease-out;
    padding: 0.55rem 0 0 0.4rem;
    margin-top: 0.4rem;
    border-left: 2px solid var(--line-soft);
    margin-left: 0.55rem;
  }
  @keyframes campaignBodyExpand {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0);    }
  }

  /* Cascade-locked sections — exist in the DOM (so search can find
     inside) but hidden by default. Force-show when the sidebar is in
     "searching" mode below. */
  .section.cascade-locked { display: none; }

  /* When search is active, show everything (collapsed body + locked
     sections) so the filter actually has something to filter. */
  #setup-sidebar-body.searching .campaign-block.collapsed .campaign-body { display: block; }
  #setup-sidebar-body.searching .section.cascade-locked { display: block; opacity: 0.55; }

  .phase-marker {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin-bottom: 0.4rem;
  }
  /* Wrap around the name + preview line so they stack vertically and
     the name keeps its single-line italic styling without conflict. */
  .campaign-label-wrap {
    display: flex;
    flex-direction: column;
    margin-right: auto;
    min-width: 0;
    flex: 1;
  }
  .phase-marker .name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-size: 0.9rem;
    font-style: italic;
    line-height: 1.1;
    /* The label-wrap holds the right-margin push now, so the name
       itself doesn't need it anymore. */
  }
  .phase-marker .line { display: none; }
  .phase-marker .meta {
    font-size: 0.54rem;
    letter-spacing: 0;
    color: var(--muted);
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
  }

  .section { margin-bottom: 1rem; }
  .section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 0.2rem;
  }
  .section-title { font-size: 0.7rem; font-weight: 500; }
  .section-counter {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-size: 0.9rem;
    color: var(--muted);
  }

  /* "facultatif" pill on optional sections (Copy is a sub-set of
     Creatives, not a required step). */
  .section-optional {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.04rem 0.34rem;
    background: var(--sand-soft);
    color: var(--p-ret);
    border: 1px solid var(--p-ret-edge);
    border-radius: var(--r-sm);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-weight: 500;
    vertical-align: middle;
    cursor: help;
  }

  /* Network group within a channel — visually groups all campaigns
     belonging to the same ad platform. */
  .network-group {
    margin-bottom: 0.85rem;
  }
  .network-group:last-child {
    margin-bottom: 0;
  }
  .network-group-header {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.5rem;
    margin-bottom: 0.4rem;
    border: 1px solid var(--line-soft);
    border-radius: var(--r-sm);
    background: var(--surface);
  }
  .network-group-swatch {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    flex-shrink: 0;
    box-shadow: inset 0 0 0 1px rgba(35, 31, 35, 0.15);
  }
  .network-group-label {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.78rem;
    flex: 1;
    line-height: 1;
  }
  .network-group-count {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .network-group-remove {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: 0 0.25rem;
    font-size: 0.95rem;
    line-height: 1;
    border-radius: 4px;
    opacity: 0;
    transition: opacity 0.12s, color 0.12s;
  }
  .network-group:hover .network-group-remove { opacity: 0.55; }
  .network-group-remove:hover { opacity: 1; color: var(--coral); }

  /* "+ Ajouter un réseau" button at the bottom of a channel body. */
  .add-network-btn {
    display: block;
    width: 100%;
    padding: 0.5rem 0.6rem;
    margin-top: 0.4rem;
    background: transparent;
    border: 1px dashed var(--line);
    border-radius: var(--r-sm);
    color: var(--muted);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
  }
  .add-network-btn:hover {
    border-color: var(--fg);
    border-style: solid;
    color: var(--fg);
    background: var(--surface);
  }

  /* Onboarding shown in the sidebar when no network has been added
     yet. Big enough to be the obvious starting point, with a clear
     CTA + secondary import / IA shortcuts underneath. */
  /* ════════ Shared empty-onboarding pattern ════════
     Used by both the Structure sidebar (when no network is added)
     and the Tests sidebar (when no test exists). Single source of
     truth so the two views stay visually identical. The .seo-* /
     .teo-* aliases are kept for the sake of legacy markup but they
     all flow through this base class. */
  .setup-empty-onboarding,
  .tests-empty-onboarding {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 2rem 1rem 1.5rem;
    gap: 0.75rem;
  }
  .seo-icon, .teo-icon {
    font-size: 2rem;
    line-height: 1;
    margin-bottom: 0.25rem;
  }
  .seo-title, .teo-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1rem;
    line-height: 1.2;
    margin: 0;
    color: var(--fg);
  }
  .seo-desc, .teo-desc {
    font-size: 0.7rem;
    line-height: 1.4;
    color: var(--muted);
    margin: 0;
    max-width: 32ch;
  }
  .seo-cta, .teo-cta {
    margin-top: 0.5rem;
    padding: 0.6rem 1rem;
    background: var(--fg);
    color: var(--surface);
    border: 1px solid var(--fg);
    border-radius: var(--r-sm);
    font-family: inherit;
    font-size: 0.7rem;
    letter-spacing: 0.04em;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, transform 0.15s;
  }
  .seo-cta:hover, .teo-cta:hover {
    background: var(--p-cold);
    border-color: var(--p-cold);
    transform: translateY(-1px);
  }
  .seo-hint, .teo-hint {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.55rem;
    color: var(--muted);
    margin-top: 0.25rem;
    flex-wrap: wrap;
    justify-content: center;
  }
  .seo-link, .teo-link {
    background: transparent;
    border: none;
    padding: 0;
    font-family: inherit;
    font-size: inherit;
    color: var(--p-test);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
  }
  .seo-link:hover, .teo-link:hover { color: var(--p-cold); }

  /* Small hint on the right of an add-network-picker entry showing
     how many campaigns will be created. */
  .network-picker-hint {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    text-transform: uppercase;
  }

  /* Phase slots — placeholder for an unactivated (network × phase)
     pair. Click → spins up the actual campaign. Visually understated
     so the existing campaigns dominate the eye. */
  .phase-slot {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    margin-bottom: 0.18rem;
    padding: 0.34rem 0.55rem;
    background: transparent;
    border: 1px dashed var(--line);
    border-radius: var(--r-sm);
    color: var(--muted);
    font-family: inherit;
    font-size: 0.74rem;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.14s, background 0.14s, color 0.14s;
  }
  .phase-slot:hover {
    border-color: var(--fg);
    border-style: solid;
    background: var(--surface);
    color: var(--fg);
  }
  .phase-slot-label {
    flex: 1;
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.86rem;
    line-height: 1;
  }
  .phase-slot-add {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.85rem;
    color: var(--muted);
    line-height: 1;
    flex-shrink: 0;
  }
  .phase-slot:hover .phase-slot-add { color: var(--fg); }

  /* ════════════════════════════════════════════════════════════════
     CUSTOM CAMPAIGN — "Type personnalisé" button + inline form
     Lets the user create campaigns outside the 4 standard phases
     by typing a name directly in the sidebar.
     ════════════════════════════════════════════════════════════════ */
  .custom-campaign-btn {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.55rem 0.7rem;
    margin-top: 0.45rem;
    background: transparent;
    border: 1.5px dashed var(--line);
    border-radius: var(--r-sm);
    cursor: pointer;
    font-family: inherit;
    color: var(--muted);
    text-align: left;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
  }
  .custom-campaign-btn:hover {
    border-color: var(--p-cold);
    background: var(--mint-soft);
    color: var(--fg);
  }
  .custom-campaign-btn .ccb-icon {
    font-family: 'Fragment Mono', monospace;
    font-size: 1rem;
    line-height: 1;
    color: var(--p-cold);
    flex-shrink: 0;
  }
  .custom-campaign-btn .ccb-label {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--fg);
  }
  .custom-campaign-btn .ccb-hint {
    margin-left: auto;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted);
    opacity: 0.7;
  }
  .custom-campaign-btn:hover .ccb-hint { opacity: 1; }

  .custom-campaign-form {
    margin-top: 0.45rem;
    padding: 0.7rem 0.7rem 0.65rem;
    background: var(--mint-soft);
    border: 1.5px solid var(--p-cold-edge);
    border-radius: var(--r-sm);
    animation: ccfSlideDown 0.18s ease-out;
  }
  @keyframes ccfSlideDown {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .ccf-row { display: flex; gap: 0.4rem; margin-bottom: 0.55rem; }
  .ccf-name {
    flex: 1;
    font-family: inherit;
    font-size: 0.8rem;
    padding: 0.45rem 0.6rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    outline: none;
    color: var(--fg);
    transition: border-color 0.15s;
  }
  .ccf-name:focus { border-color: var(--p-cold); }
  .ccf-name.ccf-shake {
    border-color: var(--s-lost);
    animation: ccfShake 0.35s;
  }
  @keyframes ccfShake {
    0%,100% { transform: translateX(0); }
    25% { transform: translateX(-4px); }
    75% { transform: translateX(4px); }
  }
  .ccf-phase-row {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-wrap: wrap;
    margin-bottom: 0.55rem;
  }
  .ccf-phase-label {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    margin-right: 0.2rem;
  }
  .ccf-phase {
    cursor: pointer;
    user-select: none;
    padding: 0.25rem 0.55rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 999px;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted);
    transition: background 0.15s, border-color 0.15s, color 0.15s;
  }
  .ccf-phase input[type="radio"] { display: none; }
  .ccf-phase:hover { border-color: var(--fg); color: var(--fg); }
  .ccf-phase:has(input:checked) {
    background: var(--fg);
    color: var(--surface);
    border-color: var(--fg);
  }
  .ccf-phase-p-test:has(input:checked) { background: var(--p-test); border-color: var(--p-test); }
  .ccf-phase-p-cold:has(input:checked) { background: var(--p-cold); border-color: var(--p-cold); }
  .ccf-phase-p-warm:has(input:checked) { background: var(--p-warm); border-color: var(--p-warm); }
  .ccf-phase-p-ret:has(input:checked)  { background: var(--p-ret);  border-color: var(--p-ret); }
  .ccf-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.4rem;
  }
  .ccf-cancel, .ccf-create {
    font-family: inherit;
    font-size: 0.7rem;
    padding: 0.4rem 0.8rem;
    border-radius: var(--r-sm);
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
  .ccf-cancel {
    background: transparent;
    border: 1px solid var(--line);
    color: var(--muted);
  }
  .ccf-cancel:hover { color: var(--fg); border-color: var(--fg); }
  .ccf-create {
    background: var(--fg);
    color: var(--surface);
    border: 1px solid var(--fg);
    font-weight: 500;
  }
  .ccf-create:hover { background: var(--p-cold); border-color: var(--p-cold); }

  /* Section kind chip — small monospace badge before the title that
     differentiates audiences (who) vs creatives/static (what visual)
     vs copy (what text). Each kind gets its own pastel colour from
     the Stack palette so they're scannable at a glance. */
  .section-title-wrap {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
  }
  .section-kind-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.22rem;
    padding: 0.08rem 0.36rem;
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.06em;
    font-weight: 600;
    text-transform: uppercase;
    flex-shrink: 0;
    line-height: 1.4;
  }
  .section-kind-chip .section-kind-icon {
    font-size: 0.62rem;
    line-height: 1;
    font-family: system-ui, -apple-system, sans-serif;
    font-weight: 400;
  }
  .section-kind-chip.kind-audience {
    background: var(--orchid-soft);
    color: var(--p-test);
    border-color: var(--p-test-edge);
  }
  .section-kind-chip.kind-creative {
    background: var(--peach-soft);
    color: var(--p-warm);
    border-color: var(--p-warm-edge);
  }
  .section-kind-chip.kind-static {
    background: var(--mint-soft);
    color: var(--p-cold);
    border-color: var(--p-cold-edge);
  }
  .section-kind-chip.kind-copy {
    background: var(--sand-soft);
    color: var(--p-ret);
    border-color: var(--p-ret-edge);
  }

  /* Per-kind section indentation. The audience section is the "primary"
     step so it stays at the campaign's left edge; creatives are
     indented one step in, then copy is indented yet another step to
     show the cascade visually (audience → creative → copy). No more
     coloured left borders — too noisy in the sidebar. */
  .section.section-kind-audience {
    padding-left: 0;
    margin-left: -0.2rem;
  }
  .section.section-kind-creative {
    padding-left: 0;
    margin-left: 0.5rem;
  }
  .section.section-kind-static {
    padding-left: 0;
    margin-left: 0.5rem;
  }

  /* Copy section visually indented and styled as a sub-set of the
     creatives above it. We push it further in than creatives so the
     hierarchy reads at a glance: audience → creative → copy. */
  .section.section-copy {
    margin-left: 1.4rem;
    padding-left: 0;
    margin-top: -0.3rem;
  }
  .section.section-copy .section-title {
    color: var(--muted);
    font-weight: 500;
  }
  .section-hint {
    font-size: 0.6rem;
    color: var(--muted);
    font-style: italic;
    margin-bottom: 0.4rem;
    padding-left: 0.5rem;
    border-left: 2px solid var(--faint);
    line-height: 1.45;
  }

  .items { list-style: none; }
  .item {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.22rem 0;
    cursor: pointer;
    user-select: none;
    transition: color 0.15s;
  }
  .item:hover { color: var(--p-cold); }
  .item input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 13px; height: 13px;
    border: 1.5px solid var(--fg);
    background: transparent;
    cursor: pointer;
    flex-shrink: 0;
    margin-top: 3px;
    position: relative;
    transition: all 0.15s;
  }
  .phase-p-test .item input:checked { background: var(--p-test); border-color: var(--p-test); }
  .phase-p-cold .item input:checked { background: var(--p-cold); border-color: var(--p-cold); }
  .phase-p-warm .item input:checked { background: var(--p-warm); border-color: var(--p-warm); }
  .phase-p-ret  .item input:checked { background: var(--p-ret);  border-color: var(--p-ret); }
  .item input:checked::after {
    content: '';
    position: absolute;
    top: 0; left: 3.5px;
    width: 3px; height: 7px;
    border: solid var(--surface);
    border-width: 0 1.5px 1.5px 0;
    transform: rotate(45deg);
  }
  .item-num {
    font-size: 0.58rem;
    color: var(--muted);
    margin-top: 3px;
    min-width: 1.1rem;
    font-variant-numeric: tabular-nums;
  }
  .item-text { flex: 1; font-size: 0.7rem; line-height: 1.45; }
  .item-test-badge {
    margin-top: 2px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    font-size: 0.56rem;
    letter-spacing: 0;
    background: var(--fg);
    color: var(--surface);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
  }
  .item.has-tests .item-text { font-weight: 500; }
  .item.has-tests { color: var(--fg); }

  /* Custom (user-added) items: subtle dotted left border to distinguish
     them from built-in items, plus an "×" button to remove them. */
  .item.item-custom {
    position: relative;
  }
  .item.item-custom .item-num {
    color: var(--p-cold);
    font-weight: 500;
  }
  .item.item-custom .item-text::before {
    content: "+ ";
    color: var(--muted);
    font-size: 0.65rem;
  }
  .item-remove-custom {
    margin-top: 1px;
    margin-left: 4px;
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 0.85rem;
    line-height: 1;
    cursor: pointer;
    border-radius: 3px;
    padding: 0;
    transition: background 0.15s, color 0.15s;
  }
  .item-remove-custom:hover {
    background: var(--s-lost, #c5474b);
    color: #fff;
  }

  /* Phase 3.B.16.b — "👁" Render dans Mockup Studio. Visible sur les
     items creative/copy. Visible au hover de la ligne pour éviter le
     bruit visuel quand l'utilisateur lit les items checkés. */
  .item-render-mockup {
    margin-top: 1px;
    margin-left: 4px;
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 0.85rem;
    line-height: 1;
    cursor: pointer;
    border-radius: 3px;
    padding: 0;
    opacity: 0;
    transition: opacity 0.15s, background 0.15s;
  }
  .item:hover .item-render-mockup,
  .item.checked .item-render-mockup {
    opacity: 0.6;
  }
  .item-render-mockup:hover {
    background: var(--ink-12, rgba(35,31,35,0.12));
    opacity: 1 !important;
  }

  /* "+ Ajouter" inline form at the bottom of each section */
  .item-add-row {
    list-style: none;
    margin-top: 0.4rem;
    padding-top: 0.4rem;
    border-top: 1px dashed var(--line);
  }
  .item-add-btn {
    width: 100%;
    text-align: left;
    background: transparent;
    border: 1px dashed var(--line);
    color: var(--muted);
    font-family: inherit;
    font-size: 0.64rem;
    letter-spacing: 0.04em;
    padding: 0.35rem 0.5rem;
    border-radius: 3px;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
  }
  .item-add-btn:hover {
    border-color: var(--p-cold);
    color: var(--p-cold);
    background: var(--surface);
  }
  .item-add-form {
    display: flex;
    gap: 0.3rem;
    align-items: center;
  }
  .item-add-input {
    flex: 1;
    min-width: 0;
    font-family: inherit;
    font-size: 0.7rem;
    padding: 0.3rem 0.45rem;
    border: 1px solid var(--p-cold);
    background: var(--surface);
    color: var(--fg);
    border-radius: 3px;
    outline: none;
  }
  .item-add-input.shake {
    animation: itemAddShake 0.35s;
    border-color: var(--s-lost, #c5474b);
  }
  @keyframes itemAddShake {
    0%, 100% { transform: translateX(0); }
    20%, 60% { transform: translateX(-3px); }
    40%, 80% { transform: translateX(3px); }
  }
  .item-add-submit, .item-add-cancel {
    font-family: inherit;
    font-size: 0.62rem;
    border-radius: 3px;
    padding: 0.3rem 0.55rem;
    cursor: pointer;
    border: 1px solid var(--p-cold);
    background: var(--p-cold);
    color: var(--surface);
    flex-shrink: 0;
  }
  .item-add-cancel {
    background: transparent;
    color: var(--muted);
    border-color: var(--line);
    width: 24px;
    padding: 0.3rem 0;
  }
  .item-add-submit:hover { filter: brightness(1.1); }
  .item-add-cancel:hover { color: var(--fg); border-color: var(--fg); }

  /* Pill shown next to the section counter when custom items exist */
  .custom-pill {
    display: inline-block;
    margin-left: 0.3rem;
    padding: 0 0.3rem;
    background: var(--p-cold);
    color: var(--surface);
    font-size: 0.55rem;
    border-radius: 3px;
    font-weight: 600;
  }

  /* Toolbar above the sections list with Export/Import CSV / IA buttons.
     Used identically in Structure (sidebar) and Tests (sidebar) so the
     two views feel like the same product. */
  .custom-items-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.3rem;
    padding: 0.3rem 1rem;
    border-bottom: 1px solid var(--line-soft);
  }
  .custom-toolbar-label {
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    margin-right: auto;
    flex-shrink: 0;
  }
  .custom-toolbar-label .at-count {
    display: inline-block;
    margin-left: 0.25rem;
    color: var(--fg);
    font-style: italic;
  }
  .custom-toolbar-btn {
    font-family: inherit;
    font-size: 0.55rem;
    letter-spacing: 0.04em;
    padding: 0.2rem 0.45rem;
    background: transparent;
    border: 1px solid var(--line);
    color: var(--muted);
    border-radius: 3px;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
    flex-shrink: 0;
  }
  .custom-toolbar-btn:hover {
    border-color: var(--p-cold);
    color: var(--p-cold);
  }
  /* ✨ IA button — slightly accented to read as "magic" without
     hijacking attention from the create button. */
  .custom-toolbar-btn.ai-btn {
    border-color: var(--p-test);
    color: var(--p-test);
  }
  .custom-toolbar-btn.ai-btn:hover {
    background: var(--p-test-soft);
    color: var(--p-test);
  }
  /* Primary action (＋ Test / ＋ Action) */
  .custom-toolbar-btn.primary {
    background: var(--ink, #231F23);
    color: var(--surface);
    border-color: var(--ink, #231F23);
  }
  .custom-toolbar-btn.primary:hover {
    background: var(--p-cold);
    border-color: var(--p-cold);
    color: var(--surface);
  }

  /* "Custom" badge next to a custom campaign name in the sidebar */
  .campaign-custom-pill {
    display: inline-block;
    margin-left: 0.3rem;
    padding: 0 0.3rem;
    background: var(--p-cold);
    color: var(--surface);
    font-size: 0.5rem;
    border-radius: 2px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    vertical-align: middle;
  }
  .campaign-remove-btn {
    margin-left: auto;
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 0.85rem;
    cursor: pointer;
    padding: 0 0.3rem;
    border-radius: 3px;
    line-height: 1;
    transition: background 0.15s, color 0.15s;
  }
  .campaign-remove-btn:hover {
    background: var(--s-lost, #c5474b);
    color: #fff;
  }

  .presets {
    display: flex;
    gap: 0.25rem;
    margin-bottom: 0.4rem;
    flex-wrap: wrap;
    align-items: center;
  }
  .presets .label {
    font-size: 0.54rem;
        letter-spacing: 0;
    color: var(--muted);
    margin-right: 0.2rem;
  }
  .preset-btn {
    font-family: inherit;
    font-size: 0.64rem;
    background: transparent;
    border: 1px solid var(--line);
    color: var(--fg);
    padding: 0.12rem 0.42rem;
    cursor: pointer;
    transition: all 0.15s ease;
  }
  .preset-btn:hover { border-color: var(--fg); }
  .preset-btn.active {
    background: var(--fg);
    color: var(--surface);
    border-color: var(--fg);
  }

  /* ═══════════ CANVAS (shared by both views) ═══════════ */
  .viz-top {
    padding: 1.25rem 2rem 0.85rem;
    border-bottom: 1px solid var(--line);
    background: var(--bg);
    z-index: 10;
  }
  .viz-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    gap: 1.5rem;
  }
  .viz-head h2 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.5rem;
    letter-spacing: -0.015em;
    line-height: 1;
  }
  .viz-head h2 em { font-style: italic; color: var(--muted); }
  .viz-head .sub {
    font-size: 0.58rem;
        letter-spacing: 0;
    color: var(--muted);
    margin-top: 0.3rem;
  }
  .stats {
    display: grid;
    padding: 0.7rem 0 0;
    margin-top: 0.8rem;
    border-top: 1px solid var(--line-soft);
  }
  #setup-stats { grid-template-columns: repeat(5, 1fr); }
  #tests-stats { grid-template-columns: repeat(6, 1fr); }
  .stat { padding: 0 0.8rem; border-right: 1px solid var(--line-soft); }
  .stat:last-child { border-right: none; }
  .stat:first-child { padding-left: 0; }
  .stat-num {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-size: 1.55rem;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    display: block;
    letter-spacing: -0.02em;
  }
  .stat-num.s-won { color: var(--s-won); }
  .stat-num.s-lost { color: var(--s-lost); }
  .stat-num.s-incon { color: var(--s-incon); }
  .stat-num.s-live { color: var(--s-live); }
  .stat-num.s-plan { color: var(--s-plan); }
  .stat-label {
    font-size: 0.52rem;
    letter-spacing: 0;
    color: var(--muted);
    margin-top: 0.3rem;
  }

  .viz-scroll {
    flex: 1;
    overflow: hidden;
    position: relative;
    cursor: grab;
  }
  /* Subtle dot-grid background on the SETUP canvas only — sits behind
     every node, edge and SVG so it reads as the canvas paper. The
     pan/zoom of the canvas drives `background-position` and
     `background-size` from JS (applyTransformSetup) for an
     "infinite paper" feel. */
  #setup-scroll {
    background-image: radial-gradient(circle, var(--line) 1px, transparent 1.5px);
    background-size: 24px 24px;
    background-position: 0 0;
  }
  /* Tests timeline canvas: plain background, no grid / lines / stripes —
     keep the swimlanes and content fully unobstructed. */
  .viz-scroll.panning { cursor: grabbing; }
  .viz-scroll.empty { cursor: default; overflow: auto; }
  .viz-container {
    position: absolute;
    top: 0; left: 0;
    will-change: transform;
    transform-origin: 0 0;
  }
  .viz-container.empty {
    position: relative;
    width: 100%;
    height: 100%;
  }
  .viz-container.animate { transition: transform 0.2s ease; }

  /* ════════ Tests timeline : sticky date ruler (3 lignes) ════════
     Bande sticky en haut de #tests-scroll, hors-flow du canvas
     transformé. Trois lignes empilées :
       1. Mois  — "Janvier 2026" (gros, italique display font)
       2. Semaine — "S22" (numéro ISO 8601, monospace, gras)
       3. Jour — "23" (monospace, fin)
     Toutes les positions X sont recalculées au pan/zoom via JS
     (updateTestsDateRulerPositions). Le tick "today" reçoit la
     classe .is-today sur les 3 niveaux (orchid + fond pill). */

  /* Phase 3.B.21+ — Layer des lignes verticales (ticks) en overlay
     sur le scroll container. Hors du #tests-container scalé, donc
     les ticks restent toujours plein hauteur du viewport visible.
     Le pan/zoom du contenu repositionne les ticks via
     updateTestsDateRulerPositions() (left: worldX * zoom + panX).
     z-index 0 + #tests-container forcé à z-index 1 (sélecteur
     spécifique pour ne PAS affecter le canvas Setup #setup-container).
     Les lignes passent ainsi DERRIÈRE les swimlane labels, dividers,
     test-nodes, action markers, et tous les autres éléments du
     #tests-container (qui est au z-index 1 supérieur).
     pointer-events: none pour ne pas bloquer les interactions. */
  .tests-vlines-layer {
    position: absolute;
    top: 66px; /* sous le ruler sticky */
    left: 0;
    right: 0;
    bottom: 0;
    pointer-events: none;
    z-index: 0;
    overflow: hidden;
    /* Lignes verticales en arrière-plan = repères subtils.
       opacity sur le layer entier pour ne pas casser le token --line ailleurs. */
    opacity: 0.5;
  }
  /* Force #tests-container à passer DEVANT le .tests-vlines-layer.
     Sélecteur par id pour ne PAS affecter #setup-container ou les
     autres canvases qui n'ont pas de vlines-layer. */
  #tests-container {
    z-index: 1;
  }
  .tests-vlines-layer .timeline-tick {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--line);
    pointer-events: none;
  }
  /* En mode Journal le canvas est caché → on cache aussi le layer
     de ticks pour ne pas voir de lignes derrière les cards kanban. */
  .pane-main.mode-journal .tests-vlines-layer {
    display: none;
  }

  .tests-date-ruler {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 66px;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    z-index: 50;
    overflow: hidden;
    pointer-events: none;
  }
  .tests-date-ruler .trd-row {
    position: relative;
    width: 100%;
  }
  .tests-date-ruler .trd-row-month {
    height: 22px;
    border-bottom: 1px solid var(--depth);
  }
  .tests-date-ruler .trd-row-week {
    height: 18px;
    border-bottom: 1px solid var(--depth);
  }
  .tests-date-ruler .trd-row-day {
    height: 22px;
  }
  .tests-date-ruler .trd-tick {
    position: absolute;
    white-space: nowrap;
    line-height: 1;
  }
  /* MOIS — gros, italique, ancré (left = world x sans translateX),
     pour suggérer un "début de période". Taille proche d'un h4. */
  .tests-date-ruler .trd-month {
    top: 5px;
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.95rem;
    font-weight: 400;
    color: var(--fg);
    letter-spacing: -0.01em;
    padding-left: 4px;
  }
  .tests-date-ruler .trd-month.is-today {
    color: var(--orchid);
    font-weight: 500;
  }
  /* SEMAINE — monospace bold, centré sur le lundi. */
  .tests-date-ruler .trd-week {
    top: 4px;
    transform: translateX(-50%);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.62rem;
    font-weight: 600;
    color: var(--muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
  }
  .tests-date-ruler .trd-week.is-today {
    color: var(--surface);
    background: var(--orchid);
    padding: 2px 6px;
    border-radius: 10px;
    top: 1px;
    line-height: 1;
  }
  /* JOUR — monospace fin, centré. Lundi est légèrement appuyé pour
     repérer les semaines visuellement. */
  .tests-date-ruler .trd-day {
    top: 5px;
    transform: translateX(-50%);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.62rem;
    color: var(--muted);
  }
  .tests-date-ruler .trd-day.is-monday {
    color: var(--fg);
    font-weight: 500;
  }
  .tests-date-ruler .trd-day.is-today {
    color: var(--surface);
    background: var(--orchid);
    padding: 2px 5px;
    border-radius: 8px;
    top: 2px;
    line-height: 1;
    font-weight: 600;
  }
  /* Sur la timeline (#tests-container), les labels de tick au-dessus
     du canvas sont devenus inutiles (le ruler sticky les remplace).
     On les masque pour ne pas avoir de doublon visuel. */
  #tests-container .timeline-tick-label {
    display: none;
  }

  /* ════════ Date range picker (Tests + Journal partagés) ════════
     Vit dans view-tests-head, top-right des stats. Visible et actif
     dans les DEUX modes (Timeline et Journal) — single source of
     truth pour la fenêtre lookback. Sémantique : "afficher les
     N derniers jours d'activité + planning futur" (timeline) OU
     "filtrer les entrées des N derniers jours" (journal). */
  .tests-range-picker {
    position: relative;
    margin-left: auto;
    flex-shrink: 0;
  }
  /* Wrapper générique pour les autres pickers temporels vanilla (intervalle ROAS). */
  .trp-wrap { position: relative; display: inline-flex; }
  /* Déclencheur — même esprit que le picker Performance. */
  .trp-trigger {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    padding: 7px 12px;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--fg);
    border-radius: var(--r-sm);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.62rem;
    letter-spacing: 0.04em;
    white-space: nowrap;
  }
  .trp-eyebrow {
    opacity: 0.55;
    text-transform: uppercase;
    font-size: 0.5rem;
    letter-spacing: 0.12em;
  }
  .trp-current { font-weight: 600; }
  .trp-caret { opacity: 0.5; }
  /* Popover : préréglages (colonne gauche) + plage perso (colonne droite). */
  .trp-pop {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 60;
    display: flex;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md, 8px);
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.14);
    overflow: hidden;
  }
  .trp-pop[hidden] { display: none; }
  .trp-presets {
    display: flex;
    flex-direction: column;
    min-width: 130px;
    border-right: 1px solid var(--line);
    padding: 6px;
  }
  .trp-btn {
    background: transparent;
    border: none;
    color: var(--fg);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.68rem;
    letter-spacing: 0.04em;
    padding: 8px 12px;
    text-align: left;
    border-radius: var(--r-sm);
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.12s, color 0.12s;
  }
  .trp-btn:hover { background: var(--depth); }
  .trp-btn.active {
    background: var(--fg);
    color: var(--surface);
  }
  .trp-custom {
    padding: 14px 16px;
    min-width: 200px;
    display: flex;
    flex-direction: column;
    gap: 4px;
  }
  .trp-custom-head {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 6px;
  }
  .trp-custom-lbl {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    color: var(--muted);
    margin-top: 6px;
  }
  .trp-custom-input {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.7rem;
    padding: 6px 8px;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--fg);
    border-radius: var(--r-sm);
  }
  .trp-apply {
    margin-top: 10px;
    padding: 8px 12px;
    border: 1px solid var(--line);
    background: var(--fg);
    color: var(--surface);
    border-radius: var(--r-sm);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    cursor: pointer;
  }
  /* view-tests-head doit être un flex pour que le picker se place
     à droite des stats. Le flex est déjà en place dans la plupart
     des layouts ; on s'assure ici de l'alignement. */
  .view-tests-head {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
  }
  /* En journal mode, on cache UNIQUEMENT le ruler sticky (il vit dans
     #tests-scroll). Le picker reste visible car il sert AUSSI au Journal. */
  .pane-main.mode-journal .tests-date-ruler {
    display: none;
  }

  /* Setup tree node styles */
  .tn {
    position: absolute;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.7rem;
    text-align: center;
    box-shadow: 0 1px 2px var(--line-soft);
    transition: box-shadow 0.18s ease, transform 0.18s ease;
    animation: tnIn 0.3s ease backwards;
    z-index: 10;
  }
  @keyframes tnIn {
    from { opacity: 0; transform: translateX(-6px); }
    to { opacity: 1; transform: translateX(0); }
  }
  .tn.clickable { cursor: pointer; }
  .tn.clickable:hover {
    box-shadow: 0 4px 12px rgba(35, 31, 35, 0.08);
    z-index: 20;
  }

  /* Draggable nodes — show a "move" cursor on the empty parts of the
     node (not on labels/buttons which keep their own cursors). When the
     user is actively dragging, we add .tn-dragging which raises z-index
     and adds a subtle highlight. */
  .tn.tn-draggable { cursor: move; }
  .tn.tn-draggable .canvas-editable { cursor: text; }
  .tn.tn-draggable .tn-add-btn,
  .tn.tn-draggable .tn-delete-btn { cursor: pointer; }
  .tn.tn-dragging {
    z-index: 100 !important;
    box-shadow: 0 8px 22px rgba(26, 26, 26, 0.22) !important;
    opacity: 0.92;
    transition: none !important;
  }

  /* Editable label support — shows a subtle text-cursor on hover and a
     dotted underline on hover to hint that the label can be renamed. */
  .canvas-editable {
    cursor: text;
    border-bottom: 1px dotted transparent;
    transition: border-color 0.15s;
    padding-bottom: 1px;
  }
  .canvas-editable:hover {
    border-bottom-color: currentColor;
    opacity: 0.95;
  }
  .canvas-rename-input {
    background: var(--surface);
    border: 1px solid var(--p-cold);
    color: var(--fg);
    padding: 1px 4px;
    border-radius: 2px;
    outline: none;
    box-sizing: border-box;
    width: 100%;
    min-width: 30px;
  }

  /* Delete (×) button on canvas nodes — invisible by default, visible
     only when the user hovers over the parent node so the canvas stays
     clean. Positioned inside the node so .tn's overflow:hidden doesn't
     clip it. */
  .tn-delete-btn {
    position: absolute;
    top: 2px;
    right: 2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: none;
    background: rgba(35, 31, 35, 0.7);
    color: #fff;
    font-size: 0.72rem;
    line-height: 1;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.15s, background 0.15s, transform 0.15s;
    z-index: 30;
    font-weight: bold;
    pointer-events: auto;
  }
  .tn:hover > .tn-delete-btn {
    opacity: 1;
  }
  .tn-delete-btn:hover {
    background: var(--s-lost, #c5474b) !important;
    transform: scale(1.15);
  }
  /* « + » ajouter une page funnel — placé à gauche du × (suppr.), teinte verte. */
  .tn-add-funnel-btn {
    position: absolute;
    top: 2px;
    right: 22px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: none;
    background: rgba(35, 31, 35, 0.55);
    color: #fff;
    font-size: 0.82rem;
    line-height: 1;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.15s, background 0.15s, transform 0.15s;
    z-index: 30;
    font-weight: bold;
    pointer-events: auto;
  }
  .tn:hover > .tn-add-funnel-btn { opacity: 1; }
  .tn-add-funnel-btn:hover {
    background: var(--s-won, #2f5d43) !important;
    transform: scale(1.15);
  }
  /* Pages funnel : même boîte que la landing, bordure pointillée pour les distinguer. */
  .tn.n-funnel { border-style: dashed !important; }
  /* Smaller variant for the tiny ad cells (height ~20px) */
  .tn-delete-btn-mini {
    width: 12px;
    height: 12px;
    top: 1px;
    right: 1px;
    font-size: 0.55rem;
  }

  /* "→" button on ads — opens the landing picker so the user can
     connect this ad to a different landing or create a new one. Only
     shown on hover (like the delete and add buttons). */
  .tn-landing-link-btn {
    position: absolute;
    top: 1px;
    right: 14px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: none;
    background: rgba(35, 31, 35, 0.7);
    color: #fff;
    font-size: 0.5rem;
    line-height: 1;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.14s, background 0.14s;
    z-index: 4;
  }
  .tn:hover .tn-landing-link-btn,
  .tn-landing-link-btn:focus-visible { opacity: 1; }
  .tn-landing-link-btn:hover { background: var(--mint-edge, #2A6B30); }

  /* Add (+) button on canvas nodes — same visual rules as the × button
     but positioned to its left so they coexist on hover. Creates a new
     child node when clicked. */
  .tn-add-btn {
    position: absolute;
    top: 2px;
    right: 22px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: none;
    background: rgba(35, 31, 35, 0.7);
    color: #fff;
    font-size: 0.78rem;
    line-height: 1;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.15s, background 0.15s, transform 0.15s;
    z-index: 30;
    font-weight: bold;
    pointer-events: auto;
  }
  .tn:hover > .tn-add-btn {
    opacity: 1;
  }
  .tn-add-btn:hover {
    background: var(--p-cold) !important;
    transform: scale(1.15);
  }

  /* Inline form that pops up when the user clicks the + button. It's a
     positioned overlay with an input + Enter/Escape support. */
  .tn-add-form {
    position: absolute;
    top: 100%;
    left: 0;
    margin-top: 4px;
    background: var(--surface);
    border: 1px solid var(--p-cold);
    border-radius: 4px;
    padding: 6px;
    box-shadow: 0 4px 12px rgba(26, 26, 26, 0.18);
    z-index: 40;
    display: flex;
    gap: 4px;
    align-items: center;
    min-width: 180px;
    animation: tnAddFormIn 0.15s ease-out;
  }
  @keyframes tnAddFormIn {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .tn-add-form-input {
    flex: 1;
    min-width: 0;
    font-family: inherit;
    font-size: 0.7rem;
    padding: 0.3rem 0.45rem;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--fg);
    border-radius: 3px;
    outline: none;
  }
  .tn-add-form-input:focus { border-color: var(--p-cold); }
  .tn-add-form-input.shake {
    animation: itemAddShake 0.35s;
    border-color: var(--s-lost, #c5474b);
  }
  .tn-add-form-submit, .tn-add-form-cancel {
    font-family: inherit;
    font-size: 0.6rem;
    padding: 0.3rem 0.45rem;
    border-radius: 3px;
    cursor: pointer;
    border: none;
    flex-shrink: 0;
  }
  .tn-add-form-submit {
    background: var(--p-cold);
    color: var(--surface);
  }
  .tn-add-form-cancel {
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--line);
  }
  .tn-add-form-submit:hover { filter: brightness(1.1); }
  .tn-add-form-cancel:hover { color: var(--fg); border-color: var(--fg); }

  .tn.n-advertiser, .tn.n-landing {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.88rem;
    background: var(--fg);
    color: var(--surface);
    border-color: var(--fg);
    flex-direction: column;
    line-height: 1.1;
    gap: 2px;
    box-shadow: 0 2px 6px rgba(35, 31, 35, 0.12);
  }
  .tn.n-advertiser small, .tn.n-landing small {
    font-family: 'Fragment Mono', monospace;
    font-style: normal;
    font-size: 0.48rem;
    letter-spacing: 0.02em;
    color: rgba(255, 255, 255, 0.55);
    text-transform: uppercase;
  }

  .tn.n-account {
    flex-direction: column;
    gap: 3px;
    border: 1px solid var(--line);
    padding: 0.3rem 0.5rem;
    background: var(--surface);
  }
  .tn-ac-label {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.46rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    text-transform: uppercase;
  }
  .tn-ac-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.95rem;
    line-height: 1;
  }
  .tn-ac-badge {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.46rem;
    letter-spacing: 0.06em;
    padding: 0.1rem 0.36rem;
    background: var(--fg);
    color: var(--surface);
    font-weight: 500;
    border-radius: var(--r-sm);
    text-transform: uppercase;
  }

  .tn.n-campaign {
    flex-direction: column;
    gap: 3px;
    border: 1.5px solid var(--line);
    padding: 0.34rem 0.52rem;
    align-items: flex-start;
    text-align: left;
    justify-content: center;
  }
  .tn.n-campaign.c-test { border-color: var(--p-test-edge); background: var(--p-test-soft); }
  .tn.n-campaign.c-cold { border-color: var(--p-cold-edge); background: var(--p-cold-soft); }
  .tn.n-campaign.c-warm { border-color: var(--p-warm-edge); background: var(--p-warm-soft); }
  .tn.n-campaign.c-ret  { border-color: var(--p-ret-edge);  background: var(--p-ret-soft); }
  .tn-c-top {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    width: 100%;
    gap: 0.3rem;
  }
  .tn-c-phase {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.44rem;
    letter-spacing: 0.06em;
    color: var(--muted);
    font-weight: 500;
    text-transform: uppercase;
  }
  .tn-c-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.95rem;
    line-height: 1;
    width: 100%;
  }

  .tn.n-adset {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 0.35rem;
    padding: 0 0.55rem;
    text-align: left;
    border: 1px solid var(--line);
  }
  .tn.n-adset.c-test { border-color: var(--p-test-edge); }
  .tn.n-adset.c-cold { border-color: var(--p-cold-edge); }
  .tn.n-adset.c-warm { border-color: var(--p-warm-edge); background: var(--surface); }
  .tn.n-adset.c-ret  { border-color: var(--p-ret-edge); }
  .tn-adset-marker {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    color: var(--muted);
    letter-spacing: 0.03em;
    font-variant-numeric: tabular-nums;
  }
  .tn-adset-name {
    font-size: 0.7rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Inline budget / allocation chip + proportional fill on canvas nodes —
     read each campaign / ad-set's € budget and its share (campaign % of
     total, ad-set % of its campaign) straight off the structure. */
  .tn-budget {
    appearance: none;
    border: none;
    background: transparent;
    cursor: pointer;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.46rem;
    letter-spacing: 0.02em;
    color: var(--muted);
    padding: 1px 4px;
    border-radius: 4px;
    white-space: nowrap;
    line-height: 1.2;
    flex-shrink: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: background .12s, color .12s;
  }
  .tn-budget:hover { background: var(--ink-8); color: var(--fg); }
  .tn-budget.set { color: var(--fg); font-weight: 600; }
  /* Dark mode : les nodes CAMPAGNE gardent un fond pastel CLAIR (couleur
     plateforme, posée inline). Le texte secondaire (phase « META · … » +
     puce budget) utilise --muted/--fg qui s'éclaircissent en dark → illisible
     sur le pastel. On force donc un texte SOMBRE, uniquement sur les campagnes
     (les ad-sets ont un fond --surface sombre, on n'y touche pas). */
  :root[data-theme="dark"] .tn.n-campaign .tn-c-phase { color: rgba(35, 31, 35, 0.62); }
  :root[data-theme="dark"] .tn.n-campaign .tn-budget { color: rgba(35, 31, 35, 0.6); }
  :root[data-theme="dark"] .tn.n-campaign .tn-budget.set { color: rgba(35, 31, 35, 0.92); }
  :root[data-theme="dark"] .tn.n-campaign .tn-budget:hover { color: rgba(35, 31, 35, 0.92); background: rgba(35, 31, 35, 0.08); }
  .tn-budget-fill {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 3px;
    background: var(--ink-32);
    border-radius: 0 2px 2px 6px;
    pointer-events: none;
    opacity: 0.9;
    transition: width .18s ease;
  }

  /* Budget-flow animation — "boules" riding the edges (toggle 🌊). The balls
     follow each edge via CSS offset-path; intensity is encoded by the inline
     style (count / size / speed / opacity ∝ the campaign's budget share). */
  @keyframes bf-flow { from { offset-distance: 0%; } to { offset-distance: 100%; } }
  .bf-ball { pointer-events: none; offset-rotate: 0deg; }
  #canvas-budget-flow.active { background: var(--p-test, #5A4ECC); color: #fff; border-color: var(--p-test, #5A4ECC); }
  @media (prefers-reduced-motion: reduce) { .bf-ball { display: none; } }

  /* Ad cells (the small leaves) — tighter radius and font for the small
     scale, plus a subtle background that picks up the phase tint. */
  .tn.tn-ad-cell {
    border-radius: var(--r-sm);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.02em;
    box-shadow: none;
  }
  .tn.tn-ad-cell:hover { box-shadow: 0 2px 6px rgba(35, 31, 35, 0.07); }

  /* Test presence badge on tree nodes */
  .tn-test-badge {
    position: absolute;
    top: -10px;
    right: -10px;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #E11D48;
    color: #fff;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.68rem;
    letter-spacing: 0;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    border: 2px solid var(--bg);
    box-shadow: 0 2px 6px rgba(225, 29, 72, 0.35);
    z-index: 30;
    border-radius: 999px;
    pointer-events: none;
  }
  @keyframes badgePulse {
    0%, 100% { box-shadow: 0 2px 6px rgba(225, 29, 72, 0.35); }
    50% { box-shadow: 0 2px 12px rgba(225, 29, 72, 0.6); }
  }
  .tn-test-badge { animation: badgePulse 2.4s ease-in-out infinite; }

  /* Médailles 🥇🥈🥉 — placées sur le côté supérieur-gauche de chaque
     nœud canvas pour ne pas chevaucher le test-badge (top-right). Sur
     les ad-cells (très petits) on les rétrécit pour rester lisibles. */
  .tn-medal {
    position: absolute;
    top: -10px;
    left: -10px;
    width: 22px;
    height: 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    line-height: 1;
    z-index: 30;
    pointer-events: none;
    /* Léger fond blanc + ombre pour la lisibilité contre fonds
       colorés (campagnes ont des fonds saturés). */
    background: #ffffff;
    border-radius: 999px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
    text-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
  }
  .tn.tn-ad-cell .tn-medal {
    width: 16px;
    height: 16px;
    top: -7px;
    left: -7px;
    font-size: 11px;
  }
  /* Les nœuds désactivés cachent leur médaille pour ne pas être
     trompeurs (un nœud désactivé n'est pas un top performeur). */
  .tn.tn-disabled .tn-medal { display: none; }

  /* Filtre médailles — quand activé via le bouton 🏅 de la zoom-ctrl,
     on dim les nœuds qui n'ont pas de médaille pour mettre en avant
     uniquement les éléments tagués. Les SVG des liens sont conservés
     à pleine opacité pour que la structure reste lisible. Utilise
     :has() pour cibler les .tn qui ne contiennent PAS de .tn-medal. */
  .canvas-medal-filter-on .tn:not(:has(.tn-medal)) {
    opacity: 0.18;
    filter: saturate(0.4);
    transition: opacity 0.2s, filter 0.2s;
  }
  .canvas-medal-filter-on .tn:has(.tn-medal) {
    opacity: 1;
    filter: none;
    /* Petit boost visuel sur les médaillés — ombre prononcée pour
       qu'ils "sortent" du fond grisé des autres. */
    box-shadow: 0 4px 16px rgba(35, 31, 35, 0.18), 0 0 0 2px rgba(212, 175, 55, 0.35);
    transition: box-shadow 0.2s;
  }

  /* Ads issus d'une section Copy — visuellement distincts des ads
     issus de visuels (static / creative). Teinte sable + un petit
     ¶ comme marqueur, pour que l'utilisateur voie immédiatement la
     provenance quand il coche un item dans la sidebar Copy. */
  .tn.tn-ad-cell.tn-ad-copy {
    background-image: linear-gradient(135deg, rgba(212, 175, 55, 0.10) 0%, rgba(212, 175, 55, 0.04) 100%);
    border-style: dashed;
  }
  .tn-ad-kind-icon {
    position: absolute;
    top: 1px;
    right: 14px;  /* au-dessus du landing-link-btn */
    font-size: 9px;
    color: rgba(140, 100, 40, 0.7);
    pointer-events: none;
    font-family: 'Fragment Mono', monospace;
    line-height: 1;
  }

  /* Disabled state — applied via state.canvasOverrides.disabled.
     The node is desaturated, dimmed, and shows a pause overlay so the
     user can see at a glance which elements are off. */
  .tn.tn-disabled {
    filter: grayscale(1) opacity(0.6);
    background: var(--depth) !important;
    border-color: var(--faint) !important;
    color: var(--muted) !important;
  }
  .tn.tn-disabled::after {
    content: "⏸";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 1.4rem;
    color: var(--fg);
    opacity: 0.55;
    pointer-events: none;
    z-index: 5;
  }
  .tn.tn-disabled.tn-ad-cell::after { font-size: 0.85rem; }
  .tn.tn-disabled .tn-test-badge { display: none; }

  /* Right-click context menu on canvas nodes */
  /* Network badge in sidebar campaign header — clickable, shows the
     campaign's current network as a coloured pill. */
  .network-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.32rem;
    padding: 0.12rem 0.42rem;
    border: none;
    border-radius: var(--r-sm);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    cursor: pointer;
    transition: transform 0.12s ease, box-shadow 0.12s ease;
    flex-shrink: 0;
  }
  .network-badge:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 6px rgba(35, 31, 35, 0.18);
  }
  .network-badge-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
  }
  .network-badge-label {
    line-height: 1;
  }

  /* Network picker popover */
  .network-picker {
    position: fixed;
    z-index: 9999;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: 0 12px 32px rgba(35, 31, 35, 0.18);
    min-width: 240px;
    max-width: 320px;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    animation: ctxMenuIn 0.12s ease-out;
    overflow: hidden;
  }
  .network-picker-wide {
    min-width: 280px;
    max-width: 360px;
  }
  .network-picker-step-hint {
    padding: 0.4rem 0.75rem;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.48rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    background: var(--depth-soft, #f5f4f2);
    border-bottom: 1px solid var(--line-soft);
  }
  .network-picker-add-title {
    padding: 0 0 0.3rem;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    text-transform: uppercase;
  }
  .network-picker-phases {
    padding: 0.5rem 0.6rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
  }
  .network-picker-phase {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    gap: 0 0.55rem;
    align-items: center;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
  }
  .network-picker-phase:hover {
    background: var(--surface);
    border-color: var(--fg);
  }
  .network-picker-phase input[type="checkbox"] {
    grid-row: 1 / 3;
    width: 14px;
    height: 14px;
    cursor: pointer;
    accent-color: var(--fg);
  }
  .network-picker-phase-label {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.86rem;
    line-height: 1.1;
  }
  .network-picker-phase-sub {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    text-transform: uppercase;
  }
  .network-picker-actions {
    display: flex;
    gap: 0.4rem;
    padding: 0.5rem 0.6rem 0.6rem;
    border-top: 1px solid var(--line-soft);
  }
  .network-picker-back {
    padding: 0.45rem 0.7rem;
    background: transparent;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    color: var(--muted);
    font-family: inherit;
    font-size: 0.72rem;
    cursor: pointer;
    transition: border-color 0.12s, color 0.12s;
  }
  .network-picker-back:hover {
    border-color: var(--fg);
    color: var(--fg);
  }
  .network-picker-confirm {
    flex: 1;
    padding: 0.5rem 0.8rem;
    background: var(--fg);
    color: var(--surface);
    border: 1px solid var(--fg);
    border-radius: var(--r-sm);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    cursor: pointer;
    transition: opacity 0.12s, transform 0.12s;
  }
  .network-picker-confirm:hover {
    opacity: 0.88;
    transform: translateY(-1px);
  }
  .network-picker-confirm:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    transform: none;
  }
  .network-picker-header {
    padding: 0.55rem 0.75rem 0.4rem;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    border-bottom: 1px solid var(--line-soft);
  }
  .network-picker-list {
    max-height: 280px;
    overflow-y: auto;
    padding: 0.25rem;
  }
  .network-picker-item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    width: 100%;
    padding: 0.42rem 0.55rem;
    background: transparent;
    border: none;
    border-radius: var(--r-sm);
    cursor: pointer;
    text-align: left;
    font-size: 0.78rem;
    color: var(--fg);
    font-family: inherit;
    position: relative;
  }
  .network-picker-item:hover { background: var(--bg); }
  .network-picker-item.is-current { background: var(--bg); font-weight: 600; }
  .network-picker-swatch {
    width: 14px;
    height: 14px;
    border-radius: 4px;
    flex-shrink: 0;
    box-shadow: inset 0 0 0 1px rgba(35, 31, 35, 0.12);
  }
  .network-picker-label { flex: 1; }
  .network-picker-check { color: var(--fg); }
  .network-picker-delete {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: 0 0.3rem;
    font-size: 0.9rem;
    line-height: 1;
    border-radius: 4px;
    opacity: 0;
    transition: opacity 0.12s, color 0.12s;
  }
  .network-picker-item:hover .network-picker-delete { opacity: 0.6; }
  .network-picker-delete:hover { opacity: 1; color: var(--coral); }

  .network-picker-add {
    border-top: 1px solid var(--line-soft);
    padding: 0.55rem 0.55rem 0.6rem;
    background: var(--bg);
  }
  .network-picker-add-row {
    display: flex;
    gap: 0.35rem;
    align-items: center;
  }
  .network-picker-add-name {
    flex: 1;
    padding: 0.28rem 0.42rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    font-family: inherit;
    font-size: 0.72rem;
    color: var(--fg);
  }
  .network-picker-add-name:focus { outline: none; border-color: var(--fg); }
  .network-picker-add-name.shake { animation: shake 0.38s ease; }
  .network-picker-add-color {
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    cursor: pointer;
    flex-shrink: 0;
  }
  .network-picker-add-btn {
    padding: 0.32rem 0.6rem;
    background: var(--fg);
    color: var(--surface);
    border: none;
    border-radius: var(--r-sm);
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    cursor: pointer;
    flex-shrink: 0;
  }
  .network-picker-add-btn:hover { filter: brightness(1.1); }
  @keyframes shake {
    10%, 90% { transform: translateX(-1px); }
    20%, 80% { transform: translateX(2px); }
    30%, 50%, 70% { transform: translateX(-3px); }
    40%, 60% { transform: translateX(3px); }
  }

  .canvas-context-menu {
    position: fixed;
    z-index: 9999;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: 0 8px 24px rgba(35, 31, 35, 0.18);
    padding: 0.3rem;
    min-width: 220px;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    font-size: 0.78rem;
    animation: ctxMenuIn 0.12s ease-out;
  }
  @keyframes ctxMenuIn {
    from { opacity: 0; transform: translateY(-4px) scale(0.97); }
    to { opacity: 1; transform: translateY(0) scale(1); }
  }
  .canvas-context-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.5rem 0.7rem;
    background: transparent;
    border: none;
    border-radius: var(--r-sm);
    cursor: pointer;
    text-align: left;
    color: var(--fg);
    font-family: inherit;
    font-size: inherit;
  }
  .canvas-context-item:hover { background: var(--bg); }
  .canvas-context-item .ctx-icon {
    font-size: 0.95rem;
    color: var(--s-live);
    width: 1.1rem;
    text-align: center;
  }
  .canvas-context-item strong { font-weight: 600; }
  .canvas-context-note {
    padding: 0.5rem 0.7rem;
    color: var(--muted);
    font-style: italic;
    font-size: 0.7rem;
  }
  .tn.has-tests,
  .tn.has-tests.n-campaign,
  .tn.has-tests.n-adset,
  .tn.has-tests.n-landing {
    box-shadow: 0 0 0 2px var(--s-live-edge), 0 4px 12px rgba(35, 31, 35, 0.08);
  }

  /* Phase 3.B.19 — Multi-sélection sur le canvas Setup via Cmd+clic
     (⌘ Mac) ou Ctrl+clic (Windows/Linux). L'utilisateur ajoute/retire
     les nœuds un par un ; la sélection reste homogène (1 seul type).
     Un pill flottant top-right affiche le compteur + un bouton
     "Actions" qui ouvre le menu d'actions groupées. */
  .tn.tn-multi-selected {
    box-shadow:
      0 0 0 2px rgba(67, 122, 235, 0.75),
      0 6px 14px rgba(35, 31, 35, 0.16) !important;
  }
  .canvas-multi-pill {
    position: fixed;
    top: 90px;
    right: 20px;
    z-index: 8500;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: 0 6px 18px rgba(35, 31, 35, 0.16);
    padding: 0.45rem 0.6rem 0.45rem 0.75rem;
    display: flex;
    align-items: center;
    gap: 0.65rem;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    font-size: 0.8rem;
    color: var(--fg);
    animation: ctxMenuIn 0.16s ease-out;
  }
  .canvas-multi-pill-count {
    font-weight: 600;
    line-height: 1.1;
  }
  .canvas-multi-pill-hint {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.62rem;
    color: var(--muted);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    line-height: 1.1;
    border-left: 1px solid var(--line);
    padding-left: 0.55rem;
  }
  .canvas-multi-pill-actions {
    border: 1px solid var(--line);
    background: var(--bg);
    border-radius: var(--r-sm);
    padding: 0.3rem 0.6rem;
    font-family: inherit;
    font-size: 0.78rem;
    color: var(--fg);
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
  }
  .canvas-multi-pill-actions:hover {
    background: var(--surface);
    border-color: var(--fg);
  }
  .canvas-multi-pill-clear {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    font-size: 1.1rem;
    line-height: 1;
    padding: 0.1rem 0.3rem;
    border-radius: var(--r-sm);
  }
  .canvas-multi-pill-clear:hover {
    color: var(--fg);
    background: var(--bg);
  }

  /* Edges — kept BELOW the nodes (lower z-index) so the bezier curves
     don't paint on top of the cards and visually clip them. */
  .tree-svg {
    position: absolute;
    top: 0; left: 0;
    overflow: visible;
    pointer-events: none;
    z-index: 1;
  }
  .edge {
    fill: none;
    stroke: var(--faint);
    stroke-width: 1.25;
    stroke-linecap: round;
  }
  .edge.e-test { stroke: var(--p-test-edge); }
  .edge.e-cold { stroke: var(--p-cold-edge); }
  .edge.e-warm { stroke: var(--p-warm-edge); }
  .edge.e-ret  { stroke: var(--p-ret-edge); }
  .edge.e-neutral { stroke: var(--muted); opacity: 0.55; }

  /* ═══════════ TESTS VIEW ═══════════ */
  .tests-list {
    padding: 0 1.75rem;
  }
  .test-card {
    margin-bottom: 0.5rem;
    padding: 0.85rem 1rem;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    cursor: pointer;
    transition: all 0.12s ease;
    position: relative;
    border-radius: var(--r-md);
  }
  .test-card:hover {
    border-color: var(--line);
    box-shadow: 0 1px 3px rgba(35, 31, 35, 0.04);
  }
  .test-card.selected {
    border-color: var(--fg);
    box-shadow: 0 0 0 1px var(--fg);
  }
  .test-card.s-won::before,
  .test-card.s-lost::before,
  .test-card.s-incon::before,
  .test-card.s-live::before,
  .test-card.s-plan::before {
    content: '';
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 3px;
    border-radius: var(--r-md) 0 0 var(--r-md);
  }
  .test-card.s-won::before { background: var(--s-won); }
  .test-card.s-lost::before { background: var(--s-lost); }
  .test-card.s-incon::before { background: var(--s-incon); }
  .test-card.s-live::before { background: var(--s-live); }
  .test-card.s-plan::before { background: var(--s-plan); }

  .test-card-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.4rem;
  }
  .test-ref {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.62rem;
    color: var(--muted);
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
    font-weight: 500;
  }
  .test-card-actions { display: flex; gap: 0.3rem; align-items: center; }
  .test-status-badge {
    font-size: 0.6rem;
    letter-spacing: -0.005em;
    padding: 0.15rem 0.5rem;
    font-weight: 500;
    border-radius: 999px;
    background: var(--surface);
  }
  .test-status-badge.s-won { color: var(--s-won); background: var(--s-won-soft); }
  .test-status-badge.s-lost { color: var(--s-lost); background: var(--s-lost-soft); }
  .test-status-badge.s-incon { color: var(--s-incon); background: var(--s-incon-soft); }
  .test-status-badge.s-live { color: var(--s-live); background: var(--s-live-soft); }
  .test-status-badge.s-plan { color: var(--s-plan); background: var(--s-plan-soft); }
  .test-status-badge.s-draft    { color: var(--muted);  background: var(--depth); }
  .test-status-badge.s-review   { color: var(--p-test); background: var(--orchid-soft); }
  .test-status-badge.s-approved { color: var(--p-cold); background: var(--mint-soft); }
  .test-status-badge.s-done     { color: var(--fg);     background: var(--depth); }
  .card-del-btn {
    background: transparent; border: none; color: var(--muted); cursor: pointer;
    font-size: 1.1rem; line-height: 1; padding: 0 0.3rem;
    opacity: 0; transition: opacity 0.15s, color 0.15s;
    border-radius: var(--r-sm);
  }
  .test-card:hover .card-del-btn { opacity: 0.45; }
  .card-del-btn:hover { opacity: 1 !important; color: var(--coral); }

  .test-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-size: 1.15rem;
    font-style: italic;
    font-weight: 400;
    line-height: 1.2;
    margin-bottom: 0.35rem;
    letter-spacing: -0.01em;
  }
  .test-target {
    font-size: 0.7rem;
    color: var(--muted);
    margin-bottom: 0.4rem;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
  }
  .target-kind {
    font-size: 0.55rem;
    letter-spacing: 0.04em;
    padding: 0.1rem 0.4rem;
    background: var(--depth);
    border-radius: 999px;
    font-weight: 500;
  }
  .target-kind.t-campaign { background: var(--mint-soft); color: var(--p-cold); }
  .target-kind.t-audience { background: var(--peach-soft); color: var(--p-warm); }
  .target-kind.t-creative { background: var(--orchid-soft); color: var(--p-test); }
  .target-kind.t-landing  { background: var(--sky-soft); color: var(--s-live); }
  .target-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 220px;
    font-weight: 500;
    color: var(--fg);
  }
  .target-missing {
    color: var(--coral);
    font-style: italic;
  }
  .target-link {
    cursor: pointer;
    transition: color 0.15s;
    text-decoration: underline;
    text-decoration-color: var(--faint);
    text-underline-offset: 2px;
  }
  .target-link:hover {
    color: var(--p-test);
    text-decoration-color: var(--p-test);
  }

  /* Pulse animation for highlighted setup node */
  .pulse-focus {
    animation: pulseFocus 0.7s ease-out 3;
    position: relative;
    z-index: 40 !important;
  }
  @keyframes pulseFocus {
    0%   { box-shadow: 0 0 0 0 rgba(90, 78, 204, 0.6), 0 0 0 2px var(--p-test); }
    50%  { box-shadow: 0 0 0 14px rgba(90, 78, 204, 0), 0 0 0 2px var(--p-test); }
    100% { box-shadow: 0 0 0 0 rgba(90, 78, 204, 0), 0 0 0 2px var(--p-test); }
  }

  .test-hypothesis {
    font-size: 0.74rem;
    color: var(--muted);
    line-height: 1.5;
    margin-bottom: 0.55rem;
  }
  .test-meta {
    display: flex;
    gap: 0.35rem;
    flex-wrap: wrap;
    font-size: 0.62rem;
  }
  .test-meta .chip {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    background: var(--depth);
    padding: 0.18rem 0.5rem;
    border-radius: 999px;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
  }
  .test-meta .chip.lift.pos { color: var(--s-won); background: var(--s-won-soft); }
  .test-meta .chip.lift.neg { color: var(--s-lost); background: var(--s-lost-soft); }

  /* Test timeline nodes (simplified) */
  .test-node {
    position: absolute;
    background: var(--surface);
    border: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    z-index: 10;
    user-select: none;
    cursor: grab;
    animation: tnIn 0.3s ease backwards;
    border-radius: var(--r-md);
    box-shadow: 0 1px 2px rgba(35, 31, 35, 0.04);
  }
  .test-node.dragging {
    cursor: grabbing;
    z-index: 200;
    box-shadow: 0 8px 20px rgba(35, 31, 35, 0.18);
    /* Kill any pending entry animation so it doesn't fight with the
       live left/top updates during the drag. */
    animation: none !important;
    transition: none !important;
  }
  .test-node.drop-invalid { box-shadow: 0 0 0 2px var(--coral); }
  .test-node.drop-valid { box-shadow: 0 0 0 2px var(--mint); }
  /* drop-snap : la position actuelle est en collision, mais le drop
     snappera automatiquement sur le slot valide le plus proche. On
     teinte en orchid (orange-violet) pour différencier visuellement de
     "rouge = bloqué" — l'user sait que lâcher la souris marchera. */
  .test-node.drop-snap { box-shadow: 0 0 0 2px var(--orchid); }
  /* silent-render : appliqué au container quand un drag/resize vient de
     commit. Désactive l'entry animation des cards (sinon l'user voit
     toutes les cards re-fader en cascade après chaque interaction —
     l'impression d'un "refresh complet" au lieu d'un déplacement ciblé). */
  #tests-container.silent-render .test-node {
    animation: none !important;
  }
  .test-node.s-won { border-color: var(--mint); }
  .test-node.s-lost { border-color: var(--coral-soft); }
  .test-node.s-incon { border-color: var(--sand); }
  .test-node.s-live { border-color: var(--sky); }
  .test-node.s-plan { border-color: var(--orchid); border-style: dashed; }

  /* ════════ Test node quick actions (hover only) ════════
     Mirrors the ✕ pattern from Structure: small buttons appear in the
     top-right corner of the test node when hovered. ↗ jumps to the
     targeted element in Structure, ✕ deletes the test (with confirm). */
  .tn-quick-actions {
    position: absolute;
    top: 3px;
    right: 3px;
    display: flex;
    gap: 2px;
    z-index: 20;
    opacity: 0;
    transition: opacity 0.15s;
    pointer-events: none;
  }
  .test-node:hover .tn-quick-actions {
    opacity: 1;
    pointer-events: auto;
  }
  .tn-qa-btn {
    width: 18px;
    height: 18px;
    border: 1px solid var(--line);
    background: var(--surface);
    border-radius: 3px;
    cursor: pointer;
    font-size: 11px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    line-height: 1;
    font-family: inherit;
    box-shadow: 0 1px 2px rgba(35, 31, 35, 0.06);
  }
  .tn-qa-btn:hover {
    border-color: var(--fg);
    color: var(--fg);
    background: var(--bg);
  }
  .tn-qa-btn.tn-qa-jump:hover {
    border-color: var(--p-cold);
    color: var(--p-cold);
    background: var(--mint-soft);
  }
  .tn-qa-btn.tn-qa-delete:hover {
    border-color: var(--s-lost);
    color: var(--s-lost);
    background: var(--coral-soft);
  }

  /* ════════ Context menu (right-click on test node) ════════ */
  .context-menu {
    position: fixed;
    z-index: 9999;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: 0 8px 24px rgba(35, 31, 35, 0.18);
    min-width: 200px;
    padding: 4px;
    animation: ctxIn 0.12s ease-out;
  }
  @keyframes ctxIn {
    from { opacity: 0; transform: translateY(-3px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .ctx-item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    width: 100%;
    padding: 0.5rem 0.65rem;
    background: transparent;
    border: none;
    border-radius: var(--r-sm);
    cursor: pointer;
    font-family: inherit;
    font-size: 0.73rem;
    color: var(--fg);
    text-align: left;
    transition: background 0.1s;
  }
  .ctx-item:hover { background: var(--bg); }
  .ctx-item.ctx-danger { color: var(--s-lost); }
  .ctx-item.ctx-danger:hover { background: var(--coral-soft); }
  .ctx-icon {
    width: 14px;
    text-align: center;
    font-size: 12px;
    color: var(--muted);
    flex-shrink: 0;
  }
  .ctx-item:hover .ctx-icon { color: var(--fg); }
  .ctx-item.ctx-danger .ctx-icon { color: var(--s-lost); }
  .ctx-divider {
    height: 1px;
    background: var(--line-soft);
    margin: 4px 6px;
  }

  /* ════════ Canvas toast (small bottom notification) ════════ */
  .canvas-toast {
    position: fixed;
    bottom: 32px;
    left: 50%;
    transform: translateX(-50%) translateY(16px);
    background: var(--fg);
    color: var(--surface);
    padding: 0.7rem 1.1rem;
    border-radius: var(--r-md);
    font-size: 0.7rem;
    line-height: 1.4;
    z-index: 9999;
    opacity: 0;
    transition: opacity 0.25s, transform 0.25s;
    pointer-events: none;
    max-width: 420px;
    text-align: center;
    box-shadow: 0 8px 24px rgba(35, 31, 35, 0.25);
  }
  .canvas-toast.show {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }

  /* ════════ Jump flash highlight on Structure canvas ════════
     Brief mint pulse around a Structure node when the user clicked
     "Voir dans Structure" on a test. Helps locate the just-jumped-to
     element. */
  .tn.tn-jump-flash {
    animation: jumpFlash 2s ease-out;
    z-index: 100 !important;
  }
  @keyframes jumpFlash {
    0%   { box-shadow: 0 0 0 0 rgba(42, 107, 48, 0); }
    15%  { box-shadow: 0 0 0 3px var(--p-cold), 0 0 0 12px rgba(42, 107, 48, 0.25); }
    50%  { box-shadow: 0 0 0 3px var(--p-cold), 0 0 0 18px rgba(42, 107, 48, 0); }
    100% { box-shadow: 0 0 0 0 rgba(42, 107, 48, 0); }
  }
  .test-node.s-draft    { border-color: var(--line); border-style: dotted; }
  .test-node.s-review   { border-color: var(--orchid); border-style: dashed; }
  .test-node.s-approved { border-color: var(--mint); border-style: dashed; }
  .test-node.s-done     { border-color: var(--depth); opacity: 0.7; }

  .test-node-header {
    padding: 0.45rem 0.65rem 0.35rem;
    flex-shrink: 0;
  }
  .test-node.s-won .test-node-header { background: var(--mint-soft); }
  .test-node.s-lost .test-node-header { background: var(--coral-soft); }
  .test-node.s-incon .test-node-header { background: var(--sand-soft); }
  .test-node.s-live .test-node-header { background: var(--sky-soft); }
  .test-node.s-plan .test-node-header { background: var(--orchid-soft); }
  .test-node.s-draft    .test-node-header { background: var(--depth); }
  .test-node.s-review   .test-node-header { background: var(--orchid-soft); }
  .test-node.s-approved .test-node-header { background: var(--mint-soft); }
  .test-node.s-done     .test-node-header { background: var(--depth); }

  .tn-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.25rem;
    font-size: 0.6rem;
    color: var(--muted);
    font-weight: 500;
  }
  .tn-status-lbl { font-weight: 500; }
  .test-node.s-won .tn-status-lbl { color: var(--s-won); }
  .test-node.s-lost .tn-status-lbl { color: var(--s-lost); }
  .test-node.s-incon .tn-status-lbl { color: var(--p-ret); }
  .test-node.s-live .tn-status-lbl { color: var(--s-live); }
  .test-node.s-plan .tn-status-lbl { color: var(--p-test); }
  .test-node.s-draft    .tn-status-lbl { color: var(--muted); }
  .test-node.s-review   .tn-status-lbl { color: var(--p-test); }
  .test-node.s-approved .tn-status-lbl { color: var(--p-cold); }

  /* ── Dark mode ──────────────────────────────────────────────────────
     The per-status .test-node-header backgrounds use light *-soft tokens
     that intentionally don't flip, but the .test-node-name title and
     .tn-row meta use --fg / --muted which DO flip (→ light) — so the
     title was invisible on the light header. Tint the headers dark
     instead and lift the status labels to their lighter edge colours. */
  :root[data-theme="dark"] .test-node.s-won .test-node-header,
  :root[data-theme="dark"] .test-node.s-approved .test-node-header { background: rgba(143, 224, 149, 0.13); }
  :root[data-theme="dark"] .test-node.s-lost .test-node-header { background: rgba(242, 166, 164, 0.13); }
  :root[data-theme="dark"] .test-node.s-incon .test-node-header { background: rgba(224, 198, 111, 0.13); }
  :root[data-theme="dark"] .test-node.s-live .test-node-header { background: rgba(166, 202, 247, 0.13); }
  :root[data-theme="dark"] .test-node.s-plan .test-node-header,
  :root[data-theme="dark"] .test-node.s-review .test-node-header { background: rgba(184, 166, 245, 0.13); }
  :root[data-theme="dark"] .test-node.is-scaling .test-node-header { background: rgba(224, 198, 111, 0.13) !important; }
  :root[data-theme="dark"] .test-node.s-won .tn-status-lbl,
  :root[data-theme="dark"] .test-node.s-approved .tn-status-lbl { color: var(--p-cold-edge); }
  :root[data-theme="dark"] .test-node.s-lost .tn-status-lbl { color: var(--coral); }
  :root[data-theme="dark"] .test-node.s-incon .tn-status-lbl,
  :root[data-theme="dark"] .test-node.is-scaling .tn-status-lbl { color: var(--p-ret-edge) !important; }
  :root[data-theme="dark"] .test-node.s-live .tn-status-lbl { color: var(--s-live-edge); }
  :root[data-theme="dark"] .test-node.s-plan .tn-status-lbl,
  :root[data-theme="dark"] .test-node.s-review .tn-status-lbl { color: var(--p-test-edge); }
  .test-node.s-done     .tn-status-lbl { color: var(--fg); }

  /* Market-sizing dark-mode legibility — pastel boxes (sand banner + mint summary)
     keep a LIGHT background in dark mode, so their theme-var text (--fg / --ink-64)
     turns light and becomes illegible. Force fixed dark ink on those boxes.
     The !important is required to beat the inline `color: var(--…)` on children. */
  :root[data-theme="dark"] .ms-darkfix,
  :root[data-theme="dark"] .ms-darkfix strong { color: #231f23 !important; }
  :root[data-theme="dark"] .ms-darkfix [style*="ink-64"] { color: rgba(35, 31, 35, 0.64) !important; }

  .test-node-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 0.95rem;
    line-height: 1.15;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-top: 3px;
    letter-spacing: -0.01em;
  }
  .test-node-target {
    font-size: 0.5rem;
    color: var(--muted);
    letter-spacing: 0.04em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-top: 1px;
  }
  .test-node-target .k {
        letter-spacing: 0;
    font-size: 0.46rem;
    margin-right: 0.25rem;
  }
  .test-node-target .k.t-audience { color: var(--p-warm); }
  .test-node-target .k.t-creative { color: var(--p-test); }
  .test-node-target .k.t-campaign { color: var(--p-cold); }
  .test-node-target .k.t-landing  { color: var(--fg); }

  .variants-bar {
    display: flex;
    flex: 1;
    min-height: 18px;
    border-top: 1px solid currentColor;
  }
  .variants-bar.empty { display: none; }

  /* ═══════════════════════════════════════════════════════════════
     SCALING — kind === 'scaling' tests use these styles instead of
     the A/B variants bar.
     The whole test-node is YELLOW (sand) so the user spots a scaling
     in 0.1s vs an A/B test on the timeline. Direction (↑/↓) is
     conveyed by arrow + colour intensity inside the deltas strip.
     ═══════════════════════════════════════════════════════════════ */
  .test-node.is-scaling {
    background: var(--sand-soft) !important;
    border-color: var(--s-incon) !important;
  }
  .test-node.is-scaling .test-node-name { color: var(--fg); }
  .test-node.is-scaling .tn-status-lbl { color: var(--p-ret); }
  /* Override the per-status background of .test-node-header so it
     stays sand even when the scaling is in 'live'/'won'/'plan' status.
     Same for the tiny dashed setup overlay on the left edge: we keep
     it grey-ish so it doesn't clash with the sand fill. */
  .test-node.is-scaling .test-node-header { background: var(--sand-soft) !important; }
  .test-node.is-scaling .tn-setup-overlay { color: var(--p-ret) !important; background-color: rgba(244, 231, 199, 0.5) !important; opacity: 0.4 !important; }

  /* Phase 3.B.21+ — Markers de transition de palier sur les
     test-node scaling. Lignes verticales subtiles qui matérialisent
     l'instant où le budget change, distribuées proportionnellement
     à la durée. Pointer-events:none pour ne pas bloquer la card.

     Positionnées en absolute par rapport au .test-node parent
     (qui est position:relative).

     Couleurs :
       · scl-tr-up    → vert (var(--s-won)) avec opacity .35
       · scl-tr-down  → rouge (var(--s-lost)) avec opacity .35
       · scl-tr-flat  → gris (var(--muted)) avec opacity .25 */
  .scl-transitions {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 2;
  }
  .scl-transition {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    transform: translateX(-0.5px);
    pointer-events: auto; /* re-active pour le title hover */
    cursor: help;
  }
  .scl-transition.scl-tr-up {
    background: var(--s-won, #2A6B30);
    opacity: 0.35;
  }
  .scl-transition.scl-tr-down {
    background: var(--s-lost, #C53D2C);
    opacity: 0.35;
  }
  .scl-transition.scl-tr-flat {
    background: var(--muted, #8A8276);
    opacity: 0.25;
  }
  /* Hover : on intensifie pour signaler la transition active. */
  .scl-transition:hover {
    opacity: 0.7;
    width: 2px;
    transform: translateX(-1px);
  }

  /* Compact one-line deltas strip — replaces the staircase chart that
     was getting clipped inside the test-node. Each step shows label +
     delta in % vs the previous step. Flex space-between so 4-5 steps
     fit comfortably even on a narrow node. */
  .scaling-deltas {
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: flex-start;
    gap: 0.4rem;
    padding: 0.2rem 0.4rem;
    overflow: hidden;
    border-top: 1px solid currentColor;
    background: rgba(255, 255, 255, 0.55);
    min-height: 22px;
    flex-wrap: nowrap;
  }
  .scaling-deltas.empty {
    justify-content: center;
    color: var(--muted);
    font-style: italic;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
  }
  .scl-step {
    display: inline-flex;
    align-items: baseline;
    gap: 0.2rem;
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
  }
  .scl-step + .scl-step::before {
    content: '·';
    color: var(--muted);
    margin-right: 0.2rem;
    flex-shrink: 0;
  }
  .scl-step-label {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    flex-shrink: 0;
  }
  .scl-step-delta {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    font-weight: 600;
    color: var(--fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .scl-up .scl-step-delta,
  .scl-down .scl-step-delta { color: var(--p-ret); }

  .v-cell {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.28rem;
    padding: 0.1rem 0.25rem;
    font-size: 0.54rem;
    letter-spacing: 0.06em;
        font-weight: 500;
    overflow: hidden;
    position: relative;
  }
  .v-cell + .v-cell { border-left: 1px solid var(--line); }
  .v-letter {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 13px; height: 13px;
    font-size: 0.55rem;
    font-weight: 700;
    background: var(--surface);
    color: var(--fg);
    flex-shrink: 0;
  }
  .v-lbl {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .v-cell.v-winner { background: var(--s-won-soft); color: var(--s-won); font-weight: 700; }
  .v-cell.v-winner .v-letter { background: var(--s-won); color: var(--surface); }
  .v-cell.v-loser { background: var(--s-lost-soft); color: var(--s-lost); }
  .v-cell.v-loser .v-letter { background: var(--s-lost); color: var(--surface); }
  .v-cell.v-incon { background: var(--s-incon-soft); color: var(--s-incon); }
  .v-cell.v-live  { background: var(--s-live-soft); color: var(--s-live); }
  .v-cell.v-plan  { background: var(--s-plan-soft); color: var(--s-plan); }
  .v-cell.v-neutral { background: var(--surface); color: var(--muted); }

  .tn-resize {
    position: absolute;
    top: 0; right: 0;
    /* Largeur ÉCRAN CONSTANTE : la card est dans un canvas scalé (transform:
       scale(zoom)), donc une largeur fixe en px mesurerait 7×zoom à l'écran.
       On divise par --tests-zoom (posée par applyTransformTests) → ~7px écran
       à tout zoom. Sans ça, à fort zoom la zone resize empiétait sur le corps
       perçu de la card → un MOVE partait en RESIZE (« s'agrandit tout seul »). */
    width: calc(7px / var(--tests-zoom, 1));
    height: 100%;
    cursor: ew-resize;
    opacity: 0;
    z-index: 1;
    /* Inerte hors survol (sécurité supplémentaire). Réactivée au survol. */
    pointer-events: none;
  }
  .test-node:hover .tn-resize { opacity: 1; pointer-events: auto; }
  .tn-resize::after {
    content: '';
    position: absolute;
    right: 2px; top: 50%;
    transform: translateY(-50%);
    width: 2px; height: 40%;
    background: var(--muted);
  }

  /* Timeline axis + swimlanes */
  .timeline-axis {
    position: absolute;
    height: 1px;
    background: var(--fg);
    pointer-events: none;
  }
  .timeline-now {
    position: absolute;
    width: 1px;
    background: var(--s-lost);
    pointer-events: none;
  }
  .timeline-now::before {
    content: 'NOW';
    position: absolute;
    top: -16px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 0.54rem;
    letter-spacing: 0;
    color: var(--s-lost);
    background: var(--bg);
    padding: 0 0.3rem;
    font-weight: 500;
  }
  .timeline-tick {
    position: absolute;
    top: -4px;
    bottom: -4px;
    width: 1px;
    background: var(--line);
    pointer-events: none;
  }
  .timeline-tick-label {
    position: absolute;
    transform: translateX(-50%);
    font-size: 0.52rem;
    letter-spacing: 0;
    color: var(--muted);
        font-variant-numeric: tabular-nums;
    pointer-events: none;
  }
  .swimlane-label {
    position: absolute;
    left: 0;
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.82rem;
    color: var(--muted);
    pointer-events: none;
    transform: translateY(-50%);
    padding-right: 0.5rem;
    background: var(--bg);
    z-index: 2;
  }
  .swimlane-divider {
    position: absolute;
    left: 0; right: 0;
    border-top: 1px solid var(--line-soft);
    pointer-events: none;
  }

  .drag-tooltip {
    position: fixed;
    background: var(--fg);
    color: var(--surface);
    padding: 0.35rem 0.6rem;
    font-size: 0.6rem;
    letter-spacing: 0;
    z-index: 300;
    pointer-events: none;
    white-space: nowrap;
    border: 1px solid var(--fg);
  }
  .drag-tooltip.invalid { background: var(--s-lost); border-color: var(--s-lost); }
  /* snap : position en collision mais snap auto disponible — orchid pour
     matcher la card .drop-snap. L'user comprend "ça va atterrir ailleurs". */
  .drag-tooltip.snap { background: var(--orchid); border-color: var(--orchid); }
  .drag-tooltip .date {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    text-transform: none;
    font-size: 0.9rem;
    letter-spacing: 0;
  }
  .drag-tooltip .reason {
    display: block;
    color: var(--faint);
    font-size: 0.52rem;
    margin-top: 0.15rem;
  }

  /* Zoom + empty */
  .zoom-ctrl {
    position: absolute;
    top: 1rem;
    right: 1.1rem;
    display: flex;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    /* Ombre plus marquée pour que les contrôles "lévitent" et
       restent visibles même au-dessus de nœuds colorés. Le user
       doit pouvoir les trouver immédiatement quand il cherche à
       recentrer le schéma. */
    box-shadow: 0 4px 16px rgba(35, 31, 35, 0.12), 0 1px 3px rgba(35, 31, 35, 0.08);
    overflow: hidden;
    z-index: 50;
  }
  .z-btn {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.74rem;
    background: transparent;
    border: none;
    border-right: 1px solid var(--line);
    padding: 0.58rem 0.72rem;
    cursor: pointer;
    color: var(--fg);
    min-width: 2.4rem;
    font-variant-numeric: tabular-nums;
    transition: background 0.12s, color 0.12s;
  }
  .z-btn:last-child { border-right: none; }
  .z-btn:hover { background: var(--fg); color: var(--surface); }
  .z-btn.wide { min-width: 3.2rem; }
  /* Variante "active" — pour le bouton filtre médailles quand activé */
  .z-btn.is-active {
    background: var(--fg);
    color: var(--surface);
  }
  .z-btn.is-active:hover {
    background: var(--fg);
    opacity: 0.85;
  }

  .empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 60vh;
    text-align: center;
    color: var(--muted);
    padding: 2rem;
  }
  .empty-state .icon {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-size: 3.5rem;
    font-style: italic;
    color: var(--faint);
    margin-bottom: 0.8rem;
  }
  .empty-state h3 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-size: 1.2rem;
    color: var(--fg);
    margin-bottom: 0.3rem;
  }
  .empty-state p { font-size: 0.7rem; max-width: 40ch; line-height: 1.5; }
  .empty-state button {
    margin-top: 1.25rem;
    padding: 0.55rem 1rem;
    background: var(--fg);
    color: var(--surface);
    border: none;
    font-family: inherit;
    font-size: 0.66rem;
    letter-spacing: 0;
    cursor: pointer;
  }

  /* ═══════════ ACTIONS (change log) ═══════════ */
  .actions-toolbar {
    padding: 0 1.75rem 0.6rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
  }
  .at-label {
    font-size: 0.58rem;
    letter-spacing: 0;
    color: var(--muted);
    display: flex;
    align-items: baseline;
    gap: 0.3rem;
  }
  .at-count {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.95rem;
    color: var(--fg);
    letter-spacing: 0;
  }
  .at-btns {
    display: flex;
    gap: 0.3rem;
  }
  .at-btns .tb-btn {
    font-size: 0.56rem;
    padding: 0.32rem 0.55rem;
    cursor: pointer;
  }

  .actions-list {
    padding: 0 1.75rem;
    margin-bottom: 1.2rem;
    max-height: 200px;
    overflow-y: auto;
  }
  .action-item {
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.45rem 0.6rem;
    margin-bottom: 0.3rem;
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 0.5rem;
    align-items: center;
    cursor: pointer;
    font-size: 0.62rem;
    transition: border-color 0.15s;
    position: relative;
  }
  .action-item:hover { border-color: var(--fg); }
  .action-item.selected { border-color: var(--fg); border-width: 2px; padding: calc(0.45rem - 1px) calc(0.6rem - 1px); }

  .action-diamond {
    display: inline-block;
    width: 10px;
    height: 10px;
    transform: rotate(45deg);
    background: var(--muted);
    flex-shrink: 0;
  }
  .action-diamond.ak-budget { background: var(--p-cold); }
  .action-diamond.ak-pause { background: var(--s-lost); }
  .action-diamond.ak-resume { background: var(--s-won); }
  .action-diamond.ak-audience { background: var(--p-warm); }
  .action-diamond.ak-creative { background: var(--p-test); }
  .action-diamond.ak-bid { background: var(--p-ret); }
  .action-diamond.ak-other { background: var(--muted); }

  .action-body { min-width: 0; }
  .action-date {
    font-size: 0.5rem;
    letter-spacing: 0;
    color: var(--muted);
        font-variant-numeric: tabular-nums;
  }
  .action-text {
    font-size: 0.64rem;
    margin-top: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .action-source {
    font-size: 0.46rem;
    letter-spacing: 0;
    color: var(--muted);
    padding: 0.08rem 0.3rem;
    border: 1px solid var(--line);
    background: var(--surface);
  }
  .action-source.src-meta { color: var(--p-cold); border-color: var(--p-cold-edge); }
  .action-source.src-google { color: var(--p-warm); border-color: var(--p-warm-edge); }
  .action-source.src-manual { color: var(--muted); }
  .action-del-btn {
    position: absolute;
    top: 2px;
    right: 4px;
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    font-size: 0.9rem;
    padding: 0 0.25rem;
    opacity: 0;
    line-height: 1;
    transition: opacity 0.15s, color 0.15s;
  }
  .action-item:hover .action-del-btn { opacity: 0.5; }
  .action-del-btn:hover { opacity: 1 !important; color: var(--s-lost); }

  /* ── Modifications remontées par les connecteurs (lecture seule) ── */
  /* Groupe hebdo dépliable : 4ᵉ colonne pour le chevron. */
  .action-item.action-week { grid-template-columns: auto 1fr auto auto; }
  .action-diamond.ak-week { background: var(--p-test); opacity: 0.9; }
  .action-source.src-connected {
    color: var(--p-test);
    border-color: var(--p-test);
    border-style: dashed;
  }
  .action-week-chevron { font-size: 0.7rem; color: var(--muted); line-height: 1; }
  .action-week-children {
    margin: -0.1rem 0 0.4rem 0;
    padding: 0.3rem 0.55rem 0.3rem 1.05rem;
    border: 1px solid var(--line);
    border-top: none;
    background: var(--surface);
  }
  .action-subitem {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.5rem;
    align-items: center;
    padding: 0.25rem 0;
    border-bottom: 1px dashed var(--line);
  }
  .action-subitem:last-child { border-bottom: none; }

  /* Tooltip des marqueurs d'action / semaine (était non stylé). */
  .action-tooltip {
    position: fixed;
    z-index: 1000;
    pointer-events: none;
    max-width: 340px;
    background: var(--fg);
    color: var(--bg);
    border: 1px solid var(--fg);
    padding: 0.4rem 0.55rem;
    font-size: 0.55rem;
    line-height: 1.5;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
  }
  .action-tooltip .a-title { font-weight: 700; display: block; margin-bottom: 2px; }
  .action-tooltip .a-meta { opacity: 0.7; margin-top: 2px; }
  .action-tooltip .a-week-list {
    margin: 4px 0 0 0;
    padding: 0 0 0 14px;
    font-size: 0.5rem;
    line-height: 1.5;
  }
  .action-tooltip .a-week-list li { margin: 1px 0; }

  .actions-empty {
    font-size: 0.62rem;
    color: var(--muted);
    font-style: italic;
    padding: 0.4rem 0.6rem;
    text-align: center;
  }

  /* Actions swimlane on timeline */
  .actions-lane-label {
    position: absolute;
    left: 0;
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.82rem;
    color: var(--muted);
    pointer-events: none;
    transform: translateY(-50%);
    padding-right: 0.5rem;
    background: var(--bg);
    z-index: 2;
  }
  .actions-lane-bg {
    position: absolute;
    left: 0;
    right: 0;
    background: rgba(138, 133, 120, 0.05);
    pointer-events: none;
    border-bottom: 1px solid var(--line-soft);
  }

  /* Action marker on timeline = diamond + trailing 3-day bar */
  .action-marker {
    position: absolute;
    pointer-events: auto;
    z-index: 12;
    cursor: pointer;
  }
  .action-marker .am-bar {
    position: absolute;
    top: 50%;
    left: 6px;
    height: 2px;
    transform: translateY(-50%);
    opacity: 0.55;
    pointer-events: none;
  }
  .action-marker .am-diamond {
    position: absolute;
    top: 50%;
    left: 0;
    width: 12px;
    height: 12px;
    transform: translate(-50%, -50%) rotate(45deg);
    background: var(--muted);
    border: 1px solid var(--fg);
    transition: transform 0.15s;
    z-index: 2;
  }
  .action-marker:hover .am-diamond {
    transform: translate(-50%, -50%) rotate(45deg) scale(1.25);
  }
  .action-marker.selected .am-diamond {
    box-shadow: 0 0 0 3px var(--bg), 0 0 0 4px var(--fg);
  }
  .action-marker .am-label {
    position: absolute;
    top: -14px;
    left: 0;
    transform: translateX(-50%);
    font-size: 0.48rem;
    letter-spacing: 0.08em;
        color: var(--muted);
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s;
  }
  .action-marker:hover .am-label,
  .action-marker.selected .am-label { opacity: 1; }

  /* Per-kind colors for markers + bars */
  .action-marker.ak-budget .am-diamond { background: var(--p-cold); }
  .action-marker.ak-budget .am-bar { background: var(--p-cold); }
  .action-marker.ak-pause .am-diamond { background: var(--s-lost); }
  .action-marker.ak-pause .am-bar { background: var(--s-lost); }
  .action-marker.ak-resume .am-diamond { background: var(--s-won); }
  .action-marker.ak-resume .am-bar { background: var(--s-won); }
  .action-marker.ak-audience .am-diamond { background: var(--p-warm); }
  .action-marker.ak-audience .am-bar { background: var(--p-warm); }
  .action-marker.ak-creative .am-diamond { background: var(--p-test); }
  .action-marker.ak-creative .am-bar { background: var(--p-test); }
  .action-marker.ak-bid .am-diamond { background: var(--p-ret); }
  .action-marker.ak-bid .am-bar { background: var(--p-ret); }
  .action-marker.ak-other .am-diamond { background: var(--muted); }
  .action-marker.ak-other .am-bar { background: var(--muted); }
  /* Marqueur hebdo connecté (regroupe les modifs de la semaine, lecture seule). */
  .action-marker.ak-week .am-diamond { background: var(--p-test); }
  .action-marker.ak-week .am-bar { background: var(--p-test); }
  .action-marker.src-connected .am-bar {
    opacity: 0.4;
    background-image: repeating-linear-gradient(90deg, transparent, transparent 3px, var(--bg) 3px, var(--bg) 5px);
  }
  .action-marker.src-connected .am-diamond {
    border: 1px dashed var(--fg);
    box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--p-test);
  }
  .action-marker .am-count {
    position: absolute;
    top: 50%;
    left: 0;
    transform: translate(-50%, -50%);
    font-size: 0.42rem;
    font-weight: 700;
    color: var(--surface);
    z-index: 3;
    pointer-events: none;
  }

  /* Filter bar in tests view */
  .filter-bar {
    padding: 0 1.75rem 1rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    align-items: center;
  }
  .filter-label {
    font-size: 0.54rem;
    letter-spacing: 0;
    color: var(--muted);
    margin-right: 0.2rem;
  }
  .filter-chip {
    font-family: inherit;
    font-size: 0.62rem;
    background: transparent;
    border: 1px solid var(--line);
    color: var(--fg);
    padding: 0.15rem 0.5rem;
    cursor: pointer;
    letter-spacing: 0.04em;
  }
  .filter-chip:hover { border-color: var(--fg); }
  .filter-chip.active { background: var(--fg); color: var(--surface); border-color: var(--fg); }
  .filter-chip.filter-focus-active {
    background: var(--s-live-soft);
    color: var(--s-live);
    border-color: var(--s-live-edge);
    font-weight: 600;
    border-radius: var(--r-sm);
  }
  .filter-chip.filter-focus-active:hover {
    background: var(--s-live);
    color: #fff;
    border-color: var(--s-live);
  }
  .filter-chip .dot {
    display: inline-block;
    width: 5px; height: 5px;
    border-radius: 50%;
    margin-right: 0.3rem;
  }
  .filter-chip .dot.s-won { background: var(--s-won); }
  .filter-chip .dot.s-lost { background: var(--s-lost); }
  .filter-chip .dot.s-incon { background: var(--s-incon); }
  .filter-chip .dot.s-live { background: var(--s-live); }
  .filter-chip .dot.s-plan { background: var(--s-plan); }
  .filter-chip .dot.s-draft    { background: var(--muted); }
  .filter-chip .dot.s-review   { background: var(--p-test); }
  .filter-chip .dot.s-approved { background: var(--p-cold); }
  .filter-chip .dot.s-done     { background: var(--fg); }

  /* Modal */
  .modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(35, 31, 35, 0.4);
    z-index: 400;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    backdrop-filter: blur(4px);
  }
  .modal {
    background: var(--surface);
    width: 100%;
    max-width: 580px;
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    border-radius: var(--r-lg);
    box-shadow: 0 20px 60px rgba(35, 31, 35, 0.18);
  }

  /* AI modal — slightly wider for prompt + JSON output readability */
  .modal.ai-modal { max-width: 640px; }
  .ai-step { display: flex; flex-direction: column; gap: 0.8rem; }
  .ai-buttons {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 0.5rem;
  }
  .ai-key-help {
    font-size: 0.6rem;
    color: var(--muted);
    margin-top: 0.4rem;
    line-height: 1.5;
  }
  .ai-key-help a { color: var(--p-test); text-decoration: underline; }
  .ai-num { width: 80px; }
  .ai-select {
    font-family: inherit;
    font-size: 0.75rem;
    padding: 0.45rem 0.55rem;
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    width: 100%;
  }
  .ai-model-info {
    font-size: 0.65rem;
    color: var(--muted);
    padding: 0.5rem 0;
    font-style: italic;
  }
  /* Loading step — centered spinner */
  .ai-step-loading {
    align-items: center;
    text-align: center;
    padding: 2rem 1rem;
  }
  .ai-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid var(--depth);
    border-top-color: var(--p-test);
    border-radius: 50%;
    animation: ai-spin 0.8s linear infinite;
  }
  @keyframes ai-spin {
    to { transform: rotate(360deg); }
  }
  .ai-step-loading p {
    font-style: italic;
    color: var(--fg);
    margin: 0;
  }
  .ai-step-loading small {
    font-size: 0.6rem;
    color: var(--muted);
  }
  /* Result step */
  .ai-result-success {
    padding: 0.7rem 0.9rem;
    background: var(--mint-soft);
    border: 1px solid var(--p-cold);
    border-radius: var(--r-sm);
    color: var(--p-cold);
    font-size: 0.8rem;
  }
  .ai-result-error {
    padding: 0.7rem 0.9rem;
    background: var(--coral-soft);
    border: 1px solid var(--s-lost);
    border-radius: var(--r-sm);
    color: var(--s-lost);
    font-size: 0.75rem;
    line-height: 1.4;
  }
  .ai-raw-output {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    background: var(--bg);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-sm);
    padding: 0.6rem;
    max-height: 300px;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
    color: var(--muted);
    margin: 0;
  }
  .modal-head {
    padding: 1.4rem 1.6rem 1rem;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
  }
  .modal-head h3 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.5rem;
    font-style: italic;
    letter-spacing: -0.02em;
  }
  .modal-close {
    background: transparent; border: none; font-size: 1.5rem;
    color: var(--muted); cursor: pointer; line-height: 1; padding: 0 0.3rem;
    border-radius: var(--r-sm);
    transition: color 0.12s;
  }
  .modal-close:hover { color: var(--fg); }
  .modal-body { padding: 0.5rem 1.6rem 1.6rem; }
  .modal-footer {
    padding: 1.1rem 1.6rem;
    border-top: 1px solid var(--line-soft);
    display: flex;
    justify-content: space-between;
    gap: 0.6rem;
    background: var(--depth);
    border-radius: 0 0 var(--r-lg) var(--r-lg);
  }
  .fld { margin-bottom: 1rem; }
  .fld-label {
    display: flex;
    justify-content: space-between;
    font-size: 0.7rem;
    color: var(--muted);
    margin-bottom: 0.4rem;
    font-weight: 500;
  }
  .fld-label .opt { font-style: italic; font-weight: 400; }
  .fld input, .fld textarea, .fld select {
    width: 100%;
    background: var(--depth);
    border: 1px solid transparent;
    padding: 0.75rem 0.875rem;
    font-family: inherit;
    font-size: 0.9rem;
    color: var(--fg);
    line-height: 1.4;
    border-radius: var(--r-md);
    transition: border-color 0.15s, background 0.15s;
  }
  .fld input:hover, .fld textarea:hover, .fld select:hover {
    border-color: rgba(35,31,35,0.16);
  }
  .fld input:focus, .fld textarea:focus, .fld select:focus {
    outline: none;
    border-color: rgba(35,31,35,0.32);
    background: var(--surface);
  }
  .fld input::placeholder, .fld textarea::placeholder {
    color: rgba(35,31,35,0.48);
  }
  .fld textarea { resize: vertical; min-height: 60px; }
  .fld-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0.85rem; }
  /* Status pipeline picker — single unified field showing the full
     lifecycle, grouped by phase (pre-launch, running, outcome,
     archived). Arrows between groups make the flow read clearly. */
  /* Pipeline row — single horizontal line that fuses the lifecycle
     picker with the inline assignees picker. The user can both
     advance the test through its phases AND assign owners from the
     same place, so we no longer need a separate Collab section
     below the modal. */
  /* ════════ Status stepper (progressive) ════════
     Linear flow with avatars on top of each step, dots in the
     middle, labels below. The connecting line between steps fills
     coloured up to the active phase, then stays grey for future
     steps. The 3 outcomes (A/B/?) sit at the end as parallel
     options, separated from the linear chain by a soft divider. */
  .status-stepper {
    display: flex;
    align-items: stretch;
    gap: 0;
    padding: 0.7rem 0.5rem 0.5rem;
    background: var(--bg);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-sm);
    flex-wrap: wrap;
    row-gap: 0.8rem;
  }
  /* Each step is a column: avatars / circle / label */
  .step {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 0;
    background: transparent;
    border: none;
    cursor: pointer;
    font-family: inherit;
    color: inherit;
    flex: 0 0 auto;
    min-width: 0;
  }
  /* Avatar slot above the dot — fixed height so the rows align even
     when some steps have avatars and others don't. */
  .step-avatars {
    display: flex;
    align-items: center;
    height: 20px;
    min-width: 20px;
    justify-content: center;
  }
  .step-avatars > * { margin-left: -4px; }
  .step-avatars > *:first-child { margin-left: 0; }
  .step-avatars-empty { width: 1px; }
  .step-avatar-more {
    font-size: 0.5rem;
    color: var(--muted);
    margin-left: 2px;
    line-height: 1;
  }
  /* The actual dot — varies by state. Past = solid filled, active =
     bigger with ring, future = empty white with grey border. */
  .step-circle {
    width: 24px;
    height: 24px;
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, border-color 0.15s, transform 0.15s, box-shadow 0.15s;
    flex-shrink: 0;
  }
  .step-check {
    color: var(--surface);
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
  }
  .step-letter {
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0;
  }
  .step-label {
    font-size: 0.6rem;
    color: var(--muted);
    line-height: 1.1;
    text-align: center;
    white-space: nowrap;
    transition: color 0.15s, font-weight 0.15s;
  }
  /* PAST state — all past steps share the same blue colour for a
     clear "validated" signal, regardless of which phase. The active
     and outcome states keep their per-status colour to surface what
     phase the test is currently in. */
  .step.step-past .step-circle {
    background: var(--mint-soft, #E5FEE7);
    border: 1.5px solid var(--s-won, #2A6B30);
  }
  .step.step-past .step-check { color: var(--s-won, #2A6B30); }
  .step.step-past .step-label { color: var(--fg); }
  /* ACTIVE state — bigger circle with soft pastel fill, ring, label
     coloured to match its status. The current pipeline position is
     unmissable. */
  .step.step-active .step-circle {
    width: 32px;
    height: 32px;
    background: var(--pos, #2A6B30);
    border: 2.5px solid var(--pos, #2A6B30);
    box-shadow: 0 0 0 4px rgba(42, 107, 48, 0.15);
  }
  .step.step-active .step-letter { color: var(--p-test); font-size: 11px; font-weight: 700; }
  /* Étapes LINÉAIRES actives : vert foncé uniforme (cf. base ci-dessus) — plus
     de couleur par statut. Les ISSUES (won/lost/incon) gardent leur couleur. */
  .step.step-active.s-won .step-circle      { border-color: var(--s-won);   background: var(--s-won-soft);   box-shadow: 0 0 0 4px rgba(42,107,48,0.18); }
  .step.step-active.s-lost .step-circle     { border-color: var(--s-lost);  background: var(--s-lost-soft);  box-shadow: 0 0 0 4px rgba(197,61,44,0.15); }
  .step.step-active.s-incon .step-circle    { border-color: var(--s-incon); background: var(--s-incon-soft); box-shadow: 0 0 0 4px rgba(138,107,26,0.15); }
  .step.step-active .step-label { color: var(--fg); font-weight: 600; }
  .step.step-active.s-plan .step-label     { color: var(--s-plan); }
  .step.step-active.s-review .step-label   { color: var(--p-test); }
  .step.step-active.s-approved .step-label { color: var(--p-cold); }
  .step.step-active.s-live .step-label     { color: var(--s-live); }
  .step.step-active.s-won .step-label      { color: var(--s-won); }
  .step.step-active.s-lost .step-label     { color: var(--s-lost); }
  .step.step-active.s-incon .step-label    { color: var(--s-incon); }
  /* FUTURE state — empty white circle, grey border, muted label */
  .step.step-future .step-circle {
    background: var(--surface);
    border: 1.5px solid var(--faint);
  }
  /* Étapes LINÉAIRES non faites : carré GRISÉ (les issues gardent leur look). */
  .step.step-future:not(.step-outcome) .step-circle {
    background: var(--depth, #ececec);
    border: 1.5px solid var(--line, #231f231f);
  }
  .step.step-future .step-letter { color: var(--muted); }
  .step.step-future .step-label  { color: var(--muted); }
  /* Outcome steps (A / B / Inconclu) — even when not chosen, they
     keep their semantic colour (green / red / yellow) so the user
     sees the 3 possible endings at a glance. */
  .step.step-outcome.s-won .step-letter,
  .step.step-outcome.s-won .step-label   { color: var(--s-won); }
  .step.step-outcome.s-lost .step-letter,
  .step.step-outcome.s-lost .step-label  { color: var(--s-lost); }
  .step.step-outcome.s-incon .step-letter,
  .step.step-outcome.s-incon .step-label { color: var(--s-incon); }
  .step.step-outcome.step-future.s-won .step-circle   { border-color: var(--s-won-edge); }
  .step.step-outcome.step-future.s-lost .step-circle  { border-color: var(--s-lost-edge); }
  .step.step-outcome.step-future.s-incon .step-circle { border-color: var(--s-incon-edge); }
  /* Hover — slight elevation on every state */
  .step:hover .step-circle { transform: translateY(-1px); }
  .step.step-future:hover .step-circle { border-color: var(--fg); }
  /* The line connecting two steps — drawn between dot+label rows.
     Fills coloured behind passed steps, stays neutral for upcoming. */
  .step-line {
    flex: 1 1 auto;
    height: 2px;
    min-width: 16px;
    align-self: center;
    margin-top: -8px; /* nudge up so it crosses dot vertical center */
    transition: background 0.2s;
  }
  .step-line-past   { background: var(--pos, #2A6B30); }
  .step-line-future { background: var(--faint); }
  /* Soft vertical divider before the outcome cluster signals "this
     is a different group" without being heavy. */
  .step-outcome-divider {
    width: 1px;
    align-self: stretch;
    background: var(--line);
    margin: 4px 0.5rem;
  }
  /* Smaller gap between A/B/? in the outcome cluster — they're
     parallel choices, not sequential, so no connecting line. */
  .step-outcome-gap {
    width: 0.3rem;
    flex-shrink: 0;
  }
  .step-outcome .step-circle {
    width: 26px;
    height: 26px;
  }
  .step-outcome.step-active .step-circle {
    width: 32px;
    height: 32px;
  }

  /* Assignees picker — bigger pills with first names so the
     phase assignment reads clearly */
  .status-assignees {
    display: flex;
    gap: 0.4rem;
    align-items: center;
    flex-wrap: wrap;
  }
  .assignee-pill {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 0.18rem 0.55rem 0.18rem 0.18rem;
    cursor: pointer;
    opacity: 0.55;
    filter: grayscale(0.5);
    transition: opacity 0.15s, filter 0.15s, border-color 0.15s, background 0.15s, transform 0.15s;
    line-height: 0;
    display: inline-flex;
    align-items: center;
  }
  .assignee-pill-name {
    font-size: 0.6rem;
    margin-left: 0.35rem;
    color: var(--fg);
    letter-spacing: 0.01em;
    line-height: 1;
  }
  .assignee-pill:hover {
    opacity: 0.85;
    filter: grayscale(0.1);
    transform: translateY(-1px);
  }
  .assignee-pill.checked {
    opacity: 1;
    filter: none;
    border-color: var(--p-cold);
    background: var(--mint-soft);
  }
  .assignee-pill.checked .assignee-pill-name { font-weight: 500; }

  /* ════════ Modal sections (collapsible) ════════ */
  .m-section {
    border-top: 1px solid var(--line-soft);
    padding: 1rem 0;
  }
  .m-section:first-child { border-top: none; padding-top: 0.4rem; }
  .m-section-idea, .m-section-pipeline {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
  }
  details.m-section { padding: 0; }
  details.m-section > summary {
    list-style: none;
    cursor: pointer;
    padding: 0.75rem 0.2rem;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    transition: background 0.15s;
  }
  details.m-section > summary::-webkit-details-marker { display: none; }
  details.m-section > summary::before {
    content: '▸';
    color: var(--muted);
    font-size: 0.7rem;
    margin-right: 0.4rem;
    transition: transform 0.15s;
    display: inline-block;
  }
  details.m-section[open] > summary::before { transform: rotate(90deg); }
  details.m-section > summary:hover { background: var(--bg); }
  .m-section-summary-label {
    font-family: 'StackSans headline Variable', system-ui, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 0.95rem;
    color: var(--fg);
    flex: 1;
  }
  .m-section-summary-hint {
    font-size: 0.6rem;
    color: var(--muted);
    letter-spacing: 0.02em;
  }
  details.m-section > .m-section-body {
    padding: 0.5rem 0 0.7rem 1.4rem;
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
  }
  /* "Locked" hint shown inside a details section when the user can't
     yet edit it (e.g. follow-ups of follow-ups before parent is
     confirmed). Reads as a soft, non-blocking explanation. */
  .m-locked-hint {
    margin: 0;
    padding: 0.7rem 0.9rem;
    background: var(--sand-soft);
    border: 1px dashed var(--p-ret);
    border-radius: var(--r-sm);
    color: var(--p-ret);
    font-size: 0.7rem;
    line-height: 1.5;
  }
  .m-locked-hint strong { font-weight: 500; color: var(--fg); }

  /* ════════ Tests canvas onboarding overlay ════════ */
  /* Positioned fixed in the visible viewport (not absolute on the
     scroll container) so it stays centered regardless of timeline
     scroll position or layout height. Doesn't intercept clicks. */
  .tests-canvas-onboarding {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    padding: 1.2rem 1.6rem;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-md);
    box-shadow: 0 4px 18px rgba(35, 31, 35, 0.08);
    max-width: 360px;
    pointer-events: none;
    z-index: 5;
    animation: tcoFadeIn 0.3s ease-out 0.1s both;
  }
  @keyframes tcoFadeIn {
    from { opacity: 0; transform: translate(-50%, -48%); }
    to { opacity: 1; transform: translate(-50%, -50%); }
  }
  .tco-icon {
    font-size: 1.6rem;
    line-height: 1;
    color: var(--p-test);
    margin-bottom: 0.35rem;
  }
  .tco-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1rem;
    margin: 0 0 0.4rem 0;
    color: var(--fg);
  }
  .tco-desc {
    font-size: 0.65rem;
    line-height: 1.45;
    color: var(--muted);
    margin: 0;
  }

  /* ════════════════════════════════════════════════════════════════
     TESTS GHOST OVERLAY — sample preview when timeline is empty
     Sits on top of the (already rendered) timeline ticks/grid so the
     user sees how their tests will be laid out, with realistic
     status colors and follow-up flags. Faded + dashed + non-
     interactive so it doesn't pretend to be real.
     ════════════════════════════════════════════════════════════════ */
  /* ════════════════════════════════════════════════════════════════
     TESTS GHOST OVERLAY — sample preview when timeline is empty
     The fake test-nodes use the EXACT same .test-node markup and
     classes as the real ones, so the preview is pixel-perfect to
     what the timeline will look like once the user creates tests.
     This wrapper just adds the "ghost" treatment on top:
     - overall opacity reduction
     - radial fade toward edges
     - pointer-events disabled (no interaction)
     - subtle "preview" label per node via ::after
     ════════════════════════════════════════════════════════════════ */
  .tests-ghost-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 6;
    /* fade outward so it feels like a sample, not real data */
    -webkit-mask-image: radial-gradient(ellipse at center, black 65%, rgba(0,0,0,0.55) 100%);
            mask-image: radial-gradient(ellipse at center, black 65%, rgba(0,0,0,0.55) 100%);
  }
  /* ════════════════════════════════════════════════════════════════
     SHARED EMPTY-STATE CARD — used by both ghost previews
     (Structure canvas + Tests timeline) so the two land on a
     consistent visual identity. The wrapper layouts (sticky vs
     in-flow) are defined per-context, but the icon/title/body
     typography is shared via .gec-* classes.
     ════════════════════════════════════════════════════════════════ */
  .ghost-empty-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: 0 4px 14px rgba(35, 31, 35, 0.08);
    padding: 1rem 1.4rem 1.05rem;
    text-align: center;
    max-width: 540px;
    pointer-events: none;
  }
  .gec-icon {
    font-size: 1.4rem;
    line-height: 1;
    color: var(--p-test);
    margin-bottom: 0.45rem;
  }
  .gec-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1.1rem;
    color: var(--fg);
    margin: 0 0 0.4rem 0;
    letter-spacing: -0.01em;
  }
  .gec-body {
    font-size: 0.72rem;
    color: var(--muted);
    line-height: 1.55;
    margin: 0;
  }
  .gec-body strong { color: var(--fg); font-weight: 500; }

  /* Tests timeline ghost — the empty card sits sticky-top so it
     stays visible as the user pans through the timeline ghost. */
  .tests-ghost-hint {
    position: sticky;
    top: 12px;
    margin: 12px auto 0;
    width: max-content;
    z-index: 8;
  }

  /* The ghost treatment applied to the real .test-node markup.
     We don't override colors / borders / typography because the
     whole point is to show the REAL design — we just fade it,
     disable interaction, and kill the entry animation. */
  .tgh-test-node {
    opacity: 0.5 !important;
    pointer-events: none !important;
    cursor: default !important;
    /* Disable the .test-node entry animation so all 6 ghosts appear
       at once (no staggered slide-in) and so it doesn't fight with
       our absolute positioning overrides. */
    animation: none !important;
    /* Subtle shadow so the cards still feel layered without being
       too prominent. */
    box-shadow: 0 1px 2px rgba(35, 31, 35, 0.04);
    /* Above the bezier links, below the hint */
    z-index: 4;
  }
  /* Override the .is-followup-ghost / :hover opacity bumps that
     would otherwise make the 2 scenario cards (EXP-005, EXP-006)
     look more opaque than the rest of the ghost preview. */
  .tgh-test-node.is-followup-ghost,
  .tgh-test-node.is-followup-ghost:hover,
  .tgh-test-node.is-followup-solid,
  .tgh-test-node.is-followup-solid:hover {
    opacity: 0.5 !important;
  }
  .tgh-test-node .tn-resize { display: none; }
  .tgh-test-node .tn-quick-actions { display: none; }
  /* Bezier links connecting parent → follow-up scenarios. The
     orchid dashed style is inherited from .fu-link-ghost; we just
     soften it a touch and place it under the nodes. */
  .tgh-link {
    opacity: 0.55;
    z-index: 3;
  }

  /* ════════ Quick Add popover ════════ */
  .quick-add-popover {
    position: absolute;
    width: 380px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: 0 12px 32px rgba(35, 31, 35, 0.18);
    z-index: 1000;
    animation: qaIn 0.16s ease-out;
  }
  @keyframes qaIn {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .qa-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.7rem 0.9rem;
    border-bottom: 1px solid var(--line-soft);
  }
  .qa-title {
    font-family: 'StackSans headline Variable', system-ui, sans-serif;
    font-style: italic;
    font-size: 0.9rem;
    color: var(--fg);
  }
  .qa-close {
    background: transparent;
    border: none;
    cursor: pointer;
    font-size: 1.2rem;
    color: var(--muted);
    line-height: 1;
    padding: 0;
    width: 22px;
    height: 22px;
  }
  .qa-close:hover { color: var(--fg); }
  .qa-body {
    padding: 0.8rem 0.9rem 0.7rem;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
  }
  .qa-input {
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: 0.5rem 0.6rem;
    font-family: inherit;
    font-size: 0.78rem;
    transition: border-color 0.15s;
    width: 100%;
  }
  .qa-input:focus {
    border-color: var(--p-test);
    outline: none;
  }
  .qa-input.qa-error {
    border-color: var(--coral);
    animation: qaShake 0.4s;
  }
  @keyframes qaShake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-3px); }
    75% { transform: translateX(3px); }
  }
  .qa-variants { display: flex; flex-direction: column; gap: 0.35rem; }
  .qa-variant-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
  }
  .qa-letter {
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.7rem;
    font-weight: 500;
    border-radius: 3px;
    color: var(--surface);
    flex-shrink: 0;
  }
  .qa-letter-a { background: var(--p-cold); }
  .qa-letter-b { background: var(--p-test); }
  .qa-foot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 0.4rem;
    gap: 0.5rem;
  }
  .qa-hint {
    font-size: 0.55rem;
    color: var(--muted);
    letter-spacing: 0.01em;
    flex: 1;
  }
  .qa-buttons { display: flex; gap: 0.4rem; }
  .qa-buttons .btn-primary,
  .qa-buttons .btn-secondary {
    padding: 0.4rem 0.8rem;
    font-size: 0.7rem;
  }

  /* Legacy 5-column status picker (kept for any other usage that
     hasn't been migrated to .pipeline). */
  .status-picker:not(.pipeline) {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 0.25rem;
  }
  .status-opt {
    font-family: inherit;
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.4rem 0.25rem;
    cursor: pointer;
    font-size: 0.52rem;
    letter-spacing: 0;
    text-align: center;
    border-radius: 3px;
    color: var(--muted);
    transition: border-color 0.15s, background 0.15s, color 0.15s;
  }
  .status-opt:hover { border-color: var(--fg); color: var(--fg); }
  /* Pre-launch group — neutral / progress hues */
  .status-opt.active.s-draft    { background: var(--depth);              border-color: var(--muted);  color: var(--muted); }
  .status-opt.active.s-review   { background: var(--orchid-soft);        border-color: var(--p-test); color: var(--p-test); }
  .status-opt.active.s-approved { background: var(--mint-soft);          border-color: var(--p-cold); color: var(--p-cold); }
  /* Running group */
  .status-opt.active.s-plan     { background: var(--s-plan-soft);        border-color: var(--s-plan); color: var(--s-plan); }
  .status-opt.active.s-live     { background: var(--s-live-soft);        border-color: var(--s-live); color: var(--s-live); }
  /* Outcome group */
  .status-opt.active.s-won      { background: var(--s-won-soft);         border-color: var(--s-won);  color: var(--s-won); }
  .status-opt.active.s-lost     { background: var(--s-lost-soft);        border-color: var(--s-lost); color: var(--s-lost); }
  .status-opt.active.s-incon    { background: var(--s-incon-soft);       border-color: var(--s-incon); color: var(--s-incon); }
  /* Archived */
  .status-opt.active.s-done     { background: var(--depth);              border-color: var(--fg);     color: var(--fg); }

  .variants-edit {
    border: 1px solid var(--line);
    background: var(--bg);
    padding: 0.4rem;
  }
  .variant-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 0.4rem;
    align-items: center;
    padding: 0.3rem 0;
    border-bottom: 1px solid var(--line-soft);
  }
  .variant-row:last-child { border-bottom: none; }
  /* Letter chip in front of each variant — A and B always carry
     their semantic colour (green for A, red for B) so the user
     immediately associates "B winner" with the lost-coloured
     follow-up. The flat black state is only used as a fallback. */
  .variant-letter-edit {
    width: 20px; height: 20px;
    background: var(--fg); color: var(--surface);
    display: flex; align-items: center; justify-content: center;
    font-size: 0.65rem; font-weight: 500;
  }
  .variant-row-a .variant-letter-edit { background: var(--s-won); }
  .variant-row-b .variant-letter-edit { background: var(--s-lost); }
  .variant-row.winner .variant-letter-edit { background: var(--s-won); }
  .variant-row-b.winner .variant-letter-edit { background: var(--s-lost); }
  .variant-name-input {
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--line-soft);
    padding: 0.15rem 0;
    font-size: 0.66rem;
    font-family: inherit;
  }
  /* Winner radio — single 🏆 button, only one variant can be picked.
     Hover/active colour follows the variant: green for A, red for B. */
  .variant-winner-radio {
    display: inline-flex;
    align-items: center;
    cursor: pointer;
    padding: 0.2rem 0.4rem;
    border: 1px solid var(--line);
    border-radius: 3px;
    background: transparent;
    transition: border-color 0.15s, background 0.15s;
  }
  .variant-row-a .variant-winner-radio:hover { border-color: var(--s-won); }
  .variant-row-b .variant-winner-radio:hover { border-color: var(--s-lost); }
  .variant-winner-radio input { display: none; }
  .variant-winner-trophy {
    font-size: 0.85rem;
    filter: grayscale(1);
    opacity: 0.4;
    transition: filter 0.15s, opacity 0.15s;
  }
  .variant-row.winner .variant-winner-radio {
    background: var(--s-won-soft);
    border-color: var(--s-won);
  }
  .variant-row-b.winner .variant-winner-radio {
    background: var(--s-lost-soft);
    border-color: var(--s-lost);
  }
  .variant-row.winner .variant-winner-trophy {
    filter: grayscale(0);
    opacity: 1;
  }
  .variant-clear-winner {
    width: 100%;
    margin-top: 0.4rem;
    background: transparent;
    border: 1px solid var(--line-soft);
    padding: 0.25rem;
    font-family: inherit;
    font-size: 0.55rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    cursor: pointer;
    border-radius: 3px;
  }
  .variant-clear-winner:hover { border-color: var(--fg); color: var(--fg); }

  .btn-primary, .btn-secondary, .btn-danger {
    font-family: inherit;
    font-size: 1rem;
    line-height: 1.25rem;
    font-weight: 400;
    letter-spacing: 0;
    border: none;
    padding: 0.875rem 1.5rem;
    cursor: pointer;
    border-radius: var(--r-md);
    transition: opacity 0.2s, transform 0.1s, background 0.15s, color 0.15s;
  }
  .btn-primary:active, .btn-secondary:active, .btn-danger:active {
    transform: translateY(1px);
  }
  .btn-primary { background: var(--fg); color: var(--surface); }
  .btn-primary:hover { background: var(--p-cold); }
  .btn-secondary {
    background: var(--surface); color: var(--fg); border: 1px solid var(--line);
  }
  .btn-secondary:hover { background: var(--bg); border-color: var(--p-cold); color: var(--p-cold); }
  .btn-danger {
    background: transparent; color: var(--coral); border: 1px solid var(--coral-soft);
  }
  .btn-danger:hover { background: var(--coral-soft); }

  /* ═══════════ CREATE VIEW ═══════════ */
  .view-create {
    display: none;
    overflow-y: auto;
    background: var(--bg);
    padding: 1.75rem 2.5rem 4rem;
  }
  .view-create.active { display: block; }
  .create-wrap {
    max-width: 1500px;
    margin: 0 auto;
  }
  .create-head {
    margin-bottom: 2rem;
  }
  .create-head .eyebrow {
    font-size: 0.7rem;
    color: var(--muted);
    margin-bottom: 0.5rem;
    font-weight: 500;
  }
  .create-head h1 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 2.4rem;
    letter-spacing: -0.025em;
    line-height: 1.05;
  }
  .create-head h1 em {
    font-style: italic;
    color: var(--p-test);
    margin-left: 0.4rem;
  }
  .create-tagline {
    font-size: 0.85rem;
    color: var(--muted);
    margin-top: 0.55rem;
    line-height: 1.5;
    max-width: 60ch;
  }

  .create-panels {
    display: grid;
    grid-template-columns: minmax(380px, 38%) 1fr;
    gap: 1.5rem;
    align-items: start;
  }
  @media (max-width: 1100px) {
    .create-panels { grid-template-columns: 1fr; }
  }

  .create-config {
    background: var(--surface);
    border-radius: var(--r-lg);
    padding: 1.5rem;
    position: sticky;
    top: 1rem;
  }
  .cfg-section {
    margin-bottom: 1.5rem;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid var(--line-soft);
  }
  .cfg-section:last-child {
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: none;
  }
  .cfg-label {
    font-size: 0.7rem;
    color: var(--muted);
    margin-bottom: 0.6rem;
    font-weight: 500;
  }

  .platform-picker, .source-picker {
    display: grid;
    gap: 0.4rem;
  }
  .platform-picker { grid-template-columns: 1fr 1fr; }
  .source-picker { grid-template-columns: 1fr; }

  .platform-btn, .source-btn {
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.6rem 0.75rem;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    transition: all 0.15s;
  }
  .platform-btn:hover, .source-btn:hover { border-color: var(--fg); }
  .platform-btn.active, .source-btn.active {
    background: var(--fg);
    color: var(--surface);
    border-color: var(--fg);
  }
  .pf-name, .src-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.9rem;
    line-height: 1;
  }
  .pf-sub, .src-sub {
    font-size: 0.52rem;
    letter-spacing: 0;
    opacity: 0.65;
  }

  .cfg-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem;
  }
  .cfg-fld { display: flex; flex-direction: column; gap: 0.25rem; }
  .cfg-fld.full { grid-column: 1 / -1; }
  .cfg-fld label {
    font-size: 0.54rem;
    letter-spacing: 0;
    color: var(--muted);
  }
  .cfg-fld label .opt {
    font-style: italic;
    text-transform: none;
    letter-spacing: 0;
    opacity: 0.7;
  }
  .cfg-fld input, .cfg-fld select, .cfg-fld textarea {
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.4rem 0.5rem;
    font-family: inherit;
    font-size: 0.68rem;
    color: var(--fg);
  }
  .cfg-fld input:focus, .cfg-fld select:focus, .cfg-fld textarea:focus {
    outline: none; border-color: var(--fg);
  }
  .cfg-fld textarea {
    resize: vertical;
    min-height: 54px;
    line-height: 1.4;
  }

  .cfg-ctx-info {
    font-size: 0.66rem;
    line-height: 1.5;
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.6rem 0.75rem;
  }
  .cfg-ctx-info strong {
    font-weight: 500;
  }
  .cfg-ctx-info .ctx-list {
    margin-top: 0.4rem;
    list-style: none;
    padding-left: 0;
  }
  .cfg-ctx-info .ctx-list li {
    font-size: 0.6rem;
    padding: 0.15rem 0;
    color: var(--muted);
    padding-left: 0.85rem;
    position: relative;
  }
  .cfg-ctx-info .ctx-list li::before {
    content: '·';
    position: absolute;
    left: 0.25rem;
    color: var(--faint);
  }

  /* Preview panel */
  .create-preview {
    background: var(--surface);
    overflow: hidden;
    border-radius: var(--r-lg);
  }
  .preview-head {
    padding: 1.25rem 1.5rem;
    border-bottom: 1px solid var(--line-soft);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
  }
  .preview-head h2 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.4rem;
    letter-spacing: -0.02em;
  }
  .preview-head h2 em { font-style: italic; color: var(--p-test); }
  .preview-meta {
    font-size: 0.7rem;
    color: var(--muted);
    margin-top: 0.25rem;
    font-family: 'Fragment Mono', monospace;
  }
  .preview-actions { display: flex; gap: 0.5rem; }

  .preview-table-wrap {
    max-height: 520px;
    overflow: auto;
    background: var(--bg);
  }
  .preview-table {
    border-collapse: collapse;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    white-space: nowrap;
    table-layout: auto;
    width: auto;
    min-width: 100%;
  }
  .preview-table thead th {
    background: var(--fg);
    color: var(--surface);
    padding: 0.4rem 0.6rem;
    text-align: left;
    font-size: 0.5rem;
    letter-spacing: 0;
    font-weight: 500;
    position: sticky;
    top: 0;
    z-index: 2;
    border-right: 1px solid rgba(255,255,255,0.1);
  }
  .preview-table tbody td {
    padding: 0.35rem 0.6rem;
    border-bottom: 1px solid var(--line);
    border-right: 1px solid var(--line);
    vertical-align: top;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .preview-table tbody tr:nth-child(even) td { background: var(--surface); }
  .preview-table tbody td.empty { color: var(--faint); }
  .preview-table tbody tr.row-campaign td { border-left: 3px solid var(--p-cold); }
  .preview-table tbody tr.row-adset td, .preview-table tbody tr.row-adgroup td { border-left: 3px solid var(--p-test); }
  .preview-table tbody tr.row-ad td, .preview-table tbody tr.row-keyword td { border-left: 3px solid var(--p-warm); }
  /* Inline-editable preview cells (Meta Title/Body/Link/Link Desc, Google
     Headline N/Description N/Final URL). Dashed underline hints editability ;
     focus expands the cell so long copy stays visible while typing. */
  .preview-table tbody td.cg-edit-cell { cursor: text; border-bottom: 1px dashed var(--accent, #7c5cff); }
  .preview-table tbody td.cg-edit-cell:hover { background: var(--bg-lift, rgba(124,92,255,0.08)); }
  .preview-table tbody td.cg-edit-cell:focus {
    outline: 2px solid var(--accent, #7c5cff);
    outline-offset: -2px;
    background: var(--surface);
    white-space: pre-wrap;
    overflow: visible;
    text-overflow: clip;
    max-width: 360px;
    position: relative;
    z-index: 1;
  }
  .preview-table tbody td.cg-edit-cell:empty::before { content: '\270E'; opacity: 0.3; }

  .preview-raw-wrap {
    border-top: 1px solid var(--line);
    padding: 0.8rem 1.25rem;
    background: var(--bg);
  }
  .preview-raw-wrap summary {
    cursor: pointer;
    font-size: 0.6rem;
    letter-spacing: 0;
    color: var(--muted);
  }
  .preview-raw-wrap summary:hover { color: var(--fg); }
  .preview-raw {
    margin-top: 0.6rem;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    line-height: 1.5;
    max-height: 300px;
    overflow: auto;
    background: var(--surface);
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--line);
    white-space: pre;
  }

  .create-empty {
    padding: 3rem 1rem;
    text-align: center;
    color: var(--muted);
  }
  .create-empty h3 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.2rem;
    color: var(--fg);
    margin-bottom: 0.4rem;
  }
  .create-empty p {
    font-size: 0.7rem;
    max-width: 48ch;
    margin: 0 auto;
    line-height: 1.5;
  }

  /* ═══════════ COLLAB (users, labels, review, comments) ═══════════ */
  /* Avatars */
  .avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    color: var(--surface);
    font-family: 'Fragment Mono', monospace;
    font-weight: 600;
    letter-spacing: 0.05em;
    flex-shrink: 0;
    border: 1.5px solid var(--bg);
    cursor: default;
      }
  .avatar.avatar-more {
    background: var(--surface);
    color: var(--muted);
    border: 1px solid var(--line);
    font-weight: 500;
  }
  .avatar-stack {
    display: inline-flex;
    flex-direction: row;
  }
  .avatar-stack .avatar + .avatar {
    margin-left: -6px;
  }

  /* Labels — pill style with pastel fill */
  .label-chip {
    display: inline-block;
    font-size: 0.6rem;
    font-weight: 500;
    padding: 0.15rem 0.55rem;
    background: color-mix(in srgb, var(--lbl-color) 15%, transparent);
    color: var(--lbl-color);
    border-radius: 999px;
    margin-right: 0.25rem;
  }
  .label-chip + .label-chip { margin-left: 0; }

  /* Review status — pill */
  .rs-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.6rem;
    letter-spacing: -0.005em;
    padding: 0.18rem 0.55rem;
    font-weight: 500;
    border-radius: 999px;
  }
  .rs-badge::before {
    content: '';
    width: 5px; height: 5px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
  }
  .rs-badge.rs-draft    { color: var(--muted); background: var(--depth); }
  .rs-badge.rs-review   { color: var(--p-test); background: var(--p-test-soft); }
  .rs-badge.rs-approved { color: var(--s-won); background: var(--s-won-soft); }
  .rs-badge.rs-live     { color: var(--s-live); background: var(--s-live-soft); }
  .rs-badge.rs-done     { color: var(--muted); background: var(--depth); }

  /* Top bar — user switch + notifications */


  /* Client mode banner */
  .client-banner {
    display: none;
    align-items: center;
    gap: 0.7rem;
    padding: 0.5rem 1.25rem;
    background: var(--peach);
    color: var(--p-warm);
    font-size: 0.72rem;
    font-weight: 500;
  }
  body.client-mode .client-banner { display: flex; }
  body.client-mode .topbar-right .tb-btn:not(.btn-exit-client) { display: none; }
  body.client-mode .actions-toolbar { display: none; }
  body.client-mode .filter-chip[data-filter="draft"] { display: none; }
  body.client-mode .card-del-btn,
  body.client-mode .action-del-btn,
  body.client-mode .btn-danger { display: none; }
  body.client-mode .modal-footer .btn-primary { display: none; }
  body.client-mode .review-status-picker .rs-opt:not([data-rs="approved"]):not([data-rs="live"]):not([data-rs="done"]) { display: none; }
  body.client-mode .test-card-actions .card-del-btn { display: none; }
  .btn-exit-client {
    background: var(--coral) !important;
    color: var(--bg) !important;
    border-color: var(--coral) !important;
  }

  /* Sub-mode toggle in tests sidebar */
  .submode-toggle {
    display: flex;
    margin: 0 1.75rem 1rem;
    background: var(--depth);
    border-radius: var(--r-md);
    padding: 3px;
    gap: 2px;
  }
  .submode-btn {
    flex: 1;
    background: transparent;
    border: none;
    padding: 0.55rem 0.75rem;
    font-family: inherit;
    font-size: 0.74rem;
    font-weight: 500;
    cursor: pointer;
    color: var(--muted);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    border-radius: calc(var(--r-md) - 2px);
    transition: all 0.12s;
  }
  .submode-btn:hover:not(.active) { color: var(--fg); }
  .submode-btn.active {
    background: var(--surface);
    color: var(--fg);
    box-shadow: 0 1px 2px rgba(35, 31, 35, 0.06);
  }
  .submode-btn .submode-count {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 1.1rem;
    letter-spacing: 0;
  }

  /* Creation card — soft orchid tint variant */
  .test-card.is-creation {
    background: var(--orchid-soft);
    border-color: transparent;
  }
  .test-card .card-collab {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 0.45rem;
    padding-top: 0.4rem;
    border-top: 1px solid var(--line-soft);
    gap: 0.5rem;
  }
  .card-collab .labels-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem;
    flex: 1;
    min-width: 0;
  }
  .card-collab .author-row {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.5rem;
    color: var(--muted);
    letter-spacing: 0.06em;
    flex-shrink: 0;
  }

  /* Comment thread (in modal) */
  .comments-section {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
  }
  .comments-section-title {
    font-size: 0.58rem;
    letter-spacing: 0;
    color: var(--muted);
    margin-bottom: 0.7rem;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
  }
  .comments-list {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    margin-bottom: 0.85rem;
  }
  .comment {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.6rem;
    align-items: start;
  }
  .comment-body {
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.5rem 0.7rem;
    font-size: 0.7rem;
    line-height: 1.5;
  }
  .comment-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
  }
  .comment-author {
    font-weight: 600;
    color: var(--fg);
        letter-spacing: 0;
  }
  .comment-ts { color: var(--muted); }
  .comment-text {
    color: var(--fg);
    white-space: pre-wrap;
    word-break: break-word;
  }
  .comment-text .mention {
    display: inline-block;
    color: var(--p-cold);
    background: var(--p-cold-soft);
    padding: 0 0.25rem;
    border-radius: 2px;
    font-weight: 500;
  }
  .comment-text .mention.is-me {
    color: var(--s-lost);
    background: var(--s-lost-soft);
  }
  .comment-empty {
    color: var(--muted);
    font-style: italic;
    font-size: 0.66rem;
    text-align: center;
    padding: 0.6rem;
  }

  .comment-form {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 0.5rem;
    align-items: stretch;
  }
  .comment-form textarea {
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.45rem 0.6rem;
    font-family: inherit;
    font-size: 0.7rem;
    color: var(--fg);
    line-height: 1.5;
    resize: vertical;
    min-height: 38px;
  }
  .comment-form textarea:focus { outline: none; border-color: var(--fg); }
  .comment-form button {
    align-self: stretch;
    padding: 0.4rem 0.85rem;
  }

  /* Activity feed */
  .activity-feed {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
  }
  .activity-item {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 0.45rem;
    padding: 0.25rem 0;
    font-size: 0.6rem;
    color: var(--muted);
    line-height: 1.4;
  }
  .activity-item .activity-text { color: var(--fg); font-size: 0.62rem; }
  .activity-item .activity-text strong { font-weight: 500; }
  .activity-item .activity-ts {
    font-size: 0.52rem;
    letter-spacing: 0.06em;
  }

  /* Review status picker (in modal) */
  .review-status-picker {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 0.25rem;
  }
  .rs-opt {
    font-family: inherit;
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.45rem 0.3rem;
    cursor: pointer;
    font-size: 0.52rem;
    letter-spacing: 0;
    text-align: center;
    transition: all 0.15s;
  }
  .rs-opt:hover { border-color: var(--fg); }
  .rs-opt.active.rs-draft    { background: var(--surface); border-color: var(--muted); color: var(--muted); }
  .rs-opt.active.rs-review   { background: var(--p-test-soft); border-color: var(--p-test); color: var(--p-test); }
  .rs-opt.active.rs-approved { background: var(--s-won-soft); border-color: var(--s-won); color: var(--s-won); }
  .rs-opt.active.rs-live     { background: var(--s-live-soft); border-color: var(--s-live); color: var(--s-live); }
  .rs-opt.active.rs-done     { background: var(--p-cold-soft); border-color: var(--p-cold); color: var(--p-cold); }

  /* Phase Setup — sub-tasks block in the test edit modal. Tracks
     prep work (strategy / creation / validation) before a test is
     launched. */
  .setup-wrap .setup-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
  }
  .setup-wrap .setup-head .opt {
    font-size: 0.5rem;
    color: var(--muted);
    letter-spacing: 0.04em;
  }
  .setup-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    cursor: pointer;
    font-size: 0.6rem;
    color: var(--muted);
    margin-left: auto;
  }
  .setup-toggle input { margin: 0; cursor: pointer; }
  .setup-progress {
    font-size: 0.55rem;
    letter-spacing: 0;
    color: var(--muted);
    padding: 0.15rem 0.45rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
  }
  .setup-progress.is-done {
    border-color: var(--s-won);
    color: var(--s-won);
    background: var(--s-won-soft);
  }
  .setup-tasks {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-top: 0.55rem;
  }
  .setup-date-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
    padding: 0.4rem 0.5rem;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: 4px;
    flex-wrap: wrap;
  }
  .setup-date-label {
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted);
    flex-shrink: 0;
  }
  .setup-date-input {
    font-family: inherit;
    font-size: 0.7rem;
    padding: 0.2rem 0.35rem;
    border: 1px solid var(--line);
    background: var(--bg);
    color: var(--fg);
    border-radius: 3px;
  }
  .setup-date-hint {
    font-size: 0.55rem;
    color: var(--muted);
    font-style: italic;
  }
  .setup-date-reset {
    margin-left: auto;
    background: transparent;
    border: 1px solid var(--line);
    color: var(--muted);
    cursor: pointer;
    padding: 0.15rem 0.4rem;
    font-size: 0.7rem;
    border-radius: 3px;
    line-height: 1;
  }
  .setup-date-reset:hover {
    color: var(--p-cold);
    border-color: var(--p-cold);
  }
  .setup-empty {
    font-size: 0.65rem;
    color: var(--muted);
    font-style: italic;
    padding: 0.4rem 0.5rem;
  }
  .setup-task {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.45rem;
    border: 1px solid var(--line-soft);
    border-radius: 6px;
    background: var(--bg);
    transition: border-color 0.15s, background 0.15s;
  }
  .setup-task:focus-within { border-color: var(--p-cold); background: var(--surface); }
  .setup-task-progress { border-left: 3px solid var(--p-test); }
  .setup-task-done {
    border-left: 3px solid var(--s-won);
    background: var(--s-won-soft);
  }
  .setup-task-done .setup-task-label { text-decoration: line-through; color: var(--muted); }
  .setup-task-status {
    font-family: inherit;
    font-size: 0.6rem;
    padding: 0.2rem 0.35rem;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--fg);
    border-radius: 3px;
    cursor: pointer;
    flex-shrink: 0;
  }
  .setup-task-label {
    flex: 1;
    min-width: 0;
    font-family: inherit;
    font-size: 0.7rem;
    padding: 0.25rem 0.4rem;
    border: 1px solid transparent;
    background: transparent;
    color: var(--fg);
    border-radius: 3px;
    outline: none;
  }
  .setup-task-label:focus {
    background: var(--surface);
    border-color: var(--line);
  }
  .setup-task-delete {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: 0.1rem 0.4rem;
    font-size: 0.85rem;
    line-height: 1;
    border-radius: 3px;
    flex-shrink: 0;
  }
  .setup-task-delete:hover { color: var(--s-lost); background: var(--s-lost-soft); }
  .setup-task-add {
    width: 100%;
    margin-top: 0.15rem;
    padding: 0.4rem;
    border: 1px dashed var(--line);
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    font-family: inherit;
    font-size: 0.62rem;
    letter-spacing: 0.04em;
    border-radius: 4px;
    transition: border-color 0.15s, color 0.15s;
  }
  .setup-task-add:hover { color: var(--p-cold); border-color: var(--p-cold); }

  /* Setup chip on test cards in the sidebar */
  .chip.setup-chip-progress {
    background: var(--p-test-soft);
    color: var(--p-test);
  }
  .chip.setup-chip-done {
    background: var(--s-won-soft);
    color: var(--s-won);
  }

  /* ─── Follow-ups (modal) ─────────────────────────────── */
  .followup-head-row {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
  }
  .followups-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem;
    margin-top: 0.4rem;
  }
  .followup-card {
    border: 1px solid var(--line-soft);
    border-radius: 6px;
    background: var(--bg);
    padding: 0.55rem 0.65rem;
    transition: border-color 0.15s, background 0.15s;
  }
  .followup-card.is-on.followup-a  { border-color: var(--s-won);  background: var(--s-won-soft); }
  .followup-card.is-on.followup-b  { border-color: var(--s-lost); background: var(--s-lost-soft); }
  .followup-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
  }
  .followup-flag {
    font-size: 0.55rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    padding: 0.15rem 0.4rem;
    border-radius: 3px;
    background: var(--surface);
  }
  .followup-card.followup-a  .followup-flag { color: var(--s-won);  }
  .followup-card.followup-b  .followup-flag { color: var(--s-lost); }
  .followup-title {
    font-size: 0.7rem;
    color: var(--fg);
    flex: 1;
  }
  .followup-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    cursor: pointer;
    font-size: 0.55rem;
    color: var(--muted);
  }
  .followup-toggle input { margin: 0; cursor: pointer; }
  .followup-body {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-top: 0.5rem;
  }
  .fu-input, .fu-textarea {
    width: 100%;
    font-family: inherit;
    font-size: 0.7rem;
    padding: 0.35rem 0.45rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 3px;
    color: var(--fg);
    outline: none;
  }
  .fu-textarea { resize: vertical; min-height: 38px; }
  .fu-input:focus, .fu-textarea:focus { border-color: var(--p-cold); }
  /* Variants A/B inputs inside a follow-up card. Two rows, each
     with the letter chip + the input. Mirrors the A/B feel of an
     actual test. */
  .fu-variants-edit {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.4rem 0.5rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 4px;
  }
  .fu-variant-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
  }
  .fu-v-letter {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    border-radius: 3px;
    font-size: 0.6rem;
    font-weight: 700;
    background: var(--depth);
    color: var(--fg);
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .followup-card.followup-a .fu-v-letter { background: var(--s-won-soft);  color: var(--s-won);  }
  .followup-card.followup-b .fu-v-letter { background: var(--s-lost-soft); color: var(--s-lost); }
  .fu-v-input {
    flex: 1;
    min-width: 0;
    font-family: inherit;
    font-size: 0.65rem;
    padding: 0.25rem 0.4rem;
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: 3px;
    color: var(--fg);
    outline: none;
  }
  .fu-v-input:focus { border-color: var(--p-cold); }
  .fu-row { display: flex; gap: 0.6rem; flex-wrap: wrap; }
  .fu-numlbl {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.55rem;
    color: var(--muted);
  }
  .fu-num {
    width: 44px;
    font-family: inherit;
    font-size: 0.65rem;
    padding: 0.15rem 0.3rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 3px;
    color: var(--fg);
    text-align: center;
  }

  /* ─── Follow-up tests on the timeline ────────────────────────
     Follow-ups are real .test-node elements (rendered by the same
     code path as any other test). We just add .is-followup-ghost
     when the parent is unresolved (suggested branch, dashed orchid
     border + soft tint) and .is-followup-solid when the parent
     resolved with a matching outcome (this branch is now the real
     next step, regular planned look in orchid).
     The follow-up flag chip ("✓ si WON" / "✗ si LOST") in the
     header keeps its scenario colour so the user always knows
     which branch they're looking at. */
  .test-node.is-followup-ghost {
    border: 1.5px dashed var(--p-test, var(--orchid));
    background: linear-gradient(180deg, var(--p-test-soft, var(--orchid-soft)) 0%, var(--lift, var(--surface)) 70%);
    opacity: 0.9;
  }
  .test-node.is-followup-ghost:hover { opacity: 1; }
  .test-node.is-followup-solid {
    border: 1.5px solid var(--p-test, var(--orchid));
  }
  .test-node.is-followup .tn-status-lbl {
    color: var(--p-test, var(--orchid));
    font-weight: 600;
  }
  /* Inline flag in the test-node header — coloured by outcome so
     WON vs LOST is distinguishable even on orchid cards. */
  .fu-flag-inline {
    font-size: 0.5rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 0.1rem 0.35rem;
    border-radius: 3px;
    background: var(--lift, var(--surface));
    margin-right: 0.3rem;
  }
  .fu-flag-inline.fu-flag-a { color: var(--s-won, var(--p-cold)); border: 1px solid var(--s-won, var(--p-cold)); }
  .fu-flag-inline.fu-flag-b { color: var(--s-lost); border: 1px solid var(--s-lost); }

  /* Banner inside the follow-up's edit modal — reminds the user
     they're editing a conditional follow-up, with a quick link
     back to the parent. */
  .fu-banner {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.7rem;
    margin-bottom: 0.8rem;
    border-radius: 4px;
    background: var(--p-test-soft, var(--orchid-soft));
    border-left: 3px solid var(--p-test, var(--orchid));
    flex-wrap: wrap;
  }
  .fu-banner-flag {
    font-size: 0.55rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 0.15rem 0.4rem;
    border-radius: 3px;
    background: var(--lift, var(--surface));
  }
  .fu-banner.fu-banner-a .fu-banner-flag { color: var(--s-won, var(--p-cold)); border: 1px solid var(--s-won, var(--p-cold)); }
  .fu-banner.fu-banner-b .fu-banner-flag { color: var(--s-lost); border: 1px solid var(--s-lost); }
  .fu-banner-text {
    font-size: 0.7rem;
    color: var(--fg);
    flex: 1;
  }
  .fu-banner-link {
    font-family: inherit;
    font-size: 0.6rem;
    padding: 0.2rem 0.5rem;
    background: var(--lift, var(--surface));
    border: 1px solid var(--line);
    border-radius: 3px;
    cursor: pointer;
    color: var(--p-test, var(--orchid));
  }
  .fu-banner-link:hover { background: var(--p-test-soft, var(--orchid-soft)); }

  /* Smooth bezier connections from parent to follow-up tests.
     Phase 3.B.20 : edges colorés par outcome pour distinguer
     la branche "si A gagne" (vert) de "si B gagne" (rouge) au
     premier coup d'œil. Combiné avec ghost (dashed = parent
     unresolved) ou solid (parent resolved, branche active).

     z-index: 5 → derrière les .test-node (z-index 10). Sans ça,
     un edge bezier d'un parent vers un follow-up éloigné passait
     PAR-DESSUS d'autres cards entre les deux, donnant l'illusion
     que ces cards faisaient partie de la chaîne. Maintenant l'edge
     passe en arrière-plan et les cards non liés restent visuellement
     séparés. */
  .fu-link-svg {
    position: absolute;
    pointer-events: none;
    overflow: visible;
    z-index: 5;
  }
  .fu-link-svg path { fill: none; }
  /* Couleurs par outcome — appliquées via les classes fu-link-a/b.
     Stroke-width plus marquée + opacité plus élevée que l'ancien
     orchid pâle pour que la branche soit clairement lisible. */
  .fu-link-svg .fu-link-a { stroke: var(--s-won, #2a6b30); }
  .fu-link-svg .fu-link-b { stroke: var(--s-lost, #c53d2c); }
  .fu-link-svg .fu-link-ghost {
    stroke-width: 1.8;
    stroke-dasharray: 5, 4;
    opacity: 0.7;
  }
  .fu-link-svg .fu-link-solid {
    stroke-width: 2.2;
    opacity: 0.95;
  }
  /* Fallback pour les anciens follow-ups sans classe a/b
     (rétro-compat — au cas où). */
  .fu-link-svg .fu-link-ghost:not(.fu-link-a):not(.fu-link-b) {
    stroke: var(--p-test, var(--orchid));
  }
  .fu-link-svg .fu-link-solid:not(.fu-link-a):not(.fu-link-b) {
    stroke: var(--p-test, var(--orchid));
  }

  /* Tinte de fond du card follow-up selon la branche.
     Phase 3.B.20 : si la branche a-t-elle gagné A ou B, le card
     prend une tinte verte ou rouge très claire en plus du
     border orchid. Subtile mais permet de scanner l'arbre par
     couleur de zone, pas seulement par couleur d'edge. */
  .test-node.is-followup.branch-a {
    background: linear-gradient(180deg,
      rgba(46, 125, 50, 0.06) 0%,
      var(--lift, var(--surface)) 60%);
  }
  .test-node.is-followup.branch-b {
    background: linear-gradient(180deg,
      rgba(197, 61, 44, 0.06) 0%,
      var(--lift, var(--surface)) 60%);
  }
  /* Quand le card est en solid (parent resolved), on accentue
     la bordure dans la couleur de la branche pour signaler que
     ce follow-up est désormais "actif". */
  .test-node.is-followup-solid.branch-a {
    border-color: var(--s-won, #2a6b30);
  }
  .test-node.is-followup-solid.branch-b {
    border-color: var(--s-lost, #c53d2c);
  }

  /* Breadcrumb dans le card follow-up : path "EXP-001 › A › A"
     pour situer le card dans l'arbre. */
  .fu-breadcrumb {
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.56rem;
    letter-spacing: 0.04em;
    color: var(--muted, #231F23B3);
    margin: 2px 0 1px;
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .fu-bc-sep {
    opacity: 0.5;
    margin: 0 1px;
  }
  .fu-bc-a {
    color: var(--s-won, #2a6b30);
    font-weight: 700;
  }
  .fu-bc-b {
    color: var(--s-lost, #c53d2c);
    font-weight: 700;
  }

  /* Setup zone on the timeline — sits to the LEFT of each test node
     and visually represents the prep work that has to happen before
     the test goes live. Hashed/striped fill so it's clearly distinct
     from the test bar itself but reads as the same project track. */
  /* Setup overlay — sits inside the test-node on the left side and
     shows the 7-day setup phase (Stratégie / Création / Validation
     / Planned) as dashed pattern. The status of the parent test
     determines the design via the .setup-{status} modifier. */
  .tn-setup-overlay {
    position: absolute;
    border-radius: 6px 0 0 6px;
    pointer-events: none;
    z-index: 1;
    background-image: repeating-linear-gradient(
      135deg,
      currentColor 0 5px,
      transparent 5px 11px
    );
    opacity: 0.55;
    border-right: 1px dashed currentColor;
  }
  /* 4 designs for the 4 pre-launch states. The colour drives both
     the dashed pattern (currentColor) and the right border so the
     phase reads at a glance even on small zoom levels. The soft
     background fill on top of the surface bumps contrast so the
     setup zone is visible on every status — including Planned. */
  .tn-setup-overlay.setup-plan {
    color: var(--ink-100);
    opacity: 0.32;
    background-color: rgba(35, 31, 35, 0.04);
  }
  .tn-setup-overlay.setup-draft {
    color: var(--p-test);
    background-color: rgba(206, 191, 250, 0.12);
  }
  .tn-setup-overlay.setup-review {
    color: #C53D2C;
    background-color: rgba(237, 116, 114, 0.10);
  }
  .tn-setup-overlay.setup-approved {
    color: var(--p-cold);
    background-color: rgba(204, 253, 207, 0.18);
  }
  /* Live / terminal: setup is in the past — keep it visible but muted */
  .tn-setup-overlay.setup-live,
  .tn-setup-overlay.setup-won,
  .tn-setup-overlay.setup-lost,
  .tn-setup-overlay.setup-incon {
    color: var(--ink-100);
    opacity: 0.22;
    background-color: rgba(35, 31, 35, 0.03);
  }
  /* The content sits in the test zone (right side of the overlay) */
  .test-node-content {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--surface);
  }

  /* Notifications panel */

  /* User switch dropdown */

  /* ═══════════ JOURNAL VIEW ═══════════ */
  /* Hide timeline-specific UI when in journal mode */
  .pane-main.mode-journal .viz-top,
  .pane-main.mode-journal .zoom-ctrl {
    display: none;
  }
  .pane-main.mode-journal .viz-scroll {
    overflow-y: auto;
    overflow-x: hidden;
    background: var(--bg);
    /* Hide the line-grid in journal mode — the kanban needs a clean
       backdrop to read clearly. */
    background-image: none !important;
  }
  .pane-main.mode-journal .viz-container {
    transform: none !important;
    width: 100%;
    height: auto;
    min-height: 100%;
    padding: 0;
  }

  .journal-wrap {
    max-width: 920px;
    margin: 0 auto;
    padding: 2rem 2.5rem 4rem;
  }

  .journal-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 2rem;
    margin-bottom: 2rem;
    flex-wrap: wrap;
  }
  .journal-head .eyebrow {
    font-size: 0.7rem;
    color: var(--muted);
    margin-bottom: 0.5rem;
    font-weight: 500;
  }
  .journal-head h1 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 2.4rem;
    letter-spacing: -0.025em;
    line-height: 1.05;
  }
  .journal-head h1 em {
    font-style: italic;
    color: var(--p-test);
  }
  .journal-tagline {
    font-size: 0.85rem;
    color: var(--muted);
    margin-top: 0.55rem;
    line-height: 1.5;
    max-width: 50ch;
  }
  .journal-stats {
    display: flex;
    gap: 1.5rem;
    align-items: center;
    flex-shrink: 0;
  }
  .jstat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
  }
  .jstat-num {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1.6rem;
    line-height: 1;
    letter-spacing: -0.02em;
    color: var(--fg);
    font-variant-numeric: tabular-nums;
  }
  .jstat-lbl {
    font-size: 0.6rem;
    color: var(--muted);
    text-transform: lowercase;
  }

  .journal-filters {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    margin-bottom: 2rem;
    padding: 1rem 1.25rem;
    background: var(--surface);
    border-radius: var(--r-md);
  }
  .jf-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
  }
  .jf-label {
    font-size: 0.6rem;
    color: var(--muted);
    margin-right: 0.4rem;
    min-width: 90px;
    font-weight: 500;
  }
  .jf-chip {
    font-family: inherit;
    background: var(--bg);
    border: 1px solid var(--line);
    color: var(--muted);
    padding: 0.25rem 0.6rem;
    cursor: pointer;
    font-size: 0.7rem;
    border-radius: 999px;
    transition: all 0.12s;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
  }
  .jf-chip:hover { color: var(--fg); border-color: var(--faint); }
  .jf-chip.active {
    background: var(--fg);
    color: var(--surface);
    border-color: var(--fg);
  }
  .jf-cat-chip.active {
    background: var(--cat-color);
    color: var(--surface);
    border-color: var(--cat-color);
  }
  .jf-status-won.active { background: var(--s-won); border-color: var(--s-won); color: var(--surface); }
  .jf-status-lost.active { background: var(--s-lost); border-color: var(--s-lost); color: var(--surface); }
  .jf-status-incon.active { background: var(--p-ret); border-color: var(--p-ret); color: var(--surface); }
  .jf-status-live.active { background: var(--s-live); border-color: var(--s-live); color: var(--surface); }
  .jf-status-plan.active { background: var(--p-test); border-color: var(--p-test); color: var(--surface); }
  .jf-count {
    font-size: 0.6rem;
    opacity: 0.6;
    font-variant-numeric: tabular-nums;
  }
  .jf-chip.active .jf-count { opacity: 0.85; }

  .jf-search-wrap {
    margin-top: 0.2rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }
  .jf-search {
    flex: 1;
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.5rem 0.75rem;
    font-family: inherit;
    font-size: 0.78rem;
    color: var(--fg);
    border-radius: var(--r-sm);
    transition: border-color 0.12s;
  }
  .jf-search:focus { outline: none; border-color: var(--fg); }

  .journal-group {
    margin-bottom: 2.5rem;
  }
  .journal-group-head {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0 0 0.85rem;
    margin-bottom: 1.2rem;
    border-bottom: 1px solid var(--line);
  }
  .journal-group-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--g-color);
    flex-shrink: 0;
  }
  .journal-group-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.5rem;
    letter-spacing: -0.02em;
    color: var(--fg);
    line-height: 1;
  }
  .journal-group-count {
    font-size: 0.7rem;
    color: var(--muted);
    background: var(--g-soft);
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
  }
  .journal-entries {
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }
  .journal-entry {
    background: var(--surface);
    padding: 1.25rem 1.5rem;
    border-radius: var(--r-md);
    cursor: pointer;
    transition: all 0.12s;
    border: 1px solid var(--line-soft);
    position: relative;
  }
  .journal-entry::before {
    content: '';
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 3px;
    border-radius: var(--r-md) 0 0 var(--r-md);
  }
  .journal-entry.s-won::before { background: var(--s-won); }
  .journal-entry.s-lost::before { background: var(--s-lost); }
  .journal-entry.s-incon::before { background: var(--p-ret); }
  .journal-entry.s-live::before { background: var(--s-live); }
  .journal-entry.s-plan::before { background: var(--p-test); }
  .journal-entry:hover {
    border-color: var(--line);
    box-shadow: 0 2px 6px rgba(35, 31, 35, 0.05);
  }
  .je-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.45rem;
    margin-bottom: 0.65rem;
    font-size: 0.65rem;
  }
  .je-ref {
    font-family: 'Fragment Mono', monospace;
    color: var(--muted);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
  }
  .je-cat {
    font-size: 0.6rem;
    padding: 0.12rem 0.55rem;
    border-radius: 999px;
    font-weight: 500;
    background: var(--cat-soft);
    color: var(--cat-color);
  }
  .je-status {
    font-size: 0.6rem;
    padding: 0.12rem 0.55rem;
    border-radius: 999px;
    font-weight: 500;
  }
  .je-status.s-won { background: var(--s-won-soft); color: var(--s-won); }
  .je-status.s-lost { background: var(--s-lost-soft); color: var(--s-lost); }
  .je-status.s-incon { background: var(--sand-soft); color: var(--p-ret); }
  .je-status.s-live { background: var(--s-live-soft); color: var(--s-live); }
  .je-status.s-plan { background: var(--orchid-soft); color: var(--p-test); }
  .je-lift {
    font-size: 0.7rem;
    font-family: 'Fragment Mono', monospace;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
  }
  .je-lift.pos { color: var(--s-won); }
  .je-lift.neg { color: var(--s-lost); }
  .je-sig {
    font-size: 0.62rem;
    color: var(--muted);
    font-family: 'Fragment Mono', monospace;
  }
  .je-date {
    margin-left: auto;
    color: var(--muted);
    font-size: 0.66rem;
    font-family: 'Fragment Mono', monospace;
    font-variant-numeric: tabular-nums;
  }
  .je-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-style: italic;
    font-size: 1.4rem;
    line-height: 1.2;
    letter-spacing: -0.015em;
    color: var(--fg);
    margin-bottom: 0.65rem;
  }
  .je-metric, .je-winner {
    font-size: 0.78rem;
    color: var(--muted);
    margin-bottom: 0.35rem;
  }
  .je-metric strong, .je-winner strong {
    color: var(--fg);
    font-weight: 500;
  }
  .je-section-lbl {
    display: block;
    font-size: 0.62rem;
    color: var(--muted);
    margin-bottom: 0.2rem;
    margin-top: 0.7rem;
    font-weight: 500;
  }
  .je-hypothesis p,
  .je-learning p {
    font-size: 0.85rem;
    line-height: 1.55;
    color: var(--fg);
  }
  .je-hypothesis p {
    color: var(--muted);
    font-style: italic;
  }
  .je-learning {
    background: var(--depth);
    padding: 0.7rem 0.9rem;
    margin-top: 0.6rem;
    border-radius: var(--r-sm);
  }
  .je-learning .je-section-lbl {
    margin-top: 0;
  }

  .journal-empty {
    text-align: center;
    padding: 4rem 2rem;
    color: var(--muted);
    background: var(--surface);
    border-radius: var(--r-md);
  }
  .journal-empty h3 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.3rem;
    color: var(--fg);
    margin-bottom: 0.5rem;
  }
  .journal-empty p {
    font-size: 0.78rem;
    line-height: 1.5;
    max-width: 50ch;
    margin: 0 auto;
  }
  .journal-empty em { color: var(--p-test); font-style: italic; }

  /* Category picker (in test modal) */
  .category-picker {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0.4rem;
  }
  .cat-opt {
    font-family: inherit;
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.55rem 0.4rem;
    cursor: pointer;
    font-size: 0.7rem;
    text-align: center;
    transition: all 0.12s;
    border-radius: var(--r-sm);
    color: var(--muted);
  }
  .cat-opt:hover:not(.active) { color: var(--fg); border-color: var(--faint); }
  .cat-opt.active {
    background: var(--cat-soft);
    border-color: var(--cat-color);
    color: var(--cat-color);
    font-weight: 500;
  }

  /* (CSS de la vue recap héritée supprimé avec elle — le CSS du kanban
     ci-dessous est CONSERVÉ : il sert au Journal des Tests.) */

  /* KANBAN — épuré, sans bandeaux pastel pleins */
  .kanban {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
    margin-bottom: 3rem;
    background: transparent;
  }
  .kb-col {
    background: transparent;
    display: flex;
    flex-direction: column;
    min-width: 0;
  }
  .kb-col-head {
    padding: 0 0 1rem;
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    border-bottom: 1px solid var(--line);
    margin-bottom: 1rem;
  }
  .kb-col-head::before {
    content: '';
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    align-self: center;
  }
  .kb-col.kb-done .kb-col-head::before { background: var(--p-cold); }
  .kb-col.kb-live .kb-col-head::before { background: var(--s-live); }
  .kb-col.kb-plan .kb-col-head::before { background: var(--p-test); }
  .kb-col-title {
    display: block;
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.25rem;
    line-height: 1;
    letter-spacing: -0.015em;
    color: var(--fg);
  }
  .kb-col-sub {
    display: none; /* hidden — info redondante avec sous-colonnes */
  }
  .kb-body {
    padding: 0;
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    min-height: 80px;
  }

  /* Two-level kanban: sous-colonnes sans bordure intrusive */
  .kb-split {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    flex: 1;
  }
  .kb-split.kb-split-single {
    grid-template-columns: 1fr;
  }
  .kb-sub {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }
  .kb-sub-head {
    padding: 0 0 0.6rem;
    font-size: 0.7rem;
    color: var(--muted);
    text-align: left;
    font-weight: 500;
    background: transparent;
    border-bottom: none;
  }

  /* Incon hint in "Ce qui n'a pas marché" */
  .kb-card.kb-card-incon {
    background: var(--sand-soft);
  }
  .kb-card-incon-badge {
    display: inline-block;
    font-size: 0.55rem;
    color: var(--p-ret);
    background: var(--sand);
    padding: 0.1rem 0.45rem;
    margin-right: 0.35rem;
    border-radius: 999px;
    font-weight: 500;
  }
  .kb-card {
    background: var(--surface);
    padding: 0.85rem 0.95rem;
    cursor: pointer;
    transition: all 0.12s;
    border-radius: var(--r-md);
    border: 1px solid var(--line-soft);
  }
  .kb-card:hover {
    border-color: var(--line);
    box-shadow: 0 1px 3px rgba(35, 31, 35, 0.04);
  }
  .kb-card-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1.05rem;
    line-height: 1.2;
    margin-bottom: 0.35rem;
    letter-spacing: -0.01em;
    color: var(--fg);
  }
  .kb-card-status {
    font-size: 0.7rem;
    font-weight: 500;
  }
  .kb-card-status.s-won { color: var(--p-cold); }
  .kb-card-status.s-lost { color: var(--s-lost); }
  .kb-card-status.s-incon { color: var(--p-ret); }
  .kb-card-status.s-live { color: var(--s-live); }
  .kb-card-status.s-plan { color: var(--p-test); }
  .kb-card-meta {
    display: block;
    font-size: 0.68rem;
    color: var(--muted);
    margin-top: 0.3rem;
    line-height: 1.4;
  }
  /* Phase 3.B.21 (révisé) — Drag-n-drop entre colonnes Plan
     (Décide ensemble ↔ Cette semaine). Plus de bouton toggle :
     les cards plan sont draggable, les .kb-body des 2 colonnes
     Plan acceptent le drop.

     · .kb-card[draggable="true"] : cursor grab pour signaler la
       possibilité de drag. Hover → grabbing au mousedown via :active.
     · .kb-card-dragging : ajouté pendant le drag pour l'opacity.
     · .kb-body.kb-drop-over : ajouté sur la drop zone active. */
  .kb-card[draggable="true"] {
    cursor: grab;
  }
  .kb-card[draggable="true"]:active {
    cursor: grabbing;
  }
  .kb-card-dragging {
    opacity: 0.4;
    transform: scale(0.98);
  }
  .kb-body.kb-drop-over {
    background: rgba(107, 91, 149, 0.06); /* var(--orchid) très soft */
    outline: 2px dashed var(--orchid, #6B5B95);
    outline-offset: -4px;
    border-radius: var(--r-md);
    transition: background 0.12s, outline 0.12s;
  }
  .kb-empty {
    font-size: 0.72rem;
    color: var(--muted);
    font-style: italic;
    text-align: left;
    padding: 0.5rem 0;
    opacity: 0.7;
  }

  /* MATRIX */
  .matrix-wrap {
    background: transparent;
    padding: 0;
  }
  .matrix-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 1.5rem;
    gap: 1.5rem;
    flex-wrap: wrap;
    padding: 1rem 0 1rem;
    border-top: 1px solid var(--line);
  }
  .matrix-head h2 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.6rem;
    letter-spacing: -0.025em;
    line-height: 1.1;
  }
  .matrix-head h2 em { font-style: italic; color: var(--muted); }
  .matrix-sub {
    font-size: 0.75rem;
    color: var(--muted);
    margin-top: 0.45rem;
    line-height: 1.4;
  }
  .matrix-actions { display: flex; gap: 0.5rem; align-items: center; }

  .csv-mapping {
    background: var(--surface);
    border-radius: var(--r-md);
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.5rem;
  }
  .map-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    margin-bottom: 1rem;
  }
  .map-head .eyebrow {
    font-size: 0.7rem;
    color: var(--muted);
    font-weight: 500;
  }
  .csv-meta {
    font-size: 0.7rem;
    color: var(--muted);
    font-family: 'Fragment Mono', monospace;
  }
  .csv-meta strong { color: var(--fg); font-weight: 500; }
  .map-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 0.85rem;
    margin-bottom: 1rem;
  }
  .map-fld { display: flex; flex-direction: column; gap: 0.35rem; }
  .map-fld label {
    font-size: 0.7rem;
    color: var(--muted);
    font-weight: 500;
  }
  .map-fld label .opt {
    font-style: italic;
    font-weight: 400;
    opacity: 0.7;
  }
  .map-fld select, .map-fld input {
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.5rem 0.65rem;
    font-family: inherit;
    font-size: 0.78rem;
    color: var(--fg);
    border-radius: var(--r-sm);
    transition: border-color 0.12s;
  }
  .map-fld select:focus, .map-fld input:focus {
    outline: none; border-color: var(--fg);
  }
  .map-thresh {
    padding-top: 1rem;
    border-top: 1px solid var(--line-soft);
  }
  .map-thresh .eyebrow {
    font-size: 0.7rem;
    color: var(--muted);
    margin-bottom: 0.6rem;
    font-weight: 500;
  }
  .thresh-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.85rem;
  }

  .matrix-canvas {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 10;
    background: var(--surface);
    border-radius: var(--r-lg);
    margin-top: 0.5rem;
    display: none;
    overflow: hidden;
  }
  .matrix-canvas.visible { display: block; }

  .matrix-axis {
    position: absolute;
    background: var(--fg);
    z-index: 2;
  }
  .matrix-axis.h { height: 1px; left: 0; right: 0; top: 50%; opacity: 0.15; }
  .matrix-axis.v { width: 1px; top: 0; bottom: 0; left: 50%; opacity: 0.15; }
  .matrix-label {
    position: absolute;
    font-size: 0.66rem;
    color: var(--muted);
    pointer-events: none;
    z-index: 3;
    font-weight: 500;
  }
  .matrix-label.axis-x-high { top: 50%; right: 0.75rem; transform: translateY(-110%); }
  .matrix-label.axis-x-low  { top: 50%; left: 0.75rem; transform: translateY(-110%); }
  .matrix-label.axis-y-high { left: 50%; top: 0.75rem; transform: translateX(0.5rem); }
  .matrix-label.axis-y-low  { left: 50%; bottom: 0.75rem; transform: translateX(0.5rem); }

  .matrix-quadrant {
    position: absolute;
    width: 50%;
    height: 50%;
    padding: 1.1rem 1.25rem;
    pointer-events: none;
  }
  /* Quadrants : juste une nuance pour distinguer, pas un aplat fort */
  .matrix-quadrant.q-star     { top: 0;   left: 50%; background: linear-gradient(135deg, var(--mint-soft) 0%, transparent 70%); }
  .matrix-quadrant.q-question { top: 0;   left: 0;   background: linear-gradient(225deg, var(--sand-soft) 0%, transparent 70%); }
  .matrix-quadrant.q-cow      { top: 50%; left: 50%; background: linear-gradient(45deg, var(--sky-soft) 0%, transparent 70%); }
  .matrix-quadrant.q-dog      { top: 50%; left: 0;   background: linear-gradient(315deg, var(--coral-soft) 0%, transparent 70%); }
  .quadrant-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1.1rem;
    margin-bottom: 0.25rem;
    line-height: 1;
    letter-spacing: -0.01em;
  }
  .q-star .quadrant-title { color: var(--p-cold); }
  .q-question .quadrant-title { color: var(--p-ret); }
  .q-cow .quadrant-title { color: var(--s-live); }
  .q-dog .quadrant-title { color: var(--s-lost); }
  .quadrant-advice {
    font-size: 0.7rem;
    color: var(--muted);
    line-height: 1.4;
    max-width: 28ch;
  }
  /* Aligner Stars / Cash cows à droite */
  .matrix-quadrant.q-star, .matrix-quadrant.q-cow {
    text-align: right;
  }
  .matrix-quadrant.q-star .quadrant-advice, .matrix-quadrant.q-cow .quadrant-advice {
    margin-left: auto;
  }

  .matrix-dot {
    position: absolute;
    border-radius: 50%;
    border: 2px solid var(--surface);
    background: var(--fg);
    cursor: pointer;
    transition: transform 0.15s, box-shadow 0.15s;
    z-index: 10;
    transform: translate(-50%, -50%);
    box-shadow: 0 2px 5px rgba(35, 31, 35, 0.18);
  }
  .matrix-dot:hover {
    box-shadow: 0 3px 10px rgba(35, 31, 35, 0.3);
    z-index: 20;
  }
  .matrix-dot.q-star { background: var(--p-cold); }
  .matrix-dot.q-question { background: var(--p-ret); }
  .matrix-dot.q-cow { background: var(--s-live); }
  .matrix-dot.q-dog { background: var(--s-lost); }

  .matrix-tooltip {
    position: fixed;
    background: var(--fg);
    color: var(--surface);
    padding: 0.65rem 0.85rem;
    z-index: 400;
    pointer-events: none;
    font-size: 0.72rem;
    line-height: 1.5;
    max-width: 280px;
    border-radius: var(--r-md);
    box-shadow: 0 6px 20px rgba(35, 31, 35, 0.25);
  }
  .matrix-tooltip .name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.95rem;
    display: block;
    margin-bottom: 0.4rem;
    letter-spacing: -0.01em;
  }
  .matrix-tooltip .metric {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    font-variant-numeric: tabular-nums;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.65rem;
    margin-top: 2px;
  }
  .matrix-tooltip .metric span:first-child {
    opacity: 0.6;
    font-size: 0.65rem;
  }
  .matrix-tooltip .quadrant-pill {
    display: inline-block;
    margin-top: 0.45rem;
    padding: 0.15rem 0.55rem;
    font-size: 0.62rem;
    background: rgba(255,255,255,0.15);
    color: var(--surface);
    border-radius: 999px;
    font-weight: 500;
  }

  .matrix-empty {
    padding: 3rem 1rem;
    text-align: center;
    color: var(--muted);
  }
  .matrix-empty .icon {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-size: 3rem;
    color: var(--faint);
    margin-bottom: 0.8rem;
    line-height: 1;
  }
  .matrix-empty h3 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.2rem;
    color: var(--fg);
    margin-bottom: 0.4rem;
  }
  .matrix-empty p {
    font-size: 0.7rem;
    line-height: 1.5;
    max-width: 50ch;
    margin: 0 auto 0.5rem;
  }
  .csv-example {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.62rem;
    opacity: 0.7;
  }
  .csv-example code {
    background: var(--bg);
    padding: 0.15rem 0.35rem;
    border: 1px solid var(--line);
  }

  /* Scrollbar */
  ::-webkit-scrollbar { width: 8px; height: 8px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: var(--line); }
  ::-webkit-scrollbar-thumb:hover { background: var(--muted); }

  @media (max-width: 900px) {
    body { overflow: auto; }
    .topbar { padding: 0 1rem; flex-wrap: wrap; }
    .view { grid-template-columns: 1fr !important; grid-template-rows: auto 75vh; height: auto; }
    .pane-side { border-right: none; border-bottom: 1px solid var(--line); max-height: 50vh; }
  }

  /* ═══════════════════════════════════════════════════════════════════════
     EMBED OVERRIDES — compact topbar for iframe-embedded use
     The topbar is kept in the DOM (the JS depends on its element IDs)
     but visually compacted: no brand block, smaller buttons, no sticky.
     ═══════════════════════════════════════════════════════════════════════ */
  body {
    background: var(--bg);
  }
  .topbar {
    /* Hauteur FIXE = .sidebar-brand (56px) pour que le filet sous la topbar
       s'aligne avec celui sous le logo du rail, sur TOUS les onglets. Avant :
       height auto + min-height → la barre grandissait selon le contenu de
       l'onglet, donc le filet sautait par rapport au rail. Contenu centré
       verticalement (align-items: center), padding vertical retiré. */
    height: 56px;
    min-height: 56px;
    padding: 0 24px;
    border-bottom: 1px solid var(--line-soft);
    background: var(--bg);
    /* Position : pinned en haut au scroll — défini dans le bloc « VERTICAL
       SIDEBAR LAYOUT » (position: fixed, comme le rail) pour rester visible
       (sélecteur projet + profil + icônes) pendant le défilement du document. */
    position: relative;
    flex-wrap: nowrap;   /* keep tabs + actions on one line */
    gap: 16px;
    align-items: center;
  }
  /* Hide the brand block (logo + "Ads · Architect / unified") to avoid
     duplication with the Webflow site header. */
  .topbar-brand {
    display: none !important;
  }
  /* Hide the "Création" tab — its content is part of the workflow but
     we don't want it as a separate top-level tab in the embedded view. */
  .topbar nav.tabs .tab[data-tab="create"] {
    display: none !important;
  }
  /* Hide the entire right action bar (Undo / +Test / Mode client /
     Notifications / User switch) — these app-level actions don't belong
     in a marketing-page embed. The JS still references their IDs but
     since they're display:none, the listeners never fire. */
  .topbar-right {
    display: none !important;
  }
  /* The tabs become the primary navigation. Make them more prominent and
     part of the body content, not a heavy header. */
  .topbar nav.tabs {
    flex: 0 1 auto;
    align-items: center;
    gap: 4px;
    height: auto;
    border: none;
  }
  .topbar nav.tabs .tab {
    padding: 8px 14px;
    font-size: 14px;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    color: var(--muted);
    background: transparent;
    border: none;
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
  }
  .topbar nav.tabs .tab:hover {
    background: rgba(35,31,35,0.06);
    color: var(--fg);
  }
  .topbar nav.tabs .tab.active {
    background: var(--fg);
    color: var(--bg);
  }
  .topbar nav.tabs .tab.active::after {
    display: none; /* underline indicator removed since active = filled bg */
  }
  .topbar nav.tabs .tab .tab-count {
    font-size: 11px;
    background: rgba(35,31,35,0.08);
    color: var(--muted);
    padding: 2px 6px;
    border-radius: 999px;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    line-height: 1.2;
  }
  .topbar nav.tabs .tab.active .tab-count {
    background: rgba(247,246,245,0.16);
    color: var(--bg);
  }
  /* Right-side action buttons: compact and discrete. */
  .topbar-right {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
    margin-left: auto;  /* push to the right of the topbar */
  }
  .topbar-right .tb-btn {
    padding: 8px 14px;
    font-size: 14px;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    border-radius: 8px;
    border: 1px solid transparent;
    background: rgba(35,31,35,0.06);
    color: var(--fg);
    cursor: pointer;
    transition: background 0.15s;
  }
  .topbar-right .tb-btn:hover:not(:disabled) {
    background: rgba(35,31,35,0.10);
  }
  .topbar-right .tb-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
  }
  .topbar-right .tb-btn.primary {
    background: var(--fg);
    color: var(--bg);
  }
  .topbar-right .tb-btn.primary:hover:not(:disabled) {
    opacity: 0.88;
  }
  /* User switch + notifications: minimize them since they don't matter
     in an embedded marketing-page context (most visitors won't be logged in). */
  /* Hide user name/role text on small embeds — keep just the avatar */
  /* Mobile: stack the topbar elements */
  @media (max-width: 768px) {
    .topbar {
      padding: 10px 16px;
      flex-wrap: wrap;
    }
    .topbar nav.tabs .tab {
      padding: 6px 10px;
      font-size: 13px;
    }
    .topbar-right .tb-btn {
      padding: 6px 10px;
      font-size: 13px;
    }
  }

  /* ═══════════════════════════════════════════════════════════════════════
     IFRAME LAYOUT FIXES — ensure views work with auto-resize
     The original code uses height: 100% on .view and .main-area to fill
     the viewport. In an iframe with postMessage auto-resize, height: 100%
     has no stable reference — content overflows or gets clipped.
     We switch to natural content height with min-heights as a floor.
     ═══════════════════════════════════════════════════════════════════════ */
  html, body {
    height: auto !important;
    min-height: 0 !important;
    overflow: visible !important;
  }
  .main-area {
    height: auto !important;
    overflow: visible !important;
  }
  .view {
    height: auto !important;
  }
  .pane-side {
    /* Sidebars become independently scrollable — they get their own
       max-height with internal scroll. This way the page height is
       driven by the canvas (right pane), not by potentially long
       sidebar content.
       Fixed pixel max-height (not vh) because vh inside an iframe
       refers to the iframe's own height, which we control via
       postMessage — that creates a feedback loop. */
    overflow-y: auto !important;
    max-height: 900px !important;
    height: auto !important;
    align-self: start;
  }
  .pane-main {
    overflow: visible !important;
    /* CRITICAL: pane-main must have an explicit height because its children
       (.viz-scroll with flex:1, .viz-container with position:absolute)
       depend on it. Without this, the canvas collapses to 0 and the right
       pane appears empty. */
    height: 900px !important;
    min-height: 900px !important;
  }
  /* The tree canvas needs to scroll horizontally if it's wider than the
     iframe viewport — but vertically it should expand. */
  .viz-scroll,
  .viz-container {
    overflow-x: auto !important;
    overflow-y: visible !important;
  }

  /* Create view has its own overflow-y: auto rules above — in iframe-embed
     mode we don't want internal scroll, the iframe's postMessage
     auto-resize handles the height. */
  .view-create,
  .view-create.active {
    overflow-y: visible !important;
    overflow-x: visible !important;
    height: auto !important;
    max-height: none !important;
  }

  /* ═══════════════════════════════════════════════════════════════════════
     SIDEBAR — compacted by default, collapsible to a thin rail
     The original layout was 38% (Setup) / 36% (Tests). Compacted to
     ~30% / 28% to give more room to the canvas (which is the main
     content). User can collapse further via the toggle button.
     ═══════════════════════════════════════════════════════════════════════ */
  .view-setup {
    grid-template-columns: minmax(280px, 22%) 1fr !important;
    /* align-items: start prevents grid from stretching both columns to the
       same height. Without it, if the sidebar is taller than the canvas
       (or vice versa), the shorter one gets stretched, leaving empty
       space at the bottom. */
    align-items: start !important;
    transition: grid-template-columns 0.25s ease;
  }
  .view-tests {
    grid-template-columns: minmax(280px, 22%) 1fr !important;
    align-items: start !important;
    transition: grid-template-columns 0.25s ease;
  }
  /* Matrix : la grille s'applique sur .matrix-wrap (et non .view-matrice
     directement) parce que la vue contient .roas-wrap > .matrix-wrap
     comme wrappers visuels. Les enfants directs de .matrix-wrap sont
     désormais <aside class="pane-side"> + <main class="pane-main">.

     Override : la .roas-wrap a un max-width: 1200px + margin auto qui
     centrait tout le contenu et créait un grand espace vide à gauche
     dès qu'on passe en layout 2-col plein largeur. On l'élargit en
     pleine largeur ici pour que la sidebar arrive au bord gauche
     comme Setup/Tests. */
  .view-matrice.active .roas-wrap {
    max-width: 1280px;
    margin: 0 auto;
    padding: 1.5rem 1.75rem 3rem;
  }
  /* Plus de rail input à gauche : layout 1 COLONNE → les réglages
     (#matrice-sidebar) passent EN HAUT en bloc pleine largeur (repliable via
     le chevron), la matrice juste en dessous, pleine largeur. Cohérent avec
     P&L / Corrélation (largeur hub 1280). */
  .view-matrice.active .matrix-wrap,
  .view-matrice.sidebar-collapsed .matrix-wrap {
    display: grid;
    grid-template-columns: 1fr;
    align-items: start;
    gap: 1rem;
  }
  #matrice-sidebar.pane-side {
    background: transparent;
    border-right: none;
    border-bottom: 1px solid var(--line-soft);
    padding: 0 0 1.25rem;
    overflow: visible;
  }
  /* Replié (chevron) : on masque le bloc de mapping (sélecteurs/affiner) ; la
     barre de titre + actions (Upload/Reset) + chevron restent visibles, la
     matrice remonte. On neutralise l'opacity-collapse hérité du rail vertical. */
  .view-matrice.sidebar-collapsed #matrice-sidebar.pane-side {
    border-bottom: none;
    padding-bottom: 0;
  }
  .view-matrice #matrice-sidebar.pane-side.collapsed > div:not(.sidebar-toggle-wrap) {
    opacity: 1;
    pointer-events: auto;
  }
  .view-matrice.sidebar-collapsed #matrice-sidebar .csv-mapping {
    display: none !important;
  }
  /* Inference (correlation) : à harmoniser dans une session suivante.
     Le React component CorrelationExplorer rend un seul root div qui
     contient plusieurs étapes (upload / mapping / analyzing / dashboard)
     selon `stage`. Une refonte propre demande de splitter le contenu en
     pane-side (upload + mapping) et pane-main (dashboard) avec une logique
     de routing adaptée. Pas inclus dans cette passe pour rester safe. */
  /* When sidebar is collapsed, the column shrinks to a thin rail
     (just enough to show the toggle button) and the canvas takes the rest. */
  .view-setup.sidebar-collapsed,
  .view-tests.sidebar-collapsed {
    grid-template-columns: 44px 1fr !important;
  }
  /* ≤900px : le layout empilé prime sur l'état replié persisté — sans
     cette règle (même spécificité+!important, mais déclarée APRÈS, donc
     gagnante), un repli sauvegardé sur desktop imposerait un rail 44px
     écrasé au chargement sur mobile. */
  @media (max-width: 900px) {
    .view-setup.sidebar-collapsed,
    .view-tests.sidebar-collapsed,
    .view-project.sidebar-collapsed {
      grid-template-columns: 1fr !important;
    }
  }
  /* Bulk : la grille est maintenant directement sur .cg-wrap (le
     wrapper .cg-panels a été supprimé du markup). Quand collapsed,
     la sidebar (#bulk-sidebar) reste à 44px de large pour garder le
     bouton chevron accessible — sinon il disparaîtrait avec son
     parent. Pattern identique à .pane-side.collapsed (Setup/Tests). */
  .view-bulk.sidebar-collapsed .cg-wrap {
    grid-template-columns: 44px 1fr !important;
  }
  /* Quand bulk est replié, on cache le contenu de la sidebar sauf
     le toggle-wrap, et on resserre le padding pour que la rail soit
     bien fine. Cohérent avec .pane-side.collapsed. */
  .view-bulk.sidebar-collapsed #bulk-sidebar > *:not(.sidebar-toggle-wrap) {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
  }
  .view-bulk #bulk-sidebar > *:not(.sidebar-toggle-wrap) {
    transition: opacity 0.25s ease 0.1s;
  }
  /* Rotation du chevron à l'état collapsed (cohérent avec
     .pane-side.collapsed .sidebar-toggle .chevron). */
  .view-bulk.sidebar-collapsed #bulk-sidebar .sidebar-toggle .chevron {
    transform: rotate(180deg);
  }
  /* The toggle button itself: floats in the top-right corner of the
     sidebar so it doesn't push the content below it down. The wrapper
     is positioned absolutely to take it out of normal flow entirely. */
  .sidebar-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: 1px solid rgba(35,31,35,0.12);
    border-radius: 999px;
    background: var(--surface);
    color: var(--fg);
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    padding: 0;
    transition: background 0.15s, border-color 0.15s;
    flex-shrink: 0;
  }
  .sidebar-toggle:hover {
    background: rgba(35,31,35,0.06);
    border-color: rgba(35,31,35,0.32);
  }
  .sidebar-toggle .chevron {
    display: inline-block;
    transition: transform 0.25s ease;
  }
  /* Sidebar content fades out when collapsed */
  .pane-side.collapsed .pane-head,
  .pane-side.collapsed > div:not(.sidebar-toggle-wrap) {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
  }
  .pane-side .pane-head,
  .pane-side > div:not(.sidebar-toggle-wrap) {
    transition: opacity 0.25s ease 0.1s;
  }
  /* When collapsed, center the toggle button vertically in the rail */
  .pane-side.collapsed {
    padding: 12px 0 4rem !important;
  }
  .pane-side.collapsed .sidebar-toggle-wrap {
    margin-right: 8px;
    margin-left: 8px;
  }
  .pane-side.collapsed .sidebar-toggle .chevron {
    transform: rotate(180deg);
  }
  /* Wrapper around the toggle button — absolutely positioned so it
     overlays the top-right corner of the sidebar without taking
     vertical space in the flow. */
  .sidebar-toggle-wrap {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 5;
    display: flex;
    justify-content: flex-end;
  }

  /* ═══════════════════════════════════════════════════════════════════════
     TOPBAR TOOLS — Reset + Guide buttons (replaces hidden topbar-right)
     These are visible by default in the embedded view. They give the user
     access to the destructive Reset and the help walkthrough.
     ═══════════════════════════════════════════════════════════════════════ */
  .topbar-tools {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    /* Les icônes utilitaires (thème / paramètres / aide) ouvrent le cluster
       de droite : margin-left:auto les pousse au bord droit (élément toujours
       visible, ancre fiable même si .topbar-right est masqué), et order:2 les
       place AVANT le chip auth (order:4) qui devient ainsi le plus à droite. */
    order: 2;
    margin-left: auto;
  }
  /* Bouton « Actualiser » : l'icône tourne pendant le rechargement. */
  @keyframes tb-spin { to { transform: rotate(360deg); } }
  #btn-refresh.is-spinning svg { animation: tb-spin 0.7s linear infinite; }
  #btn-refresh.is-spinning { cursor: default; }

  /* ── Recherche topbar « va à la page » (app-search.js) ── */
  .tb-search {
    position: relative;
    display: flex; align-items: center; gap: 6px;
    background: var(--surface, #fff);
    border: 1px solid var(--line, #231f231f);
    border-radius: 8px;
    padding: 6px 9px;
    min-width: 140px; flex: 0 1 200px;
  }
  .tb-search:focus-within { border-color: var(--ink-32, #231f2352); }
  .tb-search-ic { color: var(--ink-48, #231f237a); flex: 0 0 auto; }
  .tb-search-input {
    flex: 1; min-width: 0; border: none; background: transparent; outline: none;
    font-family: inherit; font-size: 12.5px; color: var(--fg, #231f23);
  }
  .tb-search-input::placeholder { color: var(--ink-48, #231f237a); }
  .tb-search-kbd {
    flex: 0 0 auto; font-family: 'Fragment Mono', ui-monospace, monospace; font-size: 9px;
    color: var(--ink-48, #231f237a); border: 1px solid var(--line, #231f231f);
    border-radius: 4px; padding: 1px 4px;
  }
  .tb-search-results {
    position: absolute; top: calc(100% + 6px); right: 0;
    min-width: 290px; max-width: 360px;
    background: var(--surface, #fff);
    border: 1px solid var(--line-soft, #231f230f);
    border-radius: 10px;
    box-shadow: var(--shadow-lg, 0 12px 40px rgba(35, 31, 35, 0.16));
    padding: 5px; z-index: 80; max-height: 64vh; overflow-y: auto;
  }
  .tb-sr {
    display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
    appearance: none; border: none; background: transparent; cursor: pointer;
    border-radius: 7px; padding: 8px 10px; font-family: inherit; color: var(--fg, #231f23);
  }
  .tb-sr:hover:not(:disabled), .tb-sr.is-active { background: var(--depth, #ececec); }
  .tb-sr-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
  .tb-sr-label { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .tb-sr-sub { font-size: 11px; color: var(--ink-64, #231f23a3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .tb-sr.is-soon { cursor: default; opacity: 0.5; }
  .tb-sr-soon {
    flex: 0 0 auto; font-family: 'Fragment Mono', ui-monospace, monospace; font-size: 9px;
    letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-48, #231f237a);
    border: 1px solid var(--line, #231f231f); border-radius: 999px; padding: 2px 7px;
  }
  @media (max-width: 720px) { .tb-search { display: none; } }
  /* L'auth gate (.am-backdrop.am-blocking, z-index:9999) couvre tout le
     viewport et avale les clics du topbar — qui, étant sticky + z-index:60,
     forme son propre contexte d'empilement : aucun de ses enfants ne peut
     repasser au-dessus du backdrop, donc le bouton thème devenait mort sur
     l'écran de login. On remonte le topbar au-dessus du backdrop le temps
     du gate, mais on neutralise ses clics SAUF pour .topbar-tools : la
     préférence clair/sombre (cosmétique, inoffensive) reste réglable même
     déconnecté, tandis que projet/auth restent verrouillés. Scopé via
     :has() → zéro impact en session connectée ou sur les autres modales. */
  body:has(.am-backdrop.am-blocking.am-open) .topbar {
    z-index: 10000;
    pointer-events: none;
  }
  body:has(.am-backdrop.am-blocking.am-open) .topbar-tools {
    pointer-events: auto;
  }
  /* Auth chip wrapper — dernier élément du topbar (order:4) donc collé tout
     au bord droit, à droite des icônes utilitaires (qui portent l'ancre
     margin-left:auto). margin-left donne un peu d'air entre icônes et profil. */
  .topbar-auth {
    order: 4;
    /* Même espacement que le gap inter-icônes (6px) → les boutons thème/langue
       sont collés au profil, espacement homogène. */
    margin-left: 6px;
    display: flex;
    align-items: center;
    flex-shrink: 0;
    padding-right: 4px;
  }
  /* Project zone — sits between brand and tabs. Same chip styling as
     topbar-tools so the visual rhythm of the topbar stays uniform. */
  .topbar-project {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
    padding: 0 10px;
    border-left: 1px solid var(--line-soft);
    border-right: 1px solid var(--line-soft);
    margin: 0 4px;
  }
  .tb-btn-tool {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    font-size: 13px;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    border: 1px solid rgba(35,31,35,0.12);
    background: var(--surface);
    color: var(--fg);
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.15s;
  }
  .tb-btn-tool:hover {
    background: rgba(35,31,35,0.06);
    border-color: rgba(35,31,35,0.32);
  }
  .tb-btn-tool svg {
    flex-shrink: 0;
    color: var(--muted);
  }
  .tb-btn-tool:hover svg { color: var(--fg); }
  @media (max-width: 768px) {
    .tb-btn-tool span { display: none; } /* icon-only on mobile */
    .tb-btn-tool { padding: 6px 8px; }
  }

  /* Bouton Aide (?) + popover : FAQ (bientôt) + contact support.
     Réutilise les tokens de .tb-btn-tool pour rester cohérent clair/sombre. */
  .help-wrap { position: relative; display: inline-flex; }
  .help-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    min-width: 210px;
    background: var(--surface);
    border: 1px solid rgba(35,31,35,0.14);
    border-radius: 10px;
    box-shadow: 0 8px 28px rgba(0,0,0,0.16);
    padding: 6px;
    z-index: 1200;
  }
  .help-menu[hidden] { display: none; }
  .help-menu-head {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    padding: 4px 8px 6px;
  }
  .help-menu-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    border: 0;
    background: transparent;
    color: var(--fg);
    font-family: inherit;
    font-size: 13px;
    text-align: left;
    text-decoration: none;
    border-radius: 7px;
    cursor: pointer;
  }
  .help-menu-item:hover:not([disabled]) { background: rgba(35,31,35,0.06); }
  .help-menu-item[disabled] { opacity: 0.55; cursor: default; }
  .help-soon {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--muted);
    border: 1px solid rgba(35,31,35,0.18);
    border-radius: 999px;
    padding: 1px 7px;
  }

  /* Boutons icône du topbar (thème / paramètres / question) — carrés,
     icône seule. Le toggle thème montre la lune en clair, le soleil en
     sombre (visibilité pilotée par html[data-theme="dark"]). */
  .tb-icon-btn { padding: 6px 8px; }
  .tb-icon-btn svg { display: block; }
  .tb-icon-btn .ic-sun { display: none; }
  html[data-theme="dark"] .tb-icon-btn .ic-moon { display: none; }
  html[data-theme="dark"] .tb-icon-btn .ic-sun { display: block; }

  /* Boutons « Reset » par outil (heads Setup / Tests). Reprend le look
     .tb-btn-tool mais teinté « danger » au survol pour signaler l'action
     destructive (ne vide QUE l'outil courant, pas tout le projet). */
  .tool-reset-btn:hover {
    background: rgba(179, 38, 30, 0.08);
    border-color: rgba(179, 38, 30, 0.35);
    color: #b3261e;
  }
  .tool-reset-btn:hover svg { color: #b3261e; }
  /* Head Tests : groupe le bouton reset + le sélecteur de période à droite. */
  .tests-head-right {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
  }

  /* ── Glisser-déposer universel des uploads (file-dropzone.js) ──────
     La zone survolée par un fichier en cours de drag reçoit
     .is-file-dragover : outline en pointillés (n'altère pas le layout)
     + légère teinte inset pour signaler clairement la cible. */
  .is-file-dragover {
    outline: 2px dashed var(--p-test, #5A4ECC) !important;
    outline-offset: 2px;
    box-shadow: inset 0 0 0 9999px rgba(90, 78, 204, 0.07);
    border-radius: 8px;
    transition: outline-color .12s ease;
  }

  /* ════════ Project switcher button + popover ════════ */
  /* The switcher button shows the active project's name and opens
     a popover anchored to it. The active name is bolder + colored
     so the user always sees which client they're working on. */
  .project-switcher-btn {
    background: var(--bg);
    max-width: 220px;
  }
  .project-switcher-btn .project-switcher-name {
    font-weight: 500;
    color: var(--fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 140px;
    display: inline-block;
  }
  .project-switcher-caret {
    font-size: 10px;
    color: var(--muted);
    margin-left: 2px;
  }
  .project-switcher-btn.is-open {
    background: var(--depth);
    border-color: var(--p-cold);
  }

  /* ════════ Project switcher split-button ════════ */
  /* Two buttons that look like one visual unit: the left side
     (#btn-project-switcher) navigates to the project view, the right
     side (#btn-project-switcher-caret) opens the popover.
     We use shared border + matching radii on the joined edge so it
     reads as a single capsule. */
  .project-switcher-group {
    display: inline-flex;
    align-items: stretch;
  }
  .project-switcher-main {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    border-right: none;
  }
  .project-switcher-caret-btn {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    padding: 6px 8px;
    min-width: 28px;
  }
  /* "You are here" indicator on the main side when the project view is
     the active view. Replaces the visual cue the removed tab provided. */
  .project-switcher-main.is-current-tab,
  .project-switcher-main.is-current-tab:hover {
    background: var(--depth);
    border-color: var(--p-cold);
    color: var(--p-cold);
  }
  .project-switcher-main.is-current-tab .project-switcher-name {
    color: var(--p-cold);
  }
  /* Open-popover indicator on the caret side */
  .project-switcher-caret-btn.is-open {
    background: var(--depth);
    border-color: var(--p-cold);
  }
  .project-switcher-caret-btn.is-open .project-switcher-caret {
    color: var(--p-cold);
    transform: rotate(180deg);
  }
  .project-switcher-caret {
    transition: transform 0.15s ease, color 0.15s ease;
    display: inline-block;
  }
  .project-popover {
    position: fixed;
    z-index: 9000;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: 0 12px 32px rgba(35, 31, 35, 0.18);
    min-width: 320px;
    max-width: 420px;
    max-height: 70vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    animation: ppFadeIn 0.15s ease-out;
  }
  @keyframes ppFadeIn {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .pp-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.7rem 0.9rem;
    border-bottom: 1px solid var(--line-soft);
  }
  .pp-title {
    font-family: 'StackSans headline Variable', system-ui, sans-serif;
    font-style: italic;
    font-size: 0.85rem;
    color: var(--fg);
  }
  .pp-close {
    background: transparent;
    border: none;
    font-size: 1.1rem;
    color: var(--muted);
    cursor: pointer;
    padding: 0 0.3rem;
    line-height: 1;
  }
  .pp-close:hover { color: var(--fg); }
  .pp-list {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 0.3rem 0.4rem;
    min-height: 60px;
  }
  .pp-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.55rem;
    border-radius: var(--r-sm);
    cursor: pointer;
    transition: background 0.1s;
  }
  .pp-row:hover { background: var(--bg); }
  .pp-row.active {
    background: var(--p-cold-soft);
    border: 1px solid var(--p-cold);
  }
  .pp-row.active .pp-row-name { color: var(--p-cold); font-weight: 500; }
  .pp-row-icon {
    width: 22px; height: 22px;
    border-radius: var(--r-sm);
    background: var(--bg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.65rem;
    font-weight: 600;
    color: var(--p-cold);
    flex-shrink: 0;
  }
  .pp-row.active .pp-row-icon {
    background: var(--surface);
    color: var(--p-cold);
  }
  .pp-row-main {
    flex: 1 1 auto;
    min-width: 0;
  }
  .pp-row-name {
    font-size: 0.78rem;
    color: var(--fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 1.2;
  }
  .pp-row-meta {
    font-size: 0.55rem;
    color: var(--muted);
    line-height: 1.2;
    margin-top: 1px;
  }
  .pp-row-tools {
    display: flex;
    gap: 0.15rem;
    flex-shrink: 0;
    opacity: 0;
    transition: opacity 0.15s;
  }
  .pp-row:hover .pp-row-tools { opacity: 1; }
  .pp-row-tool {
    width: 22px; height: 22px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 3px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.65rem;
    color: var(--muted);
    padding: 0;
  }
  .pp-row-tool:hover {
    border-color: var(--line);
    color: var(--fg);
    background: var(--surface);
  }
  .pp-row-tool.danger:hover {
    border-color: var(--coral-soft);
    color: var(--s-lost);
  }
  .pp-divider {
    height: 1px;
    background: var(--line-soft);
    margin: 0.2rem 0.6rem;
  }
  .pp-actions {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0.4rem 0.4rem 0.6rem;
  }
  .pp-action {
    width: 100%;
    text-align: left;
    background: transparent;
    border: 1px solid var(--line-soft);
    border-radius: var(--r-sm);
    padding: 0.5rem 0.65rem;
    font-family: inherit;
    font-size: 0.72rem;
    color: var(--fg);
    cursor: pointer;
    transition: background 0.1s, border-color 0.1s;
  }
  .pp-action:hover {
    background: var(--bg);
    border-color: var(--p-cold);
    color: var(--p-cold);
  }
  .pp-empty {
    padding: 1.2rem 0.8rem;
    text-align: center;
    color: var(--muted);
    font-size: 0.7rem;
    font-style: italic;
  }
  /* Phase 3.A.9 — Quota badge inside the project popover. */
  .pp-quota {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    padding: 0.4rem 0.6rem 0;
    font-size: 0.7rem;
    color: var(--muted);
    font-family: 'Fragment Mono', ui-monospace, monospace;
  }
  .pp-quota.is-full {
    color: var(--s-lost, #b04040);
  }
  .pp-quota-count {
    font-weight: 600;
  }
  .pp-quota-upgrade {
    appearance: none;
    background: transparent;
    border: 1px solid currentColor;
    border-radius: var(--r-sm);
    color: inherit;
    cursor: pointer;
    font-family: inherit;
    font-size: 0.65rem;
    padding: 0.18rem 0.55rem;
    transition: background 0.1s;
  }
  .pp-quota-upgrade:hover {
    background: var(--bg);
  }
  /* Inline rename input that replaces the project name when ✏ is clicked */
  .pp-row-rename-input {
    flex: 1 1 auto;
    background: var(--surface);
    border: 1px solid var(--p-cold);
    border-radius: var(--r-sm);
    padding: 0.25rem 0.4rem;
    font-family: inherit;
    font-size: 0.78rem;
    color: var(--fg);
    outline: none;
    min-width: 0;
  }


  /* ═══════════════════════════════════════════════════════════════════════
     RESET CONFIRMATION DIALOG
     ═══════════════════════════════════════════════════════════════════════ */
  .reset-dialog {
    position: fixed;
    inset: 0;
    z-index: 9998;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 80px;
    padding-left: 20px;
    padding-right: 20px;
    padding-bottom: 20px;
    animation: onboarding-fadein 0.2s ease;
  }
  .reset-dialog-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(35, 31, 35, 0.45);
  }
  .reset-dialog-card {
    position: relative;
    background: #f7f6f5;
    border-radius: 14px;
    padding: 28px 32px;
    max-width: 440px;
    width: 100%;
    box-shadow: 0 25px 60px rgba(0,0,0,0.18);
    z-index: 1;
  }
  .reset-dialog-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.4rem;
    color: #231f23;
    margin-bottom: 0.5rem;
  }
  .reset-dialog-body {
    font-size: 0.85rem;
    line-height: 1.5;
    color: rgba(35,31,35,0.7);
    margin-bottom: 1.5rem;
  }
  .reset-dialog-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
  }
  .reset-dialog-cancel {
    background: transparent;
    border: 1px solid rgba(35,31,35,0.18);
    color: #231f23;
    font-family: inherit;
    font-size: 0.85rem;
    padding: 8px 16px;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.12s;
  }
  .reset-dialog-cancel:hover {
    background: rgba(35,31,35,0.06);
  }
  .reset-dialog-confirm {
    background: #ED7472;
    border: 1px solid #ED7472;
    color: #fff;
    font-family: inherit;
    font-size: 0.85rem;
    padding: 8px 16px;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.12s;
  }
  .reset-dialog-confirm:hover {
    background: #d65f5d;
    border-color: #d65f5d;
  }

  /* ═══════════════════════════════════════════════════════════════════════
     SIDEBAR SEARCH (Setup + Tests)
     A small search input added at the top of each sidebar that filters
     visible items in real time. CSS-based filtering — no DOM rebuild,
     so collapsed/expanded states are preserved across keystrokes.
     ═══════════════════════════════════════════════════════════════════════ */
  .sidebar-search {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 1rem 0.5rem;
    padding: 4px 8px;
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 0.4rem;
    transition: all 0.12s;
    flex-wrap: nowrap;
  }
  .sidebar-search:focus-within {
    background: var(--surface, #fff);
    border-color: rgba(35, 31, 35, 0.32);
  }
  .sidebar-search .search-icon {
    color: rgba(35, 31, 35, 0.5);
    flex-shrink: 0;
  }
  .sidebar-search:focus-within .search-icon {
    color: var(--fg, #231f23);
  }
  .sidebar-search .search-input {
    flex: 1;
    min-width: 60px;
    background: transparent;
    border: none;
    outline: none;
    font-family: inherit;
    font-size: 0.72rem;
    color: var(--fg, #231f23);
    padding: 3px 0;
  }
  .sidebar-search .search-input::placeholder {
    color: rgba(35, 31, 35, 0.45);
  }
  .sidebar-search .search-clear {
    background: transparent;
    border: none;
    color: rgba(35, 31, 35, 0.5);
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0 4px;
    border-radius: 50%;
    width: 18px;
    height: 18px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
  .sidebar-search .search-clear:hover {
    background: rgba(35, 31, 35, 0.1);
    color: var(--fg, #231f23);
  }
  .sidebar-search .search-checked-only {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.7rem;
    color: rgba(35, 31, 35, 0.6);
    cursor: pointer;
    padding: 3px 6px;
    border-radius: 4px;
    user-select: none;
    flex-shrink: 0;
  }
  .sidebar-search .search-checked-only:hover {
    background: rgba(35, 31, 35, 0.08);
    color: var(--fg, #231f23);
  }
  .sidebar-search .search-checked-only input[type="checkbox"] {
    margin: 0;
    cursor: pointer;
  }

  /* Hide elements that don't match the search criteria.
     Cascade: when all children of a container are hidden, the container
     itself gets hidden too via JS adding the class. */
  .search-hidden {
    display: none !important;
  }

  /* Empty state shown when search has no results */
  .search-no-results {
    padding: 1rem 1.5rem;
    color: rgba(35, 31, 35, 0.5);
    font-size: 0.8rem;
    font-style: italic;
    text-align: center;
  }

  /* ═══════════════════════════════════════════════════════════════════════
     SAVE INDICATOR (localStorage auto-save status)
     A small pill in the topbar showing whether the work is saved locally.
     States:
       .save-indicator.saved     → green dot + "Sauvegardé"
       .save-indicator.saving    → orange dot + "Sauvegarde…"
       .save-indicator.error     → red dot + "Erreur"
       .save-indicator.disabled  → grey dot + "Hors ligne / indisponible"
       .save-indicator.conflict  → red dot + "Conflit cloud — vue à jour"
                                   (Sprint 2 — multi-device sync)
     ═══════════════════════════════════════════════════════════════════════ */
  .save-indicator {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    font-size: 11px;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    color: rgba(35, 31, 35, 0.6);
    background: transparent;
    border-radius: 999px;
    letter-spacing: 0.02em;
    user-select: none;
    transition: all 0.2s;
  }
  .save-indicator .save-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: rgba(35, 31, 35, 0.3);
    flex-shrink: 0;
    transition: background 0.2s;
  }
  .save-indicator.saved .save-dot {
    background: #2A6B30;
  }
  .save-indicator.saving .save-dot {
    background: #C57E1A;
    animation: save-pulse 1s ease-in-out infinite;
  }
  .save-indicator.error .save-dot {
    background: #C53D2C;
  }
  .save-indicator.disabled {
    opacity: 0.5;
  }
  .save-indicator.disabled .save-dot {
    background: rgba(35, 31, 35, 0.3);
  }
  /* Sprint 2 (audit fix #2) — conflit cloud détecté pendant un pull.
     Rouge + bordure pour différencier du simple 'error' (qui est
     transient). L'état est auto-reset après 8s par le handler dans
     main.jsx pour ne pas bloquer l'indicator. */
  .save-indicator.conflict {
    background: rgba(197, 61, 44, 0.08);
    border: 1px solid rgba(197, 61, 44, 0.3);
    color: #C53D2C;
  }
  .save-indicator.conflict .save-dot {
    background: #C53D2C;
    animation: save-pulse 1s ease-in-out infinite;
  }
  @keyframes save-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
  }
  @media (max-width: 768px) {
    .save-indicator .save-text { display: none; }
  }

  /* ═══════════════════════════════════════════════════════════════════════
     KANBAN IN TESTS VIEW (only visible when in Journal submode)
     The same 3-column kanban that lives in Récap is also rendered here
     above the journal listing, so the user has a one-glance overview of
     completed / live / planned tests right next to the journal entries.
     ═══════════════════════════════════════════════════════════════════════ */
  .tj-kanban-wrap {
    display: none; /* hidden by default; only mode-journal makes it visible */
    padding: 0 1.5rem 0.5rem;
    background: var(--bg);
  }
  .pane-main.mode-journal .tj-kanban-wrap {
    display: block;
  }
  .tj-kanban-wrap .tj-kanban {
    /* Tighter spacing than the Récap kanban */
    padding: 0;
    gap: 0.7rem;
  }
  /* In journal mode we don't want the timeline taking up extra space */
  .pane-main.mode-journal .viz-top {
    border-bottom: 1px solid var(--line-soft);
  }

  /* Journal header (title + tagline + stats) shown above the kanban
     when in Journal submode. Hidden in other submodes. */
  .tj-journal-header {
    display: none;
    padding: 1.25rem 1.5rem 0.75rem;
    background: var(--bg);
  }
  .pane-main.mode-journal .tj-journal-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
  }

  /* ════════════════════════════════════════════════════════════════
     CMD PALETTE — ⌘K / Ctrl+K
     ════════════════════════════════════════════════════════════════ */
  .cmd-palette {
    position: fixed;
    inset: 0;
    z-index: 9500;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 12vh;
  }
  .cmdp-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(35, 31, 35, 0.25);
    animation: cmdpFadeIn 0.15s ease;
  }
  .cmdp-card {
    position: relative;
    width: 92%;
    max-width: 580px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: 0 24px 60px rgba(35, 31, 35, 0.25);
    overflow: hidden;
    animation: cmdpSlideIn 0.18s ease-out;
  }
  @keyframes cmdpFadeIn { from { opacity: 0; } to { opacity: 1; } }
  @keyframes cmdpSlideIn {
    from { opacity: 0; transform: translateY(-8px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .cmdp-input-wrap {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.85rem 1rem;
    border-bottom: 1px solid var(--line-soft);
  }
  .cmdp-icon {
    font-size: 0.85rem;
    color: var(--muted);
    font-family: 'Fragment Mono', monospace;
    flex-shrink: 0;
  }
  .cmdp-input {
    flex: 1;
    background: transparent;
    border: none;
    outline: none;
    font-family: inherit;
    font-size: 0.95rem;
    color: var(--fg);
    padding: 0;
  }
  .cmdp-input::placeholder { color: var(--faint); }
  .cmdp-hint {
    font-size: 0.55rem;
    color: var(--muted);
    font-family: 'Fragment Mono', monospace;
    letter-spacing: 0.04em;
    flex-shrink: 0;
    white-space: nowrap;
  }
  .cmdp-results {
    max-height: 50vh;
    overflow-y: auto;
    padding: 0.3rem;
  }
  .cmdp-section {
    padding: 0.4rem 0.7rem 0.2rem;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .cmdp-result {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.55rem 0.7rem;
    cursor: pointer;
    border-radius: var(--r-sm);
    background: transparent;
    border: none;
    width: 100%;
    text-align: left;
    font-family: inherit;
    color: var(--fg);
    transition: background 0.1s;
  }
  .cmdp-result:hover,
  .cmdp-result.is-active { background: var(--bg); }
  .cmdp-result.is-active { background: var(--p-cold-soft); }
  .cmdp-result-icon {
    width: 22px; height: 22px;
    border-radius: 4px;
    background: var(--bg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8rem;
    color: var(--muted);
    flex-shrink: 0;
  }
  .cmdp-result.is-active .cmdp-result-icon { background: var(--surface); color: var(--p-cold); }
  .cmdp-result-main { flex: 1; min-width: 0; }
  .cmdp-result-label {
    font-size: 0.78rem;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cmdp-result-sub {
    font-size: 0.6rem;
    color: var(--muted);
    line-height: 1.2;
    margin-top: 1px;
  }
  .cmdp-result-shortcut {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    color: var(--muted);
    background: var(--bg);
    padding: 0.15rem 0.35rem;
    border-radius: 3px;
    border: 1px solid var(--line-soft);
    flex-shrink: 0;
  }
  .cmdp-empty {
    padding: 1.5rem 1rem;
    text-align: center;
    color: var(--muted);
    font-size: 0.7rem;
    font-style: italic;
  }

  /* ════════════════════════════════════════════════════════════════
     MONTHLY RECAP MODAL — print-friendly report
     ════════════════════════════════════════════════════════════════ */
  .recap-modal {
    position: fixed;
    inset: 0;
    z-index: 9000;
    overflow-y: auto;
    background: rgba(35, 31, 35, 0.4);
  }
  .recap-backdrop { position: absolute; inset: 0; }
  .recap-card {
    position: relative;
    max-width: 920px;
    margin: 30px auto;
    background: var(--surface);
    border-radius: var(--r-md);
    box-shadow: 0 12px 40px rgba(35, 31, 35, 0.3);
    overflow: hidden;
  }
  .recap-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.8rem 1.2rem;
    background: var(--bg);
    border-bottom: 1px solid var(--line-soft);
    flex-wrap: wrap;
  }
  .recap-toolbar-left {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    flex-wrap: wrap;
  }
  .recap-toolbar-label { font-size: 0.7rem; color: var(--muted); }
  .recap-month {
    font-family: inherit;
    font-size: 0.78rem;
    padding: 0.4rem 0.5rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    color: var(--fg);
  }
  .recap-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.7rem;
    color: var(--muted);
    cursor: pointer;
    user-select: none;
  }
  .recap-toggle input { cursor: pointer; }
  .recap-toolbar-right { display: flex; gap: 0.5rem; flex-wrap: wrap; }
  .recap-btn-secondary {
    background: transparent;
    border: 1px solid var(--line);
    padding: 0.5rem 0.85rem;
    font-family: inherit;
    font-size: 0.72rem;
    color: var(--fg);
    border-radius: var(--r-sm);
    cursor: pointer;
  }
  .recap-btn-secondary:hover { background: var(--bg); border-color: var(--p-cold); color: var(--p-cold); }
  .recap-btn-primary {
    background: var(--fg);
    color: var(--surface);
    border: 1px solid var(--fg);
    padding: 0.5rem 0.85rem;
    font-family: inherit;
    font-size: 0.72rem;
    font-weight: 500;
    border-radius: var(--r-sm);
    cursor: pointer;
  }
  .recap-btn-primary:hover { background: var(--p-cold); border-color: var(--p-cold); }
  .recap-paper {
    padding: 2.4rem 2.6rem 3rem;
    background: var(--surface);
    color: var(--fg);
    line-height: 1.55;
    font-size: 0.85rem;
  }
  .recap-paper h1 {
    font-family: 'StackSans headline Variable', system-ui, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1.6rem;
    margin: 0 0 0.3rem;
    letter-spacing: -0.015em;
  }
  .recap-paper .recap-subtitle {
    font-size: 0.75rem;
    color: var(--muted);
    margin: 0 0 1.6rem;
    font-family: 'Fragment Mono', monospace;
    letter-spacing: 0.04em;
  }
  .recap-paper h2 {
    font-family: 'StackSans headline Variable', system-ui, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1.05rem;
    margin: 1.6rem 0 0.6rem;
    color: var(--fg);
    letter-spacing: -0.01em;
  }
  .recap-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 0.6rem;
    margin: 1rem 0 1.4rem;
  }
  .recap-stat {
    background: var(--bg);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-sm);
    padding: 0.7rem 0.9rem;
  }
  .recap-stat-label {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .recap-stat-value {
    font-size: 1.6rem;
    font-weight: 500;
    line-height: 1.1;
    color: var(--fg);
    margin-top: 0.2rem;
  }
  .recap-stat-value.is-positive { color: var(--s-won); }
  .recap-stat-value.is-negative { color: var(--s-lost); }
  .recap-tests-list {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
  }
  .recap-test-row {
    display: flex;
    align-items: flex-start;
    gap: 0.8rem;
    padding: 0.7rem 0.85rem;
    background: var(--bg);
    border-left: 3px solid var(--line);
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
  }
  .recap-test-row.is-won  { border-left-color: var(--s-won); }
  .recap-test-row.is-lost { border-left-color: var(--s-lost); }
  .recap-test-row.is-incon { border-left-color: var(--s-incon); }
  .recap-test-row.is-live { border-left-color: var(--s-live); }
  .recap-test-status {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 0.2rem 0.4rem;
    border-radius: 3px;
    background: var(--surface);
    flex-shrink: 0;
  }
  .recap-test-status.is-won  { background: var(--s-won-soft);  color: var(--s-won); }
  .recap-test-status.is-lost { background: var(--s-lost-soft); color: var(--s-lost); }
  .recap-test-status.is-incon { background: var(--s-incon-soft); color: var(--s-incon); }
  .recap-test-status.is-live { background: var(--s-live-soft); color: var(--s-live); }
  .recap-test-main { flex: 1; min-width: 0; }
  .recap-test-name { font-size: 0.85rem; font-weight: 500; margin-bottom: 0.15rem; }
  .recap-test-ref { color: var(--muted); font-weight: 400; }
  .recap-test-meta { font-size: 0.65rem; color: var(--muted); margin-bottom: 0.3rem; }
  .recap-test-learning {
    font-size: 0.72rem;
    color: var(--fg);
    background: var(--surface);
    padding: 0.4rem 0.6rem;
    border-radius: var(--r-sm);
    border-left: 2px solid var(--line);
    margin-top: 0.3rem;
  }
  .recap-test-learning::before { content: '💡 '; margin-right: 2px; }
  .recap-empty-section {
    padding: 1rem;
    text-align: center;
    font-style: italic;
    color: var(--muted);
    font-size: 0.7rem;
  }
  .recap-ai-briefing {
    padding: 1rem 1.2rem;
    background: var(--orchid-soft);
    border-left: 3px solid var(--p-test);
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
    margin: 1rem 0 1.6rem;
    color: var(--fg);
    font-size: 0.78rem;
    line-height: 1.6;
  }
  .recap-ai-briefing strong { font-weight: 500; }
  .recap-ai-loading {
    padding: 1.2rem;
    text-align: center;
    color: var(--p-test);
    font-style: italic;
    font-size: 0.75rem;
    background: var(--orchid-soft);
    border-radius: var(--r-sm);
    margin: 1rem 0;
  }
  .recap-paper-footer {
    margin-top: 2rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line-soft);
    font-size: 0.6rem;
    color: var(--muted);
    text-align: center;
    font-family: 'Fragment Mono', monospace;
    letter-spacing: 0.04em;
  }
  @media print {
    body * { visibility: hidden !important; }
    .recap-modal, .recap-modal * { visibility: visible !important; }
    .recap-modal {
      position: absolute !important;
      inset: 0;
      background: white !important;
      overflow: visible !important;
    }
    .recap-card {
      box-shadow: none !important;
      border-radius: 0 !important;
      max-width: none !important;
      margin: 0 !important;
    }
    .no-print { display: none !important; }
    .recap-paper {
      padding: 1.5cm 1.8cm !important;
      background: white !important;
    }
  }
  /* ════════════════════════════════════════════════════════════════
     NEW PROJECT MODAL — name + template picker
     Pre-existing markup that lacked styles → modal was rendering as
     a flow element under the page content. Now properly fixed
     overlay with backdrop + centered card.
     ════════════════════════════════════════════════════════════════ */
  .np-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 9100;
    background: rgba(35, 31, 35, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    overflow-y: auto;
    animation: npFadeIn 0.18s ease-out;
  }
  @keyframes npFadeIn { from { opacity: 0; } to { opacity: 1; } }
  .np-modal {
    position: relative;
    background: var(--surface);
    border-radius: var(--r-md);
    box-shadow: 0 16px 48px rgba(35, 31, 35, 0.28);
    width: 100%;
    max-width: 540px;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    animation: npSlideIn 0.22s ease-out;
  }
  @keyframes npSlideIn {
    from { opacity: 0; transform: translateY(-12px) scale(0.98); }
    to { opacity: 1; transform: translateY(0) scale(1); }
  }
  .np-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1.1rem 1.3rem 0.6rem;
  }
  .np-modal-title {
    font-family: 'StackSans headline Variable', system-ui, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1.25rem;
    color: var(--fg);
    margin: 0;
    letter-spacing: -0.01em;
  }
  .np-modal-close {
    background: transparent;
    border: none;
    font-size: 1.4rem;
    line-height: 1;
    color: var(--muted);
    cursor: pointer;
    padding: 0 0.3rem;
  }
  .np-modal-close:hover { color: var(--fg); }
  .np-modal-body {
    padding: 0.5rem 1.3rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }
  .np-field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
  }
  .np-field-label {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .np-input {
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: 0.6rem 0.75rem;
    font-family: inherit;
    font-size: 0.85rem;
    color: var(--fg);
    outline: none;
    transition: border-color 0.15s, background 0.15s;
  }
  .np-input:focus {
    border-color: var(--p-cold);
    background: var(--surface);
  }
  .np-input.np-input-error {
    border-color: var(--s-lost);
    animation: npShake 0.3s ease;
  }
  @keyframes npShake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-4px); }
    75% { transform: translateX(4px); }
  }
  /* ═══════════════════════════════════════════════════════════════
     SCALING MODAL — extends .np-modal with a wider layout (the steps
     grid takes more horizontal space). Steps are rendered as rows in
     a vertical list with budget/date/ROAS inputs side by side.
     ═══════════════════════════════════════════════════════════════ */
  .scaling-modal { width: 640px; max-width: calc(100vw - 40px); }
  .sc-steps {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    margin-top: 0.4rem;
  }
  .sc-step-row {
    display: grid;
    grid-template-columns: 60px 70px 1fr 1fr 1fr 28px;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 0.55rem;
    background: var(--bg);
    border: 1px solid var(--line);
    border-left: 3px solid var(--line);
    border-radius: var(--r-sm);
    transition: border-left-color 0.15s, background 0.15s;
  }
  .sc-step-row.sc-up,
  .sc-step-row.sc-down { border-left-color: var(--s-incon); }
  .sc-step-row.sc-flat { border-left-color: var(--muted); }
  .sc-step-arrow {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    color: var(--muted);
    text-align: right;
  }
  .sc-up .sc-step-arrow,
  .sc-down .sc-step-arrow { color: var(--p-ret); font-weight: 600; }
  .sc-step-label {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: 0.35rem 0.5rem;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.7rem;
    color: var(--fg);
    outline: none;
    text-transform: uppercase;
  }
  .sc-step-num {
    display: flex;
    flex-direction: column;
    gap: 1px;
  }
  .sc-step-num > span {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.5rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .sc-step-num input {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: 0.3rem 0.5rem;
    font-family: inherit;
    font-size: 0.75rem;
    color: var(--fg);
    outline: none;
  }
  .sc-step-num input:focus { border-color: var(--p-cold); }
  .sc-step-remove {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0.4rem;
    border-radius: var(--r-sm);
    transition: background 0.15s, color 0.15s;
  }
  .sc-step-remove:hover {
    background: var(--coral-soft);
    color: var(--s-lost);
  }
  .sc-step-add {
    margin-top: 0.6rem;
    background: transparent;
    border: 1.5px dashed var(--line);
    border-radius: var(--r-sm);
    padding: 0.6rem 0.8rem;
    font-family: inherit;
    font-size: 0.75rem;
    color: var(--muted);
    cursor: pointer;
    width: 100%;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
  }
  .sc-step-add:hover {
    border-color: var(--p-cold);
    color: var(--fg);
    background: var(--mint-soft);
  }
  .np-templates {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
  }
  .np-tpl {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.6rem 0.75rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, transform 0.1s;
  }
  .np-tpl:hover {
    border-color: var(--p-cold-edge);
    background: var(--bg);
  }
  .np-tpl input[type="radio"] {
    display: none;
  }
  .np-tpl.is-selected {
    border-color: var(--p-cold);
    background: var(--mint-soft);
    box-shadow: 0 0 0 3px rgba(42, 107, 48, 0.12);
  }
  .np-tpl-icon {
    width: 32px;
    height: 32px;
    background: var(--bg);
    border-radius: var(--r-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--fg);
    flex-shrink: 0;
  }
  .np-tpl.is-selected .np-tpl-icon {
    background: var(--surface);
    color: var(--p-cold);
  }
  .np-tpl-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
  }
  .np-tpl-label {
    font-size: 0.82rem;
    font-weight: 500;
    color: var(--fg);
    line-height: 1.2;
  }
  .np-tpl-desc {
    font-size: 0.65rem;
    color: var(--muted);
    line-height: 1.35;
  }
  .np-modal-foot {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 1rem 1.3rem 1.2rem;
    border-top: 1px solid var(--line-soft);
    background: var(--bg);
  }
  .np-modal-foot .btn-secondary,
  .np-modal-foot .btn-primary {
    font-size: 0.75rem;
    padding: 0.5rem 1rem;
    border-radius: var(--r-sm);
    line-height: 1;
  }

  /* ════════════════════════════════════════════════════════════════
     CANVAS GHOST — preview using REAL .tn classes
     Reuses the actual canvas node CSS so the preview matches exactly
     what the user will see once they activate networks. The
     wrapping .canvas-ghost-real applies a soft fade + disables
     interaction; nodes themselves render in their real colors,
     fonts, and proportions.
     ════════════════════════════════════════════════════════════════ */
  .canvas-ghost-real {
    width: 100%;
    height: 100%;
    padding: 1.5rem 1.5rem 0;
    overflow-y: auto;
    overflow-x: auto;
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }
  .canvas-ghost-real .ghost-real-hint {
    margin: 0 auto;
    flex-shrink: 0;
  }
  .ghost-real-canvas {
    position: relative;
    width: var(--ghost-w, 1000px);
    height: var(--ghost-h, 520px);
    margin: 0 auto;
    flex-shrink: 0;
    /* The whole preview is faded, never interactive */
    opacity: 0.55;
    pointer-events: none;
    /* Soft radial fade toward edges so it reads as "not real yet" */
    -webkit-mask-image: radial-gradient(ellipse at center, black 70%, rgba(0,0,0,0.6) 100%);
            mask-image: radial-gradient(ellipse at center, black 70%, rgba(0,0,0,0.6) 100%);
  }
  /* SVG layer for bezier edges */
  .ghost-real-edges {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
  }
  .ghost-real-edges .g-edge {
    fill: none;
    stroke: var(--fg);
    stroke-width: 1.4;
    opacity: 0.4;
  }
  .ghost-real-edges .g-edge.e-cold { stroke: var(--p-cold); opacity: 0.5; }
  .ghost-real-edges .g-edge.e-warm { stroke: var(--p-warm); opacity: 0.5; }
  .ghost-real-edges .g-edge.g-edge-thin {
    stroke-width: 1;
    stroke-dasharray: 2 3;
    opacity: 0.3;
  }
  /* Nodes layer — uses the real .tn / .n-* classes for full visual
     accuracy. We override box-shadow / hover effects since this is a
     preview, not interactive. */
  .ghost-real-nodes {
    position: absolute;
    inset: 0;
  }
  .ghost-real-nodes .tn {
    /* Real canvas node positioning is absolute via inline style;
       inherit that. We just neutralize hover/focus behaviors. */
    cursor: default;
    transition: none;
  }
  .ghost-real-nodes .tn:hover {
    box-shadow: 0 2px 6px rgba(35, 31, 35, 0.12);
    transform: none;
  }
  /* Add a faint dashed outline ON TOP of the real node styles so it
     still reads as "preview" without losing the real colors/fonts. */
  .ghost-real-nodes .tn::after {
    content: '';
    position: absolute;
    inset: -2px;
    border-radius: inherit;
    border: 1px dashed var(--muted);
    opacity: 0.35;
    pointer-events: none;
  }

  /* When the ghost preview is shown the canvas wrapper shouldn't try
     to center the (non-existent) tree. Reset the scroll container
     overrides so the preview can flow naturally. */
  #setup-scroll.empty,
  #setup-canvas-container.empty {
    overflow: auto;
    display: block;
  }



  /* ═══════════════════════════════════════════════════════════════════════
     BULK EXPORT — view-bulk layout
     The CSV gen is rendered inside #view-csv, a sibling of the other
     view-* sections. It's a full-width content area (no sidebar).
     CSS class names are prefixed `cg-` to isolate them from the
     existing "Création" tab which uses identical names.
     ═══════════════════════════════════════════════════════════════════════ */
  .view-bulk {
    display: none;
    overflow-y: auto;
    background: var(--bg);
    padding: 0;
  }
  .view-bulk.active { display: block; }

/* ═══════════════════════════════════════════════════════════════════════
     CSV GENERATOR — rules for #view-csv
     All class names below are prefixed with `cg-` to isolate them
     from the existing "Création" view (which uses identical names).
     This is a "Nouveau de zéro" CSV generator (Meta or Google).
     ═══════════════════════════════════════════════════════════════════════ */
  /* Bulk : .cg-wrap est maintenant le grid container 2-col (sidebar +
     canvas) au lieu de l'ancien centrage 1500px. Pattern aligné sur
     Setup/Tests/Matrix : sidebar gauche minmax(340px, 28%) + canvas
     droit 1fr, bord à bord avec l'écran (plus de max-width centré).

     Sidebar sensiblement plus large que Setup/Tests (380/32% au lieu
     de 280/22%) parce que le contenu est dense (3 boutons platform,
     paramètres en grilles, liste de campagnes structurée) et qu'à
     22% les 3 boutons platform étaient illisibles, à 28% encore
     un peu serré.
     L'ancien wrapper .cg-panels a été retiré du markup. */
  .cg-wrap {
    display: grid;
    grid-template-columns: minmax(380px, 32%) 1fr;
    gap: 1.5rem;
    align-items: start;
    padding: 1.5rem 1.75rem 4rem;
    transition: grid-template-columns 0.25s ease;
  }
  /* Mobile : empilé (sidebar au-dessus du canvas) */
  @media (max-width: 1100px) {
    .cg-wrap { grid-template-columns: 1fr; }
  }
  /* Bulk sidebar : padding interne pour que le contenu (cg-head,
     cg-ai-panel, cg-config) ne soit pas collé aux bords. .pane-side
     par défaut n'a que du padding bas, ce qui marche pour
     Setup/Tests où le contenu est généré en JS avec son propre
     padding. Ici on a du contenu statique qui a besoin de marges. */
  .view-bulk #bulk-sidebar {
    padding: 1.5rem 1.25rem 4rem;
    /* Flex column pour pouvoir pousser les boutons d'export tout
       en bas via margin-top: auto sur .cg-export-bottom. Pattern
       identique à fcg-form-col + ae-nav dans Ads Economics. */
    display: flex;
    flex-direction: column;
  }
  /* Section boutons export en bas de la sidebar — pleine largeur */
  .cg-export-bottom {
    margin-top: auto;
    padding-top: 1.5rem;
    border-top: 1px solid rgba(35,31,35,0.1);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }
  .cg-export-bottom .cg-export-label {
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 10px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #231f2399;
    margin-bottom: 0.25rem;
  }
  .cg-export-bottom .tb-btn {
    width: 100%;
    padding: 12px 16px;
    font-size: 13px;
    text-align: center;
    justify-content: center;
  }
  .view-bulk.sidebar-collapsed .cg-export-bottom { display: none; }
  /* Quand collapsed, on resserre la rail */
  .view-bulk.sidebar-collapsed #bulk-sidebar {
    padding: 12px 8px 4rem !important;
    overflow: hidden;
  }
  /* Dans la sidebar Bulk (étroite), le picker de plateforme et les
     grilles de paramètres passent en colonne unique. À 3 colonnes
     les boutons "Meta Ads Manager" / "Google Ads Editor" / "Google
     Tag Manager" étaient illisibles (texte coupé, sub barré). En
     vertical chaque bouton respire et la lecture devient naturelle. */
  .view-bulk .cg-platform-picker {
    grid-template-columns: 1fr;
    gap: 0.5rem;
  }
  .view-bulk .cg-platform-btn {
    padding: 0.7rem 0.85rem;
  }
  .view-bulk .cg-cfg-grid {
    grid-template-columns: 1fr;
    gap: 0.7rem;
  }
  .cg-head {
    margin-bottom: 2rem;
  }
  .cg-head .eyebrow {
    font-size: 0.7rem;
    color: var(--muted);
    margin-bottom: 0.5rem;
    font-weight: 500;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    letter-spacing: 0.04em;
  }
  .cg-head h1 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 2.4rem;
    letter-spacing: -0.025em;
    line-height: 1.05;
  }
  .cg-head h1 em {
    font-style: italic;
    color: var(--p-test);
    margin-left: 0.4rem;
  }
  .cg-tagline {
    font-size: 0.85rem;
    color: var(--muted);
    margin-top: 0.55rem;
    line-height: 1.5;
    max-width: 60ch;
  }

  /* .cg-panels : wrapper retiré du markup (le grid est maintenant
     directement sur .cg-wrap). Cette règle est conservée vide au cas
     où d'anciens éléments ont encore cette classe — sans effet. */
  .cg-panels {
    display: contents;
  }
  @media (max-width: 1100px) {
    .cg-panels { display: contents; }
  }

  .cg-config {
    background: var(--surface);
    border-radius: var(--r-lg);
    padding: 1.5rem;
    /* position: relative pour que le bouton chevron (.sidebar-toggle-wrap
       qui est en position: absolute) s'ancre relativement au cg-config
       et pas plus haut. Sans ça, le bouton apparaîtrait dans un coin
       imprévisible. */
    position: relative;
    /* Note: position:sticky doesn't work well inside an iframe (the
       'viewport' is the iframe itself which we control via postMessage).
       So we just keep it at the top of its grid cell. */
  }
  .cg-cfg-section {
    margin-bottom: 1.5rem;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid var(--line-soft);
  }
  .cg-cfg-section:last-child {
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: none;
  }
  .cg-cfg-label {
    font-size: 0.7rem;
    color: var(--muted);
    margin-bottom: 0.6rem;
    font-weight: 500;
  }

  .cg-platform-picker {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.4rem;
  }
  .cg-platform-btn {
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0.6rem 0.75rem;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    transition: all 0.15s;
    border-radius: var(--r-sm);
  }
  .cg-platform-btn:hover { border-color: var(--fg); }
  .cg-platform-btn.active {
    background: var(--fg);
    color: var(--surface);
    border-color: var(--fg);
  }
  .cg-pf-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.9rem;
    line-height: 1;
  }
  .cg-pf-sub {
    font-size: 0.6rem;
    letter-spacing: 0;
    opacity: 0.65;
  }

  .cg-cfg-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem;
  }
  .cg-cfg-fld { display: flex; flex-direction: column; gap: 0.25rem; }
  .cg-cfg-fld.full { grid-column: 1 / -1; }
  .cg-cfg-fld label {
    font-size: 0.65rem;
    letter-spacing: 0;
    color: var(--muted);
  }
  .cg-cfg-fld label .opt {
    font-style: italic;
    text-transform: none;
    letter-spacing: 0;
    opacity: 0.7;
    margin-left: 0.25rem;
  }
  /* Form inputs: aligned to Nerdstack design — bg on --depth (#ECECEC),
     transparent border by default, ink on focus. */
  .cg-cfg-fld input, .cg-cfg-fld select, .cg-cfg-fld textarea {
    background: var(--depth);
    border: 1px solid transparent;
    padding: 0.45rem 0.6rem;
    font-family: inherit;
    font-size: 0.8rem;
    color: var(--fg);
    border-radius: 0.5rem;
    transition: all 0.12s;
  }
  .cg-cfg-fld input:focus, .cg-cfg-fld select:focus, .cg-cfg-fld textarea:focus {
    outline: none;
    border-color: var(--faint);
    background: var(--surface);
  }
  .cg-cfg-fld textarea {
    resize: vertical;
    min-height: 54px;
    line-height: 1.4;
  }

  /* Preview panel */
  .cg-preview {
    background: var(--surface);
    overflow: hidden;
    border-radius: var(--r-lg);
  }
  .cg-preview-head {
    padding: 1.25rem 1.5rem;
    border-bottom: 1px solid var(--line-soft);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
  }
  .cg-preview-head h2 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.4rem;
    letter-spacing: -0.02em;
  }
  .cg-preview-head h2 em { font-style: italic; color: var(--p-test); }
  .cg-preview-meta {
    font-size: 0.7rem;
    color: var(--muted);
    margin-top: 0.25rem;
    font-family: 'Fragment Mono', ui-monospace, monospace;
  }
  .cg-preview-actions { display: flex; gap: 0.5rem; }
  /* Boutons d'export sous le preview (duplicate de la sidebar) :
     row aligné à droite, taille raisonnable. flex:1 stretchait à
     1000px+ chacun sur écran large → moche. On limite chaque
     bouton à 180-220px et on aligne à droite (justify-end). */
  .cg-preview-actions-bottom {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
    padding: 1rem 1.5rem 1.5rem;
    border-top: 1px solid rgba(35,31,35,0.06);
    background: var(--bg);
  }
  .cg-preview-actions-bottom .tb-btn {
    flex: 0 0 auto;
    min-width: 140px;
    padding: 12px 20px;
    font-size: 13px;
    text-align: center;
    justify-content: center;
  }

  .cg-preview-table-wrap {
    max-height: 520px;
    overflow: auto;
    background: var(--bg);
  }
  .cg-preview-table {
    border-collapse: collapse;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.65rem;
    white-space: nowrap;
    table-layout: auto;
    width: auto;
    min-width: 100%;
  }
  .cg-preview-table thead th {
    background: var(--fg);
    color: var(--surface);
    padding: 0.4rem 0.6rem;
    text-align: left;
    font-size: 0.55rem;
    letter-spacing: 0;
    font-weight: 500;
    position: sticky;
    top: 0;
    z-index: 2;
    border-right: 1px solid rgba(255,255,255,0.1);
  }
  .cg-preview-table tbody td {
    padding: 0.35rem 0.6rem;
    border-bottom: 1px solid var(--line);
    border-right: 1px solid var(--line);
    vertical-align: top;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .cg-preview-table tbody tr:nth-child(even) td { background: var(--surface); }
  .cg-preview-table tbody td.empty { color: var(--faint); }
  .cg-preview-table tbody tr.row-campaign td { border-left: 3px solid var(--p-cold); }
  .cg-preview-table tbody tr.row-adset td,
  .cg-preview-table tbody tr.row-adgroup td { border-left: 3px solid var(--p-test); }
  .cg-preview-table tbody tr.row-ad td,
  .cg-preview-table tbody tr.row-keyword td { border-left: 3px solid var(--p-warm); }

  .cg-preview-raw-wrap {
    border-top: 1px solid var(--line);
    padding: 0.8rem 1.25rem;
    background: var(--bg);
  }
  .cg-preview-raw-wrap summary {
    cursor: pointer;
    font-size: 0.7rem;
    letter-spacing: 0;
    color: var(--muted);
  }
  .cg-preview-raw-wrap summary:hover { color: var(--fg); }
  .cg-preview-raw {
    margin-top: 0.6rem;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.65rem;
    line-height: 1.5;
    max-height: 300px;
    overflow: auto;
    background: var(--surface);
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--line);
    white-space: pre;
  }

  /* ═══════════════════════════════════════════════════════════════════════
     AI GENERATE PANEL
     A prominent panel at the top of the page that lets the user describe
     their campaign in natural language and have an LLM generate the CSV
     rows automatically. Three providers supported: Anthropic, Google,
     xAI. The user supplies their own API key (stored in memory only,
     not persisted) — we never proxy through a backend.
     ═══════════════════════════════════════════════════════════════════════ */
  .cg-ai-panel {
    background: var(--surface);
    border-radius: var(--r-lg);
    padding: 1.5rem;
    margin-bottom: 1.5rem;
    border: 1px solid var(--line-soft);
  }
  /* Variante <details> : repliée par défaut, expandable au clic
     sur le summary. Pour ne pas prendre toute la hauteur de la
     sidebar quand l'utilisateur veut juste configurer manuellement. */
  .cg-ai-details {
    padding: 0;
  }
  .cg-ai-details[open] {
    padding: 0 1.5rem 1.5rem;
  }
  .cg-ai-summary {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.85rem 1.5rem;
    cursor: pointer;
    list-style: none;
    user-select: none;
    transition: background 0.12s ease;
  }
  .cg-ai-summary::-webkit-details-marker {
    display: none;
  }
  .cg-ai-summary:hover {
    background: rgba(35,31,35,0.03);
  }
  .cg-ai-details[open] .cg-ai-summary {
    border-bottom: 1px solid var(--line-soft);
    margin-bottom: 1rem;
  }
  .cg-ai-summary-icon {
    display: inline-block;
    font-size: 0.75rem;
    color: var(--muted);
    transition: transform 0.2s ease;
    transform: rotate(0deg);
    width: 10px;
  }
  .cg-ai-details[open] .cg-ai-summary-icon {
    transform: rotate(90deg);
  }
  .cg-ai-summary-title {
    flex: 1;
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 0.95rem;
    letter-spacing: -0.01em;
    color: var(--fg);
  }
  .cg-ai-summary-title em {
    font-style: italic;
    color: var(--p-test);
  }
  .cg-ai-panel-head {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1rem;
    flex-wrap: wrap;
  }
  .cg-ai-panel-head h3 {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-weight: 400;
    font-size: 1.2rem;
    letter-spacing: -0.015em;
    line-height: 1;
    color: var(--fg);
  }
  .cg-ai-panel-head h3 em {
    font-style: italic;
    color: var(--p-test);
  }
  .cg-ai-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    background: var(--orchid-soft);
    color: var(--p-test);
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    padding: 0.2rem 0.5rem;
    border-radius: 999px;
    text-transform: uppercase;
  }
  .cg-ai-panel-sub {
    font-size: 0.8rem;
    color: var(--muted);
    line-height: 1.5;
    margin-bottom: 1rem;
    max-width: 70ch;
  }
  .cg-ai-config-row {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
  }
  @media (max-width: 700px) {
    .cg-ai-config-row { grid-template-columns: 1fr; }
  }
  .cg-ai-fld {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
  }
  .cg-ai-fld label {
    font-size: 0.65rem;
    color: var(--muted);
  }
  .cg-ai-fld label .opt {
    font-style: italic;
    opacity: 0.7;
    margin-left: 0.25rem;
  }
  .cg-ai-fld select,
  .cg-ai-fld input,
  .cg-ai-fld textarea {
    background: var(--depth);
    border: 1px solid transparent;
    padding: 0.5rem 0.7rem;
    font-family: inherit;
    font-size: 0.85rem;
    color: var(--fg);
    border-radius: 0.5rem;
    transition: all 0.12s;
  }
  .cg-ai-fld select:focus,
  .cg-ai-fld input:focus,
  .cg-ai-fld textarea:focus {
    outline: none;
    border-color: var(--faint);
    background: var(--surface);
  }
  .cg-ai-fld textarea {
    resize: vertical;
    min-height: 90px;
    line-height: 1.5;
    font-family: inherit;
  }
  /* "Draw from the Copywriting Bank" toggle — opt the checkbox out of the
     generic .cg-ai-fld input styling and lay it out as a labelled switch. */
  .cg-ai-bank-fld { gap: 0.3rem; }
  .cg-ai-bank-fld .cg-ai-bank-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    font-size: 0.78rem;
    color: var(--fg);
  }
  .cg-ai-bank-fld input[type='checkbox'] {
    width: 15px;
    height: 15px;
    padding: 0;
    margin: 0;
    flex: none;
    border-radius: 3px;
    background: var(--depth);
    accent-color: var(--accent, #7c5cff);
    cursor: pointer;
  }
  .cg-ai-bank-fld .cg-ai-bank-hint {
    font-size: 0.62rem;
    color: var(--muted);
    line-height: 1.4;
    opacity: 0.85;
  }
  .cg-ai-key-wrap {
    position: relative;
  }
  .cg-ai-key-wrap input {
    width: 100%;
    padding-right: 2.5rem;
  }
  .cg-ai-key-toggle {
    position: absolute;
    right: 0.4rem;
    top: 50%;
    transform: translateY(-50%);
    background: transparent;
    border: none;
    cursor: pointer;
    color: var(--muted);
    font-size: 0.85rem;
    padding: 0.3rem;
    border-radius: 4px;
  }
  .cg-ai-key-toggle:hover { color: var(--fg); }
  .cg-ai-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-top: 0.5rem;
  }
  .cg-ai-btn {
    background: var(--fg);
    color: var(--surface);
    border: 1px solid var(--fg);
    font-family: inherit;
    font-size: 0.9rem;
    font-weight: 400;
    letter-spacing: -0.005em;
    padding: 0.6rem 1.2rem;
    cursor: pointer;
    transition: all 0.15s;
    border-radius: 0.5rem;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
  }
  .cg-ai-btn:hover:not(:disabled) {
    background: #000;
    border-color: #000;
  }
  .cg-ai-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
  .cg-ai-btn .cg-spinner {
    display: none;
    width: 12px;
    height: 12px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: ai-spin 0.7s linear infinite;
  }
  .cg-ai-btn.loading .cg-spinner { display: inline-block; }
  .cg-ai-btn.loading .cg-ai-btn-label::after {
    content: '...';
  }
  @keyframes ai-spin {
    to { transform: rotate(360deg); }
  }
  .cg-ai-status {
    font-size: 0.75rem;
    color: var(--muted);
    line-height: 1.4;
  }
  .cg-ai-status.ok { color: var(--p-cold); }
  .cg-ai-status.error {
    color: var(--p-warm);
    background: var(--coral-soft);
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    margin-top: 0.5rem;
    width: 100%;
  }
  .cg-ai-key-hint {
    font-size: 0.7rem;
    color: var(--muted);
    margin-top: 0.25rem;
    line-height: 1.4;
  }
  .cg-ai-key-hint a {
    color: var(--p-test);
    text-decoration: underline;
  }
  .cg-ai-divider {
    text-align: center;
    margin: 2rem 0 1.5rem;
    font-size: 0.7rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    position: relative;
  }
  .cg-ai-divider::before,
  .cg-ai-divider::after {
    content: '';
    position: absolute;
    top: 50%;
    width: 30%;
    height: 1px;
    background: var(--line-soft);
  }
  .cg-ai-divider::before { left: 0; }
  .cg-ai-divider::after { right: 0; }

  /* ═══════════════════════════════════════════════════════════════════════
     CAMPAIGNS ACCORDION LIST (step 2 — read-only)
     Three-level nested accordion:
       Level 1: Campaign (Meta camp / Google camp)
       Level 2: Ad set (Meta) / Ad group (Google)
       Level 3: Ad (Meta) / Keyword + RSA (Google)
     Each level has a chevron that rotates when expanded.
     ═══════════════════════════════════════════════════════════════════════ */
  .cg-campaigns-count {
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.62rem;
    color: var(--muted);
    margin-left: 0.4rem;
    letter-spacing: 0;
  }
  .cg-campaigns-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
  }
  .cg-campaign-empty {
    padding: 1.5rem 1rem;
    text-align: center;
    color: var(--muted);
    font-size: 0.75rem;
    background: var(--bg);
    border-radius: 0.5rem;
    font-style: italic;
  }
  /* === Level 1: Campaign card === */
  .cg-camp-card {
    background: var(--bg);
    border-radius: 0.5rem;
    border: 1px solid transparent;
    transition: border-color 0.12s;
  }
  .cg-camp-card.expanded {
    border-color: var(--line-soft);
  }
  .cg-camp-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.7rem 0.85rem;
    cursor: pointer;
    user-select: none;
  }
  .cg-camp-head:hover { background: rgba(35,31,35,0.03); }
  .cg-camp-head .cg-chev {
    width: 14px;
    height: 14px;
    transition: transform 0.18s ease;
    flex-shrink: 0;
    color: var(--muted);
  }
  .cg-camp-card.expanded .cg-camp-head .cg-chev {
    transform: rotate(90deg);
  }
  .cg-camp-name {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 0.9rem;
    line-height: 1.1;
    color: var(--fg);
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cg-camp-meta {
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.6rem;
    color: var(--muted);
    flex-shrink: 0;
    letter-spacing: 0;
  }
  .cg-camp-body {
    display: none;
    padding: 0 0.85rem 0.7rem;
    flex-direction: column;
    gap: 0.3rem;
  }
  .cg-camp-card.expanded .cg-camp-body {
    display: flex;
  }

  /* === Level 2: Ad set / Ad group === */
  .cg-lvl2-card {
    background: var(--surface);
    border-radius: 0.4rem;
    border: 1px solid var(--line-soft);
  }
  .cg-lvl2-card.expanded {
    border-color: var(--faint);
  }
  .cg-lvl2-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.55rem 0.75rem;
    cursor: pointer;
    user-select: none;
  }
  .cg-lvl2-head:hover { background: rgba(35,31,35,0.03); }
  .cg-lvl2-head .cg-chev {
    width: 12px;
    height: 12px;
    transition: transform 0.18s ease;
    flex-shrink: 0;
    color: var(--muted);
  }
  .cg-lvl2-card.expanded .cg-lvl2-head .cg-chev {
    transform: rotate(90deg);
  }
  .cg-lvl2-name {
    font-family: inherit;
    font-size: 0.75rem;
    color: var(--fg);
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cg-lvl2-meta {
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.55rem;
    color: var(--muted);
    flex-shrink: 0;
    letter-spacing: 0;
  }
  .cg-lvl2-body {
    display: none;
    padding: 0 0.75rem 0.55rem 1.7rem;
    flex-direction: column;
    gap: 0.25rem;
  }
  .cg-lvl2-card.expanded .cg-lvl2-body {
    display: flex;
  }

  /* === Level 3: Ad / Keyword / RSA === */
  .cg-lvl3-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.55rem;
    background: var(--bg);
    border-radius: 0.3rem;
    font-size: 0.7rem;
    color: var(--fg);
  }
  .cg-lvl3-row .cg-lvl3-tag {
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.55rem;
    color: var(--muted);
    background: var(--depth);
    padding: 0.1rem 0.4rem;
    border-radius: 999px;
    letter-spacing: 0.04em;
    flex-shrink: 0;
  }
  .cg-lvl3-row .cg-lvl3-tag.tag-kw { background: var(--orchid-soft); color: var(--p-test); }
  .cg-lvl3-row .cg-lvl3-tag.tag-neg { background: var(--coral-soft); color: var(--p-warm); }
  .cg-lvl3-row .cg-lvl3-tag.tag-rsa { background: var(--mint-soft); color: var(--p-cold); }
  /* Phase 3.B.16.c.refactor — les boutons 👁 par-ligne sont retirés
     au profit d'un bouton unique "Aperçu visuel" dans la barre d'actions
     CSV (Copy / Download / Aperçu). Plus de CSS .cg-lvl3-preview. */
  .cg-lvl3-row .cg-lvl3-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cg-lvl3-row .cg-lvl3-detail {
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.55rem;
    color: var(--muted);
    flex-shrink: 0;
  }
  .cg-lvl3-section-label {
    font-size: 0.55rem;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    color: var(--muted);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    margin-top: 0.3rem;
    margin-bottom: 0.1rem;
  }
  .cg-lvl3-section-label:first-child { margin-top: 0; }

  /* ═══════════════════════════════════════════════════════════════════════
     INLINE EDIT FIELDS (step 3)
     When the user expands a campaign / adset / ad, editable fields appear
     at the start of the body. They follow the same Nerdstack input style
     as the global config form (depth bg, transparent border, ink on focus).
     The grid is 2-cols by default, with .full spanning both.
     ═══════════════════════════════════════════════════════════════════════ */
  .cg-edit-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.4rem;
    margin-bottom: 0.5rem;
    padding: 0.55rem 0.65rem 0.45rem;
    background: var(--bg);
    border-radius: 0.4rem;
  }
  .cg-edit-fld {
    display: flex;
    flex-direction: column;
    gap: 0.18rem;
  }
  .cg-edit-fld.full {
    grid-column: 1 / -1;
  }
  .cg-edit-fld label {
    font-size: 0.55rem;
    color: var(--muted);
    font-family: 'Fragment Mono', ui-monospace, monospace;
    letter-spacing: 0.04em;
    text-transform: uppercase;
  }
  .cg-edit-fld label .opt {
    font-style: italic;
    text-transform: none;
    opacity: 0.7;
    margin-left: 0.2rem;
    letter-spacing: 0;
  }
  .cg-edit-fld input,
  .cg-edit-fld select,
  .cg-edit-fld textarea {
    background: var(--depth);
    border: 1px solid transparent;
    padding: 0.35rem 0.5rem;
    font-family: inherit;
    font-size: 0.7rem;
    color: var(--fg);
    border-radius: 0.35rem;
    transition: all 0.12s;
    width: 100%;
  }
  .cg-edit-fld input:focus,
  .cg-edit-fld select:focus,
  .cg-edit-fld textarea:focus {
    outline: none;
    border-color: var(--faint);
    background: var(--surface);
  }
  .cg-edit-fld textarea {
    resize: vertical;
    min-height: 50px;
    line-height: 1.4;
    font-family: inherit;
  }
  .cg-edit-fld input::placeholder,
  .cg-edit-fld textarea::placeholder {
    color: var(--faint);
    font-style: italic;
  }
  /* Level 3 (ads/keywords/RSAs) edit area: smaller and more compact */
  .cg-lvl3-edit {
    display: none;
    margin-top: 0.25rem;
    margin-left: 1.2rem;
  }
  .cg-lvl3-row.expanded + .cg-lvl3-edit {
    display: block;
  }
  .cg-lvl3-row {
    cursor: pointer;
  }
  .cg-lvl3-row:hover { background: rgba(35,31,35,0.04); }
  .cg-lvl3-row .cg-chev-mini {
    width: 10px;
    height: 10px;
    color: var(--muted);
    transition: transform 0.18s ease;
    flex-shrink: 0;
  }
  .cg-lvl3-row.expanded .cg-chev-mini {
    transform: rotate(90deg);
  }

  /* ═══════════════════════════════════════════════════════════════════════
     ADD / DELETE BUTTONS (step 4)
     - "+ Add" buttons at the end of each list (campaigns, adsets, ads,
       keywords, negatives, rsas)
     - Trash icon on each entity row to delete it
     ═══════════════════════════════════════════════════════════════════════ */
  .cg-add-btn {
    background: transparent;
    border: 1px dashed var(--faint);
    color: var(--muted);
    font-family: inherit;
    font-size: 0.7rem;
    font-weight: 400;
    letter-spacing: -0.005em;
    padding: 0.4rem 0.7rem;
    cursor: pointer;
    border-radius: 0.4rem;
    transition: all 0.12s;
    align-self: flex-start;
    margin-top: 0.25rem;
  }
  .cg-add-btn:hover {
    border-color: var(--fg);
    color: var(--fg);
    background: var(--surface);
  }
  .cg-add-btn.cg-add-camp {
    margin-bottom: 0.5rem;
    padding: 0.55rem 0.85rem;
    font-size: 0.75rem;
    align-self: stretch;
    text-align: center;
  }
  /* Subtle delete icon — appears on hover to reduce visual noise */
  .cg-del-btn {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: 0.2rem 0.35rem;
    border-radius: 0.3rem;
    font-size: 0.85rem;
    line-height: 1;
    opacity: 0;
    transition: all 0.12s;
    flex-shrink: 0;
  }
  .cg-camp-head:hover .cg-del-btn,
  .cg-lvl2-head:hover .cg-del-btn,
  .cg-lvl3-row:hover .cg-del-btn {
    opacity: 0.6;
  }
  .cg-del-btn:hover {
    opacity: 1 !important;
    color: var(--p-warm);
    background: var(--coral-soft);
  }
  /* In edit panels, the delete is more visible since user is already
     focused on this entity */
  .cg-edit-grid .cg-del-btn-row {
    grid-column: 1 / -1;
    display: flex;
    justify-content: flex-end;
    margin-top: 0.25rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--line-soft);
  }
  .cg-edit-grid .cg-del-btn-row .cg-del-btn-text {
    background: transparent;
    border: 1px solid var(--coral-soft);
    color: var(--p-warm);
    font-family: inherit;
    font-size: 0.65rem;
    padding: 0.3rem 0.7rem;
    cursor: pointer;
    border-radius: 0.35rem;
    transition: all 0.12s;
  }
  .cg-edit-grid .cg-del-btn-row .cg-del-btn-text:hover {
    background: var(--p-warm);
    color: var(--surface);
    border-color: var(--p-warm);
  }

  /* ═══════════════════════════════════════════════════════════════════════
     VERTICAL SIDEBAR LAYOUT — left rail with brand + icon tabs
     Replaces the horizontal tabs in the topbar with a fixed 96px sidebar
     on the left. The sidebar stays visible while the main content scrolls.
     Works in both standalone and iframe-embed modes (position:fixed is
     unaffected by the embed body { overflow:visible } override).
     ═══════════════════════════════════════════════════════════════════════ */
  .app {
    /* On réserve la largeur du rail REPLIÉ (92px) + la topbar (56px). Le rail
       s'élargit au survol mais en overlay (position:fixed) → le contenu ne se
       décale jamais. */
    padding-left: 92px;
    padding-top: 56px;
  }
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    /* Replié par défaut : on ne voit que les icônes (centrées). Au survol du
       rail (n'importe où), il s'élargit pour révéler les titres d'onglets.
       Transition sobre (ease standard), overlay au-dessus du contenu. */
    width: 92px;
    height: 100vh;
    background: var(--bg);
    border-right: 1px solid var(--line-soft);
    display: flex;
    flex-direction: column;
    z-index: 60;
    overflow: hidden;
    transition: width 0.24s cubic-bezier(0.4, 0, 0.2, 1);
    will-change: width;
  }
  .sidebar:hover {
    width: 224px;
    box-shadow: 4px 0 24px rgba(35, 31, 35, 0.08);
  }
  /* Topbar fixe — même modèle que le rail. Le document défile (body
     overflow:visible) ; fixed la garde collée en haut, à droite du rail.
     left calé sur la largeur du rail REPLIÉ (92px) — l'expansion du rail
     passe en overlay par-dessus, sans pousser la topbar. */
  .topbar {
    position: fixed;
    top: 0;
    left: 92px;
    right: 0;
    z-index: 60;
  }
  .sidebar-brand {
    /* Hauteur calée sur .topbar (min-height: 56px effectif via les embed
       overrides) pour que le filet sous le logo s'aligne avec celui sous la
       topbar. Logo centré verticalement via flex (justify-content).
       Largeur figée à celle du rail replié (92px) + épinglé à gauche : au
       survol le rail s'élargit mais le logo reste centré dans les 92px de
       gauche (jamais masqué par la topbar fixe qui commence à left:92px). */
    height: 56px;
    width: 92px;
    align-self: flex-start;
    box-sizing: border-box;
    padding: 0 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    border-bottom: 1px solid var(--line-soft);
    flex-shrink: 0;
  }
  .sidebar-brand .ey {
    font-size: 0.56rem;
    letter-spacing: 0.045em;
    color: var(--muted);
    font-weight: 400;
    line-height: 1.2;
    text-transform: uppercase;
    text-align: center;
  }
  .sidebar-brand .mark {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 1.05rem;
    line-height: 1.1;
    letter-spacing: -0.02em;
    text-align: center;
  }

  /* Vertical stack of tabs inside the sidebar */
  .sidebar nav.tabs {
    flex-direction: column;
    gap: 2px;
    padding: 8px 0 12px;
    overflow-y: auto;
    overflow-x: hidden;
    align-items: stretch;
    flex: 1;
    height: auto;
    border: none;
  }
  .sidebar nav.tabs .tab {
    /* Disposition en LIGNE : icône à gauche (position fixe), titre à droite.
       Replié → seule l'icône est visible (centrée) ; au survol le titre
       apparaît en fondu sans déplacer l'icône. */
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    text-align: left;
    /* marge 8px (du .tab) + padding-gauche 25px = 33px → centre l'icône de
       26px dans le rail replié (92px : 33 + 26 + 33). L'icône ne bouge pas
       entre replié et déplié. */
    margin: 0 8px;
    padding: 11px 8px 11px 25px;
    gap: 14px;
    border-radius: 10px;
    min-height: 0;
    font-size: 12.5px;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    font-weight: 500;
    letter-spacing: -0.005em;
    line-height: 1.2;
    color: var(--muted);
    background: transparent;
    border: none;
    position: relative;
    white-space: nowrap;
    overflow: hidden;
    transition: background 0.15s, color 0.15s;
    cursor: pointer;
  }
  .sidebar nav.tabs .tab .tab-icon {
    /* Icônes un poil plus grosses (le rail replié ne montre qu'elles). */
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    color: currentColor;
    stroke-width: 1.7;
  }
  /* Titre d'onglet : masqué (replié) → révélé en fondu au survol du rail. */
  .sidebar nav.tabs .tab > span:not(.tab-ext-arrow):not(.tab-count) {
    opacity: 0;
    transition: opacity 0.16s ease;
  }
  .sidebar:hover nav.tabs .tab > span:not(.tab-ext-arrow):not(.tab-count) {
    opacity: 1;
    transition-delay: 0.04s;
  }
  .sidebar nav.tabs .tab:hover {
    background: var(--depth);
    color: var(--fg);
  }
  .sidebar nav.tabs .tab.active {
    background: var(--orchid-soft);
    color: var(--fg);
  }
  /* Dark mode : --orchid-soft reste un pastel CLAIR → avec --fg (clair en
     dark) le texte devenait illisible. On passe sur une teinte orchidée
     sombre (tint sur le rail sombre) pour que le texte clair ressorte. */
  :root[data-theme="dark"] .sidebar nav.tabs .tab.active {
    background: rgba(184, 166, 245, 0.18);
  }
  .sidebar nav.tabs .tab.active::after { display: none; }

  /* ── Boutons-menus (accordéon) du rail ──────────────────────────────
     En-tête de groupe = .tab.tab-group (hérite du style .tab) + chevron.
     Sous-onglets (.tab-subs) masqués si le rail est replié ; révélés quand le
     groupe est ouvert ET le rail déplié (survol). Indentés sous l'icône. */
  .sidebar nav.tabs .tab-group-wrap { display: flex; flex-direction: column; gap: 0; }
  .sidebar nav.tabs .tab-group-caret {
    width: 15px; height: 15px; margin-left: auto; flex-shrink: 0;
    opacity: 0; transition: transform 0.18s ease, opacity 0.16s ease;
  }
  .sidebar:hover nav.tabs .tab-group-caret { opacity: 0.5; }
  .sidebar nav.tabs .tab-group-wrap.open > .tab-group .tab-group-caret { transform: rotate(90deg); }
  /* En-tête du groupe contenant l'onglet actif → surligné (visible même replié). */
  .sidebar nav.tabs .tab-group-wrap.has-active > .tab-group {
    background: var(--orchid-soft);
    color: var(--fg);
  }
  :root[data-theme="dark"] .sidebar nav.tabs .tab-group-wrap.has-active > .tab-group {
    background: rgba(184, 166, 245, 0.18);
  }
  /* Accordéon fluide : grid-template-rows 0fr→1fr anime la hauteur des sous-
     onglets sans la connaître (easing parfait, support evergreen). L'inner
     porte overflow:hidden + min-height:0 pour que la ligne 0fr collapse vraiment. */
  .sidebar nav.tabs .tab-subs {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    /* visibility:hidden retire les sous-onglets fermés du layout focusable
       (tab-order) + les rend « cachés » (comme l'ancien display:none). La
       transition 0s DIFFÉRÉE (delay = durée du collapse) bascule visible→hidden
       seulement APRÈS la fermeture, pour ne pas couper l'animation. */
    visibility: hidden;
    transition: grid-template-rows 0.26s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease, visibility 0s linear 0.26s;
  }
  .sidebar nav.tabs .tab-subs-inner {
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-top: 2px; /* respiration sous l'en-tête, collapse avec le reste */
    position: relative; /* ancre le filet guide vertical (::before) */
  }
  /* Filet guide vertical, descendant SOUS l'icône de l'en-tête du menu ouvert
     (centre ≈46px du bord du rail), à gauche des icônes/titres des sous-onglets
     — repère l'arborescence du menu (style ElevenLabs). Caché avec les sous-
     onglets : l'inner est collapsé (hauteur 0) + visibility:hidden quand fermé
     ou rail replié, donc le filet ne s'affiche que menu ouvert + rail déplié. */
  .sidebar nav.tabs .tab-subs-inner::before {
    content: '';
    position: absolute;
    left: 45px;
    top: 4px;
    bottom: 6px;
    width: 1px;
    background: var(--line, var(--ink-16));
    border-radius: 1px;
    pointer-events: none;
  }
  /* Ouverture au CLIC uniquement (toggle de .open via l'en-tête .tab-group).
     Pas d'ouverture au survol. Tous les menus fermés par défaut. */
  .sidebar nav.tabs .tab-group-wrap.open .tab-subs {
    grid-template-rows: 1fr;
    opacity: 1;
    visibility: visible; /* immédiat à l'ouverture (pas de delay) */
    transition: grid-template-rows 0.26s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease, visibility 0s;
  }
  /* Rail replié (non survolé) : jamais de sous-onglets déployés. */
  .sidebar:not(:hover) nav.tabs .tab-subs { grid-template-rows: 0fr !important; opacity: 0 !important; visibility: hidden !important; }
  /* Respect de l'accessibilité : pas d'animation si l'utilisateur la refuse. */
  @media (prefers-reduced-motion: reduce) {
    .sidebar nav.tabs .tab-subs { transition: none; }
    .sidebar nav.tabs .tab-group-caret { transition: none; }
  }
  /* Sous-onglets indentés + icône réduite (visibles uniquement rail déplié). */
  .sidebar nav.tabs .tab-subs .tab { padding: 9px 8px 9px 50px; font-size: 12px; }
  .sidebar nav.tabs .tab-subs .tab .tab-icon { width: 20px; height: 20px; }
  /* Teinte AU REPOS des sous-onglets un poil plus claire (--ink-48) que les
     en-têtes de menu (.tab-group, en --muted) → distingue visuellement menu vs
     sous-menu (icône + texte, via currentColor). On exclut hover/actif/disabled
     pour préserver leurs couleurs propres (sinon cette règle, plus spécifique,
     les écraserait). */
  .sidebar nav.tabs .tab-subs .tab:not(.active):not(:hover):not(:disabled):not([aria-disabled="true"]) {
    color: var(--ink-48, var(--muted));
  }

  /* ── Aide & support — épinglé tout en bas du rail (nav.tabs flex:1 le pousse
     en bas). Stylé comme un onglet : icône centrée si replié, libellé au survol. */
  .sidebar .sidebar-help {
    margin-top: auto;
    flex-shrink: 0;
    border-top: 1px solid var(--line-soft);
    padding: 6px 0 10px;
  }
  .sidebar .sidebar-help .help-btn {
    display: flex; flex-direction: row; align-items: center; gap: 14px;
    width: auto; margin: 0 8px; padding: 11px 8px 11px 25px;
    border-radius: 10px; border: 0; background: transparent;
    color: var(--muted); cursor: pointer;
    font-family: 'StackSans text Variable', system-ui, -apple-system, sans-serif;
    font-weight: 500; font-size: 12.5px; letter-spacing: -0.005em; line-height: 1.2;
    white-space: nowrap; overflow: hidden;
    transition: background 0.15s, color 0.15s;
  }
  .sidebar .sidebar-help .help-btn .tab-icon {
    width: 26px; height: 26px; flex-shrink: 0; color: currentColor; stroke-width: 1.7;
  }
  .sidebar .sidebar-help .help-btn > span { opacity: 0; transition: opacity 0.16s ease; }
  .sidebar:hover .sidebar-help .help-btn > span { opacity: 1; transition-delay: 0.04s; }
  .sidebar .sidebar-help .help-btn:hover { background: var(--depth); color: var(--fg); }
  /* Le menu d'aide s'ouvre vers le HAUT depuis le bas du rail. Largeur FIXE :
     il ne suit PLUS la largeur du rail — sinon il est écrasé/rogné (texte qui
     passe à la ligne, badge coupé) quand le rail se replie à 92px. Il flotte
     par-dessus le contenu à droite ; .sidebar.help-open lève le overflow:hidden
     du rail tant que le menu est ouvert (toggle JS sur #btn-ask). */
  .sidebar-help .help-menu {
    top: auto; bottom: calc(100% + 8px);
    left: 8px; right: auto;
    width: 224px; min-width: 224px; max-width: calc(100vw - 24px);
  }
  .sidebar.help-open { overflow: visible; }

  /* Count badge — small italic number under the label */
  .sidebar nav.tabs .tab .tab-count {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-weight: 400;
    font-size: 0.78rem;
    color: var(--muted);
    letter-spacing: 0;
    line-height: 1;
    background: transparent;
    padding: 0;
    border-radius: 0;
  }
  .sidebar nav.tabs .tab.active .tab-count {
    color: var(--fg);
    background: transparent;
  }

  /* External tab — small ↗ corner badge instead of inline arrow */
  .sidebar nav.tabs .tab.tab-ext {
    text-decoration: none;
    border-bottom: none;
  }
  .sidebar nav.tabs .tab.tab-ext:hover {
    color: var(--p-test);
    background: var(--orchid-soft);
    border-bottom: none;
  }
  .sidebar nav.tabs .tab.tab-ext:hover .tab-icon { color: var(--p-test); }
  .sidebar nav.tabs .tab.tab-ext .tab-ext-arrow {
    position: absolute;
    top: 4px;
    right: 5px;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 0.6rem;
    color: var(--faint);
    margin: 0;
    transform: none;
    transition: color 0.15s;
  }
  .sidebar nav.tabs .tab.tab-ext:hover .tab-ext-arrow {
    color: var(--p-test);
    transform: none;
  }

  /* ════════ Variant: inline analysis tool sections ════════ */
  .view-reporting, .view-pnl, .view-matrice, .view-correlation, .view-creative, .view-tracking, .view-kickoff, .view-simulation {
    display: none;
    overflow-y: auto;
    background: var(--bg);
    padding: 0;
  }
  .view.view-reporting.active,
  .view.view-pnl.active,
  .view.view-matrice.active,
  .view.view-correlation.active,
  .view.view-creative.active,
  .view.view-tracking.active,
  .view.view-kickoff.active,
  .view.view-simulation.active {
    display: block;
  }

  /* ── Share mode (?c= or ?t=) ────────────────────────────────────────
     When a client opens a freelancer's share URL, the page should
     render ONLY the relevant Kickoff content (access checklist or
     timeline) — no sidebar, no topbar, no project switcher. The boot
     script at the bottom of the page detects the URL params and adds
     `is-share-mode` to <body>; these rules then strip the chrome.
     The Kickoff JS itself still handles read-only rendering via
     state.mode === "client", so the actual content stays narrow and
     focused; we just unbox it from the Ads Architect shell. ── */
  body.is-share-mode .sidebar,
  body.is-share-mode .topbar,
  body.is-share-mode .topbar-tools,
  body.is-share-mode #setup-send-to-bulk {
    display: none !important;
  }
  body.is-share-mode .app {
    /* The shell normally pushes the main area right of the sidebar;
       without sidebar we want full-width content. */
    grid-template-columns: 1fr !important;
    grid-template-rows: 1fr !important;
    /* Topbar masquée en share mode → pas d'offset haut réservé. */
    padding-top: 0 !important;
  }
  body.is-share-mode .main-area {
    margin: 0 !important;
    padding: 0 !important;
    height: 100vh !important;
    overflow-y: auto;
  }
  body.is-share-mode .view {
    margin: 0 !important;
    padding: 0 !important;
  }
  /* Force only the Kickoff view to be visible in share mode — even if
     other tab classes are mistakenly toggled. Defensive layer. */
  body.is-share-mode .view:not(.view-kickoff) { display: none !important; }
  body.is-share-mode .view-kickoff { display: block !important; }
  /* ── Coming-soon placeholder (used by view-tracking and any future
     view that's announced but not yet implemented). Centered card with
     soft borders and muted copy. ── */
  /* ── Reporting landing (choice screen) ───────────────────────
     Two big cards side-by-side: one launches the report-generation
     wizard, the other opens the share-able dashboard. Kept simple
     so it visually echoes the Setup tab's tone without copying
     its specific component classes. ── */
  .reporting-landing-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 24px;
    max-width: 980px;
    margin: 32px auto 0;
  }
  @media (max-width: 760px) {
    .reporting-landing-grid { grid-template-columns: 1fr; }
  }
  .reporting-landing-card {
    text-align: left;
    background: var(--surface);
    border: 1px solid var(--ink-8, rgba(35,31,35,0.08));
    border-radius: 16px;
    padding: 36px 32px;
    cursor: pointer;
    transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s;
    font-family: inherit;
    color: inherit;
    display: flex;
    flex-direction: column;
    gap: 14px;
  }
  .reporting-landing-card:hover {
    border-color: #231f23;
    transform: translateY(-2px);
    box-shadow: 0 8px 24px rgba(35,31,35,0.08);
  }
  .reporting-landing-card-icon {
    width: 48px;
    height: 48px;
    border-radius: 12px;
    background: rgba(35,31,35,0.04);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #231f23;
  }
  .reporting-landing-card-icon svg { width: 24px; height: 24px; }
  .reporting-landing-card-ey {
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: #8A8276;
    font-weight: 600;
  }
  .reporting-landing-card-title {
    margin: 0;
    font-size: 22px;
    line-height: 1.2;
    font-weight: 500;
  }
  .reporting-landing-card-title em {
    font-style: italic;
    color: #5A4ECC;
    font-weight: 500;
  }
  .reporting-landing-card-text {
    margin: 0;
    color: #4a464a;
    font-size: 14px;
    line-height: 1.5;
  }
  .reporting-landing-card-cta {
    margin-top: 8px;
    font-size: 13px;
    font-weight: 600;
    color: #231f23;
    letter-spacing: 0.02em;
  }

  /* ── Reporting dashboard (cumulative margin chart) ────────────
     Toolbar = filter pills + share/back actions on opposite
     ends. Chart wrap gives the SVG breathing room. Stats strip
     shows the totals + applied margin rate below the chart. ── */
  /* Mini-section "Données" au-dessus du chart : 3 cards
     (Meta / Google / Taux de marge) en grid responsive. Permet
     d'importer un CSV ou de régler le taux sans passer par
     l'onglet Génération. */
  .dash-data-section {
    margin: 24px 0 16px;
  }
  .dash-data-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 12px;
  }
  @media (max-width: 720px) {
    .dash-data-grid { grid-template-columns: 1fr; }
  }
  .dash-data-card {
    position: relative;
    background: var(--surface);
    border: 1px dashed #c8c8c0;
    border-radius: 10px;
    padding: 16px 18px;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
    text-align: center;
    min-height: 110px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
  }
  .dash-data-card:hover {
    border-color: #231f23;
    background: #fafaf7;
  }
  .dash-data-card.dragover {
    border-color: #231f23;
    border-style: solid;
    background: #f4e7c7;
  }
  .dash-data-card.is-loaded {
    border-style: solid;
    border-color: #2d7a3d;
    background: #edf6ef;
  }
  .dash-data-card.dash-data-margin {
    cursor: default;
    text-align: center;
  }
  .dash-data-card.dash-data-margin:hover {
    background: var(--surface);
    border-color: #c8c8c0;
  }
  .dash-data-card-icon {
    font-size: 22px;
    line-height: 1;
  }
  .dash-data-card-title {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: #231f23;
  }
  .dash-data-card-meta {
    font-size: 11px;
    color: #2d7a3d;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-weight: 500;
  }
  .dash-data-card-hint {
    font-size: 10px;
    color: #8A8276;
    line-height: 1.4;
  }
  .dash-data-card-clear {
    position: absolute;
    top: 6px;
    right: 8px;
    background: transparent;
    border: none;
    color: #8A8276;
    font-size: 16px;
    cursor: pointer;
    line-height: 1;
    padding: 2px 6px;
    border-radius: 4px;
  }
  .dash-data-card-clear:hover {
    color: #c53d2c;
    background: rgba(35,31,35,0.05);
  }
  .dash-margin-input-wrap {
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
    margin-top: 4px;
  }
  .dash-margin-input-wrap input {
    width: 60px;
    padding: 4px 8px;
    border: 1px solid #c8c8c0;
    border-radius: 6px;
    font-family: inherit;
    font-size: 18px;
    font-weight: 600;
    text-align: center;
    color: #231f23;
  }
  .dash-margin-input-wrap input:focus {
    outline: none;
    border-color: #231f23;
  }
  .dash-margin-input-suffix {
    font-size: 14px;
    color: #231f23a3;
    font-weight: 500;
  }
  /* Hint sous l'input — explique que le taux est récupéré
     automatiquement, sans encombrer le visuel avec un bouton. */
  .dash-margin-hint {
    margin-top: 6px;
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 9px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #231f237a;
    line-height: 1.4;
  }

  .dash-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    margin: 24px 0 16px;
    padding: 14px 18px;
    background: var(--surface);
    border: 1px solid var(--ink-8, rgba(35,31,35,0.08));
    border-radius: 10px;
  }
  .dash-toolbar-group {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
  }
  .dash-toolbar-label {
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #8A8276;
    margin-right: 4px;
  }
  .dash-pill {
    background: transparent;
    border: 1px solid #c8c8c0;
    color: #231f23;
    padding: 7px 14px;
    border-radius: 999px;
    font-size: 12px;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
  }
  .dash-pill:hover { border-color: #231f23; }
  .dash-pill.is-active {
    background: #231f23;
    color: #f7f6f5;
    border-color: #231f23;
  }
  .dash-chart-wrap {
    background: var(--surface);
    border: 1px solid var(--ink-8, rgba(35,31,35,0.08));
    border-radius: 10px;
    padding: 24px;
    overflow-x: auto;
  }
  .dash-chart-svg { width: 100%; height: auto; min-height: 340px; }
  .dash-stats-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px;
    margin-top: 16px;
  }
  .dash-stat {
    background: var(--surface);
    border: 1px solid var(--ink-8, rgba(35,31,35,0.08));
    border-radius: 8px;
    padding: 14px 16px;
  }
  .dash-stat-label {
    font-size: 10px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #8A8276;
    margin-bottom: 6px;
  }
  .dash-stat-value {
    font-size: 18px;
    font-weight: 600;
    color: #231f23;
  }

  /* Share-mode override: when the Reporting dashboard is opened
     via a client share URL (?rd=…), allow .view-reporting to be
     visible alongside the kickoff rule above. The body class
     `is-share-mode-reporting` is added by detectShareMode below
     and is more specific so it wins against the generic kickoff
     rule. ── */
  body.is-share-mode-reporting .view:not(.view-reporting) {
    display: none !important;
  }
  body.is-share-mode-reporting .view-reporting {
    display: block !important;
  }

  .coming-soon-wrap {
    max-width: 520px;
    margin: 120px auto;
    padding: 48px 40px;
    background: var(--surface);
    border: 1px solid var(--ink-8, rgba(35,31,35,0.08));
    border-radius: 12px;
    text-align: center;
  }
  .coming-soon-wrap .coming-soon-icon {
    width: 48px;
    height: 48px;
    margin: 0 auto 16px;
    color: var(--muted);
    opacity: 0.6;
  }
  .coming-soon-wrap .coming-soon-icon svg { width: 100%; height: 100%; }
  .coming-soon-wrap .eyebrow {
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    color: var(--muted);
    text-transform: uppercase;
    margin-bottom: 8px;
  }
  .coming-soon-wrap .coming-soon-title {
    font-family: 'StackSans headline Variable', system-ui, sans-serif;
    font-weight: 600;
    font-size: 1.85rem;
    color: var(--fg);
    margin: 0 0 6px;
    line-height: 1.15;
  }
  .coming-soon-wrap .coming-soon-title em {
    font-family: 'Instrument Serif', Georgia, serif;
    font-style: italic;
    font-weight: 400;
    color: var(--muted);
  }
  .coming-soon-wrap .coming-soon-sub {
    font-size: 0.95rem;
    color: var(--muted);
    margin: 0 0 24px;
    font-style: italic;
  }
  .coming-soon-wrap .coming-soon-text {
    font-size: 0.92rem;
    color: var(--fg);
    line-height: 1.6;
    opacity: 0.78;
    margin: 0;
  }

  /* ── Setup → Bulk handoff buttons ─────────────────────────────────
     Same visual language across the two trigger sites :
       • #setup-send-to-bulk : in the Setup header (top-right of view)
       • #cg-import-from-setup : in the Bulk header (top-right of view)
     Style mirrors the existing `.tb-btn.primary` and the orchid accent
     used for "AI" / generation actions, so the button reads as a
     creative shortcut, not a destructive one. ── */
  .setup-head-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
    margin-left: auto;
    padding-right: 14px;
  }
  .cg-head-actions {
    margin-top: 10px;
  }
  .setup-send-to-bulk,
  .cg-import-from-setup {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 12px;
    background: var(--orchid, #CEBFFA);
    color: var(--p-test, #5A4ECC);
    border: 1px solid transparent;
    border-radius: 8px;
    font-family: inherit;
    font-size: 0.82rem;
    font-weight: 500;
    letter-spacing: -0.005em;
    cursor: pointer;
    transition: background 0.12s ease, transform 0.06s ease, color 0.12s ease;
    white-space: nowrap;
  }
  .setup-send-to-bulk svg,
  .cg-import-from-setup svg {
    flex-shrink: 0;
    opacity: 0.85;
  }
  .setup-send-to-bulk:hover,
  .cg-import-from-setup:hover {
    background: var(--p-test, #5A4ECC);
    color: #FFFFFF;
  }
  .setup-send-to-bulk:active,
  .cg-import-from-setup:active {
    transform: translateY(1px);
  }
  .setup-send-to-bulk:disabled,
  .cg-import-from-setup:disabled {
    opacity: 0.45;
    cursor: not-allowed;
    background: var(--depth, #ECECEC);
    color: var(--muted);
  }
  .cg-import-from-setup {
    /* Slightly larger in the Bulk header for prominence */
    padding: 8px 14px;
    font-size: 0.87rem;
  }

  /* ── Multi-pages canvas : onglets (max 3) + Retour/Avant (undo/redo) ── */
  .setup-pages-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
  }
  .setup-pages {
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .canvas-page-tab {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 170px;
    padding: 6px 9px;
    background: var(--depth, #ECECEC);
    color: var(--muted, #6b6b6b);
    border: 1px solid transparent;
    border-radius: 8px;
    font-family: inherit;
    font-size: 0.8rem;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
    white-space: nowrap;
  }
  .canvas-page-tab:hover { color: var(--ink, #1a1a1a); }
  .canvas-page-tab.active {
    background: var(--bg, #fff);
    color: var(--ink, #1a1a1a);
    border-color: var(--p-test, #5A4ECC);
    box-shadow: 0 1px 0 rgba(90, 78, 204, 0.18);
  }
  .canvas-page-tab .cpt-label {
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 132px;
  }
  .canvas-page-tab .cpt-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    font-size: 0.9rem;
    line-height: 1;
    color: var(--muted, #6b6b6b);
    opacity: 0.6;
  }
  .canvas-page-tab .cpt-close:hover {
    opacity: 1;
    background: rgba(179, 38, 30, 0.12);
    color: #b3261e;
  }
  .canvas-page-add {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    background: transparent;
    color: var(--muted, #6b6b6b);
    border: 1px dashed var(--line-soft, #ddd);
    border-radius: 8px;
    font-size: 1.05rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  }
  .canvas-page-add:hover {
    color: var(--p-test, #5A4ECC);
    border-color: var(--p-test, #5A4ECC);
    border-style: solid;
  }
  .setup-hist-group {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding-left: 6px;
    border-left: 1px solid var(--line-soft, #e5e5e5);
  }
  .setup-hist-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 9px;
    background: transparent;
    color: var(--muted, #6b6b6b);
    border: 1px solid transparent;
    border-radius: 8px;
    font-family: inherit;
    font-size: 0.8rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
    white-space: nowrap;
  }
  .setup-hist-btn svg { flex-shrink: 0; opacity: 0.9; }
  .setup-hist-btn:hover:not(:disabled) {
    background: var(--depth, #ECECEC);
    color: var(--ink, #1a1a1a);
  }
  .setup-hist-btn:active:not(:disabled) { transform: translateY(1px); }
  .setup-hist-btn:disabled { opacity: 0.35; cursor: not-allowed; }

  /* On narrow screens the stats wrap below the title — push the button
     onto its own row too so it doesn't collide with the stats. */
  @media (max-width: 980px) {
    .setup-head-actions {
      margin-left: 0;
      padding-right: 0;
      width: 100%;
      justify-content: flex-end;
    }
  }

  /* Kickoff CSS extracted to public/styles/kickoff.css (Phase 1.F). */
  /* ── BrandFinder embedded-mode overrides ──────────────────────────
     BrandFinder a deux <style> blocks qui appliquent du padding à
     #brand-finder-root pour s'adapter à son déploiement standalone sur
     une page Webflow nue :
       (a) AcbTabsBar : `padding-left: 96px` pour libérer la place du
           rail vertical portalé dans document.body.
       (b) AdIntelDesignTokens : `padding-top: 9rem; padding-bottom: 5rem`
           pour passer sous la navbar Webflow sticky (~80px) + le promo
           banner (~50px).
     Dans Ads Architect (iframe sans navbar Webflow visible, sidebar
     fournie par la coque), ces paddings produisent du vide inutile.
     AcbTabsBar a été neutralisé côté JSX (return null), donc (a) ne
     s'injecte plus. (b) en revanche s'injecte toujours — on l'annule
     ici avec une spécificité boostée par le préfixe `html` pour gagner
     contre les styles React-injected qui arrivent plus tard en source.
     Les règles `display:none` ci-dessous sont défensives — au cas où
     une future itération de BrandFinder réintroduirait les éléments.
     ── */
  html #brand-finder-root { padding: 0 !important; }
  html body > .acb-sidebar { display: none !important; }
  html #brand-finder-root > .acb-topbar { display: none !important; }
  /* Tool mount wrappers: NO enforced min-height or flex centering at
     the wrapper level. That was tempting (it made the "Chargement…"
     placeholder land nicely in the middle of the view), but once the
     React app mounts inside the wrapper, the same flex+centering
     vertically centers the entire app — which manifests as the app
     content "floating in the middle" of an empty cream area, often
     pushed near the bottom because of a feedback loop with
     setupIframeAutoResize() (taller iframe → bigger 100vh → bigger
     centered container → taller iframe again).
     Instead, the wrapper is a passthrough; the centering lives on
     the placeholder itself, which disappears on React mount. */
  .variant-tool-mount {
    /* layout-neutral by default */
  }
  /* For Reporting specifically, the inline tool needs its wrapper to
     fill the view height so its own `.rep-tool { height: 100% }` rule
     resolves correctly. The override below targets the Reporting case
     specifically — it's more specific than the (now empty)
     `.variant-tool-mount` rule above and wins for that one tool. */
  .variant-tool-placeholder {
    /* The placeholder is the loading hint shown briefly before
       React mounts. Give it its own visual presence: a centered
       block in the middle of the available view space. The
       fixed-pixel min-height (not vh) avoids feeding into the
       iframe-resize loop while the placeholder is alive. */
    min-height: 360px;
    margin: 0 auto;
    max-width: 480px;
    padding: 4rem 2rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    color: var(--muted);
  }

  /* Matrice CSS extracted to public/styles/matrice.css (Phase 1.F). */

  .variant-tool-placeholder h2 {
    font-family: 'StackSans headline Variable', system-ui, serif;
    font-style: italic;
    font-weight: 500;
    color: var(--fg);
    margin: 0 0 0.6rem;
  }
  .variant-tool-placeholder p {
    font-size: 0.85rem;
    line-height: 1.5;
    margin: 0;
  }

  
  /* ═══════════════════════════════════════════════════════════════════
     NERDSTACK DESIGN SYSTEM — shared with ads-architect-bundle.
     Tokens: bg #F7F6F5 (sand), ink #231F23, line, surface, soft pastels.
     Fonts: 'StackSans Variable' headlines (italic), 'Fragment Mono'
     for tables/labels/numbers. Pixel-perfect parity with the other tool
     so a user moving from one to the other doesn't feel a context switch.
     ═══════════════════════════════════════════════════════════════════ */
  

  
  

  /* Back-to-P&L bar — affiché en haut de la vue Reporting (parcours
     D et E du P&L). Subtil, mono, peu visible mais accessible. */
  .rep-back-to-pnl {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 10px 20px;
    background: #fafaf7;
    border-bottom: 1px solid rgba(35, 31, 35, 0.1);
    font-family: 'Fragment Mono', ui-monospace, monospace;
    font-size: 10px;
    letter-spacing: 0.06em;
  }
  /* Aligné sur le bouton retour standard de RoasAudit (.btn-ghost « ← Retour ») :
     fond doux, sentence-case, arrondi — plus la petite pilule mono MAJUSCULES. */
  .rep-back-link {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: rgba(35, 31, 35, 0.08);
    border: 1px solid transparent;
    padding: 8px 16px;
    font-family: system-ui, -apple-system, 'Segoe UI', 'Helvetica Neue', sans-serif;
    font-size: 12.5px;
    letter-spacing: 0;
    text-transform: none;
    color: #231f23;
    cursor: pointer;
    border-radius: 8px;
    flex: 0 0 auto;
    transition: background 0.15s, color 0.15s;
  }
  .rep-back-link:hover {
    background: #231f23;
    color: #f7f6f5;
    border-color: #231f23;
  }
  .rep-back-label {
    color: #231f23a3;
    text-transform: none;
    letter-spacing: 0.02em;
    font-size: 11px;
  }
  .rep-back-label strong {
    color: #231f23;
    font-weight: 600;
    margin-left: 6px;
  }
  /* En mode share (lien public envoyé au client) on masque tout signe
     que l'app a une organisation en parcours — le bandeau est caché
     par render() qui set display:none directement. */
  body.is-share-mode .rep-back-to-pnl { display: none !important; }

  /* Reporting CSS (.rep-tool .*) extracted to public/styles/reporting.css (Phase 1.I). */

  /* Hide the duplicate "Création" tab in the sidebar too (already hidden in topbar). */
  .sidebar nav.tabs .tab[data-tab="create"] {
    display: none !important;
  }

  /* Mobile: collapse the sidebar into a horizontal strip at the top */
  @media (max-width: 768px) {
    /* Rail + topbar repassent dans le flux (strip horizontal en haut) → on
       annule l'offset desktop et le fixed de la topbar. */
    .app { padding-left: 0; padding-top: 0; }
    .topbar { position: relative; left: auto; right: auto; }
    .sidebar {
      position: relative;
      width: 100%;
      height: auto;
      border-right: none;
      border-bottom: 1px solid var(--line-soft);
    }
    /* Pas d'expansion au survol sur mobile (strip horizontal, pas de hover). */
    .sidebar:hover { width: 100%; box-shadow: none; }
    .sidebar-brand { display: none; }
    .sidebar nav.tabs {
      flex-direction: row;
      overflow-x: auto;
      overflow-y: hidden;
      flex: 0 0 auto;
      padding: 8px 12px;
    }
    /* Onglets : on rétablit la pile verticale (icône + titre) du strip mobile,
       et les titres restent TOUJOURS visibles (pas de hover en tactile). */
    .sidebar nav.tabs .tab {
      flex-direction: column;
      justify-content: center;
      text-align: center;
      margin: 0;
      padding: 8px 10px;
      gap: 4px;
      min-width: 72px;
      min-height: 56px;
      font-size: 11px;
      flex-shrink: 0;
    }
    .sidebar nav.tabs .tab > span:not(.tab-ext-arrow):not(.tab-count) {
      opacity: 1;
    }
  }

/* ─── Phase 3.B.14.unify.shell · Creative & UX ──────────────────────
   Plus de panes séparés : AdMockupStudio est l'unique mount React et
   héberge Brand Finder / Créas Benchmark dans son canvas. Sidebar
   persistante, toggle d'outils via état React activeTool. */

  /* ═══════════════════════════════════════════════════════════════════════
     TODOLIST V1 — section projet (.pdetail-todos) + rail global (.rail-todos)
     Section projet : 2-col grid sous le project hub (todos LEFT, activité
     RIGHT). Rail global : sidebar fixed RIGHT, visible sur tous les views,
     collapsable à 44px via .collapsed (même pattern que .pane-side).
     Les .todo-card sont partagés entre les deux surfaces — un seul jeu
     de styles pour la cohérence visuelle.
     ═══════════════════════════════════════════════════════════════════════ */

  /* ── Grille 2-col du bas de la page projet ─────────────────────── */
  .pdetail-bottom-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    margin-top: 1rem;
  }
  @media (max-width: 900px) {
    .pdetail-bottom-grid { grid-template-columns: 1fr; }
  }

  /* ── Section Todolist (project view) ───────────────────────────── */
  .pdetail-todos {
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-md);
    padding: 1rem 1.1rem 0.9rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    min-width: 0;
  }
  .pdetail-todos-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
    padding-bottom: 0.7rem;
    border-bottom: 1px solid var(--line-soft);
  }
  .pdetail-todos-title {
    font-family: 'StackSans headline Variable', system-ui, -apple-system, sans-serif;
    font-style: italic;
    font-size: 1.05rem;
    color: var(--fg);
  }
  .pdetail-todos-count {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .pdetail-todos-add {
    display: flex;
    gap: 6px;
    align-items: center;
    flex-wrap: wrap;
  }
  .pdetail-todos-add input[type=text] {
    flex: 1 1 180px;
    padding: 6px 10px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    font-size: 0.82rem;
    font-family: inherit;
    transition: border-color 0.15s;
    min-width: 0;
  }
  .pdetail-todos-add input[type=text]:focus {
    outline: none;
    border-color: var(--fg);
  }
  /* Assignee picker (project section) — ~140px wide */
  .pdetail-todos-assignee-select {
    flex: 0 0 140px;
    padding: 6px 8px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    font-size: 0.78rem;
    font-family: inherit;
    transition: border-color 0.15s;
    cursor: pointer;
  }
  .pdetail-todos-assignee-select:disabled {
    opacity: 0.6;
    cursor: wait;
  }
  .pdetail-todos-assignee-select:focus {
    outline: none;
    border-color: var(--fg);
  }
  /* Date input (project section) — ~130px wide */
  .pdetail-todos-date-input {
    flex: 0 0 130px;
    padding: 6px 8px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    font-size: 0.78rem;
    font-family: inherit;
    transition: border-color 0.15s;
  }
  .pdetail-todos-date-input:focus {
    outline: none;
    border-color: var(--fg);
  }
  .pdetail-todos-add-btn {
    padding: 6px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    cursor: pointer;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    transition: background 0.15s, border-color 0.15s;
  }
  .pdetail-todos-add-btn:hover {
    background: var(--depth);
    border-color: var(--fg);
  }
  .pdetail-todos-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    max-height: 60vh;
    overflow-y: auto;
    padding-right: 2px;
  }

  /* ── Carte todo partagée (.todo-card) ──────────────────────────── */
  .todo-card {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px 10px;
    border: 1px solid var(--line-soft);
    border-radius: var(--r-sm);
    background: var(--surface);
    font-size: 0.82rem;
    transition: border-color 0.12s, background 0.12s;
  }
  .todo-card:hover { border-color: var(--line); }
  .todo-card.is-done { opacity: 0.55; }
  .todo-card.is-done .todo-title {
    text-decoration: line-through;
    color: var(--muted);
  }
  .todo-checkbox {
    width: 16px;
    height: 16px;
    border: 1.5px solid var(--muted);
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
    flex-shrink: 0;
    margin-top: 2px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    line-height: 1;
    color: var(--surface);
    padding: 0;
    transition: background 0.12s, border-color 0.12s;
  }
  .todo-checkbox:hover { border-color: var(--fg); }
  .todo-card.is-done .todo-checkbox {
    background: var(--mint-edge, #2A6B30);
    border-color: var(--mint-edge, #2A6B30);
    color: var(--surface);
  }
  .todo-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
  }
  .todo-title {
    font-size: 0.82rem;
    color: var(--fg);
    line-height: 1.35;
    word-break: break-word;
    cursor: text;
  }
  .todo-title-edit {
    width: 100%;
    padding: 2px 6px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    font-size: 0.82rem;
    font-family: inherit;
    line-height: 1.35;
  }
  .todo-title-edit:focus { outline: none; border-color: var(--fg); }
  .todo-meta {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
  }
  .todo-assignee {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 7px 2px 3px;
    border-radius: 999px;
    background: var(--depth);
    font-size: 0.62rem;
    font-family: 'Fragment Mono', monospace;
    color: var(--fg);
    letter-spacing: 0.02em;
    max-width: 100%;
  }
  .todo-assignee.is-mine {
    background: var(--orchid, #CEBFFA);
    color: var(--fg);
  }
  .todo-assignee-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .todo-assignee-avatar {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--fg);
    color: var(--surface);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.55rem;
    font-weight: 600;
    text-transform: uppercase;
    flex-shrink: 0;
  }
  .todo-assignee.is-mine .todo-assignee-avatar {
    background: var(--surface);
    color: var(--fg);
  }
  .todo-delete {
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 0 4px;
    color: var(--muted);
    opacity: 0;
    transition: opacity 0.12s, color 0.12s;
    font-size: 1rem;
    line-height: 1;
    align-self: flex-start;
    margin-top: 2px;
  }
  .todo-card:hover .todo-delete { opacity: 0.7; }
  .todo-delete:hover { opacity: 1; color: var(--coral, #ED7472); }
  .todo-empty {
    font-size: 0.74rem;
    color: var(--muted);
    font-style: italic;
    padding: 14px 4px;
    text-align: center;
  }

  /* ── Due date chip (.todo-due) ──────────────────────────────────
     Sibling of .todo-assignee inside .todo-meta. Native button so
     it's clickable to edit. Variants : is-today/is-tomorrow (orchid),
     is-overdue (coral + bold), is-soon (normal fg), is-muted (done). */
  .todo-due {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border: none;
    border-radius: 999px;
    background: var(--depth);
    font-size: 0.62rem;
    font-family: 'Fragment Mono', monospace;
    color: var(--muted);
    letter-spacing: 0.02em;
    cursor: pointer;
    line-height: 1.2;
    transition: filter 0.12s;
  }
  .todo-due:hover { filter: brightness(1.05); }
  .todo-due-icon { font-size: 0.7rem; line-height: 1; }
  .todo-due.is-today,
  .todo-due.is-tomorrow {
    color: var(--orchid, #8856b0);
    background: rgba(136, 86, 176, 0.1);
  }
  .todo-due.is-overdue {
    color: var(--coral, #c43d2f);
    background: rgba(196, 61, 47, 0.1);
    font-weight: 600;
  }
  .todo-due.is-soon { color: var(--fg); }
  .todo-due.is-muted { color: var(--muted); }
  .todo-due.is-empty {
    opacity: 0;
    transition: opacity 0.12s, filter 0.12s;
  }
  .todo-card:hover .todo-due.is-empty { opacity: 0.6; }
  .todo-due.is-empty:hover { opacity: 1; }
  .todo-due-empty-label {
    text-transform: uppercase;
    letter-spacing: 0.04em;
  }
  /* Inline date picker — replaces the chip while editing */
  .todo-due-edit {
    padding: 2px 6px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    font-size: 0.7rem;
    font-family: inherit;
  }
  .todo-due-edit:focus { outline: none; border-color: var(--fg); }

  /* ═══════════════════════════════════════════════════════════════════════
     RAIL GLOBAL DROIT — "Mes tâches"
     Visible sur tous les views (hors share mode), pinned right, sous le
     topbar. La main-area gagne un padding-right pour ne pas être couvert.
     ═══════════════════════════════════════════════════════════════════════ */
  .rail-todos {
    position: fixed;
    top: 64px;             /* sous le topbar (h: 64px) */
    right: 0;
    bottom: 0;
    width: 280px;
    z-index: 40;
    background: var(--bg);
    border-left: 1px solid var(--line-soft);
    overflow-y: auto;
    padding: 0 0 1.5rem;
    transition: width 0.2s ease;
  }
  .rail-todos.collapsed { width: 44px; }
  .rail-todos .sidebar-toggle-wrap {
    position: absolute;
    top: 8px;
    left: 8px;             /* bord gauche pour rail droit */
    right: auto;
    z-index: 5;
    display: flex;
    justify-content: flex-start;
  }
  .rail-todos.collapsed > *:not(.sidebar-toggle-wrap) {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
  }
  .rail-todos > *:not(.sidebar-toggle-wrap) {
    transition: opacity 0.25s ease 0.1s;
  }
  .rail-todos.collapsed .sidebar-toggle .chevron {
    transform: rotate(180deg);
  }
  .rail-todos .pane-head {
    padding: 1rem 1rem 0 2.75rem;
    margin-bottom: 0.75rem;
  }
  .rail-todos .pane-head h1 {
    font-size: 1.25rem;
  }
  .rail-todos .pane-sub {
    font-family: 'Fragment Mono', monospace;
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    margin-top: 0.4rem;
    padding-right: 0;
  }
  /* Vertical stack to fit the narrow 280px rail. Children :
     1. Title input full-width
     2. .rail-todos-add-row (select [project view only] + date + ＋ btn)
     The submit button lives inside the row so we keep the rail compact. */
  .rail-todos-add {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 0.5rem 1rem 0.75rem;
    border-bottom: 1px dashed var(--line-soft);
    margin-bottom: 0.6rem;
  }
  .rail-todos-add input[type=text] {
    width: 100%;
    padding: 6px 10px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    font-size: 0.78rem;
    font-family: inherit;
    min-width: 0;
    transition: border-color 0.15s;
    box-sizing: border-box;
  }
  .rail-todos-add input[type=text]:focus {
    outline: none;
    border-color: var(--fg);
  }
  .rail-todos-add-row {
    display: flex;
    gap: 6px;
    align-items: center;
  }
  .rail-todos-select {
    flex: 1 1 auto;
    min-width: 0;
    padding: 5px 6px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    font-size: 0.72rem;
    font-family: inherit;
    cursor: pointer;
    transition: border-color 0.15s;
  }
  .rail-todos-select:disabled { opacity: 0.6; cursor: wait; }
  .rail-todos-select:focus { outline: none; border-color: var(--fg); }
  .rail-todos-date {
    flex: 0 1 auto;
    width: 110px;
    padding: 5px 6px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    font-size: 0.72rem;
    font-family: inherit;
    transition: border-color 0.15s;
  }
  .rail-todos-date:focus { outline: none; border-color: var(--fg); }
  .rail-todos-btn {
    padding: 5px 10px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--fg);
    cursor: pointer;
    font-family: 'Fragment Mono', monospace;
    font-size: 0.95rem;
    line-height: 1;
    transition: background 0.15s, border-color 0.15s;
    flex-shrink: 0;
  }
  .rail-todos-btn:hover {
    background: var(--depth);
    border-color: var(--fg);
  }
  .rail-todos-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 0 1rem;
  }

  /* ── Layout : laisse de la place pour le rail droit ─────────────
     Le rail couvre la zone à droite — on shifte la main-area de la
     même largeur pour éviter le chevauchement. Quand le rail est
     replié, le body porte .rail-todos-collapsed et la main-area
     ne réserve plus que 44px.
     En share mode (#?share=...) le rail n'est pas monté ; on
     remet padding-right à 0 pour ce cas. */
  .main-area {
    padding-right: 280px;
    transition: padding-right 0.2s ease;
  }
  body.rail-todos-collapsed .main-area {
    padding-right: 44px;
  }
  body.is-share-mode .main-area {
    padding-right: 0;
  }
  body.is-share-mode .rail-todos {
    display: none;
  }
  /* Le rail flotte au-dessus de toutes les modals/popovers en partie
     supérieure (topbar z=60) mais reste derrière les modals plein
     écran. z=40 = sous la project-popover (50), au-dessus du contenu. */

  /* Responsive : sur petit écran, le rail se replie automatiquement.
     L'utilisateur peut toujours l'ouvrir via le toggle. */
  @media (max-width: 900px) {
    .rail-todos:not(.collapsed) {
      width: 240px;
    }
    .main-area {
      padding-right: 44px;
    }
    body:not(.rail-todos-collapsed) .main-area {
      padding-right: 240px;
    }
  }

/* ============================================================
   Brand Finder — utility layer (SCOPED to .bf-app)
   brand-finder-ui.jsx is written with Tailwind-style utility
   classes (grid, flex, gap-*, p-*, mb-*, text-sm, leading-*…)
   but those utilities were only ever defined under `.roas-app`,
   never for the Brand Finder tree — so the results page silently
   lost all its grids, spacing and type scale (cramped, single
   column, tiny text). Defining them here, scoped to `.bf-app`,
   restores the intended layout without leaking to other apps.
   ============================================================ */
.bf-app .flex { display: flex; }
.bf-app .grid { display: grid; }
.bf-app .flex-col { flex-direction: column; }
.bf-app .flex-wrap { flex-wrap: wrap; }
.bf-app .flex-1 { flex: 1 1 0%; }
.bf-app .flex-grow { flex-grow: 1; }
.bf-app .items-center { align-items: center; }
.bf-app .items-start { align-items: flex-start; }
.bf-app .items-baseline { align-items: baseline; }
.bf-app .justify-between { justify-content: space-between; }
.bf-app .justify-center { justify-content: center; }

.bf-app .relative { position: relative; }
.bf-app .absolute { position: absolute; }
.bf-app .sticky { position: sticky; }
.bf-app .inset-0 { top: 0; right: 0; bottom: 0; left: 0; }
.bf-app .top-0 { top: 0; }
.bf-app .z-10 { z-index: 10; }
.bf-app .z-50 { z-index: 50; }
.bf-app .overflow-hidden { overflow: hidden; }
.bf-app .pointer-events-none { pointer-events: none; }
.bf-app .cursor-pointer { cursor: pointer; }

.bf-app .gap-1\.5 { gap: 0.375rem; }
.bf-app .gap-2 { gap: 0.5rem; }
.bf-app .gap-3 { gap: 0.75rem; }
.bf-app .gap-4 { gap: 1rem; }
.bf-app .gap-6 { gap: 1.5rem; }
.bf-app .gap-x-6 { column-gap: 1.5rem; }
.bf-app .gap-x-8 { column-gap: 2rem; }
.bf-app .gap-y-3 { row-gap: 0.75rem; }
.bf-app .gap-y-4 { row-gap: 1rem; }
.bf-app .gap-y-6 { row-gap: 1.5rem; }

.bf-app .grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.bf-app .space-y-1\.5 > * + * { margin-top: 0.375rem; }
.bf-app .space-y-2 > * + * { margin-top: 0.5rem; }
.bf-app .space-y-4 > * + * { margin-top: 1rem; }
.bf-app .space-y-7 > * + * { margin-top: 1.75rem; }
.bf-app .space-y-12 > * + * { margin-top: 3rem; }

.bf-app .w-full { width: 100%; }
.bf-app .w-1\.5 { width: 0.375rem; }
.bf-app .h-1\.5 { height: 0.375rem; }
.bf-app .min-h-screen { min-height: 100vh; }
.bf-app .min-h-\[4rem\] { min-height: 4rem; }
.bf-app .mx-auto { margin-left: auto; margin-right: auto; }
.bf-app .mt-auto { margin-top: auto; }
.bf-app .max-w-sm { max-width: 24rem; }
.bf-app .max-w-xl { max-width: 36rem; }
.bf-app .max-w-2xl { max-width: 42rem; }
.bf-app .max-w-3xl { max-width: 48rem; }
.bf-app .max-w-4xl { max-width: 56rem; }
.bf-app .max-w-5xl { max-width: 64rem; }

.bf-app .text-xs { font-size: 11px; line-height: 1.4; }
.bf-app .text-sm { font-size: 13px; line-height: 1.5; }
.bf-app .text-base { font-size: 15px; line-height: 1.5; }
.bf-app .text-lg { font-size: 17px; line-height: 1.5; }
.bf-app .text-xl { font-size: 20px; line-height: 1.4; }
.bf-app .text-2xl { font-size: 24px; line-height: 1.3; }
.bf-app .text-7xl { font-size: 56px; line-height: 1.05; }
.bf-app .text-center { text-align: center; }
.bf-app .leading-relaxed { line-height: 1.625; }
.bf-app .leading-snug { line-height: 1.375; }
.bf-app .font-light { font-weight: 300; }
.bf-app .whitespace-pre-wrap { white-space: pre-wrap; }
.bf-app .opacity-20 { opacity: 0.2; }
.bf-app .opacity-40 { opacity: 0.4; }
.bf-app .opacity-60 { opacity: 0.6; }

.bf-app .bg-black { background-color: #000; }
.bf-app .bg-white { background-color: #fff; }
.bf-app .rounded-full { border-radius: 9999px; }
.bf-app .border-b { border-bottom-width: 1px; border-bottom-style: solid; }
.bf-app .border-t { border-top-width: 1px; border-top-style: solid; }
.bf-app .border-black { border-color: #000; }
.bf-app .border-black\/15 { border-color: rgba(0, 0, 0, 0.15); }
.bf-app .border-black\/20 { border-color: rgba(0, 0, 0, 0.2); }

.bf-app .mb-1 { margin-bottom: 0.25rem; }
.bf-app .mb-1\.5 { margin-bottom: 0.375rem; }
.bf-app .mb-2 { margin-bottom: 0.5rem; }
.bf-app .mb-2\.5 { margin-bottom: 0.625rem; }
.bf-app .mb-3 { margin-bottom: 0.75rem; }
.bf-app .mb-4 { margin-bottom: 1rem; }
.bf-app .mb-5 { margin-bottom: 1.25rem; }
.bf-app .mb-6 { margin-bottom: 1.5rem; }
.bf-app .mb-8 { margin-bottom: 2rem; }
.bf-app .mb-10 { margin-bottom: 2.5rem; }
.bf-app .mb-12 { margin-bottom: 3rem; }
.bf-app .mb-16 { margin-bottom: 4rem; }
.bf-app .mb-20 { margin-bottom: 5rem; }
.bf-app .mb-24 { margin-bottom: 6rem; }
.bf-app .mt-1 { margin-top: 0.25rem; }
.bf-app .mt-2 { margin-top: 0.5rem; }
.bf-app .mt-3 { margin-top: 0.75rem; }
.bf-app .mt-4 { margin-top: 1rem; }
.bf-app .mt-6 { margin-top: 1.5rem; }
.bf-app .mt-8 { margin-top: 2rem; }
.bf-app .mt-10 { margin-top: 2.5rem; }
.bf-app .mt-14 { margin-top: 3.5rem; }
.bf-app .mt-20 { margin-top: 5rem; }
.bf-app .my-16 { margin-top: 4rem; margin-bottom: 4rem; }
.bf-app .ml-2 { margin-left: 0.5rem; }
.bf-app .ml-3 { margin-left: 0.75rem; }

.bf-app .p-3 { padding: 0.75rem; }
.bf-app .p-4 { padding: 1rem; }
.bf-app .p-5 { padding: 1.25rem; }
.bf-app .p-6 { padding: 1.5rem; }
.bf-app .pb-2 { padding-bottom: 0.5rem; }
.bf-app .pb-4 { padding-bottom: 1rem; }
.bf-app .pt-6 { padding-top: 1.5rem; }
.bf-app .pt-10 { padding-top: 2.5rem; }
.bf-app .pl-5 { padding-left: 1.25rem; }
.bf-app .pr-4 { padding-right: 1rem; }
.bf-app .px-5 { padding-left: 1.25rem; padding-right: 1.25rem; }
.bf-app .px-8 { padding-left: 2rem; padding-right: 2rem; }
.bf-app .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.bf-app .py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.bf-app .py-4 { padding-top: 1rem; padding-bottom: 1rem; }
.bf-app .py-5 { padding-top: 1.25rem; padding-bottom: 1.25rem; }
.bf-app .py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
.bf-app .py-12 { padding-top: 3rem; padding-bottom: 3rem; }
.bf-app .py-32 { padding-top: 8rem; padding-bottom: 8rem; }

@media (min-width: 768px) {
  .bf-app .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .bf-app .md\:grid-cols-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .bf-app .md\:col-span-1 { grid-column: span 1 / span 1; }
  .bf-app .md\:col-span-3 { grid-column: span 3 / span 3; }
  .bf-app .md\:col-span-4 { grid-column: span 4 / span 4; }
  .bf-app .md\:col-span-8 { grid-column: span 8 / span 8; }
  .bf-app .md\:col-span-9 { grid-column: span 9 / span 9; }
  .bf-app .md\:col-span-11 { grid-column: span 11 / span 11; }
  .bf-app .md\:text-3xl { font-size: 30px; line-height: 1.2; }
  .bf-app .md\:text-8xl { font-size: 72px; line-height: 1.05; }
}

/* Codes du secteur × Actions — table beside its footer blocks (disruptions /
   pivots). Table gets the wider track; stacks under 900px so the 640px-min
   table never gets crushed. */
.bf-app .bf-codes-split {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(240px, 1fr);
  column-gap: 1.75rem;
  row-gap: 1.5rem;
  align-items: start;
}
/* Align the first footer block with the table top (drop its leading margin). */
.bf-app .bf-codes-split > div:last-child > *:first-child { margin-top: 0; }
@media (max-width: 900px) {
  .bf-app .bf-codes-split { grid-template-columns: 1fr; }
  /* Restore the separating margin once stacked under the table. */
  .bf-app .bf-codes-split > div:last-child > *:first-child { margin-top: 1.5rem; }
}
