/*
 * Octopia — light-first token system.
 * Owned by the app (docs/architecture.md); public_html holds the entrypoint and
 * static assets only. No framework, no build step.
 */

:root {
  /* Brand — blue (owner rebrand, 2026-09-01; completes the badge-blue rename
     started earlier, and gives landing.css's own --blue-* references, until
     now undefined anywhere in the repo, an actual value). */
  --blue-900: #0c3159;
  --blue-700: #14487f;
  --blue-600: #1c5fa1;
  --blue-500: #2c78c2;
  --blue-100: #d9e8f6;
  --blue-050: #eef4fb;

  --amber-600: #b98314;
  --amber-100: #fbf0d6;

  /* Neutrals */
  --ink-900: #101614;
  --ink-700: #2c3a36;
  --ink-500: #5c6b66;
  --ink-400: #7d8b86;
  --ink-300: #aab4b0;
  --line: #e3e8e6;
  --line-strong: #cfd8d5;
  --surface: #ffffff;
  --surface-sunken: #f6f8f7;
  --canvas: #f2f5f4;

  --danger-600: #b3261e;
  --danger-050: #fdecea;
  --success-600: #1c7a4f;
  --success-050: #e8f5ee;

  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 16px;
  --shadow-sm: 0 1px 2px rgba(16, 22, 20, .06);
  --shadow: 0 4px 16px rgba(16, 22, 20, .07);
  --shadow-lg: 0 18px 48px rgba(16, 22, 20, .12);

  --sidebar-width: 196px;
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

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

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-900);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--blue-600); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.01em; }
h1 { font-size: 24px; }
h2 { font-size: 18px; }
h3 { font-size: 15px; }
p { margin: 0 0 12px; }

/* ---------------------------------------------------------------- layout */

.shell {
  display: flex;
  /* The interior alone, not the public landing or guest screens: at 15px
     base the app read small on a 27" display, forcing two browser zoom
     steps (owner, 2026-09-01). `zoom` reproduces exactly that — fonts,
     spacing, icons all scale together.
     No `min-height: 100vh` here: a viewport unit inside this zoom is
     multiplied by it (a 94vh panel measured 113 % of the screen, owner
     2026-09-03), so the shell was a fifth taller than the window and every
     page scrolled even with nothing to read. The full-height column is the
     fixed sidebar's job now.

     Essayé à 1 le 2026-09-15 pour rendre de la largeur aux cadres de
     comparaison, puis reposé : le confort de lecture de TOUTE l'interface
     n'est pas le prix d'un cadre (propriétaire, même jour). C'est
     `.preview-frame` qui se dézoome, lui seul. */
  zoom: 1.2;
}

/* Pinned to the window, never to the page: the merchant reads the whole
   sidebar — nav, credits, organization — without scrolling to the foot of a
   long product page (owner, 2026-09-04).
   `top: 0; bottom: 0` rather than any height in `vh`: zero is the one offset
   `zoom` cannot distort, so the column is exactly one window tall whichever
   way the browser resolves viewport units inside a zoom. */
.sidebar {
  position: fixed;
  top: 0; bottom: 0; left: 0;
  z-index: 10;
  width: var(--sidebar-width);
  background: var(--surface);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  padding: 20px 14px;
  gap: 20px;
  /* A window too short for the whole column scrolls the column itself,
     rather than pushing its foot out of reach again. */
  overflow-y: auto;
}

.brand { display: flex; align-items: center; gap: 10px; padding: 0 8px; }
.brand-mark {
  width: 30px; height: 30px; border-radius: 9px;
  background: linear-gradient(145deg, var(--blue-500), var(--blue-700));
  color: #fff; font-weight: 700; font-size: 15px;
  display: grid; place-items: center;
}
.brand-name { font-weight: 650; letter-spacing: -.02em; }

.nav { display: flex; flex-direction: column; gap: 2px; }
.nav a {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: var(--radius-sm);
  color: var(--ink-700); font-weight: 500; text-decoration: none;
}
.nav a:hover { background: var(--surface-sunken); }
.nav a.is-active { background: var(--blue-050); color: var(--blue-700); }
.nav a.is-active .nav-dot { background: var(--blue-600); }
.nav-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-300); }

.sidebar-footer { margin-top: auto; display: flex; flex-direction: column; gap: 10px; }
.sidebar-link { font-size: 13px; color: var(--ink-500); text-decoration: none; padding: 0 2px; }
.sidebar-link:hover { color: var(--blue-700); text-decoration: underline; }

/* Le voile du tiroir mobile. Sous la sidebar (z-index 10), au-dessus de tout
   le reste : la page derriere reste lisible mais n'est plus l'affaire. */
.sidebar-backdrop { position: fixed; inset: 0; z-index: 9; background: rgba(15, 23, 42, .42); }
.sidebar-backdrop[hidden] { display: none; }
/* Le voile appartient au tiroir : au-dessus de 900 px la sidebar est une
   colonne, et un voile reste de l'ouverture precedente assombrissait toute
   l'application apres un simple redimensionnement. */
@media (min-width: 901px) { .sidebar-backdrop { display: none; } }

/* The sidebar is out of flow, so the room it occupies is reserved here. Same
   custom property on both, so they scale together under the shell's zoom. */
.main { flex: 1; min-width: 0; margin-left: var(--sidebar-width); display: flex; flex-direction: column; }

.topbar {
  height: 58px; display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 0 24px; background: var(--surface); border-bottom: 1px solid var(--line);
}
.topbar .user { display: flex; align-items: center; gap: 12px; font-size: 14px; color: var(--ink-500); }
.topbar-lead { display: flex; align-items: center; gap: 10px; min-width: 0; flex: 1 1 auto; }

/* Le hamburger n'existe que la ou la sidebar disparait : au-dessus de
   900 px la colonne est la, le bouton ne servirait a rien. */
.topbar-menu { display: none; }
.topbar-menu-bars, .topbar-menu-bars::before, .topbar-menu-bars::after {
  display: block; width: 17px; height: 2px; border-radius: 2px; background: currentColor; content: '';
}
.topbar-menu-bars { position: relative; }
.topbar-menu-bars::before { position: absolute; top: -5px; }
.topbar-menu-bars::after { position: absolute; top: 5px; }

/* L'organisation, sortie du pied de la sidebar (proprietaire, 2026-09-05) :
   un reperemuet a cote du solde, jamais une commande. */
.org-chip {
  display: inline-flex; align-items: baseline; gap: 6px; min-width: 0;
  padding: 3px 10px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface-sunken); font-size: 13px;
}
.org-chip-name { font-weight: 600; color: var(--ink-900); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 18ch; }
.org-chip-role { font-size: 12px; color: var(--ink-500); white-space: nowrap; }

/* ------------------------------------------------------- credit (topbar)
 * The balance the merchant spends, where they can always see it: a pill in
 * the topbar and the panel it opens. Same idiom as the rest of the app —
 * light surface, one blue accent, amber only when something is actually
 * short.
 */
.credit { position: relative; }

.credit-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 12px 4px 10px;
  border: 1px solid var(--line-strong); border-radius: 999px;
  background: var(--surface); color: var(--ink-900);
  font: inherit; font-size: 13px; cursor: pointer;
  box-shadow: var(--shadow-sm);
}
.credit-pill:hover { border-color: var(--blue-500); }
.credit-pill[aria-expanded="true"] { border-color: var(--blue-500); box-shadow: 0 0 0 3px var(--blue-050); }
.credit-mark { width: 15px; height: 15px; color: var(--blue-600); flex: 0 0 auto; }
.credit-count { font-weight: 700; letter-spacing: -.01em; }
/* Les traitements image portent leur propre couleur : jaune, pour se
   distinguer du credit produit d'un coup d'oeil (proprietaire,
   2026-09-05). */
.credit-mark-image { color: #eab308; }
.credit-pill.is-short { border-color: #edd9a6; background: var(--amber-100); }
/* Deux soldes dans la pastille : le manque se lit sur le segment concerne,
   pas sur les deux (proprietaire, 2026-09-05). */
.credit-seg { display: inline-flex; align-items: center; gap: 5px; }
.credit-sep { width: 1px; align-self: stretch; margin: 2px 2px; background: var(--line-strong); }
.credit-pill.is-short .credit-sep { background: #edd9a6; }
.credit-seg.is-short .credit-mark,
.credit-seg.is-short .credit-count { color: var(--amber-600); }

.credit-panel {
  position: absolute; top: calc(100% + 10px); right: 0; z-index: 40;
  width: 272px; padding: 14px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  text-align: left;
}
.credit-panel-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin-bottom: 10px;
}
.credit-panel-title { font-weight: 600; }
.credit-panel-head a { font-size: 12.5px; }

.credit-figures {
  background: var(--surface-sunken); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px; text-align: center;
}
.credit-total { font-size: 2.2rem; font-weight: 700; line-height: 1.05; letter-spacing: -.02em; color: var(--blue-700); }
.credit-total.is-short { color: var(--amber-600); }
.credit-total-label { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-400); }
.credit-split {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line);
}
.credit-split-value { font-weight: 600; font-size: 13.5px; color: var(--ink-900); }
.credit-split-value.is-short { color: var(--amber-600); }
.credit-split-label { font-size: 11px; color: var(--ink-400); }

.credit-note {
  margin: 10px 0 0; padding: 8px 10px;
  background: var(--amber-100); border-radius: var(--radius-sm);
  color: var(--amber-600); font-size: 12.5px;
}
.credit-cta { display: block; width: 100%; margin-top: 8px; text-align: center; }

.content { padding: 24px; width: 100%; }

.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
.page-head .subtitle { color: var(--ink-500); font-size: 14px; }
/* L'action de l'en-tete ne se coupe pas en deux lignes : c'est un libelle,
   pas un paragraphe (proprietaire, 2026-09-05). */
.page-head > .btn, .page-head > a.btn { white-space: nowrap; flex: 0 0 auto; }

/* ---------------------------------------------------------------- cards */

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.card + .card { margin-top: 16px; }
/* Dans une grille, l'espace entre deux cartes est le `gap` : la marge de la
   regle ci-dessus s'y ajoutait, decalant la deuxieme carte vers le bas et la
   raccourcissant d'autant — deux cartes cote a cote n'avaient plus la meme
   hauteur (proprietaire, 2026-09-05). */
.grid > .card + .card { margin-top: 0; }
/* Une grille n'est pas une carte : rien ne separait un bloc en grille du
   bloc suivant, et les cartes se touchaient. Borne a `.grid.two`, la grille
   de cartes : `.pg-form-grid` (formulaire de contact) espace ses rangees par
   la marge de ses champs, et une marge de plus les doublait. */
.card + .grid.two, .grid.two + .card, .grid.two + .grid.two { margin-top: 16px; }

.card-head { padding: 16px 18px; border-bottom: 1px solid var(--line); display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.card-body { padding: 18px; }
.card-body.tight { padding: 12px 18px; }

/* Une carte qu'on ouvre quand on en a besoin : le titre reste lisible, le
   reste attend. `details` porte la carte, `summary` porte l'en-tete. */
.card-quiet { background: var(--surface-sunken); box-shadow: none; }
.card-quiet > summary.card-head {
  cursor: pointer; list-style: none; border-bottom: none;
  font-size: 14px; font-weight: 600; color: var(--ink-500);
}
.card-quiet[open] > summary.card-head { border-bottom: 1px solid var(--line); color: var(--ink-700); }
.card-quiet > summary.card-head h2 { font-size: 14px; font-weight: 600; color: inherit; margin: 0; }
.card-quiet > summary.card-head::-webkit-details-marker { display: none; }
.card-quiet > summary.card-head::after {
  content: '▾'; margin-left: auto; font-size: 12px; color: var(--ink-400);
  transition: transform .12s ease;
}
.card-quiet[open] > summary.card-head::after { transform: rotate(180deg); }
.card-quiet > summary.card-head:hover { color: var(--ink-900); }
.card-quiet > .card-body { background: var(--surface); border-radius: 0 0 var(--radius-lg) var(--radius-lg); }

.grid { display: grid; gap: 16px; }
.grid.two { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

/* ---------------------------------------------------------------- forms */

label { display: block; font-size: 13px; font-weight: 550; color: var(--ink-700); margin-bottom: 6px; }
.field { margin-bottom: 16px; }
.field .help { font-size: 12.5px; color: var(--ink-500); margin-top: 6px; }

input[type=text], input[type=email], input[type=password], input[type=search], input[type=date], select, textarea {
  width: 100%; padding: 9px 11px; font: inherit; font-size: 14px; color: var(--ink-900);
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  transition: border-color .12s ease, box-shadow .12s ease;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--blue-500); box-shadow: 0 0 0 3px var(--blue-050);
}
input::placeholder { color: var(--ink-300); }

.checkbox { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--ink-700); }
.checkbox input { width: auto; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 9px 16px; font: inherit; font-size: 14px; font-weight: 550;
  border-radius: var(--radius-sm); border: 1px solid transparent; cursor: pointer;
  text-decoration: none; transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.btn:hover { text-decoration: none; }
.btn-primary { background: var(--blue-600); color: #fff; }
.btn-primary:hover { background: var(--blue-700); }
.btn-secondary { background: var(--surface); color: var(--ink-700); border-color: var(--line-strong); }
.btn-secondary:hover { background: var(--surface-sunken); }
.btn-ghost { background: transparent; color: var(--ink-500); }
.btn-ghost:hover { background: var(--surface-sunken); color: var(--ink-700); }
.btn-danger { background: var(--surface); color: var(--danger-600); border-color: var(--line-strong); }
.btn-danger:hover { background: var(--danger-050); }
.btn-sm { padding: 5px 10px; font-size: 13px; }
.btn-block { width: 100%; }
.btn[disabled], .btn.is-disabled { opacity: .55; pointer-events: none; }

.inline-form { display: inline; }

/* ---------------------------------------------------------------- feedback */

.alert { border-radius: var(--radius); padding: 12px 14px; font-size: 14px; margin-bottom: 16px; border: 1px solid transparent; }
.alert-success { background: var(--success-050); border-color: #bfe3ce; color: var(--success-600); }
.alert-error { background: var(--danger-050); border-color: #f3c9c5; color: var(--danger-600); }
.alert-info { background: var(--blue-050); border-color: var(--blue-100); color: var(--blue-700); }
.alert-warn { background: var(--amber-100); border-color: #edd9a6; color: var(--amber-600); }
.alert code { font-family: var(--mono); font-size: 12.5px; }

.errors { list-style: none; margin: 0 0 16px; padding: 12px 14px; border-radius: var(--radius); background: var(--danger-050); color: var(--danger-600); font-size: 13.5px; }
.errors li + li { margin-top: 4px; }

.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 550;
  background: var(--surface-sunken); color: var(--ink-500); border: 1px solid var(--line);
}
.badge-ok { background: var(--success-050); color: var(--success-600); border-color: #bfe3ce; }
.badge-warn { background: var(--amber-100); color: var(--amber-600); border-color: #edd9a6; }
.badge-off { background: var(--surface-sunken); color: var(--ink-400); }
.badge-danger { background: var(--danger-050); color: var(--danger-600); border-color: #f3c9c5; }
.badge-blue { background: var(--blue-050); color: var(--blue-700); border-color: var(--blue-100); }

/* ON/OFF switch, iOS-style: solid borderless pill, large white knob.
   As a <button type="submit"> inside a form it toggles without any JS;
   as a <span> it is a read-only state indicator. */
.switch {
  position: relative; display: inline-block; flex: none;
  width: 40px; height: 22px; padding: 0; vertical-align: middle;
  border: 0; border-radius: 999px;
  background: var(--ink-300); cursor: pointer;
  transition: background .15s ease;
}
.switch::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--surface); box-shadow: 0 1px 3px rgba(16, 22, 20, .25);
  transition: left .15s ease;
}
.switch.is-on { background: var(--success-600); }
.switch.is-on::after { left: 20px; }
button.switch:hover { filter: brightness(.95); }
button.switch.is-on:hover { filter: brightness(1.08); }
span.switch { cursor: default; }

.muted { color: var(--ink-400); }
/* Named for a screen reader, invisible to everyone else. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.mono { font-family: var(--mono); font-size: 12.5px; }
.small { font-size: 13px; }

/* ---------------------------------------------------------------- table */

.filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; align-items: end; }
.filters .field { margin: 0; }
.filters-actions { display: flex; gap: 8px; }

.table-wrap { overflow-x: auto; }
table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
table.data th {
  text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-400); font-weight: 600; padding: 10px 14px; border-bottom: 1px solid var(--line);
  white-space: nowrap; background: var(--surface-sunken);
}
table.data td { padding: 12px 14px; border-bottom: 1px solid var(--line); vertical-align: middle; }
table.data tr:last-child td { border-bottom: none; }
table.data tbody tr:hover { background: var(--blue-050); }

.thumb {
  width: 48px; height: 48px; border-radius: var(--radius-sm); object-fit: cover;
  background: var(--surface-sunken); border: 1px solid var(--line); display: block;
}
.thumb-empty { display: grid; place-items: center; color: var(--ink-300); font-size: 11px; }

.cell-title { font-weight: 550; color: var(--ink-900); }
.cell-sub { font-size: 12.5px; color: var(--ink-400); }
.chips { display: flex; flex-wrap: wrap; gap: 4px; }

.empty-state { padding: 48px 24px; text-align: center; color: var(--ink-500); }
.empty-state h3 { margin-bottom: 6px; color: var(--ink-700); }

.pagination { display: flex; gap: 6px; padding: 14px 18px; flex-wrap: wrap; align-items: center; }
.pagination a, .pagination span { padding: 4px 9px; border-radius: var(--radius-sm); font-size: 13px; }
.pagination a { border: 1px solid var(--line); color: var(--ink-700); }
.pagination a:hover { background: var(--surface-sunken); text-decoration: none; }
.pagination .current { background: var(--blue-600); color: #fff; }
.pagination .disabled { color: var(--ink-300); }

/* ---------------------------------------------------------------- guest */

.guest {
  min-height: 100vh; display: grid; grid-template-columns: 1.05fr 1fr;
}
.guest-hero {
  background: linear-gradient(160deg, var(--blue-700), var(--blue-900) 70%);
  color: #fff; padding: 56px 52px; display: flex; flex-direction: column; justify-content: space-between;
}
.guest-hero .brand-name { color: #fff; }
.guest-hero .brand-mark { background: rgba(255,255,255,.16); }
.guest-hero h1 { font-size: 34px; line-height: 1.2; max-width: 15ch; }
.guest-hero p { color: rgba(255,255,255,.78); max-width: 44ch; }
.hero-points { list-style: none; padding: 0; margin: 24px 0 0; display: grid; gap: 12px; }
.hero-points li { display: flex; gap: 10px; align-items: flex-start; color: rgba(255,255,255,.86); font-size: 14px; }
.hero-points .n {
  flex: 0 0 22px; height: 22px; border-radius: 50%; background: rgba(255,255,255,.16);
  display: grid; place-items: center; font-size: 12px; font-weight: 600;
}
.guest-form { display: grid; place-items: center; padding: 40px 24px; background: var(--canvas); }
.guest-form .box { width: 100%; max-width: 400px; }
.guest-form .box h2 { margin-bottom: 4px; }
.guest-form .box .intro { color: var(--ink-500); font-size: 14px; margin-bottom: 22px; }
.guest-foot { margin-top: 18px; font-size: 13.5px; color: var(--ink-500); text-align: center; }

.steps { display: flex; gap: 8px; margin-bottom: 22px; flex-wrap: wrap; }
.step { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--ink-400); }
.step .n { width: 20px; height: 20px; border-radius: 50%; border: 1px solid var(--line-strong); display: grid; place-items: center; font-size: 11px; }
.step.is-done { color: var(--blue-700); }
.step.is-done .n { background: var(--blue-100); border-color: var(--blue-100); color: var(--blue-700); }
.step.is-current { color: var(--ink-900); font-weight: 550; }
.step.is-current .n { background: var(--blue-600); border-color: var(--blue-600); color: #fff; }

.lang-switch { display: flex; gap: 4px; }
.lang-switch button,
.lang-switch a {
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-500);
  border-radius: var(--radius-sm); padding: 3px 8px; font: inherit; font-size: 12px; cursor: pointer;
  text-decoration: none; line-height: 1.4;
}
.lang-switch button.is-active, .lang-switch a.is-active { background: var(--blue-050); border-color: var(--blue-100); color: var(--blue-700); font-weight: 600; }

@media (max-width: 900px) {
  .guest { grid-template-columns: 1fr; }
  .guest-hero { display: none; }
  /* La colonne devient un tiroir : masquee par defaut, posee sur la page
     quand le hamburger l'ouvre. */
  .sidebar { display: none; width: min(84vw, 300px); box-shadow: var(--shadow-lg); }
  .sidebar.is-open { display: flex; }
  .main { margin-left: 0; }

  .topbar-menu {
    display: inline-grid; place-items: center; flex: 0 0 auto;
    width: 34px; height: 34px; padding: 0;
    border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
    background: var(--surface); color: var(--ink-700); cursor: pointer;
  }
  .topbar-menu[aria-expanded="true"] { border-color: var(--blue-500); color: var(--blue-700); }

  /* Le zoom de confort est taillé pour un 27 pouces (propriétaire,
     2026-09-01) ; sur un téléphone il ne fait que pousser la topbar hors
     de l'écran. */
  .shell { zoom: 1; }

  .topbar { padding: 0 14px; gap: 10px; }
  /* Le fil d'Ariane tient sur UNE ligne et se coupe proprement : c'est lui
     qui cède la place, jamais le solde ni la déconnexion. */
  .topbar > .small,
  .topbar-lead > .small { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .topbar .user { flex: 0 0 auto; gap: 8px; }
  .topbar .user .btn-sm { white-space: nowrap; }

  .content { padding: 16px 14px; }
}

@media (max-width: 600px) {
  /* Le nom du compte est un rappel, pas une commande : sur un écran étroit
     il laisse la place au solde de crédits et à la déconnexion. Le repere
     d'organisation cede en meme temps, et le role avant le nom. */
  .topbar .user > span { display: none; }
  .org-chip { display: none; }
}

@media (max-width: 780px) {
  .org-chip-role { display: none; }
  .org-chip-name { max-width: 12ch; }
}

/* ---------------------------------------------------------------------------
 * Workspace (M2): Team Live, timeline, dossier, run panel.
 * Every animated state binds to persisted events — no simulated activity.
 * ------------------------------------------------------------------------- */

/* M4.5: the workspace is a single full-width column (LIGE structure). The run's
   own context sits in a row of its own rather than in a sidebar, so nothing
   below it — the editorial preview above all — is squeezed. */
/* Deux cartes techniques du workspace — « Traitement » (état du run) et
   « Coûts du traitement » — masquées à la demande du propriétaire
   (2026-09-04) : elles restent dans le DOM, il les rallume au besoin en
   passant ce `display` à `grid` dans l'inspecteur. */
.card-run-state,
.card-run-costs { display: none; }

.workspace-context { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; align-items: start; margin-top: 16px; }
.workspace-context > .card { margin: 0; }
.workspace-context + .card { margin-top: 16px; }

/* Hero row: agent timeline (left) + current work / gate (right), ported from
   LIGE's `.desc-workspace-grid` (owner request 2026-08-31). `stretch` makes
   both columns the height of the taller one — here, the gate/working-hero
   card, which sizes to its own content — and the timeline card, forced into
   a column flexbox, fills that same height with its body scrolling inside
   rather than growing the row (owner request 2026-08-31). */
.hero-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: stretch; }
.hero-row > .card { margin: 0; display: flex; flex-direction: column; min-height: 0; }
.hero-row + .card, .hero-row + .workspace-context { margin-top: 16px; }

/* The row's height is decided by the RIGHT panel (or by this column's own
   50vh floor), never by the history's length: the timeline card is taken out
   of flow inside its column, so it can only ever fill the height already
   set — a long history scrolls, it does not stretch the row. */
.timeline-col { position: relative; min-height: 50vh; }
.timeline-col > .card { position: absolute; inset: 0; margin: 0; display: flex; flex-direction: column; min-height: 0; }

/* Une fois la rangée empilée, ce qui se passe MAINTENANT passe avant ce qui
   s'est passé : la décision humaine / le travail en cours d'abord, l'histoire
   ensuite (propriétaire, 2026-09-03). Même seuil que l'empilement, pour qu'il
   n'existe aucune largeur où la colonne unique commence par l'histoire. */
@media (max-width: 1050px) {
  /* `1fr` seul reprend sa taille implicite au contenu : une ligne longue non
     coupée (le texte de l'action en cours) élargissait toute la carte hors
     de l'écran plutôt que de passer à la ligne (propriétaire, 2026-09-06).
     `minmax(0, 1fr)`, déjà utilisé par la disposition à deux colonnes,
     rétablit la possibilité de rétrécir. */
  .hero-row { grid-template-columns: minmax(0, 1fr); }
  .hero-row > .timeline-col { order: 2; }
  .hero-row > .gate-panel { order: 1; }
}

/* New-product intro: one card, one big decision — nothing else on the page
   yet (owner request 2026-08-31). */
.workspace-intro { max-width: 640px; margin: 32px auto 0; display: flex; flex-direction: column; gap: 16px; }
.launch-cta { display: flex; justify-content: center; }
.btn-launch { padding: 16px 44px; font-size: 16px; font-weight: 650; border-radius: var(--radius); }

/* Le bouton qui lance : même relief et même reflet sur la landing (`.v2-btn-glow`)
   et dans l'app, pour que « lancer » se reconnaisse d'un écran à l'autre. */
.btn-glow, .v2-btn-glow {
  background: var(--blue-500); color: #fff; border: 0; position: relative; overflow: hidden;
  box-shadow: 0 12px 34px rgba(44, 120, 194, .45);
  transition: transform .25s ease, box-shadow .25s ease, background .12s ease;
}
.btn-glow:hover, .v2-btn-glow:hover { transform: translateY(-2px); box-shadow: 0 18px 44px rgba(44, 120, 194, .55); }
.btn-glow:disabled, .btn-glow[disabled] { opacity: .5; transform: none; box-shadow: none; cursor: not-allowed; }
.btn-glow::after, .v2-btn-glow::after {
  content: ""; position: absolute; top: 0; left: -60%; width: 40%; height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .35), transparent);
  transform: skewX(-20deg); animation: v2-sheen 3.4s ease-in-out infinite;
}
.btn-glow:disabled::after, .btn-glow[disabled]::after { display: none; }
@keyframes v2-sheen { 0%, 60% { left: -60%; } 100% { left: 130%; } }

/* Source logo buttons — page-head, once a run exists (owner request
   2026-08-31): replaces "Ouvrir dans Shopify" with one icon per connected
   source. */
.source-links { display: flex; align-items: center; gap: 8px; }
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0;
  background: var(--surface); border: 1px solid var(--line-strong);
  transition: border-color .12s ease, box-shadow .12s ease;
}
.icon-btn:hover { border-color: var(--blue-500); box-shadow: var(--shadow-sm); }
.icon-btn img { width: 20px; height: 20px; display: block; }
/* The run's own verbs sit in this row as icon buttons (2026-09-10). A <button>
   inherits neither the page colour nor its font, so the stroke would fall back
   to the UA's buttontext and the title tooltip is the only label there is. */
button.icon-btn { padding: 0; cursor: pointer; color: var(--ink-700); font: inherit; }
.icon-btn svg { width: 18px; height: 18px; display: block; }
button.icon-btn:disabled { opacity: .45; cursor: not-allowed; }
button.icon-btn:disabled:hover { border-color: var(--line-strong); box-shadow: none; }
button.icon-btn-primary { color: var(--blue-600); border-color: var(--blue-100); background: var(--blue-050); }
button.icon-btn-danger:hover { border-color: var(--danger-600); color: var(--danger-600); }

.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 14px 18px; }
.card .card-head + .card-body { padding-top: 10px; }

/* Team Live — the LIGE dashboard structure in Octopia's light tokens
   (owner decision 2026-08-29; ADR-0005 palette unchanged). */
.workflow-progress { height: 6px; border-radius: 3px; background: var(--surface-sunken); overflow: hidden; margin-bottom: 4px; }
.workflow-progress-bar { height: 100%; background: var(--blue-500); border-radius: 3px; }

.pole { margin-bottom: 18px; }
.pole:last-child { margin-bottom: 0; }
.pole-head { display: flex; align-items: baseline; gap: 9px; margin-bottom: 9px; }
.pole-number { font-size: 15px; font-weight: 700; color: var(--blue-600); font-variant-numeric: tabular-nums; }
.pole-title { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-500); display: block; }
.pole-subtitle { display: block; }
.team-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px; }

.agent-card { position: relative; display: flex; flex-direction: column; gap: 8px; padding: 11px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); transition: border-color .2s, box-shadow .2s; min-width: 0; }
.agent-corner-state { position: absolute; top: 8px; right: 10px; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-400); }
.agent-head { display: flex; gap: 10px; align-items: center; padding-right: 78px; }
.agent-portrait { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; background: var(--surface-sunken); flex-shrink: 0; }
.agent-portrait-lg { width: 76px; height: 76px; border-radius: 50%; object-fit: cover; }
.agent-name { font-weight: 600; font-size: 13px; display: block; }
.agent-role { font-size: 11px; color: var(--ink-500); display: block; }
.agent-message { font-size: 11.5px; color: var(--ink-700); margin: 0; }

.agent-lines { display: grid; grid-template-columns: minmax(58px, auto) 1fr; gap: 1px 8px; margin: 0; font-size: 11px; }
.agent-lines dt { color: var(--ink-400); text-transform: uppercase; letter-spacing: .04em; font-weight: 600; }
.agent-lines dd { margin: 0; color: var(--ink-700); min-width: 0; overflow-wrap: anywhere; }

.agent-substeps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; font-size: 11px; }
.agent-substeps li { display: flex; align-items: center; gap: 6px; }
.state-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-300); flex-shrink: 0; }
.substep-completed .state-dot { background: var(--success-600); }
.substep-working .state-dot { background: var(--blue-500); animation: pulse 1.4s ease-in-out infinite; }
.substep-blocked .state-dot { background: var(--danger-600); }
.substep-awaiting_decision .state-dot { background: var(--amber-600); }

.agent-documents { display: flex; flex-direction: column; gap: 5px; }
.agent-chip-group { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.agent-chip-label { font-size: 10px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-400); font-weight: 600; width: 100%; }
.agent-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 10.5px; padding: 2px 7px; border: 1px solid var(--line); border-radius: 999px; color: var(--ink-700); text-decoration: none; background: var(--surface-sunken); }
.agent-chip:hover { border-color: var(--blue-500); color: var(--blue-700); }
.agent-chip-out { background: var(--blue-050); border-color: var(--blue-100); }
.agent-chip-current { font-weight: 600; color: var(--blue-700); }
.agent-last-trace { margin: 0; }

.agent-card.state-working { border-color: var(--blue-500); box-shadow: 0 0 0 3px var(--blue-050); }
.agent-card.state-working .agent-corner-state { color: var(--blue-600); }
.agent-card.state-completed .agent-corner-state { color: var(--success-600); }
.agent-card.state-blocked { border-color: var(--danger-600); }
.agent-card.state-blocked .agent-corner-state { color: var(--danger-600); }
.agent-card.state-awaiting_decision { border-color: var(--amber-600); background: var(--amber-100); }
.agent-card.state-awaiting_decision .agent-corner-state { color: var(--amber-600); }
.agent-card.state-not_required { opacity: .5; }

/* A specialist whose work needs a connected shop, on a product read from its
   public address (ADR-0092). Dimmed like `not_required`, and carrying the
   reason one hover away rather than a card of its own listing what is absent. */
.agent-card.state-disabled { opacity: .55; }
.agent-card.state-disabled .agent-corner-state { color: var(--ink-400); }
.agent-state-info { display: inline-flex; align-items: center; justify-content: center; width: 13px; height: 13px; margin-left: 4px; border: 1px solid var(--ink-300); border-radius: 50%; font-size: 9px; font-weight: 700; font-style: italic; line-height: 1; text-transform: none; color: var(--ink-500); cursor: help; vertical-align: middle; }
.agent-state-info:hover, .agent-state-info:focus-visible { border-color: var(--ink-500); color: var(--ink-700); outline: none; }

@keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.5); opacity: .55; } }

/* Timeline — LIGE's event-card STRUCTURE in Octopia's light tokens: the site
   is light-first (ADR-0005), so the cards stay on the light surface and the
   accent rail carries the event's state. */
#timeline-panel .card-body { flex: 1; min-height: 0; overflow-y: auto; }
.timeline { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.timeline-item {
  padding: 11px 12px; border-radius: var(--radius);
  background: var(--surface-sunken); border: 1px solid var(--line);
  border-left: 3px solid var(--blue-500);
}
.timeline-item.event-pipeline_error, .timeline-item.event-technical_block { border-left-color: var(--danger-600); }
.timeline-item.event-awaiting_human { border-left-color: var(--amber-600); }
.timeline-meta { display: flex; gap: 8px; align-items: baseline; }
.timeline-actor { font-weight: 600; font-size: 13px; }
.timeline-message { font-size: 13px; color: var(--ink-700); margin-top: 3px; }

/* Run panel */
.kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; margin: 0 0 12px; font-size: 13px; }
.kv dt { color: var(--ink-500); }
.kv dd { margin: 0; text-align: right; }
.kv .total { font-weight: 600; }

.notice { border-radius: var(--radius-sm); padding: 8px 10px; font-size: 12.5px; margin: 0 0 10px; }
.notice-warn { background: var(--amber-100); color: var(--amber-600); }
.notice-error { background: var(--danger-050); color: var(--danger-600); }

.run-badge-queued, .run-badge-running, .run-badge-publishing { background: var(--blue-100); color: var(--blue-700); }
.run-badge-ready_for_review, .run-badge-awaiting_human, .run-badge-ready_to_publish { background: var(--amber-100); color: var(--amber-600); }
.run-badge-published { background: var(--success-050); color: var(--success-600); }
.run-badge-completed { background: var(--success-050); color: var(--success-600); }
.run-badge-failed { background: var(--danger-050); color: var(--danger-600); }
.run-badge-cancelled { background: var(--surface-sunken); color: var(--ink-400); }

/* Sources */
.source-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--line); }
.source-row:first-child { padding-top: 0; }
.source-add { margin-top: 12px; display: grid; gap: 6px; }
.source-add label { font-size: 12px; font-weight: 600; color: var(--ink-700); }
.source-add input { border: 1px solid var(--line-strong); border-radius: var(--radius-sm); padding: 8px 10px; font: inherit; font-size: 13px; }

/* Artifacts (advanced) */
.artifact-json { margin: 0; padding: 10px 12px; background: var(--surface-sunken); font-family: var(--mono); font-size: 11px; max-height: 380px; overflow: auto; border-top: 1px solid var(--line); border-radius: var(--radius-sm); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Artifact report drawer (M4.5 phase A, ported from LIGE's
   dashboard/description.php right-hand drawer). One persistent shell:
   documents are moved into it, not re-rendered, so the reader always sees
   the same server output the JSON pane shows. */
.artifact-doc-head { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.artifact-doc-head h2 { margin: 0; font-size: 1.15rem; }
.artifact-doc-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.artifact-doc-actions .tab-btn.active { background: var(--blue-600); color: #fff; border-color: var(--blue-600); }
.artifact-doc-pane.hidden { display: none; }

/* The documents live in the page but are not laid out on it (owner,
   2026-08-31): they are read in the drawer, opened from wherever they are
   referenced. Opening one MOVES it out of `#artifact-docs`, which is what
   makes it visible; with no JavaScript the anchor still reveals the one it
   targets, so nothing on the page becomes unreachable. */
#artifact-docs > .artifact-doc { display: none; }
#artifact-docs > .artifact-doc:target { display: block; }

.artifact-drawer[hidden] { display: none; }
.artifact-drawer { position: fixed; inset: 0; z-index: 1000; display: flex; justify-content: flex-end; }
.artifact-drawer-overlay { position: absolute; inset: 0; background: rgba(16, 22, 20, .45); }
.artifact-drawer-panel {
  position: relative; width: min(760px, 96vw); height: 100%; overflow-y: auto;
  background: var(--surface); box-shadow: var(--shadow-lg); padding: 56px 24px 32px;
}
.artifact-drawer-close { position: absolute; top: 14px; right: 14px; }

/* ── Gates & editorial (M3) ─────────────────────────────────────────── */

/* Never let the grid's stretch balloon a short state (a compact "agent
   working" hero) up to the timeline's 50vh floor — only its own content
   decides its height, so only a genuinely long human question grows past
   50vh, and the timeline follows it (owner request 2026-08-31). */
.gate-panel { border-left: 2px solid var(--amber-600, #b98314); align-self: start; }
.gate-head h2 { color: var(--amber-700, #8a6210); }
/* The decision card carries its own, quieter head, and everything in it is
   centred on the question (owner, 2026-09-01): the gate's name is a label
   above the decision, not a title competing with it. It also claims its own
   50vh floor rather than sizing to its short content — the same floor the
   timeline column already has (owner request 2026-09-01) — so the row reads
   as one deliberate screen, not a tall history beside a cramped decision.
   The card body centres and spaces itself inside that floor, breathing more
   on a tall viewport (clamp) without spilling past a short one. */
.gate-head-decision { flex-direction: column; align-items: center; justify-content: center; gap: 2px; text-align: center; }
/* Le spécialiste qui pose la question, marqué d'un point d'interrogation dans
   son angle haut-droit (propriétaire, 2026-09-03) : on voit QUI demande avant
   de lire CE QU'il demande. */
.gate-avatar { position: relative; display: inline-block; line-height: 0; margin-bottom: 8px; }
.gate-avatar img {
  display: block; width: 72px; height: 72px; border-radius: 50%; object-fit: cover;
  background: var(--surface-sunken); box-shadow: 0 6px 18px rgba(16, 22, 20, .16);
}
.gate-avatar-mark {
  position: absolute; top: -3px; right: -3px; width: 26px; height: 26px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--amber-600); color: #fff; border: 2px solid var(--surface);
  font-size: 15px; font-weight: 800; line-height: 1;
}
.gate-head-decision h2 { color: var(--ink-900, #16232b); font-size: .95rem; }
.gate-panel:has(.gate-head-decision) { min-height: 50vh; }
.gate-panel:has(.gate-head-decision) .card-body {
  flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center;
  text-align: center; gap: clamp(14px, 2.5vh, 28px); padding-block: clamp(20px, 5vh, 56px);
}
.gate-question { font-size: 1.15rem; font-weight: 600; margin: 0; max-width: 46ch; }
#editorial .compare h3 { font-size: .82rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-500, #5b6b74); margin: 0 0 6px; }
.gate-list { margin: 0; padding-left: 18px; }
.gate-list li { margin-bottom: 4px; }
.gate-recommendation { color: var(--ink-500, #5b6b74); margin: 0; max-width: 50ch; }
/* Les deux verbes d'un blocage qualité, empilés, le moins cher d'abord : la
   relecture (gratuite, immédiate) puis la reprise à neuf (ADR-0086). Chaque
   bouton garde sa phrase sous lui. */
.qa-block-verbs { display: flex; flex-direction: column; gap: 14px; }
.qa-block-verbs form { margin: 0; }
.qa-block-verbs form p { margin: 6px 0 0; max-width: 62ch; }
.gate-binding { margin: 0; }
.gate-binding a { color: inherit; }
.gate-decision { margin: 0; }
.gate-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.gate-btn { padding: 13px 26px; font-size: 15px; }
.gate-btn-accept { background: var(--success-600, #1c7a4f); color: #fff; }
.gate-btn-reject { background: var(--surface-2, #eef2f4); color: var(--danger-600, #b3382e); }
/* Le plan catalogue en trois listes, et les rapports à ouvrir l'un sous
   l'autre, chacun à droite du portrait de son auteur (propriétaire,
   2026-09-03 ; ADR-0043) : les deux blocs se lisent à gauche, dans la
   colonne centrée de la décision. */
.gate-plan { display: grid; gap: 12px; width: 100%; max-width: 46ch; text-align: left; }
.gate-plan-group h3 { margin: 0 0 4px; font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-500, #5b6b74); }
.gate-plan-additions h3 { color: var(--success-600, #1c7a4f); }
.gate-plan-removals h3 { color: var(--danger-600, #b3382e); }
.gate-plan-group ul { margin: 0; padding-left: 18px; }
.gate-plan-group li { margin-bottom: 2px; }
.gate-reports { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; width: 100%; max-width: 46ch; }
.gate-report { display: flex; align-items: center; justify-content: flex-start; gap: 10px; width: 100%; text-align: left; }
.gate-report-portrait { flex: none; width: 26px; height: 26px; border-radius: 50%; object-fit: cover; background: var(--surface-sunken, #eef2f4); }
.gate-report-title { min-width: 0; }

/* ── "Who is working right now" hero (M4.5, owner 2026-08-29) ────────
   Ported from LIGE's `.desc-working-agent`: the panel that carries the human
   gate becomes, while the team works, a large animated card — rotating accent
   ring, breathing glow, full-height portrait, and the current task typed out.
   The ring's colour is the SPECIALIST's own accent (TeamRegistry), set inline
   as `--agent-accent` exactly as LIGE does it; gold is the fallback. */
.working-agent {
  --agent-accent: var(--amber-600);
  position: relative; isolation: isolate;
  /* `clip`, pas `hidden` : une boîte en overflow hidden reste un conteneur
     de défilement, et le premier scrollIntoView venu (le portrait actif de
     la bande de relais) la faisait défiler vers la gauche, anneau coloré à
     nu sur la droite (propriétaire, 2026-09-03). `hidden` reste en repli. */
  overflow: hidden; overflow: clip;
  border-left: 0; padding: 5px;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--agent-accent) 24%, transparent), var(--shadow);
  animation: agent-breathe 2.8s ease-in-out infinite;
}
.working-agent::before {
  content: ""; position: absolute; z-index: 0; inset: -95%;
  background: conic-gradient(from 0deg,
    color-mix(in srgb, var(--agent-accent) 70%, #fff) 0 8%,
    var(--agent-accent) 13% 24%,
    color-mix(in srgb, var(--agent-accent) 35%, #fff) 29% 36%,
    color-mix(in srgb, var(--agent-accent) 68%, #fff) 42% 52%,
    color-mix(in srgb, var(--agent-accent) 45%, #fff) 60% 70%,
    color-mix(in srgb, var(--agent-accent) 80%, #fff) 77% 86%,
    var(--agent-accent) 91% 100%);
  animation: agent-ring 2.8s linear infinite;
}
.working-agent::after {
  content: ""; position: absolute; z-index: 0; inset: 5px; border-radius: var(--radius);
  background: linear-gradient(145deg, color-mix(in srgb, var(--agent-accent) 6%, var(--surface)), var(--surface) 58%, color-mix(in srgb, var(--agent-accent) 9%, var(--surface)));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--agent-accent) 26%, transparent);
}
.working-agent > .card-head, .working-agent > .card-body { position: relative; z-index: 1; }
.working-agent .gate-head h2 { color: color-mix(in srgb, var(--agent-accent) 72%, var(--ink-900)); }

.working-hero { display: grid; grid-template-columns: minmax(150px, 34%) 1fr; align-items: center; gap: 22px; min-height: 250px; }
.working-portrait {
  display: block; width: min(100%, 300px); height: auto; aspect-ratio: 416 / 463;
  justify-self: center; border-radius: var(--radius-lg); object-fit: cover;
  background: var(--surface-sunken);
  box-shadow: 0 18px 38px color-mix(in srgb, var(--agent-accent) 26%, transparent);
}
.working-copy { display: grid; gap: 8px; align-content: center; min-width: 0; }
.working-kicker { font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: color-mix(in srgb, var(--agent-accent) 78%, var(--ink-700)); }
.working-name { margin: 0; font-size: clamp(2rem, 4vw, 3.7rem); line-height: .95; color: var(--ink-900); }
.working-role { margin: 0; font-size: .95rem; color: var(--ink-500); }
/* De quel pôle vient le spécialiste au travail (propriétaire, 2026-09-03) :
   son nom seul ne disait pas où l'on en est dans la chaîne. */
.working-pole {
  justify-self: start; margin: 2px 0 0; padding: 3px 10px; border-radius: 999px;
  font-size: .78rem; font-weight: 600; letter-spacing: .02em;
  color: color-mix(in srgb, var(--agent-accent) 82%, var(--ink-900));
  background: color-mix(in srgb, var(--agent-accent) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--agent-accent) 28%, var(--line));
}
.working-action { display: flex; align-items: flex-end; gap: 4px; min-height: 3.2em; margin: 10px 0 0; font-size: 1.05rem; line-height: 1.5; color: var(--ink-900); }
.working-cursor { display: inline-block; width: 3px; height: 1.25em; margin-bottom: .1em; border-radius: 2px; background: var(--agent-accent); animation: agent-caret 1s steps(1, end) infinite; }
/* La jauge du passage en cours (propriétaire, 2026-09-03) : juste sous
   l'action, sans libellé. Elle se remplit vers la durée médiane que ce step a
   mise les fois précédentes ; passé cette durée elle reste pleine et respire,
   parce qu'un passage plus long que d'habitude reste un passage en cours. */
/* Le compte d'images du passage media : une ligne sobre sous l'action. */
.working-count { margin: 6px 0 0; font-size: .9rem; color: var(--ink-500); }

.working-pace {
  height: 4px; margin: 2px 0 0; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--agent-accent) 14%, var(--surface-sunken));
}
.working-pace-bar {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--agent-accent) 55%, #fff), var(--agent-accent));
  transition: width .9s linear;
}
.working-pace.is-overrun .working-pace-bar { animation: working-pace-breathe 2.4s ease-in-out infinite; }
@keyframes working-pace-breathe {
  0%, 100% { opacity: 1; }
  50% { opacity: .42; }
}
@media (prefers-reduced-motion: reduce) {
  .working-pace-bar { transition: none; }
  .working-pace.is-overrun .working-pace-bar { animation: none; opacity: .6; }
}

/* L'avancement du run vit au pied de « Travail en cours » (propriétaire,
   2026-09-03) : il était en tête de « Votre équipe, en direct », loin du
   spécialiste dont on regarde le travail. */
.working-progress { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }
.working-progress p { margin: 6px 0 0; }

/* La bande de relais au pied du bloc (propriétaire, 2026-09-03) : une seule
   ligne, qui défile horizontalement plutôt que de passer à la ligne. Fait =
   pleine opacité, en cours = halo à l'accent du spécialiste, à venir = éteint. */
.relay-strip {
  display: flex; align-items: center; gap: 10px;
  margin-top: 14px; padding: 10px 2px 4px; border-top: 1px solid var(--line);
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  /* La barre de défilement reste fonctionnelle (au clavier, à la molette),
     seule sa piste visible disparaît (propriétaire, 2026-09-03) : la ligne se
     lit comme une rangée de portraits, pas comme un widget de scroll. */
  scrollbar-width: none; -ms-overflow-style: none;
}
.relay-strip::-webkit-scrollbar { display: none; }
.relay-seat { flex: 0 0 auto; line-height: 0; border-radius: 50%; }
.relay-seat img { display: block; width: 34px; height: 34px; border-radius: 50%; object-fit: cover; background: var(--surface-sunken); }
.relay-done { opacity: 1; }
.relay-upcoming { opacity: .38; filter: grayscale(.5); }
.relay-current {
  opacity: 1; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent, var(--amber-600));
  animation: relay-halo 2.2s ease-in-out infinite;
}
@keyframes relay-halo {
  0%, 100% { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent, var(--amber-600)); }
  50% { box-shadow: 0 0 0 2px var(--surface), 0 0 0 7px color-mix(in srgb, var(--accent, var(--amber-600)) 40%, transparent); }
}

/* The briefing — the seconds between "Lancer l'équipe" and the first stage
   (owner, 2026-09-01). The same hero as the working panel in a getting-ready
   key: a spinner instead of a typed sentence, and the order of play lighting
   up step by step so the wait shows the shape of the work rather than an empty
   card. Pure CSS; the list is complete without motion. */
.briefing-state { display: flex; align-items: center; gap: 9px; margin: 12px 0 0; font-size: 1.02rem; color: var(--ink-700); }
.briefing-spinner {
  width: 14px; height: 14px; flex-shrink: 0; border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--agent-accent) 28%, transparent);
  border-top-color: var(--agent-accent); animation: briefing-spin .9s linear infinite;
}
.briefing-steps { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 6px; }
.briefing-step {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  padding: 6px 10px; border-radius: var(--radius); background: var(--surface-sunken);
  opacity: .55;
  animation: briefing-step-in .45s ease both, briefing-step-glow 4.5s ease-in-out infinite;
  animation-delay: calc(var(--step) * .1s), calc(var(--step) * .9s);
}
.briefing-step-portrait { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; background: var(--surface); }
.briefing-step-label { font-weight: 600; color: var(--ink-900); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.briefing-step-name { margin-left: auto; white-space: nowrap; }

@keyframes briefing-spin { to { transform: rotate(1turn); } }
@keyframes briefing-step-in { from { opacity: 0; transform: translateY(5px); } to { opacity: .55; transform: none; } }
@keyframes briefing-step-glow {
  0%, 10% { opacity: 1; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--agent-accent) 45%, transparent); }
  26%, 100% { opacity: .55; box-shadow: none; }
}

@keyframes agent-ring { to { transform: rotate(1turn); } }
@keyframes agent-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
@keyframes agent-breathe {
  0%, 100% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--agent-accent) 24%, transparent), var(--shadow); }
  50% { box-shadow: 0 0 0 9px color-mix(in srgb, var(--agent-accent) 40%, transparent), var(--shadow-lg); }
}

@media (max-width: 720px) {
  /* Sur mobile, les logos de sources descendent sous le titre au lieu de le
     comprimer. */
  .page-head { flex-direction: column; align-items: stretch; }
  .page-head .source-links { flex-wrap: wrap; }
  .working-hero { grid-template-columns: 1fr; min-height: 0; }
  .working-portrait { width: min(100%, 320px); }
  .working-name { font-size: 2.2rem; }
}

@media (prefers-reduced-motion: reduce) {
  .btn-glow::after, .v2-btn-glow::after { animation: none; }
  .btn-glow, .v2-btn-glow { transition: none; }
  .working-agent, .working-agent::before { animation: none; }
  .working-cursor { display: none; }
  .briefing-spinner { animation: none; }
  .briefing-step { animation: none; opacity: 1; }
  .relay-current { animation: none; }
}

.editorial-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.tab-btn.active { background: var(--blue-600, #1c5fa1); color: #fff; }
.tab-btn.qa-fail, .tab-btn.qa-human_review_required { border-color: var(--danger-600, #b3382e); }
/* Un gros drapeau, rien d'autre, sur les onglets d'Éditorial (propriétaire, 2026-09-08). */
.tab-btn-flag { font-size: 22px; line-height: 1; padding: 6px 12px; }
.editorial-pane.hidden, .desc-report-pane.hidden { display: none; }
.compare { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

/* Le cadre rend la page du marchand, écrite pour une vitrine : à la largeur
   d'un panneau de comparaison son grid `col-md-*` s'effondre et l'avant/après
   ne compare plus rien. `zoom` élargit le viewport INTERNE de l'iframe — même
   boîte à l'écran, mais la page s'y croit plus large — là où un
   `transform: scale()` n'aurait fait que flouter.

   0.65 est mesuré, pas choisi : sous le zoom 1.2 de `.shell`, un cadre reçoit
   ~538 px sur une fenêtre de 1600, et 538 / 0.65 ≈ 828 px passent le point de
   rupture `col-md-*` (768 px) avec de la marge. La hauteur minimale suit la
   réduction (propriétaire, 2026-09-15). */
.preview-frame { width: 100%; min-height: 1100px; zoom: .65; border: 1px solid var(--line, #d8e0e4); border-radius: var(--radius-sm, 6px); background: #fff; }
/* The merchant's body is written for a storefront width: below it the
   `col-md-*` grid collapses and the comparison stops being meaningful. Side by
   side, captured and draft, now that `.content` no longer caps out at 1360px
   (owner request 2026-09-01) — a narrow viewport still stacks them full width
   rather than squeeze both panes under that floor. */
@media (max-width: 1200px) { .compare { grid-template-columns: 1fr; } }
.editor-details { margin-top: 10px; }
.body-editor { width: 100%; box-sizing: border-box; font-family: var(--mono, monospace); font-size: .82rem; border: 1px solid var(--line, #d8e0e4); border-radius: var(--radius-sm, 6px); padding: 10px; }
.editor-form { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }

.notice-success { background: #eef8f2; border: 1px solid #bfe3cf; color: #1e6b43; border-radius: var(--radius-sm, 6px); padding: 8px 12px; }

/* Localization card (M4): the languages produced, the routes actually proved,
   and the canonical debt the chain deliberately refused to repair. */
.locale-list { list-style: none; margin: 0 0 14px; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.locale-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; padding: 6px 10px; border-radius: var(--radius-sm, 6px); background: var(--surface-2, #f6f8f9); }
.locale-row-canonical { background: #eef8f2; border: 1px solid #bfe3cf; }
#localization h3 { font-size: .82rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-500, #5b6b74); margin: 14px 0 6px; }
#localization code { font-family: var(--mono, monospace); font-size: .8rem; background: var(--surface-2, #f6f8f9); border-radius: 4px; padding: 1px 4px; }
/* Bloc masqué à la demande du propriétaire (2026-09-08) — marqué en CSS, pas retiré du gabarit. */
#localization { display: none; }

/* ---------------------------------------------------------------- M4.5
   Workspace legibility: a decision brief above the fold, Milo's media
   gallery, typed artifact reports, and the specialists' portraits. */

.badge-amber { background: var(--amber-100); color: var(--amber-600); border-color: var(--amber-100); }

/* Decision brief — level 1 of the run report. */
.decision-brief .brief-list { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.brief-point { display: flex; align-items: baseline; gap: 9px; font-size: .92rem; color: var(--ink-700); }
.brief-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--success-600); flex-shrink: 0; }
.brief-point.tone-warn .brief-dot { background: var(--amber-600); }
.brief-point.tone-risk .brief-dot { background: var(--danger-600); }
.brief-point.tone-risk { color: var(--danger-600); font-weight: 600; }

/* Milo's media gallery. */
.media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px; margin-bottom: 12px; }
.media-card { margin: 0; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface); display: flex; flex-direction: column; }
.media-card img { display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: contain; background: var(--surface-sunken); }
.media-placeholder { display: grid; place-items: center; aspect-ratio: 1 / 1; background: var(--surface-sunken); color: var(--ink-400); font-size: .75rem; text-align: center; padding: 8px; }
.media-card figcaption { padding: 8px 10px 10px; display: flex; flex-direction: column; gap: 3px; }
.media-reason { color: var(--ink-700); }
.media-summary { color: var(--ink-500); }
.media-card.media-excluded img, .media-card.media-duplicate img, .media-card.media-failed img { opacity: .5; filter: grayscale(.6); }
.media-card.media-used { border-color: var(--success-600); box-shadow: 0 0 0 2px var(--success-050); }
.media-badge-used { background: var(--success-050); color: var(--success-600); border-color: color-mix(in srgb, var(--success-600) 30%, var(--success-050)); }
.media-badge-kept { background: var(--surface-sunken); color: var(--ink-500); }
.media-badge-excluded { background: var(--danger-050); color: var(--danger-600); border-color: color-mix(in srgb, var(--danger-600) 28%, var(--danger-050)); }
.media-badge-duplicate { background: var(--surface-sunken); color: var(--ink-400); }
.media-badge-failed { background: var(--danger-050); color: var(--danger-600); }

/* Canonical media library (M4.5): readiness badges, before/after crop pair and
   the analysis drawer behind each verdict. */
.media-readiness-editorial_ready_original { background: var(--blue-050); color: var(--blue-700); border-color: var(--blue-100); }
.media-readiness-editorial_ready_derivative { background: var(--blue-050); color: var(--blue-700); border-color: var(--blue-100); }
.media-readiness-cleanup_pending { background: var(--amber-100); color: var(--amber-600); }
.media-readiness-technical_evidence_only { background: var(--surface-sunken); color: var(--ink-500); }
.media-readiness-editorial_rejected { background: var(--danger-050); color: var(--danger-600); }
.media-crop-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; background: var(--line); }
.media-crop-pair > span { display: flex; flex-direction: column; background: var(--surface); }
.media-crop-pair > span > .small { padding: 3px 6px; text-align: center; }
.media-details { margin-top: 4px; }
.media-details > summary { cursor: pointer; color: var(--ink-500); }
.media-details dl { display: grid; grid-template-columns: minmax(90px, 40%) 1fr; gap: 1px 10px; margin: 6px 0 0; }
.media-details dt { color: var(--ink-500); }
.media-details dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* Artifacts — level 2 readable rendering above the level-3 raw JSON. */
.artifact-readable { padding: 8px 0 4px; }
.readable-kv { display: grid; grid-template-columns: minmax(120px, 220px) 1fr; gap: 2px 14px; margin: 0 0 4px; }
.readable-kv > dt { color: var(--ink-500); font-size: .78rem; text-transform: uppercase; letter-spacing: .03em; }
.readable-kv > dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.readable-list { list-style: disc; margin: 0; padding-left: 18px; }
.readable-list > li { margin: 2px 0; }
.artifact-raw { margin-top: 6px; }
@media (max-width: 720px) { .readable-kv { grid-template-columns: 1fr; } }

/* Portraits: the specialists' illustrated faces, the System actor a drawn mark. */
.timeline-actor { display: inline-flex; align-items: center; gap: 6px; }
.timeline-portrait { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.timeline-actor .portrait-mark { flex-shrink: 0; }
.timeline-artifact {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 8px;
  padding: 5px 10px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line-strong);
  color: var(--ink-700); font-weight: 600; text-decoration: none;
}
.timeline-artifact:hover { border-color: var(--blue-500); color: var(--blue-700); }

/* Typed artifact reports (M4.5): the three-mode drawer and its section kinds. */
.desc-report { margin-top: 8px; }
.desc-report-modes { display: flex; gap: 6px; margin-bottom: 8px; flex-wrap: wrap; }
.desc-report-pane { padding-top: 4px; }
.desc-report-section { margin: 0 0 14px; }
.desc-report-title { display: block; margin: 0 0 5px; font-size: .82rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-500); font-weight: 600; }
details.desc-report-section > summary.desc-report-title { cursor: pointer; }
details.desc-report-section[open] > summary.desc-report-title { margin-bottom: 7px; }
.desc-report-section p { margin: 0 0 6px; }
.desc-report-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 8px; }
.desc-report-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 8px 10px; background: var(--surface); }
.desc-report-card p { margin: 3px 0 0; color: var(--ink-700); white-space: pre-line; }
.desc-report-badges { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
.desc-report-arguments, .desc-report-plan { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 7px; }
.desc-report-findings, .desc-report-links { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.desc-report-table-scroll { overflow-x: auto; }
.desc-report-table { border-collapse: collapse; width: 100%; font-size: .82rem; }
.desc-report-table th, .desc-report-table td { border: 1px solid var(--line); padding: 4px 7px; text-align: left; vertical-align: top; }
.desc-report-table th { background: var(--surface-sunken); color: var(--ink-500); font-weight: 600; }
.desc-report-callout { border-left: 3px solid var(--line); padding: 6px 10px; background: var(--surface-sunken); border-radius: 0 var(--radius) var(--radius) 0; }
.desc-report-callout ul { margin: 0; padding-left: 18px; }
.desc-report-tone-ok { border-left-color: var(--blue-500); }
.desc-report-tone-warning { border-left-color: var(--amber-600); }
.desc-report-tone-info { border-left-color: var(--ink-400); }

/* M4.5 phase B: the Readable view is curated, so it says where the rest is,
   and a media ULID renders as the photo it names rather than as the ULID. */
.artifact-doc-hint { margin: 0 0 12px; padding-bottom: 8px; border-bottom: 1px solid var(--line); }
.desc-report-media { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px; }
.desc-report-media > li { display: flex; gap: 9px; align-items: flex-start; border: 1px solid var(--line); border-radius: var(--radius); padding: 8px; background: var(--surface); }
.desc-report-media img { width: 72px; height: 72px; object-fit: contain; flex: 0 0 auto; background: var(--surface-sunken); border-radius: var(--radius); }
.desc-report-media > li > div { min-width: 0; }

/* Reset zone — folded by default: destructive, typed on purpose. */
.reset-zone { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.reset-zone > summary { cursor: pointer; color: var(--danger-600); font-size: 12.5px; }
.reset-zone ul { margin: 6px 0 10px; padding-left: 18px; }
.reset-zone li { margin-bottom: 4px; }
.reset-form { display: grid; gap: 6px; }
.reset-form input[type=text] { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

/* Legal floor (ADR-0033): the links row under the guest forms, and the
   consent line above the registration button. */
.guest-legal { margin-top: 10px; display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; font-size: 12.5px; }
.guest-legal a { color: var(--ink-400); }
.legal-consent { margin: 0 0 14px; font-size: 12.5px; line-height: 1.45; color: var(--ink-500); }
.legal-consent a { color: var(--ink-700); }

/* ── Attente de l'analyse des consignes (page Règles, propriétaire
   2026-09-03) ─────────────────────────────────────────────────────────
   « Analyser mes consignes » part vers un job en file : une à deux minutes
   peuvent s'écouler entre le clic et la première proposition, et la simple
   ligne d'information qui les occupait se lisait comme une page figée. Ce
   panneau prend cette attente en charge — anneau, barre indéterminée, et les
   trois étapes qui s'allument l'une après l'autre. Il ne mesure rien : c'est
   un signe de vie, pas une progression, et il reste entièrement lisible sans
   animation (cf. le bloc prefers-reduced-motion plus bas). Autonome, sans
   --agent-accent : ce panneau vit hors de l'atelier. */
.rule-import-loading {
  --import-accent: var(--blue-600);
  margin: 4px 0 2px;
  padding: 20px 22px;
  border: 1px solid color-mix(in srgb, var(--import-accent) 22%, var(--line));
  border-radius: var(--radius);
  background: linear-gradient(160deg, color-mix(in srgb, var(--import-accent) 6%, var(--surface)), var(--surface) 62%);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--import-accent) 8%, transparent);
}
.rule-import-loading-head { display: flex; align-items: center; gap: 14px; }
.rule-import-spinner {
  width: 30px; height: 30px; flex-shrink: 0; border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--import-accent) 20%, transparent);
  border-top-color: var(--import-accent);
  animation: rule-import-spin .85s linear infinite;
}
.rule-import-loading-title { margin: 0; font-size: 1.05rem; font-weight: 700; color: var(--ink-900); }
.rule-import-loading-sub { margin: 2px 0 0; font-size: 13px; color: var(--ink-500); }

.rule-import-bar {
  position: relative; overflow: hidden;
  height: 5px; margin-top: 16px; border-radius: 999px;
  background: color-mix(in srgb, var(--import-accent) 14%, var(--surface-sunken));
}
.rule-import-bar > span {
  position: absolute; top: 0; bottom: 0; left: 0; width: 38%; border-radius: 999px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--import-accent) 25%, transparent),
    var(--import-accent) 55%,
    color-mix(in srgb, var(--import-accent) 25%, transparent));
  animation: rule-import-sweep 1.7s cubic-bezier(.55, 0, .45, 1) infinite;
}

.rule-import-steps { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 6px; }
.rule-import-step {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; border-radius: var(--radius-sm);
  font-size: 14px; color: var(--ink-700);
  background: var(--surface-sunken);
  opacity: .55;
  animation: rule-import-step-in .4s ease both, rule-import-step-glow 4.2s ease-in-out infinite;
  animation-delay: calc(var(--step) * .1s), calc(var(--step) * 1.4s);
}
.rule-import-step-dot {
  width: 8px; height: 8px; flex-shrink: 0; border-radius: 50%;
  background: var(--import-accent);
}

@keyframes rule-import-spin { to { transform: rotate(1turn); } }
@keyframes rule-import-sweep {
  0% { left: -40%; }
  100% { left: 100%; }
}
@keyframes rule-import-step-in { from { opacity: 0; transform: translateY(4px); } to { opacity: .55; transform: none; } }
@keyframes rule-import-step-glow {
  0%, 12% { opacity: 1; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--import-accent) 40%, transparent); }
  30%, 100% { opacity: .55; box-shadow: none; }
}

@media (prefers-reduced-motion: reduce) {
  .rule-import-spinner { animation: none; }
  .rule-import-bar > span { animation: none; width: 100%; }
  .rule-import-step { animation: none; opacity: 1; }
}


/* --- Publication band (ADR-0047) ------------------------------------- */
.publication-panel .notice-info { background: var(--blue-050, #eef4fb); border: 1px solid var(--blue-100, #cfe0f5); color: var(--blue-700, #1f4f8f); border-radius: var(--radius-sm, 6px); padding: 8px 12px; }
.publication-live { display: flex; align-items: center; gap: 10px; }
.publication-reason { margin: 8px 0 0; font-size: 14px; }
.publication-links { display: flex; flex-wrap: wrap; gap: 10px; margin: 12px 0 4px; }
.publication-checkpoints-title { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; font-size: 14px; margin: 16px 0 6px; }
.publication-checkpoints { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.publication-checkpoint { display: grid; grid-template-columns: 12px 1fr auto; align-items: center; gap: 8px; font-size: 13px; padding: 5px 8px; border-radius: var(--radius-sm, 6px); background: var(--surface-2, #f5f7f9); }
.publication-checkpoint .cp-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--ink-300, #b8c0c8); }
.publication-checkpoint.cp-verified .cp-dot, .publication-checkpoint.cp-rolled_back .cp-dot { background: var(--success-600, #1c7a4f); }
.publication-checkpoint.cp-write_started .cp-dot, .publication-checkpoint.cp-written .cp-dot, .publication-checkpoint.cp-upload_started .cp-dot, .publication-checkpoint.cp-uploaded .cp-dot, .publication-checkpoint.cp-ready .cp-dot { background: var(--amber-600, #b98314); animation: octopia-pulse 1.2s ease-in-out infinite; }
.publication-checkpoint.cp-failed .cp-dot, .publication-checkpoint.cp-rollback_blocked .cp-dot { background: var(--danger-600, #b3382e); }
.publication-checkpoint .cp-status { color: var(--ink-500, #5f6b76); font-size: 12px; white-space: nowrap; }
.publication-backup { margin: 8px 0 0; }
.publication-actions { margin-top: 16px; }
.publication-hint { text-align: center; margin: 8px 0 0; }
.publication-perimeter .gate-plan-group h3 { font-size: 13px; }

/* Les langues en drapeaux, sur une ligne (propriétaire, 2026-09-08). La
   locale reste dans l'infobulle et pour les lecteurs d'écran ; une locale
   sans drapeau déclaré s'affiche en toutes lettres et doit donc rester
   lisible dans la même bande. */
/* `.gate-plan-group ul` indents every list in the panel; this one is a row of
   flags, so the selector has to outrank it rather than hope. */
.publication-perimeter .gate-plan-group ul.publication-locales {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    margin: 0; padding: 0; list-style: none;
}
.publication-perimeter .publication-locale { margin: 0; font-size: 20px; line-height: 1; cursor: default; }
.publication-perimeter .publication-locale.is-primary { position: relative; }
.publication-perimeter .publication-locale.is-primary::after {
    content: ''; position: absolute; left: 50%; bottom: -4px; transform: translateX(-50%);
    width: 12px; height: 2px; border-radius: 2px; background: var(--brand-600, #1f6f8b);
}
.publication-perimeter .publication-locale.is-skipped { opacity: .35; filter: grayscale(1); }

/* La table de spécifications : ce qu'Octopia ajoute, ce qui reste au
   marchand. L'action est un emoji ; le mot vit dans .sr-only. */
/* `table-layout: fixed` is what stops one long value widening the whole
   table: the columns are sized by the header row, and a value that does not
   fit wraps inside its own cell instead of pushing the panel sideways. */
.publication-characteristics {
    width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 13px;
    border: 1px solid var(--line-200, #e6ecef); border-radius: 4px;
}
.publication-characteristics th,
.publication-characteristics td {
    text-align: left; padding: 5px 8px; vertical-align: top;
    border: 1px solid var(--line-200, #e6ecef);
}
.publication-characteristics th { font-weight: 600; color: var(--ink-500, #5b6b74); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
.publication-characteristics th:nth-child(1), .publication-characteristics td:nth-child(1) { width: 44%; }
.publication-characteristics th:nth-child(3), .publication-characteristics td:nth-child(3) { width: 44px; }
.publication-characteristics td:nth-child(2) { color: var(--ink-700, #33474f); overflow-wrap: anywhere; }
.publication-characteristics .characteristic-action { white-space: nowrap; text-align: center; font-size: 15px; }
.publication-characteristics .characteristic-keep td { opacity: .6; }
@keyframes octopia-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* ── Media review dialog (ADR-0048) ─────────────────────────────────
   Milo's images grouped as he proposed them — accepted, refused, to clean
   — in a near-fullscreen dialog over the workspace. One click moves a card
   to its new group; the gate's own form validates the lot. Sized in vh/vw:
   `.shell` zooms its interior, and only viewport units resolve against the
   real viewport inside it. */
.media-review[hidden] { display: none; }
.media-review [hidden] { display: none !important; }
.media-review { position: fixed; inset: 0; z-index: 1001; display: flex; align-items: center; justify-content: center; }
.media-review-overlay { position: absolute; inset: 0; background: rgba(16, 22, 20, .55); }
/* Anchored to the viewport's edges, never sized in vh: inside `.shell`'s
   zoom — 1.2 at the time — a 94vh panel measured 113 % of the screen and lost
   its head and its foot (owner, 2026-09-03). `inset` on a fixed ancestor stays
   in real viewport pixels whatever the zoom, so this holds at any value the
   shell takes. */
.media-review-panel {
  position: absolute; inset: 20px; margin: 0 auto; max-width: 1480px;
  display: flex; flex-direction: column;
  background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); overflow: hidden;
}
.media-review-head { display: flex; align-items: center; gap: 16px; padding: 16px 24px; border-bottom: 1px solid var(--line); }
.media-review-head .gate-avatar { margin: 0; flex: none; }
.media-review-heading { min-width: 0; }
.media-review-heading h2 { font-size: 1.15rem; }
.media-review-lead { margin: 4px 0 0; color: var(--ink-500); font-size: 13.5px; max-width: 80ch; }
.media-review-close { margin-left: auto; flex: none; }
.media-review-body { flex: 1; min-height: 0; overflow-y: auto; padding: 18px 24px 24px; display: grid; gap: 24px; align-content: start; }
.media-review-group h3 { display: flex; align-items: center; gap: 10px; margin: 0 0 2px; font-size: 1rem; }
.media-review-group-count { font-size: 12px; }
.media-review-group-accepted h3 { color: var(--success-600); }
.media-review-group-rejected h3 { color: var(--danger-600); }
.media-review-group-cleanup h3 { color: var(--amber-600); }
.media-review-group-hint { margin: 0 0 10px; color: var(--ink-500); font-size: 13px; }
.media-review-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 12px; }
.media-review-card {
  margin: 0; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  background: var(--surface); display: flex; flex-direction: column;
}
.media-review-card img { display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: contain; background: var(--surface-sunken); }
.media-review-card .media-crop-pair img { aspect-ratio: 1 / 1; }
.media-review-card figcaption { padding: 8px 10px 10px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
.media-review-badges { display: flex; flex-wrap: wrap; gap: 4px; }
.media-review-overlays { color: var(--ink-700); }
.media-review-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: auto; padding-top: 6px; }
.media-review-group-rejected .media-review-card img { opacity: .6; filter: grayscale(.5); }
.media-review-card.is-adjusted { border-color: var(--blue-500); box-shadow: 0 0 0 2px var(--blue-050); }
.media-review-adjusted { background: var(--blue-050); color: var(--blue-700); border-color: var(--blue-100); }
.media-review-empty { margin: 0; color: var(--ink-400); font-size: 13px; }
.media-review-foot { display: flex; align-items: center; gap: 14px; padding: 12px 24px; border-top: 1px solid var(--line); background: var(--surface); }
.media-review-note { margin: 0; }
.media-review-foot .gate-actions { margin-left: auto; }
html.media-review-open { overflow: hidden; }
/* The panel's summary of Milo's proposal, above the two verbs. */
.gate-media-summary { text-align: center; justify-items: center; }
.gate-media-counts { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 18px; }
.gate-media-count { display: flex; flex-direction: column; align-items: center; font-size: 13px; color: var(--ink-500); }
.gate-media-count strong { font-size: 1.4rem; line-height: 1.1; color: var(--ink-900); }
.gate-media-count-accepted strong { color: var(--success-600); }
.gate-media-count-rejected strong { color: var(--danger-600); }
.gate-media-count-cleanup strong { color: var(--amber-600); }
.gate-media-open { justify-self: center; }
@media (max-width: 720px) {
  .media-review-panel { inset: 0; border-radius: 0; }
  /* Le cadre haut (portrait + titre + phrase d'aide) rendait à peine les
     images visibles sur téléphone (propriétaire, 2026-09-06) : portrait et
     textes rétrécissent, la phrase d'aide se limite à deux lignes au lieu
     de s'étaler. */
  .media-review-head { flex-wrap: wrap; gap: 10px; padding: 10px 14px; }
  .media-review-head .gate-avatar { margin-bottom: 0; }
  .media-review-head .gate-avatar img { width: 40px; height: 40px; }
  .media-review-head .gate-avatar-mark { width: 16px; height: 16px; font-size: 10px; }
  .media-review-heading h2 { font-size: .95rem; }
  .media-review-lead {
    font-size: 12px;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .media-review-body { padding: 12px 14px 16px; gap: 16px; }
  .media-review-grid { grid-template-columns: repeat(auto-fill, minmax(135px, 1fr)); gap: 8px; }
  /* Le texte descriptif sous chaque image (résumé, provenance, dimensions)
     prenait plus de place que l'image elle-même : le résumé se borne à deux
     lignes, la ligne de provenance/dimensions disparaît (elle reste dans le
     tiroir de détail au clic sur l'image). */
  .media-review-card figcaption { padding: 6px 8px 8px; gap: 3px; }
  .media-review-card .media-summary {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .media-review-card figcaption > .muted.small { display: none; }
  /* Le bouton « Valider la sélection » se compressait jusqu'à passer son
     libellé sur trois lignes, faute de largeur — pied de dialogue empilé,
     bouton pleine largeur. */
  .media-review-foot { flex-direction: column; align-items: stretch; gap: 8px; padding: 10px 14px; }
  .media-review-foot .media-review-note { text-align: center; }
  .media-review-foot .gate-actions { margin-left: 0; width: 100%; }
  .media-review-foot .gate-btn { width: 100%; padding: 11px 16px; font-size: 14px; }
}
/* The merchant's own word on a library card (ADR-0048). */
.media-verdict { background: var(--blue-050); color: var(--blue-700); border-color: var(--blue-100); }
.media-verdict-reject { background: var(--danger-050); color: var(--danger-600); border-color: color-mix(in srgb, var(--danger-600) 28%, var(--danger-050)); }

/* What a product read from its public address does not get (ADR-0083). Same
   rules as the demonstration page's own list in pages.css: the workspace and
   that page show the identical sentences, so they read identically. Repeated
   rather than shared because the two layouts load different stylesheets, and
   pulling pages.css into the workspace would drag a whole public-site theme
   in for seven declarations. */
.pv-limits { list-style: none; margin: 8px 0 4px; padding: 0; text-align: left; }
.pv-limit { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; border-top: 1px solid var(--line); }
.pv-limit:first-child { border-top: 0; }
.pv-limit-portrait { width: 34px; height: 34px; border-radius: 50%; border: 2px solid var(--line); flex: none; object-fit: cover; }
.pv-limit-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pv-limit-would { color: var(--ink-900); line-height: 1.5; }

/* The two ways into the catalog, side by side on the list screen's header. */
.page-head-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
