*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --cream:        #fdf8f0;
  --cream-dark:   #f0e8d8;
  --cream-surf:   #fefcf8;
  --cream-dim:    #f0e8d8;
  --charcoal:     #1c1a17;
  --charcoal-mid: #3d3a35;
  /* Secondary ink. Reads ~5.1:1 on --cream, so it stays legible at the small
     uppercase mono sizes the UI leans on. Use --warm-gray-soft only for
     decoration (icons, rules, disabled states) — never for text. */
  --warm-gray:      #6f6a61;
  --warm-gray-soft: #8b857c;
  --emerald:      #2b6a64;
  --emerald-dark: #21514c;
  --emerald-dk:   #21514c;
  /* --amber is a fill (badges, rules, marks); --amber-ink is its text-safe
     counterpart (~4.8:1 on cream) for the small uppercase section labels. */
  --amber:        #c8a45a;
  --amber-ink:    #8a6a2e;
  --white:        #ffffff;
  --border-warm:  rgba(120,80,30,0.24);
  --grid-minor:   rgba(120,80,30,0.05);
  --grid-major:   rgba(120,80,30,0.10);
  --serif:        'Playfair Display', Georgia, serif;
  --sans:         'DM Sans', system-ui, sans-serif;
  --mono:         'DM Mono', 'Courier New', monospace;

  /* Type scale. Everything in the app is small, tracked, and often uppercase,
     so these are the floor sizes — don't hard-code px below --fs-micro.
     --fs-xs exists for the dense editor/product panels where --fs-sm
     would reflow a fixed-width column. */
  --fs-micro:  10px;   /* count badges, superscript-ish chips */
  --fs-2xs:    11px;   /* dense mono labels */
  --fs-xs:   11.5px;   /* mono controls inside packed side panels */
  --fs-sm:     12px;   /* default mono UI label / button */
  --fs-md:     13px;   /* secondary prose */
  --fs-base:   14px;   /* body prose */
}

html { scroll-behavior: smooth; }
body {
  font-family: var(--sans);
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--charcoal);
}

/* ─── Tailwind utility remap ───────────────────────────────────────────────
   Most app pages build their markup in JS with Tailwind utilities (~200 call
   sites). Tailwind's cool grays are off-palette and too faint on cream, and
   its small sizes sit below the scale above. Remapping here beats editing
   every call site. `!important` is required because the Tailwind CDN injects
   its stylesheet after this file; hover variants are remapped too so they
   still take precedence over the base colour. */
.text-gray-300 { color: var(--warm-gray-soft) !important; }
.text-gray-400,
.text-gray-500 { color: var(--warm-gray) !important; }
.text-gray-600,
.text-gray-700 { color: var(--charcoal-mid) !important; }
.text-gray-800 { color: var(--charcoal) !important; }
.hover\:text-gray-600:hover,
.hover\:text-gray-700:hover { color: var(--charcoal) !important; }

.text-\[8px\]  { font-size: var(--fs-micro) !important; }
.text-\[9px\]  { font-size: var(--fs-2xs) !important; }
.text-\[10px\] { font-size: var(--fs-xs) !important; }
.text-\[11px\] { font-size: var(--fs-sm) !important; }
.text-\[13px\] { font-size: var(--fs-base) !important; }
.text-xs       { font-size: var(--fs-md) !important; }

/* Surfaces and rules, same reasoning: a cool-gray panel sitting on cream reads
   as dirty rather than neutral, and gray-100/200 hairlines all but vanish
   against it. Warm them onto the cream ramp so edges stay visible. */
.bg-white      { background-color: var(--cream-surf) !important; }
.bg-gray-50,
.hover\:bg-gray-50:hover  { background-color: var(--cream) !important; }
.bg-gray-100,
.hover\:bg-gray-100:hover { background-color: var(--cream-dim) !important; }

.border-gray-100 { border-color: rgba(120,80,30,0.14) !important; }
.border-gray-200 { border-color: var(--border-warm) !important; }
.border-gray-300,
.hover\:border-gray-300:hover { border-color: rgba(120,80,30,0.34) !important; }
.divide-gray-100 > :not([hidden]) ~ :not([hidden]) {
  border-color: rgba(120,80,30,0.14) !important;
}

/* ─── Site Header ─── */
.site-header {
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;   /* the three slots never touch, however tight the viewport gets */
  padding: 0 24px;
  background: rgba(253,248,240,0.97);
  border-bottom: 1px solid var(--border-warm);
  backdrop-filter: blur(8px);
  flex-shrink: 0;
  /* Keep the header — and the mode dropdown menu that escapes it — above the
     page content (canvas / grid), whatever stacking context those create. */
  position: relative;
  z-index: 100;
}

.site-header__slot {
  display: flex;
  align-items: center;
  min-width: 0;
}

/* Two equal side slots put the centre slot on the page's centre line — but
   only while both of them actually fit. `max-content` is the floor that stops
   a half from being squeezed narrower than the controls it holds: without it
   the flex algorithm keeps the halves equal no matter what, and the actions
   cluster (mode dropdown + save + upgrade + language + account ≈ 630px) spills
   out of its own box below ~1530px, drawing its buttons on top of each other.
   With the floor in place the centre gives up the middle instead, and the
   density tiers at the bottom of this file trim the cluster from there. */
.site-header__slot--left,
.site-header__slot--actions {
  flex: 1 1 0;
  min-width: max-content;
}

/* The only slot with slack, so it is the first to shrink. */
.site-header__slot--center {
  flex: 0 1 auto;
  justify-content: center;
}

.site-header__slot--actions {
  justify-content: flex-end;
  gap: 12px;   /* separate the page actions cluster from the user menu */
}

.site-header__logo {
  display: flex;
  align-items: center;
  text-decoration: none;
}

/* Brand. Two <img>s, exactly one of them shown: the horizontal signature
   (/logo-lockup.svg) normally, the monogram alone (/logo-mark.svg) once the
   header is too narrow for a wordmark. Both SVGs carry the logo's own colours
   (ink + gold), so the only job here is to size the box. 46px of the 52px
   header: the signature includes the construction grid, which reaches past the
   G on every side, so the letter itself lands around 37px. */
.site-header__lockup {
  height: 46px;
  width: auto;
  flex-shrink: 0;
}

.site-header__mark {
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  display: none;
}

@media (max-width: 480px) {
  .site-header__lockup { display: none; }
  .site-header__mark { display: block; }
}

.app-header-left {
  display: flex;
  align-items: center;
  gap: 20px;
  min-width: 0;
}

.app-home-logo {
  flex: 0 0 auto;
}

.app-back-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--warm-gray);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.1em;
  text-decoration: none;
  text-transform: uppercase;
  transition: color 0.15s ease;
}

.app-back-link:hover {
  color: var(--charcoal);
}

.app-back-link svg {
  flex: 0 0 auto;
}

.app-title-input {
  width: 220px;
  max-width: 36vw;
  /* Overrides the automatic minimum size a flex item gets from its intrinsic
     width, so the title box can give room back to the header controls. */
  min-width: 0;
  border: 1px solid var(--border-warm);
  border-radius: 0;
  background: transparent;
  color: var(--charcoal);
  font-family: var(--mono);
  font-size: var(--fs-base);
  font-weight: 500;
  letter-spacing: 0.06em;
  outline: none;
  padding: 4px 8px;
  text-align: center;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.app-title-input:hover {
  background: var(--cream-dim);
  border-color: rgba(120,80,30,0.28);
}

.app-title-input:focus {
  background: var(--cream-surf);
  border-color: var(--emerald);
}

.app-header-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* Mode switcher — a native <details> dropdown. The <summary> is the closed
   control (current mode + chevron); the menu floats below it. Square corners
   throughout, matching the gallery skin. */
.app-mode-dropdown {
  position: relative;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  line-height: 1;
  text-transform: uppercase;
}

.app-mode-dropdown__current {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-width: 92px;
  min-height: 28px;
  padding: 0 9px;
  border: 1px solid rgba(120,80,30,0.25);
  border-radius: 0;
  background: var(--cream-surf);
  color: var(--charcoal);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  list-style: none;            /* hide the default disclosure triangle */
  transition: background 0.15s ease, border-color 0.15s ease;
}

.app-mode-dropdown__current::-webkit-details-marker { display: none; }

.app-mode-dropdown__current:hover {
  background: var(--cream);
  border-color: rgba(120,80,30,0.4);
}

.app-mode-dropdown__chevron {
  flex: 0 0 auto;
  color: var(--warm-gray);
  transition: transform 0.15s ease;
}

.app-mode-dropdown[open] .app-mode-dropdown__chevron {
  transform: rotate(180deg);
}

.app-mode-dropdown__menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 50;
  min-width: 100%;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 4px;
  border: 1px solid rgba(120,80,30,0.25);
  border-radius: 0;
  background: var(--cream-surf);
  box-shadow: 0 8px 24px rgba(28,26,23,0.16);
}

.app-mode-dropdown__item {
  display: block;
  padding: 6px 12px;
  border-radius: 0;
  background: transparent;
  color: var(--warm-gray);
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}

a.app-mode-dropdown__item:hover {
  background: var(--cream);
  color: var(--charcoal);
}

.app-mode-dropdown__item.is-active {
  background: var(--charcoal);
  color: var(--white);
}

.app-mode-dropdown__item.is-disabled {
  cursor: not-allowed;
  opacity: 0.4;
}

.app-mode-dropdown__separator {
  height: 1px;
  margin: 4px 2px;
  background: var(--border-warm);
}

.app-header-status {
  color: var(--warm-gray);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
}

/* Sketch save indicator. Its fixed 8px footprint never changes, so save-state
   copy cannot push the remaining header controls around. The full message stays
   available to assistive tech and as a hover/focus title. */
.app-save-status {
  position: relative;
  display: inline-flex;
  flex: 0 0 8px;
  width: 8px;
  height: 8px;
  overflow: visible;
  border-radius: 50%;
  background: var(--warm-gray-soft);
  color: transparent;
  font-size: 0;
  letter-spacing: 0;
  outline-offset: 4px;
}

.app-save-status.is-dirty { background: var(--amber); }
.app-save-status.is-saving {
  background: var(--amber);
  animation: save-status-pulse 900ms ease-in-out infinite alternate;
}
.app-save-status.is-saved { background: var(--emerald); }
.app-save-status.is-error { background: #b4473f; }

.app-save-status.is-error::after,
.app-save-status.show-message::after {
  content: attr(data-message);
  position: absolute;
  top: 20px;
  right: -10px;
  z-index: 80;
  width: max-content;
  max-width: 260px;
  padding: 7px 9px;
  border: 1px solid var(--border-warm);
  background: var(--cream-surf);
  box-shadow: 0 6px 20px rgba(28,26,23,0.14);
  color: var(--charcoal-mid);
  font-family: var(--mono);
  font-size: var(--fs-xs);
  letter-spacing: 0;
  line-height: 1.35;
  white-space: normal;
}

@keyframes save-status-pulse {
  from { opacity: 0.45; }
  to { opacity: 1; }
}

.app-header-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 0;
  height: 28px;
  border: 0;
  border-radius: 0;
  background: var(--charcoal);
  color: var(--white);
  cursor: pointer;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  padding: 0 10px;
  text-decoration: none;
  text-transform: uppercase;
  transition: background 0.15s ease;
}

.app-header-button:hover {
  background: var(--emerald);
}

.app-header-button svg {
  flex: 0 0 auto;
}

.app-save-button {
  border: 1px solid transparent;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.app-save-button.is-dirty { background: var(--emerald); }
.app-save-button.is-dirty:hover { background: var(--emerald-dark); }
.app-save-button.is-saving { cursor: wait; background: var(--charcoal-mid); }
.app-save-button.is-error { background: #8f3b35; }
.app-save-button.is-error:hover { background: #74302b; }
.app-save-button.is-saved,
.app-save-button.is-saved:hover {
  border-color: var(--border-warm);
  background: var(--cream-surf);
  color: var(--emerald);
  cursor: default;
}

.app-save-button__icon--saved { display: none; }
.app-save-button.is-saved .app-save-button__icon--save { display: none; }
.app-save-button.is-saved .app-save-button__icon--saved { display: block; }

.header-upgrade-btn {
  min-width: 0;
  background: var(--emerald);
  white-space: nowrap;
}

.header-upgrade-btn:hover,
.header-upgrade-btn:focus-visible {
  background: var(--charcoal);
}

.header-upgrade-btn[hidden] { display: none; }

/* The short label is the phone-tier swap — see the density tiers below. */
.header-upgrade-btn__short { display: none; }

/* ─── Language switcher ─────────────────────────────────────────────────── */
.language-switcher {
  position: relative;
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.06em;
}

.language-switcher__current {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 38px;
  min-width: 38px;
  height: 28px;
  padding: 0 5px;
  border: 1px solid rgba(120,80,30,0.25);
  background: var(--cream-surf);
  color: var(--charcoal);
  cursor: pointer;
  list-style: none;
  user-select: none;
  white-space: nowrap;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.language-switcher__current::-webkit-details-marker { display: none; }
.language-switcher__current:hover {
  border-color: rgba(120,80,30,0.4);
  background: var(--cream);
}

.language-switcher__chevron { transition: transform 0.15s ease; }
.language-switcher[open] .language-switcher__chevron { transform: rotate(180deg); }

.language-switcher__menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 60;
  display: flex;
  min-width: 100%;
  flex-direction: column;
  gap: 1px;
  padding: 4px;
  border: 1px solid rgba(120,80,30,0.25);
  background: var(--cream-surf);
  box-shadow: 0 8px 24px rgba(28,26,23,0.16);
}

/* Usually <button>s driven by i18n.js. The landing page renders them as <a>
   links instead (one URL per language) — hence text-decoration. */
.language-switcher__option {
  border: 0;
  background: transparent;
  color: var(--warm-gray);
  cursor: pointer;
  text-decoration: none;
  font: inherit;
  letter-spacing: inherit;
  padding: 7px 8px;
  text-align: center;
}

.language-switcher__option:hover { background: var(--cream); color: var(--charcoal); }
.language-switcher__option.is-active { background: var(--charcoal); color: var(--white); }

/* ─── Header user menu ───────────────────────────────────────────────────── */
/* Wired by static/js/userMenu.js. Square, cream-surfaced icon buttons that
   match the mode dropdown and "Nouveau patron" controls. */
.user-menu {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.app-header-iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid rgba(120,80,30,0.25);
  border-radius: 0;
  background: var(--cream-surf);
  color: var(--charcoal);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.app-header-iconbtn:hover {
  background: var(--cream);
  border-color: var(--emerald);
  color: var(--emerald);
}

.app-header-iconbtn svg { display: block; }

/* These buttons name themselves only inside the phone menu below, where there
   is room for a label and no hover tooltip to fall back on. The label stays in
   the DOM at every width, so it never changes the accessible name. */
.app-header-iconbtn__label { display: none; }

/* ─── Header tools: language + account ───────────────────────────────────── */
/* `display: contents` dissolves both the wrapper and its panel, so above the
   phone tier the language switcher and the user menu are direct flex children
   of the actions slot exactly as if this grouping weren't here. Everything
   that makes it a menu lives in the phone tier at the bottom of this file. */
.header-tools,
.header-tools__panel {
  display: contents;
}

.header-tools__toggle {
  display: none;
}

.header-tools__chevron {
  transition: transform 0.15s ease;
}

/* ─── "Mon compte" modal ──────────────────────────────────────────────────── */
/* Fixed title bar + identity header + tab bar, one scrolling panel, fixed
   footer. Sections are never stacked in a single long scroll. */
.account-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(28,26,23,0.45);
}

.account-modal-overlay.is-open { display: flex; }

.account-modal {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(540px, 100%);
  max-height: min(88vh, 700px);
  border: 1px solid var(--border-warm);
  border-radius: 0;
  background: var(--cream-surf);
  box-shadow: 0 18px 50px rgba(28,26,23,0.28);
}

.account-modal__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex: 0 0 auto;
  padding: 13px 20px;
  border-bottom: 1px solid var(--border-warm);
}

.account-modal__title {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--charcoal);
}

.account-modal__close {
  border: 0;
  background: transparent;
  color: var(--warm-gray);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease;
}

.account-modal__close:hover { color: var(--charcoal); }

#account-modal-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

/* ── Identity header ─────────────────────────────────────────────────────── */
.account-head {
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  flex: 0 0 auto;
  padding: 18px 20px;
}

.account-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border: 1px solid rgba(43,106,100,0.3);
  background: rgba(43,106,100,0.08);
  color: var(--emerald);
  font-family: var(--mono);
  font-size: var(--fs-base);
  font-weight: 500;
  letter-spacing: 0.06em;
}

.account-head__main { min-width: 0; }

.account-head__email {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--charcoal);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-head__plan {
  display: flex;
  align-items: center;
  margin: 4px 0 0;
}

/* Membership-level badge — "decouverte" | "premium" | "pro" | "admin".
   Shared by the account modal (account-head__plan) and the admin page's
   member table (admin.css reuses this class, no separate copy). */
.level-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  border: 1px solid var(--border-warm);
  color: var(--warm-gray);
}

.level-badge[data-level="premium"],
.level-badge[data-level="pro"] {
  background: rgba(200,164,90,0.14);
  border-color: rgba(138,106,46,0.35);
  color: var(--amber-ink);
}

.level-badge[data-level="admin"] {
  background: rgba(43,106,100,0.12);
  border-color: rgba(43,106,100,0.3);
  color: var(--emerald-dark);
}

.account-note {
  margin: -8px 20px 10px;
  padding: 7px 10px;
  background: rgba(179,38,30,0.07);
  color: #8f241c;
  font-size: var(--fs-md);
}

/* ── Tabs ────────────────────────────────────────────────────────────────── */
.account-tabs {
  display: flex;
  gap: 22px;
  flex: 0 0 auto;
  padding: 10px 20px 0;
  border-bottom: 1px solid var(--border-warm);
  overflow-x: auto;
}

.language-switcher__label { display: none; }

.account-tab {
  position: relative;
  padding: 0 0 10px;
  border: 0;
  background: transparent;
  cursor: pointer;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--warm-gray);
  transition: color 0.15s ease;
}

.account-tab:hover { color: var(--charcoal); }
.account-tab.is-active { color: var(--charcoal); }

.account-tab.is-active::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--emerald);
}

.account-panels {
  flex: 1 1 auto;
  min-height: 200px;
  overflow: auto;
  padding: 18px 20px 20px;
}

.account-panel__hint {
  margin: 0 0 6px;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--warm-gray);
}

/* ── Atelier panel ───────────────────────────────────────────────────────── */
.account-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border-warm);
}

.account-stat { min-width: 0; }

.account-stat__value {
  display: block;
  font-family: var(--mono);
  font-size: 26px;
  line-height: 1;
  color: var(--charcoal);
}

.account-stat__label {
  display: block;
  margin-top: 7px;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--warm-gray);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.account-resume { margin-top: 16px; }

.account-resume__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  min-height: 34px;
  padding: 6px 0;
  border-bottom: 1px solid rgba(120,80,30,0.14);
  color: var(--charcoal);
  text-decoration: none;
}

.account-resume__row strong {
  min-width: 0;
  font-size: var(--fs-base);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: color 0.15s ease;
}

.account-resume__row span {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--warm-gray);
}

.account-resume__row:hover strong { color: var(--emerald); }

.account-footnote {
  margin: 16px 0 0;
  font-size: var(--fs-md);
  line-height: 1.7;
  color: var(--warm-gray);
}

.account-workshop-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}

/* ── Billing panel ──────────────────────────────────────────────────────── */
.account-billing-notice {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 0 0 14px;
  padding: 10px 12px;
  border-left: 3px solid var(--amber);
  background: rgba(200,164,90,0.11);
  font-size: var(--fs-base);
  line-height: 1.45;
}

.account-billing-notice[data-tone="danger"] {
  border-left-color: #b3261e;
  background: rgba(179,38,30,0.06);
}

.account-billing-details {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 20px;
  margin: 0;
}

.account-billing-details > div {
  min-width: 0;
  padding: 9px 0;
  border-bottom: 1px solid rgba(120,80,30,0.14);
}

.account-billing-details dt {
  margin-bottom: 4px;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--warm-gray);
}

.account-billing-details dd {
  margin: 0;
  color: var(--charcoal);
  font-size: var(--fs-base);
  font-weight: 500;
}

.account-billing-offers { margin-top: 18px; }

.account-billing-offer {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
  padding: 12px;
  border: 1px solid var(--border-warm);
  background: var(--cream-surf);
}

.account-billing-offer.is-current {
  border-color: rgba(43,106,100,0.45);
  background: rgba(43,106,100,0.05);
}

.account-billing-offer > div,
.account-billing-offer > p {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  margin: 0;
}

.account-billing-offer > div span,
.account-billing-offer > p span {
  color: var(--warm-gray);
  font-size: var(--fs-sm);
}

.account-billing-offer > p { align-items: flex-end; }
.account-billing-offer > p strong { font-family: var(--serif); font-size: 20px; }

.account-billing-offer em {
  position: absolute;
  top: -8px;
  right: 10px;
  padding: 2px 7px;
  background: var(--amber);
  color: var(--charcoal);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-style: normal;
}

.account-billing-terms {
  margin: 14px 0 0;
  color: var(--warm-gray);
  font-size: var(--fs-sm);
  line-height: 1.55;
}

.account-billing-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}

/* ── Field rows (préférences + mot de passe) ─────────────────────────────── */
.account-fields { display: grid; }

.account-field {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 172px;
  align-items: center;
  gap: 14px;
  min-width: 0;
  padding: 9px 0;
  border-bottom: 1px solid rgba(120,80,30,0.14);
}

.account-field > span {
  font-size: var(--fs-base);
  color: var(--charcoal-mid);
}

.account-field input,
.account-field select {
  width: 100%;
  min-width: 0;
  height: 36px;
  padding: 0 9px;
  border: 1px solid rgba(120,80,30,0.28);
  border-radius: 0;
  background: var(--white);
  color: var(--charcoal);
  font: var(--fs-base) var(--sans);
}

.account-field select {
  appearance: none;
  padding-right: 26px;
  background-image: linear-gradient(45deg, transparent 50%, var(--warm-gray) 50%),
                    linear-gradient(135deg, var(--warm-gray) 50%, transparent 50%);
  background-position: calc(100% - 15px) 16px, calc(100% - 10px) 16px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.account-field input:focus,
.account-field select:focus {
  outline: none;
  border-color: var(--emerald);
}

.account-form__actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 14px;
}

/* ── Buttons ─────────────────────────────────────────────────────────────── */
.account-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 34px;
  padding: 0 14px;
  border: 1px solid transparent;
  border-radius: 0;
  cursor: pointer;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.account-btn--primary {
  background: var(--emerald);
  color: var(--white);
}

.account-btn--primary:hover { background: var(--emerald-dark); }

.account-btn--ghost {
  border-color: rgba(120,80,30,0.24);
  background: var(--white);
  color: var(--charcoal);
}

.account-btn--ghost:hover {
  border-color: var(--emerald);
  color: var(--emerald);
}

.account-btn--danger {
  border-color: rgba(179,38,30,0.42);
  background: var(--white);
  color: #8f241c;
}

.account-btn--danger:hover { background: rgba(179,38,30,0.05); }

.account-btn:disabled { opacity: 0.55; cursor: default; }

/* ── Footer ──────────────────────────────────────────────────────────────── */
.account-modal__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex: 0 0 auto;
  padding: 12px 20px;
  border-top: 1px solid var(--border-warm);
}

.account-status {
  min-width: 0;
  margin: 0;
  font-size: var(--fs-md);
  color: var(--warm-gray);
}

.account-status[data-tone="ok"] { color: var(--emerald); }
.account-status[data-tone="danger"] { color: #b3261e; }

.account-modal__logout {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 34px;
  padding: 0 14px;
  border: 1px solid rgba(120,80,30,0.24);
  border-radius: 0;
  background: transparent;
  color: var(--charcoal);
  cursor: pointer;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.account-modal__logout:hover {
  border-color: #b3261e;
  color: #b3261e;
}

.account-loading,
.account-error {
  margin: 28px 20px;
  color: var(--warm-gray);
  font-size: var(--fs-base);
}

.account-error { color: #b3261e; }

@media (max-width: 560px) {
  .account-head {
    grid-template-columns: 42px minmax(0, 1fr);
    row-gap: 12px;
  }

  .account-head .account-btn {
    grid-column: 1 / -1;
    justify-self: start;
  }

  .account-field {
    grid-template-columns: minmax(0, 1fr);
    gap: 5px;
  }

  .account-billing-details { grid-template-columns: 1fr; }
  .account-billing-offer { grid-template-columns: 1fr; }
  .account-billing-offer > p { align-items: flex-start; }
}

/* ─── Header density tiers ────────────────────────────────────────────────
   The app header fits a brand, a document title, a mode dropdown and one to
   three buttons into 52px, and every one of those is `white-space: nowrap` —
   so as the viewport narrows the cluster has to *give something up* rather
   than compress. The tiers below drop them in order of how little they carry:
   the wordmark first, then the label text on the controls. Nothing here is
   optional polish — the slots are content-sized now (see `.site-header__slot--*`),
   so whatever these tiers fail to trim turns into a horizontally scrolling
   page. */

/* Large laptops (1440-wide screens). Nothing is dropped yet — the header just
   tightens its gaps. The mode dropdown and buttons are already content-sized
   at every width, so the halves stay even and the title box keeps the page's
   centre line here. */
@media (max-width: 1500px) {
  .site-header {
    padding: 0 20px;
  }

  .site-header__slot--actions,
  .app-header-actions {
    gap: 10px;
  }

  .app-header-left {
    gap: 16px;
  }
}

/* Standard laptops — 1280/1366 panels, and 1920 ones at 150% Windows scaling,
   which is where the buttons used to start overlapping. */
@media (max-width: 1300px) {
  .site-header {
    padding: 0 16px;
  }

  .app-header-left {
    gap: 14px;
  }
}

/* Small laptops / large tablets: the wordmark is the widest thing left in the
   header and the least load-bearing, so the brand falls back to the monogram. */
@media (max-width: 1040px) {
  .site-header {
    gap: 12px;
    padding: 0 12px;
  }

  .app-home-logo .site-header__lockup {
    display: none;
  }

  .app-home-logo .site-header__mark {
    display: block;
  }

  .app-back-link {
    font-size: var(--fs-sm);
  }

  .app-header-left {
    gap: 10px;
  }

  .app-title-input {
    width: 180px;
  }

  .site-header__slot--actions,
  .app-header-actions {
    gap: 8px;
  }
}

/* Tablets: the back link keeps its chevron (still a target, still obvious)
   but loses its word, and the title box starts giving up width. */
@media (max-width: 880px) {
  .site-header {
    gap: 10px;
  }

  .app-back-link__label {
    display: none;
  }

  .app-title-input {
    width: 150px;
    max-width: 26vw;
  }

  .language-switcher__current {
    min-width: 38px;
    width: 38px;
    padding: 0 5px;
  }
}

/* Phones: buttons that carry an icon drop to it. `.app-header-button__label`
   is only ever put on those — a label with no icon behind it (the upgrade
   button) keeps its text, since hiding it would leave an empty box. The label
   stays in the DOM, so the accessible name is unchanged, and each of those
   buttons repeats it as a `title` that i18n.js translates with everything else. */
@media (max-width: 720px) {
  .app-title-input {
    width: 110px;
    max-width: 30vw;
  }

  .app-header-button__label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .app-header-button {
    padding: 0 9px;
  }
}

/* Below this the row simply runs out of room, so the header stops pretending
   to be one row: it wraps, the title takes a line of its own (full width —
   easier to tap than a 100px box), and the brand/controls line wraps again on
   its own if even that doesn't fit. A taller header beats controls sliding off
   the right edge, which is what used to happen. */
@media (max-width: 600px) {
  .site-header {
    height: auto;
    min-height: 52px;
    flex-wrap: wrap;
    gap: 8px;
    padding: 8px 12px;
  }

  .site-header__slot--actions,
  .app-header-actions {
    gap: 6px;
  }

  .site-header__slot--center {
    order: 1;
    flex: 1 0 100%;
  }

  /* Pages with no title (landing, login, libraries, legal) leave the slot
     empty — but at 100% it would still open a zero-height second line, and the
     row gap below the brand pushed the logo and buttons off the header's
     vertical centre. Every page that fills it does so with an element. */
  .site-header__slot--center:not(:has(*)) {
    display: none;
  }

  .app-title-input {
    width: 100%;
    max-width: none;
    text-align: left;
  }

  .app-mode-dropdown__current {
    min-width: 0;
    gap: 6px;
    padding: 0 8px;
  }

  .header-upgrade-btn__full {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .header-upgrade-btn__short { display: inline; }
}

/* Phones: the language switcher, the admin shortcut and the account button are
   three separate targets competing with the title and the Premium CTA for a
   ~340px header — the row they sit in is the first thing to overflow. They
   collapse into one toggle that drops a menu holding all three, each as a
   labelled full-width row. Gated on `:has(.user-menu)` so pages with nothing
   to collapse keep the flat switcher: the public pages leave that block empty,
   and userMenu.js removes the menu outright when there's no session. */
@media (max-width: 720px) {
  .header-tools:has(.user-menu) {
    position: relative;
    display: block;
    flex: 0 0 auto;
  }

  .header-tools:has(.user-menu) > .header-tools__toggle {
    display: inline-flex;
    width: auto;
    gap: 2px;
    padding: 0 5px;
  }

  .header-tools:has(.user-menu) > .header-tools__panel {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 60;
    display: none;
    flex-direction: column;
    gap: 4px;
    min-width: 176px;
    padding: 6px;
    border: 1px solid rgba(120,80,30,0.25);
    background: var(--cream-surf);
    box-shadow: 0 8px 24px rgba(28,26,23,0.16);
  }

  .header-tools.is-open:has(.user-menu) > .header-tools__panel {
    display: flex;
  }

  .header-tools.is-open .header-tools__chevron {
    transform: rotate(180deg);
  }

  .header-tools__panel .language-switcher,
  .header-tools__panel .user-menu {
    display: flex;
    width: 100%;
    flex-direction: column;
    gap: 4px;
  }

  .header-tools__panel .app-header-iconbtn,
  .header-tools__panel .language-switcher__current {
    width: 100%;
    min-width: 0;
    height: 32px;
    justify-content: flex-start;
    gap: 8px;
    padding: 0 8px;
  }

  .header-tools__panel .language-switcher__label {
    display: inline;
  }

  /* The language switcher keeps its own <details> inside the menu, but its
     options go back into the flow: a panel this small should push the rows
     below down rather than have FR/EN cover them. (Only reachable while the
     <details> is open — the browser hides the content when it is closed.) */
  .header-tools__panel .language-switcher__menu {
    position: static;
    flex-direction: row;
    box-shadow: none;
  }

  .header-tools__panel .language-switcher__option {
    flex: 1 1 0;
  }

  .header-tools__panel .language-switcher__chevron {
    margin-left: auto;
  }

  .header-tools__panel .app-header-iconbtn__label {
    display: inline;
    font-family: var(--mono);
    font-size: var(--fs-sm);
    letter-spacing: 0.06em;
  }
}

/* ─── Subscription popup ──────────────────────────────────────────────────── */
/* Built by static/js/subscribeModal.js: a landscape spread of two facing
   pages — the plan and the billing block on the left (pattern-paper grid),
   the payment and its recap on the right,
   where Stripe's Payment Element is mounted in `#subscribe-payment` and themed
   from JS (it lives in an iframe and can't read these rules). Stacks above the
   "Mon compte" modal (z-index 100), which can be open underneath it. Below
   820px the pages stack. */
.subscribe-overlay {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(28,26,23,0.55);
}

.subscribe-overlay.is-open { display: flex; }

.subscribe-modal {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(940px, 100%);
  max-height: min(94vh, 780px);
  border: 1px solid var(--border-warm);
  background: var(--cream-surf);
  box-shadow: 0 22px 60px rgba(28,26,23,0.32);
}

.subscribe-modal__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex: 0 0 auto;
  padding: 13px 20px;
  border-bottom: 1px solid var(--border-warm);
}

.subscribe-modal__title {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--charcoal);
}

.subscribe-modal__close {
  border: 0;
  background: transparent;
  color: var(--warm-gray);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease;
}

.subscribe-modal__close:hover { color: var(--charcoal); }

.subscribe-modal__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.subscribe-note {
  flex: 0 0 auto;
  margin: 0;
  padding: 10px 20px;
  border-bottom: 1px solid var(--border-warm);
  background: var(--cream);
  font-size: var(--fs-base);
  line-height: 1.45;
  color: var(--charcoal-mid);
}

.subscribe-note[data-tone="warn"] {
  background: rgba(179,38,30,0.06);
  color: #8f241c;
}

/* ── The spread ──────────────────────────────────────────────────────────── */
.subscribe-spread {
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  min-height: 100%;
}

.subscribe-page {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 22px 26px 20px;
}

/* Left page: the site's pattern-paper grid, and a soft shadow along the
   gutter so the two halves read as facing pages. */
.subscribe-page--offer {
  border-right: 1px solid var(--border-warm);
  background-color: var(--cream);
  background-image:
    linear-gradient(var(--grid-minor) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-minor) 1px, transparent 1px),
    linear-gradient(var(--grid-major) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-major) 1px, transparent 1px);
  background-size: 20px 20px, 20px 20px, 100px 100px, 100px 100px;
  box-shadow: inset -14px 0 18px -16px rgba(28,26,23,0.28);
}

.subscribe-page--pay { background: var(--cream-surf); }

.subscribe-kicker {
  margin: 0 0 10px;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--warm-gray);
}

.subscribe-kicker:not(:first-child),
.subscribe-recap { margin-top: 20px; }

.subscribe-label {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--warm-gray);
}

/* ── Left page: plans ────────────────────────────────────────────────────── */
.subscribe-plans {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.subscribe-plan {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  width: 100%;
  padding: 12px 16px 12px 42px;
  border: 1px solid var(--border-warm);
  border-radius: 0;
  background: var(--white);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* Radio mark, drawn so it follows the theme instead of the browser's. */
.subscribe-plan::before {
  content: '';
  position: absolute;
  left: 16px;
  top: 50%;
  width: 14px;
  height: 14px;
  margin-top: -7px;
  border: 1px solid var(--border-warm);
  border-radius: 50%;
  background: var(--white);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.subscribe-plan:hover:not([disabled]) { border-color: var(--emerald); }

.subscribe-plan.is-selected {
  border-color: var(--emerald);
  box-shadow: inset 0 0 0 1px var(--emerald), 0 8px 22px rgba(28,26,23,0.08);
}

.subscribe-plan.is-selected::before {
  border-color: var(--emerald);
  box-shadow: inset 0 0 0 3px var(--white);
  background: var(--emerald);
}

.subscribe-plan:focus-visible {
  outline: 2px solid var(--emerald);
  outline-offset: 2px;
}

.subscribe-plan[disabled] { cursor: default; }
.subscribe-plan[disabled]:not(.is-selected) { background: var(--cream); opacity: 0.7; }

.subscribe-plan__main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  min-width: 0;
}

.subscribe-plan__label {
  font-family: var(--mono);
  font-size: var(--fs-md);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--charcoal);
}

.subscribe-plan__badge {
  padding: 2px 7px;
  background: var(--amber);
  color: var(--charcoal);
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.06em;
}

.subscribe-plan__price {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  flex: 0 0 auto;
}

.subscribe-plan__amount {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 700;
  line-height: 1.1;
  color: var(--charcoal);
}

.subscribe-plan__interval {
  font-size: var(--fs-sm);
  color: var(--warm-gray);
}

.subscribe-plan__meta {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.04em;
  color: var(--emerald);
}

.subscribe-reminder {
  margin: 4px 0 0;
  text-align: center;
  font-size: var(--fs-sm);
  line-height: 1.45;
  color: var(--warm-gray);
}

/* ── Left page: billing block (who the invoices are made out to) ─────────── */
.subscribe-billing { margin-top: 20px; }
.subscribe-billing[hidden] { display: none; }

.subscribe-segment {
  display: inline-flex;
  margin-bottom: 10px;
  border: 1px solid var(--border-warm);
  background: var(--white);
}

.subscribe-segment__option {
  height: 30px;
  padding: 0 14px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--warm-gray);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.subscribe-segment__option + .subscribe-segment__option { border-left: 1px solid var(--border-warm); }
.subscribe-segment__option:hover { color: var(--charcoal); }

.subscribe-segment__option.is-selected {
  background: var(--emerald);
  color: var(--white);
}

.subscribe-segment__option:focus-visible {
  outline: 2px solid var(--emerald);
  outline-offset: 2px;
}

.subscribe-fields {
  display: grid;
  gap: 10px;
  margin-bottom: 10px;
}

.subscribe-fields[hidden] { display: none; }
.subscribe-fields--pair { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.subscribe-fields--company { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
.subscribe-fields--street { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); }
.subscribe-fields--city { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr) minmax(0, 1.3fr); }

.subscribe-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.subscribe-input {
  width: 100%;
  min-width: 0;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--border-warm);
  border-radius: 0;
  background: var(--white);
  color: var(--charcoal);
  font-family: var(--sans);
  font-size: var(--fs-base);
}

.subscribe-input::placeholder { color: var(--warm-gray-soft); }

.subscribe-input:focus {
  outline: none;
  border-color: var(--emerald);
  box-shadow: 0 0 0 1px var(--emerald);
}

.subscribe-input.is-invalid {
  border-color: #b3261e;
  box-shadow: 0 0 0 1px #b3261e;
}

/* Same drawn chevron as the account modal's selects (.account-field select). */
.subscribe-select {
  appearance: none;
  padding-right: 26px;
  background-image: linear-gradient(45deg, transparent 50%, var(--warm-gray) 50%),
                    linear-gradient(135deg, var(--warm-gray) 50%, transparent 50%);
  background-position: calc(100% - 15px) 15px, calc(100% - 10px) 15px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  text-overflow: ellipsis;
  cursor: pointer;
}

.subscribe-billing .subscribe-error { margin-top: 0; }

/* ── Right page: recap (promo code + the amounts Stripe computed) ────────── */
.subscribe-summary {
  margin: 12px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--border-warm);
}

.subscribe-summary__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 2px 0;
  font-size: var(--fs-base);
  color: var(--charcoal-mid);
}

.subscribe-summary__row[hidden] { display: none; }
.subscribe-summary__row dt,
.subscribe-summary__row dd { margin: 0; }

.subscribe-summary__row.is-total {
  margin-top: 4px;
  color: var(--charcoal);
}

.subscribe-summary__row.is-total dt {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.subscribe-summary__row.is-total dd {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 700;
}

.subscribe-then {
  margin: 0;
  text-align: right;
  font-size: var(--fs-md);
  color: var(--warm-gray);
}

.subscribe-then[hidden] { display: none; }

/* ── Right page: payment ─────────────────────────────────────────────────── */
.subscribe-email {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px dashed var(--border-warm);
}

.subscribe-email__value {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-base);
  color: var(--charcoal);
}

/* Stripe sizes its own iframe; this is only the mount point. */
.subscribe-payment { min-height: 150px; }

.subscribe-loading {
  margin: 24px 0;
  text-align: center;
  color: var(--warm-gray);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.subscribe-fallback {
  margin: 0 0 8px;
  text-align: center;
  font-size: var(--fs-base);
}

.subscribe-fallback a { color: var(--emerald); }

.subscribe-link {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--emerald);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.06em;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.subscribe-link:hover { color: var(--emerald-dark); }
.subscribe-link[hidden] { display: none; }

.subscribe-promo__form {
  display: flex;
  gap: 8px;
}

.subscribe-promo__form[hidden] { display: none; }

.subscribe-promo__input {
  flex: 1 1 auto;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.subscribe-promo__apply {
  flex: 0 0 auto;
  height: 36px;
  padding: 0 14px;
  border: 1px solid var(--charcoal);
  border-radius: 0;
  background: transparent;
  color: var(--charcoal);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.subscribe-promo__apply:hover { background: var(--charcoal); color: var(--white); }

.subscribe-promo__applied {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
}

.subscribe-promo__applied[hidden] { display: none; }

.subscribe-promo__code {
  padding: 3px 8px;
  border: 1px dashed var(--emerald);
  color: var(--emerald);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
}

.subscribe-error {
  margin: 10px 0 0;
  font-size: var(--fs-md);
  line-height: 1.4;
  color: #8f241c;
}

.subscribe-error[hidden] { display: none; }

.subscribe-cta {
  height: 38px;
  padding: 0 22px;
  border: 0;
  background: var(--emerald);
  color: var(--white);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.15s ease, opacity 0.15s ease;
}

.subscribe-cta:hover:not([disabled]) { background: var(--emerald-dark); }

.subscribe-pay {
  width: 100%;
  height: 46px;
  margin-top: 16px;
}

.subscribe-pay[disabled] {
  opacity: 0.45;
  cursor: default;
}

.subscribe-legal {
  margin: 10px 0 0;
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--warm-gray);
}

.subscribe-legal a { color: var(--emerald); }

.subscribe-blocked {
  margin: 0 0 16px;
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--charcoal-mid);
}

/* ── Done step ───────────────────────────────────────────────────────────── */
.subscribe-done {
  padding: 48px 24px 40px;
  text-align: center;
}

.subscribe-done__title {
  margin: 0 0 8px;
  font-family: var(--serif);
  font-size: 24px;
  font-weight: 700;
  color: var(--charcoal);
}

.subscribe-done__text {
  margin: 0 auto 20px;
  max-width: 380px;
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--charcoal-mid);
}

/* ── Footer note ─────────────────────────────────────────────────────────── */
.subscribe-foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  flex: 0 0 auto;
  margin: 0;
  padding: 12px 20px;
  border-top: 1px solid var(--border-warm);
  color: var(--warm-gray);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.05em;
  text-align: center;
}

.subscribe-foot svg { flex-shrink: 0; }

@media (max-width: 820px) {
  .subscribe-spread { grid-template-columns: minmax(0, 1fr); }

  .subscribe-page { padding: 20px 18px; }

  .subscribe-page--offer {
    border-right: 0;
    border-bottom: 1px solid var(--border-warm);
    box-shadow: inset 0 -14px 18px -16px rgba(28,26,23,0.28);
  }
}

@media (max-width: 560px) {
  .subscribe-overlay { padding: 0; }

  .subscribe-modal {
    width: 100%;
    height: 100%;
    max-height: none;
    border: 0;
  }

  .subscribe-modal__bar { padding: 12px 16px; }
  .subscribe-page { padding: 18px 16px; }
  .subscribe-plan { padding-right: 12px; }
  .subscribe-fields--street { grid-template-columns: minmax(0, 1fr); }
  .subscribe-fields--city { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); }
  .subscribe-fields--city [data-field="country"] { grid-column: 1 / -1; }
  .subscribe-foot {
    padding: 8px 12px;
    font-size: var(--fs-2xs);
    letter-spacing: 0.02em;
  }
}

/* ── Sharing: read-only workspaces & shared-card badge ──────────────────────
   A creation someone shared with you opens in read-only mode. `readOnly.js`
   stamps `data-readonly="1"` on <body>; the rules below are what makes the page
   look inert. They target *controls*, never their containers, so the panels can
   still be scrolled and read — which is the entire point of a read-only view.
   The real enforcement is in api.js (every non-GET is refused); this is the
   half that tells the user before they try. */

/* The canvas itself stays live on purpose: clicking a point, a curve, a cote or
   a piece is how a reader opens the popup that shows its angle, distance,
   handles or seam allowances. What would *change* the drawing — dragging a
   handle, a label, a zone or a piece — is refused in JS (`state.readOnly` in
   editor/mouse.js, product/events.js, plan/pieceActions.js), because
   pointer-events cannot tell "click to look" from "drag to move". */

/* Actions: dimmed and inert. These do something, and here they cannot.
   The selector list sits inside :where() so it carries no specificity of its
   own — that is what lets the "still allowed" rule below simply override it,
   instead of an arms race of ids or an !important. */
body[data-readonly="1"] :where(
  .editor-toolbar button,
  #vars-panel button:not(#vars-panel-toggle),
  #vars-panel select,
  #stroke-panel button,
  #image-panel button,
  #image-panel input,
  .measurement-list-action[data-action="visibility"],
  .measurement-list-action[data-action="export"],
  #piece-list button,
  #piece-props button,
  #optimize-btn,
  #add-images-btn,
  #save-btn
) {
  pointer-events: none;
  opacity: 0.45;
}

/* Edit popups open for reading. Every field keeps its value on screen but no
   longer reacts (readOnly.js also blurs it, so the keyboard cannot type into
   it either); labels are included because clicking one toggles its checkbox. */
body[data-readonly="1"] :where(
  #edit-point-modal,
  #handle-modal,
  #curve-modal,
  #on-line-modal,
  #on-curve-modal,
  #cross-modal,
  #measurement-edit-modal,
  .seam-modal-card
) :is(input, select, textarea, label, button, svg) {
  pointer-events: none;
}

/* …except what closes the popup, and the seam popup's side picker, which only
   chooses whose allowance is displayed. An id inside :is() lends every match
   id specificity, so this wins over the rule above without !important. */
body[data-readonly="1"] :is(
  #edit-modal-cancel,
  #handle-modal-cancel,
  #curve-modal-cancel,
  #on-line-cancel,
  #on-curve-cancel,
  #cross-cancel,
  #measurement-edit-cancel,
  .seam-modal-close,
  .seam-modal-done,
  .seam-edge-trigger
) {
  pointer-events: auto;
}

/* Buttons that would commit, delete or reshape: not offered at all. The
   floating piece toolbar (Pattern and Plan) is nothing but such buttons. */
body[data-readonly="1"] :is(
  #edit-modal-confirm,
  #edit-modal-delete,
  #handle-modal-confirm,
  #curve-modal-confirm,
  #on-line-confirm-btn,
  #on-line-delete-btn,
  #on-curve-confirm-btn,
  #on-curve-delete-btn,
  #cross-confirm-btn,
  #cross-delete-btn,
  #projection-confirm-btn,
  #projection-delete-btn,
  #measurement-edit-confirm,
  .seam-modal-delete,
  .piece-toolbar
) {
  display: none;
}

/* Values: inert but fully legible. A shared sketch is opened *for* its
   measurement chart and its seam/fabric numbers — dimming those would take away
   the reason the reader is here. */
body[data-readonly="1"] :where(
  #vars-panel input,
  #piece-props input,
  #plan-width,
  #plan-height,
  #plan-spacing,
  #plan-copies,
  #plan-multi-sheet-toggle,
  #plan-sheet-options
) {
  pointer-events: none;
}

/* Switching size re-derives the drawing locally and saves nothing, so it stays
   available: seeing a graded sketch in another size is reading, not editing.
   The dependency tree and the moodboard popup sit in the editor toolbar but
   only display, so they stay available too. */
body[data-readonly="1"] #size-selector-btn,
body[data-readonly="1"] #size-dropdown button,
body[data-readonly="1"] #deps-tree-btn,
body[data-readonly="1"] #moodboard-btn,
body[data-readonly="1"] #size-selector {
  pointer-events: auto;
  opacity: 1;
}

/* The name keeps its normal look minus the caret — greying out the title of the
   thing you are trying to read would be the wrong trade. */
body[data-readonly="1"] .app-title-input {
  pointer-events: none;
  opacity: 0.75;
  cursor: default;
}

.readonly-banner {
  position: fixed;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--border-warm);
  border-radius: 999px;
  background: var(--cream-surf);
  box-shadow: 0 6px 20px rgba(28, 26, 23, 0.12);
  color: var(--charcoal-mid);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.04em;
  pointer-events: none;   /* never in the way of the canvas underneath */
  max-width: calc(100vw - 32px);
  flex-wrap: wrap;
  justify-content: center;
  row-gap: 6px;
}

.readonly-banner__icon {
  display: flex;
  color: var(--amber-ink);
}

/* A neutral variant of .account-note: this states a consequence, it does not
   report a problem, so it must not read like the unverified-email warning. */
.account-note--info {
  /* The base .account-note is placed for the modal header (negative top margin,
     20px side inset); inside a panel it needs neither. */
  margin: 16px 0 0;
  border-left: 2px solid var(--amber);
  background: var(--cream-dim);
  color: var(--warm-gray);
}

/* Share dialog: addresses already used in earlier shares, one click to refill
   the field. Pills in the warm palette so they read as suggestions, not as
   the emerald action button next to them. */
.share-recents-list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.share-recent-chip {
  max-width: 100%;
  padding: 2px 9px;
  border: 1px solid var(--border-warm);
  border-radius: 999px;
  background: var(--cream-surf);
  color: var(--charcoal-mid);
  font-size: var(--fs-xs);
  line-height: 1.5;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background-color 0.15s;
}

.share-recent-chip:hover,
.share-recent-chip:focus-visible {
  border-color: var(--emerald);
  background: var(--cream);
  color: var(--emerald);
  outline: none;
}

/* Library card badge naming the person a shared creation came from. */
.card-shared-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 100%;
  padding: 1px 8px;
  border: 1px solid var(--border-warm);
  border-radius: 999px;
  background: var(--cream-dim);
  color: var(--amber-ink);
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Versions: header button, card badge, popup rows, frozen banner ─────────
   The header button is secondary to Save, so it takes the outlined cream look
   rather than the charcoal fill. On a frozen version it turns amber — the same
   "you are looking, not editing" tone as the read-only banner. */
.app-version-button {
  border: 1px solid var(--border-warm);
  background: var(--cream-surf);
  color: var(--charcoal);
}

.app-version-button:hover,
.app-version-button:focus-visible {
  border-color: var(--emerald);
  background: var(--cream);
  color: var(--emerald);
  outline: none;
}

.app-version-button.is-frozen {
  border-color: var(--amber);
  color: var(--amber-ink);
}

/* With actions the banner wraps onto several lines on a phone, where a pill
   radius turns it into a blob — a card radius holds any number of lines. */
.readonly-banner:has(.readonly-banner__action) {
  border-radius: 16px;
}

/* The banner itself lets clicks through to the canvas; its links must not. */
.readonly-banner__action {
  pointer-events: auto;
  padding: 2px 10px;
  border: 1px solid var(--border-warm);
  border-radius: 999px;
  background: var(--cream);
  color: var(--emerald);
  font: inherit;
  font-size: var(--fs-xs);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color 0.15s, background-color 0.15s;
}

.readonly-banner__action:hover,
.readonly-banner__action:focus-visible {
  border-color: var(--emerald);
  background: var(--cream-surf);
  outline: none;
}

/* "V3" on a library card whose file has frozen versions; opens the popup. */
.card-version-badge {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border: 1px solid var(--border-warm);
  border-radius: 999px;
  background: var(--cream-surf);
  color: var(--emerald);
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.04em;
  line-height: 1.5;
  cursor: pointer;
  transition: border-color 0.15s, background-color 0.15s;
}

.card-version-badge:hover,
.card-version-badge:focus-visible {
  border-color: var(--emerald);
  background: var(--cream);
  outline: none;
}

.version-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  border-bottom: 1px solid var(--grid-major);
}

.version-row:last-child {
  border-bottom: 0;
}

.version-row.is-shown {
  border-radius: 4px;
  background: var(--cream-dim);
}

.version-number {
  flex: 0 0 auto;
  min-width: 2.4em;
  color: var(--emerald);
  font-family: var(--mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
}

/* "En cours" on the frozen version the working copy came back to. */
.version-tag {
  flex: 0 0 auto;
  color: var(--emerald);
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.version-shown {
  flex: 0 0 auto;
  color: var(--amber-ink);
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.version-action {
  flex: 0 0 auto;
  padding: 3px 8px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: transparent;
  color: var(--charcoal-mid);
  font-size: var(--fs-xs);
  cursor: pointer;
  transition: border-color 0.15s, background-color 0.15s;
}

.version-action:hover:not(:disabled),
.version-action:focus-visible {
  border-color: var(--border-warm);
  background: var(--cream);
  outline: none;
}

.version-action--primary {
  color: var(--amber-ink);
}

.version-action:disabled {
  opacity: 0.45;
  cursor: default;
}

/* A row wraps so its "changes since" block sits under the line, aligned
   with the note (past the V-number badge). */
.version-row {
  flex-wrap: wrap;
}

.version-note-edit {
  flex: 0 0 auto;
  padding: 0 4px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: transparent;
  color: var(--warm-gray-soft);
  font-size: var(--fs-2xs);
  line-height: 1.4;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background-color 0.15s;
}

.version-note-edit:hover:not(:disabled),
.version-note-edit:focus-visible {
  border-color: var(--border-warm);
  background: var(--cream);
  color: var(--emerald);
  outline: none;
}

.version-note-edit:disabled {
  opacity: 0.45;
  cursor: default;
}

.version-note-input {
  width: 100%;
  padding: 2px 6px;
  border: 1px solid var(--emerald);
  border-radius: 4px;
  background: var(--white);
  color: var(--charcoal);
  font-size: var(--fs-xs);
  outline: none;
}

.version-changes {
  flex: 1 0 100%;
  margin: 2px 0 0 calc(2.4em + 8px);
  color: var(--charcoal-mid);
  font-size: var(--fs-2xs);
  line-height: 1.5;
}

.version-changes--empty {
  color: var(--warm-gray);
  font-style: italic;
}

.version-changes summary {
  width: fit-content;
  color: var(--emerald);
  cursor: pointer;
  list-style-position: inside;
}

.version-changes summary:hover,
.version-changes summary:focus-visible {
  color: var(--emerald-dark);
  outline: none;
  text-decoration: underline;
}

.version-changes ul {
  margin: 2px 0 2px;
  padding-left: 10px;
  border-left: 2px solid var(--cream-dim);
}

.version-changes li {
  overflow-wrap: anywhere;
}

.version-change-label {
  color: var(--warm-gray);
}

.version-change-label.has-items::after {
  content: "\00a0:";
}

html[lang="en"] .version-change-label.has-items::after {
  content: ":";
}
