/*
  Atlas Bassignac — feuille de style
  Direction : bibliothèque éditoriale sombre. La mangue Bassignac (sceau rond,
  /assets/mango.png) sert de marque, utilisée avec parcimonie (login + topbar).
  Trois rôles typographiques, comme un vrai catalogue de bibliothèque :
    - sans display (var(--font-display), graisse forte, léger resserrement)  → titres "Atlas", titres de document, titres de carte — RÈGLE DE MARQUE : jamais de serif chez Bassignac
    - sans système (var(--font-sans))       → interface, boutons, recherche
    - mono système (var(--font-mono))       → étiquettes de collection, fil d'Ariane, compteur de pages
  Accent : ambre mangue (var(--accent)) — jamais en aplat large, seulement
  liserés, focus, hover, badges. Zéro framework, zéro import externe.
*/

:root {
  color-scheme: dark;
  --bg: #14110d;
  --surface: #1d1912;
  --surface-2: #262019;
  --surface-translucent: rgba(29, 25, 18, 0.88);
  --text: #f2ead9;
  --muted: #a89a80;
  --accent: #e8973a;
  --accent-soft: rgba(232, 151, 58, 0.16);
  --accent-border: rgba(232, 151, 58, 0.35);
  --danger: #d1543a;
  --border: rgba(242, 234, 217, 0.08);
  --shadow: rgba(0, 0, 0, 0.45);
  --radius: 8px;
  --radius-sm: 4px;

  --font-display: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", "Helvetica Neue", Arial, sans-serif;
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

/* ---------- reset léger ---------- */
*, *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; }
h1, h2, h3, p, ul { margin: 0; padding: 0; }
ul { list-style: none; }
a { color: inherit; text-decoration: none; }
button, input { font: inherit; color: inherit; background: none; border: 0; }
/* tue le délai de 350 ms au double-tap iOS sur tout élément interactif */
a, button, #suggest li { touch-action: manipulation; }
/* « chrome » (navigation/contrôles) non sélectionnable au tap-and-hold mobile —
   volontairement PAS appliqué au panneau d'infos du lecteur (texte OCR à copier) */
.topbar, .reader-bar, .nav, .card, .chip, .result, .result-group-header,
#suggest, .view > button {
  -webkit-user-select: none;
  user-select: none;
}
img {
  max-width: 100%;
  display: block;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none; /* anti-vol §7 : pas de menu "enregistrer l'image" au tap long iOS */
}

/* préchargement image (imgstore.js) : fondu doux au moment où le src bascule
   du pixel transparent vers l'image réelle (cache mémoire ou fetch signé) */
img[data-page] {
  opacity: 0;
  transition: opacity .25s ease;
}
img[data-page].img-loaded { opacity: 1; }

::selection { background: var(--accent); color: var(--bg); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- squelettes de chargement (P1-4, P2-3) : shimmer commun ---------- */
/* P2-3 (revue) — bug racine découvert en mesurant l'écart de hauteur du panneau token : TOUS les
   squelettes de l'app sont des <span> (jamais des <div>) pour rester de simples marqueurs inertes
   dans le flux ; sans `display:block`, un <span> non remplacé IGNORE width/height/aspect-ratio
   (spec CSS) et s'effondre en boîte 0×0 — un rectangle de fond invisible plutôt qu'une barre de
   shimmer. Vérifié empiriquement (Playwright) : `.token-panel-skel-title` mesurait 0×0 malgré
   `height:18px` dans son style calculé. `display:block` sur la classe commune corrige d'un coup
   TOUS les squelettes existants (accueil, grille document, panneau token) et tout futur usage.
   Sans effet sur les squelettes déjà enfants directs d'un conteneur flex/grid (.token-panel-
   skel-btn dans .token-panel-skel-actions, .skel-card/.skel-title-bar au sein de .skel-grid) : un
   item flex/grid est de toute façon "blockifié" par la cascade CSS indépendamment de son display
   déclaré — ceux-là fonctionnaient déjà, ce correctif comble le reste (barres seules dans un bloc
   plein, ex. les barres de titre/sous-titre/tags du panneau token, enfants directs non-flex). */
.skeleton {
  display: block;
  background: linear-gradient(100deg, var(--surface-2) 40%, #2e2620 50%, var(--surface-2) 60%);
  background-size: 200% 100%;
  animation: skeleton-slide 1.2s ease-in-out infinite;
  border-radius: var(--radius-sm);
}
@keyframes skeleton-slide { to { background-position: -200% 0; } }
.skel-header-bar { width: 140px; height: 12px; margin-bottom: 16px; }
.skel-card { aspect-ratio: 2 / 3; border-radius: var(--radius); margin-bottom: 8px; }
.skel-title-bar { width: 70%; height: 10px; }
.skel-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 16px; }
@media (min-width: 768px) {
  .skel-grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 20px; }
}
@media (max-width: 480px) {
  .skel-grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 10px; }
}

/* ---------- transitions de vue (P2-11) : chorégraphie d'entrée du contenu ---------- */
.view-enter { animation: view-in 180ms cubic-bezier(0.2, 0.8, 0.2, 1) both; }
@keyframes view-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.grid .card { animation: view-in 200ms cubic-bezier(0.2, 0.8, 0.2, 1) both; animation-delay: calc(var(--i, 0) * 22ms); }

/* ---------- carte quittée (P3-9) : anneau de repère au retour depuis le lecteur ---------- */
.card.flash-return {
  animation: card-flash-ring 800ms ease-out;
}
@keyframes card-flash-ring {
  from { box-shadow: 0 0 0 2px var(--accent-border); }
  to { box-shadow: 0 0 0 2px transparent; }
}

/* ---------- body ---------- */
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  min-height: 100vh;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
}

/* ---------- login ---------- */
.login-page {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: max(24px, env(safe-area-inset-top)) max(24px, env(safe-area-inset-right))
    max(24px, env(safe-area-inset-bottom)) max(24px, env(safe-area-inset-left));
  background:
    radial-gradient(ellipse 640px 420px at 50% 8%, var(--accent-soft), transparent 70%),
    var(--bg);
}

.login-card {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.login-card .seal {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  margin-bottom: 22px;
  box-shadow: 0 10px 32px var(--shadow);
}

.login-card .brand {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

.login-card h1 {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  font-weight: 500;
  font-size: clamp(48px, 14vw, 64px);
  line-height: 1;
  margin: 8px 0 16px;
  color: var(--text);
}

.login-card .tagline {
  font-size: 15px;
  color: var(--muted);
  max-width: 26ch;
  margin-bottom: 36px;
  text-wrap: balance;
}

.btn-google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 28px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-size: 15px;
  font-weight: 600;
  transition: background-color .15s ease, color .15s ease;
}
.btn-google:hover, .btn-google:focus-visible {
  background: var(--accent);
  color: var(--bg);
}
.btn-google svg { flex-shrink: 0; }

.login-error {
  margin-top: 20px;
  font-size: 13px;
  color: var(--danger);
}

/* ---------- login : entrée séquencée (P3-3) ---------- */
.login-anim {
  opacity: 0;
  animation: view-in 400ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.login-card .seal.login-anim { animation-delay: 0ms; }
.login-card .brand.login-anim { animation-delay: 60ms; }
.login-card h1.login-anim { animation-delay: 120ms; }
.login-card .tagline.login-anim { animation-delay: 180ms; }
.btn-google.login-anim { animation-delay: 240ms; }

/* ---------- topbar ---------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: calc(12px + env(safe-area-inset-top)) calc(20px + env(safe-area-inset-right)) 12px
    calc(20px + env(safe-area-inset-left));
  background: var(--surface-translucent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}

.topbar .brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  font-size: 20px;
  color: var(--text);
  white-space: nowrap;
}
.topbar .brand .mark {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
}
.topbar .brand span {
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.searchbox {
  position: relative;
  flex: 1 1 auto;
  max-width: 480px;
  margin: 0 auto;
}
#search {
  width: 100%;
  height: 40px;
  padding: 0 32px 0 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 14px;
  transition: border-color .15s ease, box-shadow .15s ease;
  /* P1-6 : masque le ✕ natif (non thémé, rendu clair) — remplacé par .search-clear maison */
  -webkit-appearance: none;
  appearance: none;
}
#search::placeholder { color: var(--muted); }
#search::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
#search:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.search-clear {
  position: absolute;
  top: 0;
  right: 0;
  width: 40px;
  height: 40px;
  display: none;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  transition: color .15s ease;
}
.search-clear.is-visible { display: inline-flex; }
.search-clear:hover, .search-clear:focus-visible { color: var(--accent); }
#suggest {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  max-height: min(50dvh, 360px);
  overflow: hidden auto; /* x: hidden (coins arrondis des <li> au survol) ; y: auto (scroll) */
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 12px 32px var(--shadow);
  z-index: 30;
}
#suggest li {
  padding: 10px 14px;
  font-size: 14px;
  cursor: pointer;
  transition: background-color .1s ease, color .1s ease;
}
#suggest li mark {
  background: none;
  color: inherit;
  font-weight: 700;
  padding: 0;
}
#suggest li:hover, #suggest li:focus, #suggest li.active {
  background: var(--accent-soft);
  color: var(--accent);
}

.topbar .user {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.topbar .user a,
.topbar .user button.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 44px;
  min-height: 44px;
  padding: 0 10px;
  border-radius: var(--radius-sm);
  font-size: 15px;
  color: var(--muted);
  transition: color .15s ease, background-color .15s ease;
}
.topbar .user a:hover, .topbar .user a:focus-visible,
.topbar .user button.icon-btn:hover, .topbar .user button.icon-btn:focus-visible {
  color: var(--accent);
  background: var(--accent-soft);
}
.topbar .user button.icon-btn svg { flex-shrink: 0; }
.topbar .user [data-account-trigger] {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.03em;
}

/* Sortie vers le choix des applications du studio. La grille de neuf points est
   la convention établie pour « changer d'application » : elle se comprend sans
   son libellé, qui disparaît donc quand la place manque. */
.topbar .user .app-switch {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.03em;
  color: var(--muted);
}
.topbar .user .app-switch:hover,
.topbar .user .app-switch:focus-visible {
  color: var(--accent);
  background: var(--accent-soft);
}
@media (max-width: 860px) {
  .topbar .user .app-switch span { display: none; }
}

/* ---------- menu compte (P2-6) : popover ancré sous le nom ---------- */
.account-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: 180px;
  padding: 6px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 12px 32px var(--shadow);
  z-index: 30;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity .16s ease, transform .16s ease;
  pointer-events: none;
}
.account-menu.is-open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.account-menu a, .account-menu button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 40px;
  padding: 0 10px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--text);
  text-align: left;
  transition: background-color .12s ease, color .12s ease;
}
.account-menu a:hover, .account-menu a:focus-visible,
.account-menu button:hover, .account-menu button:focus-visible {
  background: var(--accent-soft);
  color: var(--accent);
}
.account-menu hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 6px 2px;
}
.account-menu svg { flex-shrink: 0; }

/* ≤640px : passage en deux rangées — marque + compte sur la première (pour
   rester utilisable à une main, le bouton de compte reste atteignable du
   pouce sans redescendre le regard tout en bas), recherche pleine largeur
   sur la seconde, jamais compressée entre les deux. */
@media (max-width: 640px) {
  .topbar { flex-wrap: wrap; row-gap: 8px; }
  .topbar .brand { order: 1; }
  .topbar .user { order: 2; margin-left: auto; }
  .searchbox { order: 3; flex: 1 1 100%; max-width: none; margin: 0; }
}

@media (max-width: 480px) {
  .topbar {
    gap: 10px;
    padding: calc(10px + env(safe-area-inset-top)) calc(12px + env(safe-area-inset-right)) 10px
      calc(12px + env(safe-area-inset-left));
  }
  .topbar .brand span { display: none; }
}

/* ---------- vue principale ---------- */
.view {
  max-width: 1200px;
  margin: 0 auto;
  padding: 28px 20px 80px;
}

.crumbs {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 10px;
}
.crumbs a { color: var(--muted); transition: color .15s ease; }
.crumbs a:hover, .crumbs a:focus-visible { color: var(--accent); }

.view > h1 {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  font-weight: 500;
  font-size: clamp(28px, 5vw, 40px);
  color: var(--text);
  margin-bottom: 24px;
}

.collection { margin-bottom: 40px; }
.collection h2 {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 16px;
}
.collection h2::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}
.collection h2::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ---------- grille & cartes ---------- */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 16px;
}
@media (min-width: 768px) {
  .grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 20px; }
}
/* ≤480px (iPhone SE 375px, 360×740, 390×844…) : minmax(160px) ferait retomber
   à 1 colonne sous ~372px de large. 132px + gap resserré garantit 2 colonnes
   dès 360px de viewport (marge intérieure .view réduite en parallèle). */
@media (max-width: 480px) {
  .view { padding: 20px 12px 60px; }
  .grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 10px; }
}

.card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), border-color 220ms cubic-bezier(0.2, 0.8, 0.2, 1),
    box-shadow 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.card:hover, .card:focus-visible {
  border-color: var(--accent-border);
  transform: translateY(-2px);
  box-shadow: 0 14px 30px var(--shadow);
}
.card img,
.card-empty {
  width: 100%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  background: var(--surface-2);
  transition: transform 400ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.card:hover img { transform: scale(1.03); }
.card-empty { display: flex; align-items: center; justify-content: center; }
.card-empty::after {
  content: '';
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px dashed var(--border);
}
.card-title {
  padding: 10px 12px 0;
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  font-size: 15px;
  line-height: 1.3;
  color: var(--text);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.card-count {
  align-self: flex-start;
  margin: 6px 12px 12px;
  padding: 2px 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.03em;
  color: var(--accent);
  border: 1px solid var(--accent-border);
  border-radius: 999px;
}

.empty {
  padding: 60px 20px;
  text-align: center;
  color: var(--muted);
  font-size: 15px;
}

/* ---------- lecteur (plein écran ; contenu ajouté en T14) ---------- */
.reader {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  height: 100vh;
  height: 100dvh; /* barre d'outils Safari iOS qui apparaît/disparaît : pas de saut de mise en page */
  overscroll-behavior: none; /* pas de rebond de scroll qui laisse entrevoir la page derrière */
}
.reader[hidden] { display: none; }

.reader-bar {
  position: relative;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: calc(10px + env(safe-area-inset-top)) calc(14px + env(safe-area-inset-right)) 10px
    calc(14px + env(safe-area-inset-left));
  background: var(--surface-translucent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.reader-bar button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  min-width: 44px;
  min-height: 44px;
  border-radius: var(--radius-sm);
  font-size: 18px;
  color: var(--muted);
  transition: color .15s ease, background-color .15s ease;
}
.reader-bar button:hover, .reader-bar button:focus-visible {
  color: var(--accent);
  background: var(--accent-soft);
}
/* P3-6 : état "ouvert" du bouton ℹ tant que le panneau d'infos est visible */
.reader-bar button[data-info].active {
  color: var(--accent);
  background: var(--accent-soft);
}
.reader-bar button svg { flex-shrink: 0; }
.reader-title {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--muted);
}
/* le titre du document tronque (…) s'il est trop long — le compteur de page,
   lui, ne doit JAMAIS disparaître derrière l'ellipse (repère de navigation) */
.reader-title-doc {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.reader-title-count {
  flex-shrink: 0;
  color: var(--accent);
}
/* P2-1c : pastille « N tokens » à côté du compteur de pages — découvrabilité de la feature */
.reader-title-tokens {
  flex-shrink: 0;
  font-size: 11px;
  color: var(--accent);
  border: 1px solid var(--accent-border);
  border-radius: 999px;
  padding: 1px 8px;
}
.reader-title-tokens[hidden] { display: none; }
@media (max-width: 480px) {
  .reader-title { font-size: 11px; gap: 4px; }
}

.stage {
  position: relative;
  z-index: 1;
  flex: 1;
  /* P1-1 : cellule de grille explicite 100%/100% — sans ça la rangée implicite se dimensionne
     sur le contenu (circularité), max-width/max-height:100% de .stage-img ne se résout jamais,
     l'image se rend à sa taille intrinsèque et overflow:hidden ampute la planche (fix validé
     empiriquement, capture 40 de la critique). */
  grid-template: 100% / 100%;
  display: grid;
  place-items: center;
  overflow: hidden;
  touch-action: none;
  background: var(--bg);
}
/* P1-3 : deux <img> superposées dans la même cellule de grille (grid-column/row explicites,
   sinon l'auto-placement les empilerait dans des rangées implicites distinctes) pour crossfader
   d'une page à l'autre sans repasser par le noir. */
.stage-img {
  grid-column: 1;
  grid-row: 1;
  max-width: 100%;
  max-height: 100%;
  will-change: transform, opacity;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  opacity: 0;
  transition: opacity 180ms ease-out;
}
.stage-img.is-active { opacity: 1; }
/* volontairement PAS d'attribut data-page sur .stage-img (viewer.js) : le fondu générique
   img[data-page].img-loaded (couvertures/vignettes, règle globale ci-dessus) ne doit jamais
   matcher les images du lecteur — .is-active pilote seule leur opacité (crossfade P1-3). */

/* P1-3 (point 2) : pulse d'attente si la résolution dépasse 300 ms (réseau lent) */
.stage-pulse {
  position: absolute;
  z-index: 2;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  animation: stage-pulse 1s ease-in-out infinite;
  pointer-events: none;
}
@keyframes stage-pulse {
  0%, 100% { opacity: .3; transform: scale(.8); }
  50% { opacity: 1; transform: scale(1.15); }
}

/* Couche de zones tactiles des tokens (spec §14) : positionnée/transformée en JS (viewer.js
   layoutTokenZones()/apply()) pour épouser exactement le rectangle rendu de l'image et suivre
   le pan/zoom. Entièrement invisible et inerte au pointer — la détection de tap est manuelle
   (getBoundingClientRect), jamais un clic/tap natif sur ces éléments (ne doit JAMAIS intercepter
   un geste de pan/pinch/swipe destiné à .stage). */
.token-zones {
  position: absolute;
  left: 0;
  top: 0;
  grid-column: 1;
  grid-row: 1;
  pointer-events: none;
  will-change: transform;
}
.token-zone {
  position: absolute;
  pointer-events: none;
  border-radius: 6px;
}
/* P2-1a : pulse d'annonce unique par planche vue (Set en module scope dans viewer.js) */
.token-zones.discover .token-zone {
  animation: token-zone-announce 900ms ease-out;
}
@keyframes token-zone-announce {
  0% { box-shadow: inset 0 0 0 2px var(--accent-border); opacity: 0; }
  30% { opacity: 1; }
  100% { box-shadow: inset 0 0 0 2px var(--accent-border); opacity: 0; }
}
/* P2-1b : survol desktop — la zone hitTest-ée reçoit un liseré, .stage un curseur pointer */
.stage.hovering-zone { cursor: pointer; }
.token-zone.is-hovered {
  outline: 1.5px solid var(--accent-border);
}

.nav {
  position: absolute;
  z-index: 2;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 48px;
  min-height: 48px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--surface-translucent);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 22px;
  line-height: 1;
  transition: border-color .15s ease, color .15s ease, opacity .15s ease;
}
.nav:hover, .nav:focus-visible {
  border-color: var(--accent-border);
  color: var(--accent);
}
.nav:disabled { opacity: 0.25; pointer-events: none; }
.nav.prev { left: calc(12px + env(safe-area-inset-left)); }
.nav.next { right: calc(12px + env(safe-area-inset-right)); }
@media (min-width: 768px) {
  .nav.prev { left: calc(20px + env(safe-area-inset-left)); }
  .nav.next { right: calc(20px + env(safe-area-inset-right)); }
}

.reader-info {
  position: absolute;
  z-index: 3;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(340px, 100%);
  padding: 76px 20px 20px; /* le sommet dégage la hauteur de .reader-bar (z-index supérieur) */
  overflow-y: auto;
  background: var(--surface-translucent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-left: 1px solid var(--border);
}
.reader-info[hidden] { display: none; }
.reader-info-grabber { display: none; }
.reader-info h3 {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  font-weight: 500;
  font-size: 20px;
  color: var(--text);
  margin: 0 0 10px;
}
.reader-info p {
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
  margin-bottom: 12px;
}
.reader-info .tags {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.03em;
  color: var(--accent);
}
.reader-info details {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.reader-info summary {
  cursor: pointer;
  font-size: 13px;
  color: var(--muted);
}
.reader-info summary:hover, .reader-info summary:focus-visible { color: var(--accent); }
.reader-info pre {
  margin-top: 10px;
  max-height: 40vh;
  overflow-y: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.5;
  color: var(--text);
}
.reader-tokens {
  margin-bottom: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.reader-tokens[hidden] { display: none; }
.reader-tokens h4 {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 10px;
}
.reader-tokens-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
/* ≤640px : le panneau d'infos passe de rail latéral droit à feuille de fond
   (« bottom sheet ») — plus naturel au pouce sur téléphone, coins arrondis
   en haut, hauteur plafonnée pour laisser la page toujours visible derrière. */
@media (max-width: 640px) {
  .reader-info {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-height: 60vh;
    max-height: 60dvh;
    padding: 28px 20px calc(20px + env(safe-area-inset-bottom));
    border-left: 0;
    border-top: 1px solid var(--border);
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -12px 32px var(--shadow);
    /* P2-2 : entrée/sortie animée plutôt qu'un bascule sec de [hidden] */
    transform: translateY(100%);
    transition: transform 240ms cubic-bezier(0.32, 0.72, 0, 1);
  }
  .reader-info.open { transform: translateY(0); }
  .reader-info.dragging { transition: none; }
  .reader-info-grabber {
    display: block;
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--border);
    touch-action: none;
  }
}

/* ---------- lexique des tokens : chip partagée (recherche + rangée du lecteur) ---------- */
.token-chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px; /* cible tactile ≥44px (spec §14 frontend), même sans vignette */
  padding: 6px 16px 6px 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  text-align: left;
  cursor: pointer;
  opacity: 1;
  transition: border-color .15s ease, background-color .15s ease, transform .2s ease, opacity .2s ease;
}
.token-chip:hover, .token-chip:focus-visible {
  border-color: var(--accent-border);
  background: var(--accent-soft);
}
.token-chip.token-chip-label-only { padding: 6px 16px; }
.token-chip-thumb {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: 50%;
  overflow: hidden;
  background: var(--surface-2);
}
.token-chip-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.token-chip-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  max-width: 220px;
}
.token-chip-en {
  font-size: 13px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.token-chip-fr {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Variante « recherche vivante » (spec item 1) : mini-crop 56-72px, plus respirante. */
.token-chip-lg {
  gap: 12px;
  padding: 8px 18px 8px 8px;
}
.token-chip-lg .token-chip-thumb {
  width: 64px;
  height: 64px;
}

/* ---------- recherche : zone « Tokens » live ---------- */
.token-results {
  margin-bottom: 28px;
}
.token-results-title {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 12px;
}
.token-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* ---------- étage 3 du Lexique : assistance IA (recherche assistée) ---------- */
/* P1-5 : le bandeau garde TOUJOURS sa place dans le flux (height/margin fixes) — on n'alterne
   plus jamais son affichage via display:none (qui insère/retire 32px de layout) mais via
   opacity + visibility, donc plus aucun saut vertical du contenu déjà lu en dessous. Le
   sélecteur `.assist-strip[hidden]` gagne (spécificité classe+attribut) sur le `[hidden]{display:
   none}` du user-agent : l'élément reste dans le flux, juste invisible/inerte. */
.assist-strip {
  display: flex;
  align-items: center;
  min-height: 32px;
  margin: -8px 0 20px;
  padding: 6px 14px;
  border: 1px solid var(--accent-border);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.03em;
  color: var(--accent);
  opacity: 1;
  visibility: visible;
  transition: opacity 150ms ease;
}
.assist-strip[hidden] {
  display: flex;
  opacity: 0;
  visibility: hidden;
}
/* seul le glyphe scintille désormais — plus d'aplat ambre plein-largeur (règle maison) */
.assist-strip .assist-glyph {
  display: inline-block;
  margin-right: 8px;
  animation: assist-glyph-pulse 1.2s ease-in-out infinite;
}
@keyframes assist-glyph-pulse {
  0%, 100% { opacity: .4; }
  50% { opacity: 1; }
}

/* P1-5 : la section IA s'insère sous la zone « Tokens » FTS — son arrivée ne pousse que du
   contenu situé plus bas (acceptable, non lu au moment de l'apparition) — ouverture
   chorégraphiée en hauteur. */
#assist-section {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 200ms ease-out;
  overflow: hidden;
}
#assist-section.has-content { grid-template-rows: 1fr; }
#assist-section > .assist-section-inner { min-height: 0; overflow: hidden; }

/* section « ✦ Suggestions de l'IA » : réutilise le format .token-results (mêmes espacements,
   même grille de chips) pour rester visuellement cohérente avec la section « Tokens » FTS
   juste au-dessus — seul le titre prend l'accent (au lieu du gris muted) pour se distinguer
   comme une couche différente (IA vs. correspondance texte exacte). */
.assist-results-title { color: var(--accent); }
.assist-results .token-grid + .assist-pages { margin-top: 14px; }
/* P3-8 : message d'anticlimax explicite plutôt qu'un escamotage sec */
.assist-empty {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
  padding: 4px 0 4px 16px;
  border-left: 2px solid var(--border);
  transition: opacity 300ms ease;
}

/* ---------- panneau token (visionneuse de crop) ---------- */
.token-panel {
  position: fixed;
  inset: 0;
  z-index: 60; /* au-dessus du lecteur (z-index 50) : ouvrable depuis une zone tactile du
    lecteur, le panneau doit toujours apparaître PAR-DESSUS lui, jamais derrière */
  display: grid;
  place-items: center;
  padding: 20px;
}
.token-panel[hidden] { display: none; }
.token-panel-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(10, 8, 5, 0.72);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.token-panel-sheet {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 480px;
  max-height: min(84vh, 84dvh);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 24px 64px var(--shadow);
  padding: 20px;
  animation: token-panel-in .2s ease;
}
@keyframes token-panel-in {
  from { opacity: 0; transform: translateY(8px) scale(.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.token-panel-close {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  border-radius: 50%;
  color: var(--muted);
  font-size: 16px;
  background: var(--surface-translucent);
  transition: color .15s ease, background-color .15s ease;
}
.token-panel-close:hover, .token-panel-close:focus-visible {
  color: var(--accent);
  background: var(--accent-soft);
}
.token-panel-image {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-2);
  margin-bottom: 16px;
}
.token-panel-image img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: var(--surface-2);
}
.token-panel-body h3 {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  font-weight: 500;
  font-size: 22px;
  color: var(--text);
  margin-bottom: 6px;
  padding-right: 40px; /* dégage le bouton fermer */
}
.token-panel-fr {
  font-size: 14px;
  color: var(--muted);
  margin-bottom: 10px;
}
.token-panel-tags {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.03em;
  color: var(--accent);
  margin-bottom: 18px;
}
.token-panel-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
/* ---------- P2-3 : squelette isomorphe au contenu final (chargement du panneau) ---------- */
/* P2-3 (revue) : hauteurs/marges calées empiriquement sur le corps réel (h3 33px+marge 6,
   fr 21px+marge 10, tags 18px+marge 18, actions 44px = 150px) — mesuré via Playwright sur un
   token à labelFr + tags renseignés (cas courant). Avant ce calage, le squelette ne réservait que
   ~43px de corps contre ~150px réels : la feuille grandissait de +104px à l'arrivée du contenu. */
.token-panel-skel-title { width: 60%; height: 24px; margin-bottom: 15px; }
.token-panel-skel-sub { width: 35%; height: 16px; margin-bottom: 15px; }
.token-panel-skel-tags { width: 45%; height: 13px; margin-bottom: 23px; }
.token-panel-skel-actions { display: flex; gap: 10px; }
.token-panel-skel-btn { width: 120px; height: 44px; border-radius: var(--radius-sm); }
.token-panel-body-real { animation: view-in 150ms ease both; }
.token-panel-actions button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-size: 14px;
  font-weight: 600;
  transition: background-color .15s ease, color .15s ease;
}
.token-panel-actions button:hover, .token-panel-actions button:focus-visible {
  background: var(--accent);
  color: var(--bg);
}
.token-panel-actions button.is-copied {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--bg);
}
@media (max-width: 640px) {
  .token-panel { align-items: flex-end; padding: 0; }
  .token-panel-sheet {
    max-width: none;
    max-height: 88vh;
    max-height: 88dvh;
    border-radius: 16px 16px 0 0;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
  }
}

/* ---------- chip générique (documents récents en état vide, P3-4) ---------- */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 24px;
}
.chip {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.03em;
  color: var(--muted);
  transition: border-color .15s ease, color .15s ease, background-color .15s ease;
}
.chip:hover, .chip:focus-visible {
  border-color: var(--accent-border);
  color: var(--accent);
}

/* ---------- résultats de recherche (groupés par document) ---------- */
.result-groups {
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.result-group-header {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
  transition: border-color .15s ease;
}
.result-group-header:hover, .result-group-header:focus-visible {
  border-color: var(--accent-border);
}
.result-group-title {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  font-size: 18px;
  color: var(--text);
}
.result-group-header:hover .result-group-title,
.result-group-header:focus-visible .result-group-title {
  color: var(--accent);
}
.results {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.result {
  display: flex;
  gap: 14px;
  padding: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color .15s ease, transform .15s ease;
}
.result:hover, .result:focus-visible {
  border-color: var(--accent-border);
  transform: translateX(2px);
}
.result img {
  /* P3-11 : ratio 2:3 des planches (identité visuelle du produit) au lieu d'un carré tronqué */
  width: 72px;
  height: 108px;
  flex-shrink: 0;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.result > div {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  min-width: 0;
}
/* P2-10 : titre de page + badge « p. N » — savoir où on atterrit avant de cliquer */
.result-page-line {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.result-page-title {
  font-size: 14px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.result-page-badge {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--accent);
  border: 1px solid var(--accent-border);
  border-radius: 999px;
  padding: 1px 8px;
}
.result-snippet {
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
mark {
  background: var(--accent-soft);
  color: var(--accent);
  padding: 0 2px;
  border-radius: 2px;
}

/* ---------- admin / synchro ---------- */
.view > h2 {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 28px 0 12px;
}
.view > p:not(.empty) {
  font-size: 15px;
  color: var(--muted);
  margin-bottom: 20px;
}
.view > button {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 20px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-size: 14px;
  font-weight: 600;
  transition: background-color .15s ease, color .15s ease, opacity .15s ease;
}
.view > button:hover:not(:disabled), .view > button:focus-visible:not(:disabled) {
  background: var(--accent);
  color: var(--bg);
}
.view > button:disabled { opacity: 0.5; cursor: default; }
.view > button .spinner {
  width: 14px;
  height: 14px;
  margin-right: 8px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.view > ul {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 20px;
}
.view > ul li {
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}
.view code {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text);
  background: var(--surface-2);
  padding: 1px 6px;
  border-radius: var(--radius-sm);
}

/* P2-7 : lignes de synchro humanisées — pastille de statut + libellé verbal */
.run-line {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.run-dot {
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  margin-top: 6px;
}
.run-dot.is-error { background: var(--danger); }

/* ---------- P2-9 : écran d'erreur de vue dédié ---------- */
.view-error {
  padding: 60px 20px;
  text-align: center;
}
.view-error p {
  color: var(--muted);
  font-size: 15px;
  margin-bottom: 20px;
}
.view-error button, .view-error a.btn {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 20px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-size: 14px;
  font-weight: 600;
  transition: background-color .15s ease, color .15s ease;
}
.view-error button:hover, .view-error button:focus-visible,
.view-error a.btn:hover, .view-error a.btn:focus-visible {
  background: var(--accent);
  color: var(--bg);
}

/* ---------- P3-4 : états vides avec identité (mangue en filigrane) ---------- */
.empty-state {
  padding: 60px 20px;
  text-align: center;
}
.empty-state img {
  width: 48px;
  height: 48px;
  margin: 0 auto 16px;
  opacity: .35;
  filter: grayscale(.4);
}
.empty-state p {
  color: var(--muted);
  font-size: 15px;
  margin-bottom: 20px;
}
.empty-state .chips { justify-content: center; margin-bottom: 0; }
.empty-state button, .empty-state a.btn {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 20px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-size: 14px;
  font-weight: 600;
  transition: background-color .15s ease, color .15s ease;
}
.empty-state button:hover, .empty-state button:focus-visible {
  background: var(--accent);
  color: var(--bg);
}
