:root {
  --fg: #1c2b23;
  --bg: #fbf9f4;
  --accent: #2f6f4f;
  --accent-dark: #234f38;
  --card: #ffffff;
  --border: #e3ddd0;
  --muted: #6b6255;
  --radius: 18px;
  --serif: Georgia, "Palatino Linotype", Palatino, serif;
}

* { box-sizing: border-box; }

/* A visible, on-brand focus ring for every interactive element — keyboard
   and switch-control users get the same "what's selected" clarity a mouse
   user gets from hover, instead of inheriting whatever (or nothing) the
   browser default supplies. */
a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible,
.tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

body {
  margin: 0;
  font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--fg);
  line-height: 1.55;
}

/* Full-page background photo (landing page only, via body.landing-page) —
   fixed and covering the entire scrollable page depth, not just the first
   viewport. Sections that sit on top go semi-transparent so the photo reads
   through the whole page, with a solid card for the form itself for legibility. */
body.landing-page {
  background-image: linear-gradient(180deg, rgba(20, 26, 22, .25), rgba(15, 20, 17, .55) 45%, rgba(15, 20, 17, .88)), url(/static/images/hero.jpg?v=362c4ab44885);
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
  background-repeat: no-repeat;
  min-height: 100vh;
  min-height: 100dvh;
  /* Center the hero+form as one block in the viewport instead of letting
     the hero pin to the top and push the form (the page's only actionable
     element) down past the fold — on an iPhone SE viewport the submit
     button was fully cut off with zero scrolling. min-height (not height)
     keeps this safe if content ever runs long: flexbox only has room to
     center when there's slack to distribute, so taller-than-viewport
     content just falls back to normal top-aligned flow instead of
     centering off both edges. */
  display: flex;
  flex-direction: column;
  justify-content: center;
}
@media (max-width: 600px) {
  body.landing-page { background-attachment: scroll; }
}

.wrap {
  max-width: 640px;
  margin: 0 auto;
  padding: 0 0 64px;
}

a { color: var(--accent); }

/* ---------- Landing: hero text over the full-page body background ---------- */
/* Was 70vh — reasonable for a marketing/browse page, wrong for this one:
   the whole page's job is capturing one email field, and burning 70% of a
   phone's viewport on photography before the form even starts is what put
   the submit button off-screen in the first place. Trimmed so hero+form
   together comfortably fit one screen on common phone heights, letting the
   body's vertical centering (above) actually center instead of falling
   back to overflow. */
.cover-hero {
  position: relative;
  min-height: 34vh;
  min-height: 34dvh;
  display: flex;
  align-items: flex-end;
  padding: 28px 20px 32px;
  color: #fdf9ee;
}
.cover-hero-inner { width: 100%; max-width: 600px; margin: 0 auto; }
.cover-hero .kicker {
  display: block;
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: .78rem;
  font-weight: 600;
  color: #d7e6db;
  margin-bottom: 10px;
}
.cover-hero h1 {
  font-family: var(--serif);
  font-size: 2rem;
  line-height: 1.2;
  margin: 0 0 14px;
  color: #fff;
}
.cover-hero .lead {
  color: #ecece2;
  font-size: 1.02rem;
  margin: 0;
  max-width: 46ch;
}

/* ---------- Small hero (booking page banner) ---------- */
.hero.small {
  position: relative;
  padding: 26px 20px 24px;
  text-align: left;
  background-image: linear-gradient(180deg, rgba(20, 26, 22, .2), rgba(15, 20, 17, .72)), url(/static/images/hero.jpg?v=362c4ab44885);
  background-size: cover;
  background-position: center 65%;
  color: #fff;
}
.hero.small .wrap-inner { max-width: 600px; margin: 0 auto; }
.hero.small h1 {
  font-family: var(--serif);
  font-size: 1.5rem;
  margin: 0 0 4px;
  color: #fff;
}
.hero.small .lead { color: #e9e9df; margin: 0; font-size: .95rem; }
.hero.small h1 a { color: inherit; text-decoration: none; border-bottom: 1px solid rgba(255,255,255,.4); }
.hero.small h1 a:hover { border-bottom-color: #fff; }

/* ---------- Persistent mini-header ----------
   Hidden until the hero (with its own contact buttons) scrolls out of
   view (see booking.html's IntersectionObserver), so a guest deep in a
   tab isn't stuck scrolling all the way back up to reach the concierge. */
.mini-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  background: var(--accent-dark);
  color: #fff;
  box-shadow: 0 2px 12px rgba(0, 0, 0, .18);
  transform: translateY(-100%);
  transition: transform .2s ease;
}
.mini-header.is-visible { transform: translateY(0); }
.mini-header-title { font-weight: 700; font-size: .95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mini-header-actions { display: flex; gap: 14px; flex: none; }
.mini-header-actions a { text-decoration: none; font-size: 1.1rem; line-height: 1; }
@media (prefers-reduced-motion: reduce) { .mini-header { transition: none; } }

/* ---------- Big hero (booking page) ---------- */
.hero.big {
  position: relative;
  min-height: 50vh;
  min-height: 50dvh;
  display: flex;
  align-items: flex-end;
  padding: 28px 20px 28px;
  text-align: left;
  background-size: cover;
  background-position: center 55%;
  /* Solid fallback so a property with no resolvable photo (no upload yet,
     object storage unreachable, heroImageURL came back empty) still reads
     as a deliberate panel instead of transparent — was invisible before
     this, letting the page background show through where a photo should
     be (see cmd/guest/booking.go and quotes.go's heroImageURL). The
     inline background-image (set per-page, only when a URL resolves)
     paints over this when present. */
  background-color: #1c2b23;
  color: #fff;
}
/* On a phone, half the viewport of decorative photo pushes the one thing
   guests actually need — the status rail — below the fold. The hero still
   shows (property name, dates, contact), it just stops trying to fill half
   the screen; height now follows its content instead of a fixed vh. */
@media (max-width: 640px) {
  .hero.big { min-height: 0; padding: 20px 16px 22px; }
}
/* Below ~360px-wide devices the background photo is pure decoration costing
   real mobile data for no gain — the hero-card's own gradient already
   guarantees contrast on its own, so the photo can drop out entirely and
   the hero still reads fine as a plain brand-colored panel. !important is
   needed here: the photo URL arrives as a per-property inline style (see
   booking.html), which this media query must outrank. */
@media (max-width: 380px) {
  .hero.big { background-image: linear-gradient(160deg, #1c2b23, #14201a) !important; }
}
.hero.big .wrap-inner { max-width: 600px; margin: 0 auto; width: 100%; }
/* While the real photo is still loading (see booking.html's preload
   Image()), a soft animated tone stands in for it instead of a flash of
   the bare gradient-only hero snapping to a photo a beat later. */
.hero.big.is-loading-photo { background-image: none !important; background-color: #24352a; animation: hero-pulse 1.4s ease-in-out infinite; }
@keyframes hero-pulse { 0%, 100% { background-color: #24352a; } 50% { background-color: #2c4034; } }
@media (prefers-reduced-motion: reduce) { .hero.big.is-loading-photo { animation: none; } }

/* Text sits on its own gradient card rather than directly on the photo —
   the photo already carries a dark scrim (inline style, set per-property in
   booking.html) but that alone can leave light patches of the photo behind
   the title on a bright image. A second, tighter gradient here guarantees
   contrast regardless of what the underlying photo looks like. */
.hero-card {
  /* Nommé, parce que le bandeau de menu doit annuler exactement ce
     rembourrage pour toucher les deux bords — et qu'il change avec la
     largeur. Une valeur recopiée à la main y était fausse dès le premier
     point de rupture. */
  --hero-pad-y: 24px;
  --hero-pad-x: 22px;
  background: linear-gradient(160deg, rgba(15, 20, 17, .82), rgba(15, 20, 17, .58));
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border-radius: var(--radius);
  padding: var(--hero-pad-y) var(--hero-pad-x);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .28);
}
.hero.big h1 {
  font-family: var(--serif);
  font-size: 1.7rem;
  margin: 0 0 6px;
  color: #fff;
}
.hero.big h1 a { color: inherit; text-decoration: none; border-bottom: 1px solid rgba(255,255,255,.4); }
.hero.big h1 a:hover { border-bottom-color: #fff; }
.hero.big .lead { color: #e9e9df; margin: 0 0 18px; font-size: .95rem; }

.hero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin: 0 0 20px;
  padding: 0;
  list-style: none;
}
.hero-meta li {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.hero-meta .meta-label {
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: .68rem;
  color: #d7e6db;
  font-weight: 600;
}
.hero-meta .meta-value {
  font-size: 1.02rem;
  font-weight: 700;
  color: #fff;
}

@media (min-width: 900px) {
  .hero.big { padding: 48px 40px; }
  .hero-card { --hero-pad-y: 32px; --hero-pad-x: 36px; max-width: 640px; }
  .hero.big h1 { font-size: 2.4rem; }
  .hero.big .lead { font-size: 1.05rem; }
  .hero-meta .meta-value { font-size: 1.15rem; }
}

/* ---------- Status rail (top of the booking dashboard) ---------- */
.status-rail {
  display: flex;
  align-items: center;
  gap: 0;
  list-style: none;
  margin: 20px 0 8px;
  padding: 4px 2px;
  overflow-x: auto;
  /* Fades the last ~24px so a step cut off at the edge reads as "more to
     scroll" instead of looking like the rail simply stops there. */
  mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
}
.rail-step {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}
.rail-step:not(:last-child)::after {
  content: "";
  width: 22px;
  height: 1px;
  background: var(--border);
  margin: 0 8px;
  flex: none;
}
.rail-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid var(--border);
  flex: none;
}
.rail-label { font-size: .82rem; color: var(--muted); white-space: nowrap; }
.rail-done .rail-dot { background: var(--accent); border-color: var(--accent); }
.rail-done .rail-label { color: var(--fg); }
.rail-now .rail-dot { background: #fff; border-color: #b8823c; box-shadow: 0 0 0 3px rgba(184,130,60,.18); }
.rail-now .rail-label { color: #8a611c; font-weight: 700; }

/* Mobile stand-in for .status-rail — a filled segment bar (no scrolling
   needed to see "how far along") plus the current step's name in plain
   text. Hidden on desktop, where the dot rail above already works fine. */
.status-rail-mobile { display: none; }
.status-rail-mobile-bar {
  display: flex;
  gap: 3px;
  height: 6px;
  margin: 20px 0 8px;
}
.status-rail-mobile-seg { flex: 1; border-radius: 999px; background: var(--border); }
.status-rail-mobile-seg.rail-done { background: var(--accent); }
.status-rail-mobile-seg.rail-now { background: #b8823c; }
.status-rail-mobile-text { margin: 0; font-size: .85rem; color: var(--muted); }
.status-rail-mobile-text strong { color: #8a611c; }

/* ---------- Bottom thumb-reach tab bar (mobile only) ----------
   Same visual pattern as cmd/desk's mobile nav (fixed bottom, icon over
   label, active = brand color) — see booking.html's comment on why the
   "active" state is driven by the shared activate() JS instead of a URL
   prefix match like desk's version. */
.mobile-tab-bar {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 100;
  background: var(--card);
  border-top: 1px solid var(--border);
  padding-bottom: env(safe-area-inset-bottom, 0);
  box-shadow: 0 -2px 8px rgba(0, 0, 0, .08);
}
.mobile-tab-bar-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .15rem;
  min-height: 56px;
  padding: .4rem .2rem;
  background: none;
  border: none;
  color: var(--muted);
  font-size: .68rem;
  font-weight: 600;
  position: relative;
  cursor: pointer;
  /* Les entrées sont des <button> ; la règle reste au cas où l'une redevienne
     un lien, et parce qu'elle ne coûte rien. Elle servait à la barre recopiée
     de booking_messages.html, page supprimée depuis. */
  text-decoration: none;
}
.mobile-tab-bar-icon { font-size: 1.2rem; line-height: 1; }
.mobile-tab-bar-item.is-active { color: var(--accent-dark); }
.mobile-tab-bar-badge {
  position: absolute;
  top: .3rem;
  right: calc(50% - 1.2rem);
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: #b8823c;
  color: #fff;
  font-size: .62rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
@media (max-width: 640px) {
  .status-rail { display: none; }
  .status-rail-mobile { display: block; }
  .tab-bar-scroller { display: none; }
  .mobile-tab-bar { display: flex; }
  body:has(.mobile-tab-bar) .wrap { padding-bottom: 72px; }
}

/* ---------- Tabs ---------- */
.tab-bar-scroller { position: relative; margin: 18px 0 0; }
.tab-bar {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  border-bottom: 1px solid var(--border);
  mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
}
.tab-bar .tab {
  flex: none;
  padding: 12px 14px;
  font-size: .92rem;
  font-weight: 600;
  color: var(--muted);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  white-space: nowrap;
}
.tab-bar .tab.is-active { color: var(--accent-dark); border-bottom-color: var(--accent); }
.tab-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: 5px;
  border-radius: 999px;
  background: #b8823c;
  color: #fff;
  font-size: .7rem;
  font-weight: 700;
  vertical-align: 2px;
}
.unread-note { color: #8a611c; }
.tab-panel { display: none; }
.tab-panel.is-active { display: block; }

/* ---------- Status pills (contract / caution / profile-saved) ---------- */
.pill {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: .82rem;
  font-weight: 700;
}
.pill-ok { background: #eef3ee; color: var(--accent-dark); }
.pill-pending { background: #f6ecd9; color: #8a611c; }

/* ---------- Other stays (booking.html "Aperçu" tab) ---------- */
.other-stays { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 12px; }
.other-stay {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}
.other-stay:last-child { border-bottom: none; }
.other-stay-info { display: flex; flex-direction: column; gap: 2px; }
.other-stay-property { font-weight: 600; }
.other-stay-dates { font-size: .85rem; }

.message-form label { font-weight: 600; font-size: .95rem; }

/* ---------- Options table (booking.html "Options" tab) ----------
   Invoice/statement style rather than a card list — thin horizontal rules
   instead of a full grid, figures right-aligned and tabular, a single rule
   before the total. Already-paid and payable options share one table
   (paid rows shown checked-and-greyed) so the whole cost of the stay reads
   as one object, the way a real invoice would show it. */
.smart-options { width: 100%; border-collapse: collapse; font-size: .92rem; }
.smart-options caption {
  text-align: left; font-size: .78rem; color: var(--muted);
  padding-bottom: 10px; text-transform: uppercase; letter-spacing: .04em;
}
.smart-options thead th {
  text-align: left; font-weight: 600; font-size: .78rem; color: var(--muted);
  padding: 0 8px 10px; border-bottom: 2px solid var(--fg);
}
.smart-options th.num, .smart-options td.num { text-align: right; font-variant-numeric: tabular-nums; }
.smart-options th.sel, .smart-options td.sel { text-align: center; width: 36px; }
.smart-options td { padding: 12px 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
.smart-options tbody tr:last-child td { border-bottom: 1px solid var(--fg); }
.smart-options .opt-name { font-weight: 600; }
/* « En savoir plus » : le lien vers la page qui vend l'option, quand elle en
   a une. Discret par construction — c'est une porte de sortie vers de la
   lecture, et la décision qu'on veut est la case à cocher juste à gauche. Il
   ne doit pas peser autant qu'elle. */
/* Le récapitulatif de la décision : quand rien n'est encore coché, la ligne
   des options n'est pas un montant mais une proposition, et elle se lit sur
   trois niveaux — ce qu'on propose, l'action, ce que ça n'engage pas. */
.recap-invite{display:flex;flex-direction:column;align-items:flex-start;gap:.15rem;}
.recap-detail{font-size:.82rem;color:#5d6d64;}
.btn-link-strong{font-weight:600;}
/* La ligne vers laquelle on a été envoyé depuis le site public : elle doit
   se voir sans clignoter — on arrive sur une page qu'on ne connaît pas et il
   faut comprendre en une seconde ce qui a été coché pour nous. */
.smart-options tr.is-highlighted{background:rgba(47,111,79,.09);
  box-shadow:inset 3px 0 0 var(--accent,#2f6f4f);}
.smart-options .opt-detail { color: var(--muted); font-size: .84rem; }
.smart-options tbody tr.is-selected { background: #f7faf7; }
.smart-options tbody tr.is-paid td { color: var(--muted); }
.smart-options tbody tr.is-paid .sel { color: var(--accent); font-weight: 700; }
.smart-options .paid-tag {
  display: inline-block; font-size: .68rem; padding: 2px 8px; margin-left: 6px;
  border-radius: 999px; background: #eef3ee; color: var(--accent-dark);
}
/* Commandé mais pas réglé : la même ligne, un autre statut. La couleur est la
   même que celle des échéances en attente plus haut (.pill-pending), pour que
   « à régler » se lise pareil partout sur la page — et le mot est dans le
   texte, pas seulement dans la teinte. */
.smart-options tbody tr.is-ordered td { color: var(--muted); }
.smart-options tbody tr.is-ordered .sel { color: #8a611c; font-weight: 700; }
.smart-options .paid-tag-pending { background: #f6ecd9; color: #8a611c; }
.smart-options tfoot tr.foot-note td { font-weight: 400; padding-top: 2px; }
.smart-options tfoot tr.foot-note .num { font-size: .95rem; color: var(--muted); }
.smart-options input[type="checkbox"] {
  width: 20px; height: 20px; accent-color: var(--accent); cursor: pointer;
}
.smart-options input[type="checkbox"]:disabled { cursor: not-allowed; }
.smart-options tfoot td { padding: 14px 8px 4px; font-weight: 700; border-bottom: none; }
.smart-options tfoot .num { font-size: 1.1rem; color: var(--accent-dark); font-variant-numeric: tabular-nums; transition: color .15s ease; }
.smart-options tfoot .num.is-updated { color: #b8823c; }
@media (prefers-reduced-motion: reduce) { .smart-options tfoot .num { transition: none; } }
/* A "par personne" option priced while the party size is unknown (see
   optionRow.NeedsGuestCount) — checkbox disabled, an explicit prompt
   instead of a silently-wrong price computed as if for one guest. */
.option-warning { font-size: .82rem; color: #8a611c; }
.option-warning a { color: inherit; text-decoration: underline; font-weight: 600; }
.st-cta-row { display: flex; justify-content: flex-end; margin-top: 14px; }
.st-cta-row .btn { margin: 0; }
.st-cta-row .btn:disabled { opacity: .5; cursor: not-allowed; }
/* ---------- Le sélecteur d'options sur un téléphone ----------
   Cinq colonnes — case, nom, prix, quantité, total — ne tiennent pas dans
   390 points. Elles ne débordaient pas : elles se comprimaient, et chaque
   ligne devenait quatre lignes de mots coupés, un champ nombre de la largeur
   d'un pouce et un en-tête « Total » rogné par le bord de l'écran. Une
   personne qui choisit ce qu'elle achète lisait un tableau illisible.

   Alors le tableau cesse d'être un tableau. Chaque option devient un bloc :
   son nom en clair, la case à gauche, et dessous les chiffres côte à côte
   avec leur étiquette — celle que l'en-tête portait, rendue par data-label,
   parce qu'un nombre sans son nom ne veut rien dire une fois la colonne
   partie.

   Les trois tableaux qui portent .smart-options y passent ensemble : la
   commande du séjour, le panier d'options, et celui du devis. Le même geste
   sur le même objet. */
@media (max-width: 560px) {
  .smart-options,
  .smart-options caption,
  .smart-options tbody,
  .smart-options tfoot,
  .smart-options tbody tr,
  .smart-options tfoot tr,
  .smart-options td { display: block; width: auto; }
  .smart-options thead { display: none; }

  .smart-options tbody tr {
    position: relative;
    padding: 12px 0 12px 34px;
    border-bottom: 1px solid var(--border);
  }
  .smart-options tbody tr:last-child { border-bottom: 1px solid var(--fg); }
  /* Les cellules ne portent plus de trait : en display:block chacune traçait
     le sien, et une ligne de tableau devenait trois filets empilés. Le trait
     appartient au bloc, qui est maintenant la ligne. */
  .smart-options td,
  .smart-options tbody tr:last-child td { border-bottom: none; padding: 0; }

  /* La case reste à gauche du bloc, alignée sur la première ligne du nom. */
  .smart-options td.sel {
    position: absolute; left: 0; top: 14px;
    width: auto; text-align: left;
  }
  /* La commande du séjour n'a pas de case : pas de gouttière à réserver. */
  .smart-options.due-table tbody tr { padding-left: 0; }

  .smart-options .opt-name { display: block; }

  /* Les chiffres, sur une ligne, chacun sous son étiquette, et sur une
     grille : trois colonnes fixes plutôt que trois blocs qui se poussent, de
     sorte que « TOTAL » tombe au même endroit d'une option à l'autre et que
     l'œil descende la colonne au lieu de la chercher. */
  .smart-options tbody tr:has(td.qty) { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px 12px; }
  .smart-options tbody tr:has(td.qty) > td:not(.sel):not(.num):not(.qty) { grid-column: 1 / -1; }
  .smart-options td.num,
  .smart-options td.qty { margin: 2px 0 0; text-align: left; }

  /* « 50,00 € / unité » sur une ligne : le retour à la ligne coûtait une
     ligne par option, dix fois. */
  .smart-options td.num > br { display: none; }
  .smart-options td.num .opt-detail { margin-left: .3em; }
  .smart-options td[data-label]::before {
    content: attr(data-label);
    display: block; margin-bottom: 2px;
    font-size: .68rem; color: var(--muted);
    text-transform: uppercase; letter-spacing: .04em;
  }
  .smart-options td.qty input[type="number"] { width: 4.5em; }

  /* La commande du séjour : le nom sur sa ligne, puis l'état et le montant
     côte à côte — l'argent à droite, où l'œil le cherche. */
  .smart-options.due-table tbody tr {
    display: grid; grid-template-columns: 1fr auto;
    gap: 6px 10px; align-items: baseline;
  }
  .smart-options.due-table tbody tr > td:first-child { grid-column: 1 / -1; }
  .smart-options.due-table td.num { text-align: right; margin: 0; font-weight: 600; }

  /* Les totaux deviennent des lignes libellé / montant. */
  .smart-options tfoot tr { display: flex; justify-content: space-between; gap: 12px; padding: 10px 0 0; }
  .smart-options tfoot td { padding: 0; }
  .smart-options tfoot td:empty { display: none; }

  /* Une case à cocher se vise avec un pouce, pas avec une souris. */
  .smart-options input[type="checkbox"] { width: 24px; height: 24px; }
}

@media (max-width: 480px) {
  .smart-options { font-size: .86rem; }
}

/* ---------- Dedicated messages page ---------- */
.contact-links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 0 0 8px;
}
.contact-link {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 220px;
  padding: 16px 18px;
  background: #eef3ee;
  border: 1px solid var(--border);
  border-radius: 14px;
  color: var(--accent-dark);
  text-decoration: none;
  font-weight: 700;
  font-size: 1rem;
}
.contact-link:hover { background: #e3ede5; }
.contact-link .contact-icon { font-size: 1.3rem; }
.back-link { display: inline-block; margin-bottom: 16px; font-weight: 600; }


.content-pad { padding: 0 16px; }
.content-pad.landing { padding: 24px 20px 8px; }

.brand {
  text-align: center;
  padding: 18px 8px 0;
  font-family: var(--serif);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: .02em;
  color: var(--accent-dark);
}

h1 { font-size: 1.6rem; margin: 8px 0; color: var(--accent-dark); }

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px 20px;
  margin: 20px 0;
  box-shadow: 0 4px 16px rgba(28, 43, 34, 0.06);
}

h2 { margin-top: 0; font-size: 1.15rem; color: var(--accent-dark); }
h3 { font-size: 1rem; color: var(--accent); margin: 18px 0 8px; }

.lookup-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.lookup-form label {
  font-weight: 600;
  margin-top: 12px;
  font-size: .95rem;
}

input, textarea {
  width: 100%;
  padding: 14px;
  font-size: 1rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: #fffefb;
  color: var(--fg);
  font-family: inherit;
}
textarea { min-height: 100px; resize: vertical; }

.btn {
  display: inline-block;
  padding: 15px 22px;
  font-size: 1rem;
  font-weight: 600;
  border-radius: 12px;
  border: none;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  min-height: 50px;
}
.btn-primary { background: var(--accent); color: #fff; margin-top: 16px; }
.btn-primary:hover { background: var(--accent-dark); }
.btn-secondary {
  background: #eef3ee;
  color: var(--accent-dark);
  border: 1px solid var(--border);
  font-size: .9rem;
  padding: 10px 16px;
  min-height: 42px;
}
.btn-secondary:hover { background: #e3ede5; }

.hint {
  text-align: center;
  color: var(--muted);
  font-size: .9rem;
  padding: 4px 20px 0;
}

/* ---------- Dev preview badge ---------- */
.dev-preview {
  display: block;
  text-align: center;
  margin: 28px auto 0;
  max-width: 320px;
}
.dev-preview a {
  display: inline-block;
  padding: 8px 16px;
  border: 1px dashed #9a8f7d;
  border-radius: 999px;
  color: #6b5f4d;
  background: #f4efe4;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .02em;
  text-decoration: none;
}
.dev-preview a:hover { background: #ece4d2; }

.summary {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 16px;
  margin: 0;
}
.summary dt { color: var(--muted); font-weight: 600; }
.summary dd { margin: 0; }
.mono { font-family: ui-monospace, monospace; }

/* Acompte/solde ledger (booking.html "Votre séjour" card) — reuses the same
   invoice-style .smart-options table as the options selector, so "what's
   paid, what's due" reads with the same visual language as the rest of the
   money-related UI in this app instead of a one-off callout. */
.due-table { margin-top: 18px; }
.due-table + .st-cta-row, .st-cta-row + .st-cta-row { margin-top: 10px; }

/* Inline "Voyageurs" field in the "Votre séjour" summary (booking.html) —
   only rendered when the count is unknown, right where the guest already
   sees Arrivée/Départ/Prix total instead of a separate settings page. */
.inline-count-form {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
.inline-count-form input[type="number"] {
  width: 64px;
  padding: 8px 10px;
  min-height: 0;
}
.inline-count-form .btn { padding: 8px 14px; min-height: 0; }

.badge {
  display: inline-block;
  padding: 3px 12px;
  border-radius: 999px;
  background: #eef3ee;
  color: var(--accent-dark);
  font-size: .85rem;
  font-weight: 600;
}

.general-info {
  white-space: pre-wrap;
  background: var(--bg);
  border-radius: 12px;
  padding: 14px;
  margin-top: 14px;
  border: 1px solid var(--border);
}

.muted { color: var(--muted); }
.small-note { font-size: .85rem; margin-top: -8px; }

.option-list { list-style: none; padding: 0; margin: 0; }
.option-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
}
.option-item:last-child { border-bottom: none; }
.option-item.purchased { color: var(--accent-dark); }
.option-name { font-weight: 600; flex: 1 1 auto; }
.option-price { color: var(--muted); font-size: .9rem; }
.inline-form { flex: 0 0 auto; }

/* Ce que dit un refus de dates — voir cmd/guest/start_availability.go.
   Trois blocs, dans l'ordre où on les propose : les autres dates de ce gîte,
   les autres gîtes à ces dates-là, puis un humain. */
.stay-alt-label {
  margin: 22px 0 8px;
  font-size: .78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
}
.stay-alt-dates { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; }
.stay-date-chip {
  display: inline-block;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface, #fff);
  font-size: .9rem;
  text-decoration: none;
}
.stay-date-chip:hover { border-color: var(--accent); }
.stay-alt-list { margin-top: 4px; }
/* Un gîte pris reste dans la liste — on cherche parfois une maison précise —
   mais il ne se lit pas comme une réponse. */
.option-item-taken { opacity: .7; }
.option-item-taken:hover { opacity: 1; }

/* « Prévenez-moi si ces dates se libèrent » — un champ et un bouton sur une
   ligne. Il tient sous les alternatives et au-dessus du devis : c'est ce
   qu'on propose à quelqu'un que rien de ce qui précède n'a convaincu. */
.stay-alert-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
  margin: 22px 0 0;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
}
.stay-alert-form label { flex: 1 1 240px; margin: 0; }
.stay-alert-form .btn { flex: 0 0 auto; }

/* La sélection multi-gîtes : plusieurs maisons côte à côte, pour la même
   semaine. Elles se comparent en largeur — c'est le geste de la page — et
   passent en colonne dès qu'il n'y a plus la place de les mettre côte à côte,
   parce qu'une carte de 180 px ne se compare plus, elle se devine. */
.proposal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 18px;
  margin-top: 18px;
}
.proposal-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface, #fff);
}
.proposal-card.is-chosen { border-color: var(--accent); border-width: 2px; }
.proposal-photo { height: 160px; background-size: cover; background-position: center; }
.proposal-body { display: flex; flex-direction: column; gap: 6px; padding: 16px; }
.proposal-body h3 { margin: 0; font-size: 1.05rem; }
.proposal-meta { margin: 0; color: var(--muted); font-size: .9rem; }
.proposal-price { margin: 4px 0 10px; font-size: 1.15rem; font-weight: 600; }
.proposal-price span { display: block; font-size: .82rem; font-weight: 400; color: var(--muted); }
/* Le bouton pousse en bas de la carte : sans cela, trois maisons aux textes
   de longueurs différentes alignent trois boutons à trois hauteurs, et l'œil
   lit un désordre là où il y a un choix. */
.proposal-body form { margin-top: auto; }
.proposal-body .btn { width: 100%; }
.proposal-chosen { margin-top: auto; color: var(--accent); font-weight: 600; }

.messages {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 18px;
}
.message {
  border-radius: 14px;
  padding: 12px 16px;
  max-width: 90%;
}
.message-guest {
  background: #eaf2ec;
  align-self: flex-end;
  margin-left: auto;
}
.message-concierge {
  background: #f4efe4;
  align-self: flex-start;
}
.message-meta {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: .75rem;
  color: var(--muted);
  margin-bottom: 4px;
}
.message-body { white-space: pre-wrap; }

.message-form { display: flex; flex-direction: column; gap: 10px; }

.error-card { text-align: center; }
.error-card h1 { margin-top: 8px; }

/* ---------- Desktop scale-up ---------- */
@media (min-width: 900px) {
  .wrap { max-width: 760px; }
  .cover-hero { min-height: 32vh; padding: 40px 40px 44px; }
  .cover-hero h1 { font-size: 2.6rem; }
  .cover-hero .lead { font-size: 1.1rem; }
  .content-pad.landing { padding: 32px 0 8px; }
  .hero.small { padding: 36px 40px; }
  .hero.small h1 { font-size: 1.9rem; }
  .card { padding: 28px 32px; }
}

@media (min-width: 1400px) {
  .wrap { max-width: 900px; }
  .cover-hero { min-height: 30vh; }
  .cover-hero h1 { font-size: 3.1rem; }
  .cover-hero .lead { font-size: 1.2rem; }
}

/* Monetary amounts (see internal/money and the "eur" template helper).
   nowrap keeps the € with its number and stops a thousands separator
   becoming a line break; tabular-nums gives every digit the same advance so
   a right-aligned column lines up on the decimal comma. */
.money { white-space: nowrap; font-variant-numeric: tabular-nums; }
td.amount, th.amount, .amount { text-align: right; }
td.amount .money, td.amount { font-variant-numeric: tabular-nums; }

/* Le code de la boîte à clés est lu debout devant une porte, souvent dans le
   noir, souvent avec les mains prises : il ne partage pas la mise en page des
   autres lignes du livret, il occupe sa propre place et il est grand. */
.lockbox {
  display: flex; flex-direction: column; gap: 0.2rem;
  margin: 0 0 1rem; padding: 0.9rem 1.1rem;
  background: #f0f5f1; border-radius: 12px;
}
.lockbox-label {
  font-size: 0.74rem; font-weight: 600; letter-spacing: 0.09em;
  text-transform: uppercase; color: #5c6f62;
}
.lockbox-code {
  font-family: ui-monospace, monospace;
  font-size: 2.1rem; font-weight: 700; letter-spacing: 0.22em;
  line-height: 1.15; color: #1f4a33;
}

/* Le mandat de prélèvement : une case à cocher qui engage, donc lisible comme
   une phrase et non comme une mention légale reléguée en petits caractères. */
.check-item.mandate {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  margin: 0.9rem 0;
  font-size: 0.9rem;
  line-height: 1.5;
}
.check-item.mandate input { margin-top: 0.25rem; flex: none; }

/* ---------------------------------------------------------------------------
   La décision : accepter un devis
   ---------------------------------------------------------------------------
   Le récapitulatif, ce qui va se passer, puis UNE action. Accepter et refuser
   ne sont pas deux options équivalentes présentées côte à côte : l'une est ce
   qu'on attend, l'autre une sortie. Deux boutons de même poids font hésiter
   sur ce qui est demandé, et l'hésitation à cet endroit précis se paie. */
.card-decision { border-color: var(--accent); }

.decision-recap { margin-bottom: 1.25rem; }
.decision-recap dt.decision-total,
.decision-recap dd.decision-total {
  padding-top: .55rem;
  border-top: 1px solid var(--line, #dfe4e2);
  font-weight: 700;
  font-size: 1.05rem;
}

/* Ce qui se passe après le clic, dit avant. Un voyageur qui découvre la
   signature électronique une fois engagé est un voyageur qui referme. */
.decision-steps {
  margin: 0 0 1.25rem;
  padding-left: 1.15rem;
  color: var(--muted);
  font-size: .92rem;
  line-height: 1.55;
}
.decision-steps li { margin: .2rem 0; }

.decision-action { margin: 0; }
.decision-action .small-note { margin: .5rem 0 0; font-size: .85rem; }

/* Le refus reste atteignable et ne se déguise pas — il n'a simplement pas le
   poids visuel de l'action principale. */
.decision-decline { margin: 1rem 0 0; }

.btn-lg { font-size: 1.05rem; padding: .85rem 1.5rem; width: 100%; }
@media (min-width: 620px) { .btn-lg { width: auto; } }

.btn-link {
  background: none; border: 0; padding: 0;
  color: var(--accent-dark); font: inherit; font-size: .92rem;
  text-decoration: underline; cursor: pointer;
}
.btn-link:hover { color: var(--accent); }

.form-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem;
  margin-top: 1rem;
}

/* Une option qui ne peut pas être chiffrée tant que le nombre de voyageurs
   n'est pas connu : visible, désactivée, et qui dit pourquoi — la masquer
   ferait disparaître à la fois l'option et la raison. */
.smart-options tbody tr.needs-guest-count { opacity: .6; }

/* La colonne « combien » : un champ étroit, la légende de l'unité dessous.
   Le nombre par défaut vient du séjour mais ne le décide pas — la pension
   complète est prise un jour ou deux, pas onze. */
.smart-options th.qty, .smart-options td.qty { text-align: center; width: 5.5rem; }
.smart-options td.qty input[type="number"] {
  width: 3.6rem; padding: .3rem .4rem; text-align: center;
  border: 1px solid var(--line, #dfe4e2); border-radius: 6px;
  font: inherit; font-variant-numeric: tabular-nums;
}
.smart-options td.qty .opt-detail { display: block; margin-top: .15rem; font-size: .78rem; }
/* Un nombre que le séjour a déjà fixé : même place, même colonne, mais sans
   le cadre ni les flèches qui promettent qu'on peut le changer. */
.smart-options td.qty .qty-fixed { display: inline-block; padding: .35rem 0; font-variant-numeric: tabular-nums; }
/* La restauration côté voyageur : le tableau des repas et l'offre pour ceux
   qui n'ont rien commandé. */
.cat-state-line{display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap;margin:0 0 .8rem;}
.cat-table{width:100%;border-collapse:collapse;font-size:.9rem;}
.cat-table th,.cat-table td{padding:.45rem .5rem;text-align:left;border-bottom:1px solid #e6e2d8;vertical-align:baseline;}
.cat-table thead th{font-size:.75rem;text-transform:uppercase;letter-spacing:.03em;color:#6b7a70;}
.cat-table .num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;}
.cat-table tfoot td{font-weight:700;border-top:1px solid #cfd8d2;border-bottom:0;}
/* Discrète : le service existe, et se dit une fois — sans concurrencer ce que
   la page a à faire. */
@media (max-width:40rem){
  .cat-table,.cat-table tbody,.cat-table tr,.cat-table td{display:block;width:100%;}
  .cat-table thead,.cat-table tfoot tr td:first-child{display:none;}
  .cat-table tr{border:1px solid #e6e2d8;border-radius:8px;margin:0 0 .5rem;padding:.4rem .6rem;}
  .cat-table td{border:0;display:flex;justify-content:space-between;gap:.6rem;padding:.15rem 0;}
  .cat-table td::before{content:attr(data-label);color:#6b7a70;font-size:.75rem;text-transform:uppercase;}
  .cat-table .num{text-align:right;}
}

/* ---------- La carte qui ouvre la page, et le nombre ----------
   Voir GUEST.md §4 et §6. Une carte d'action, un montant, et rien d'autre au
   même niveau de voix : tout le reste de la page est du détail qu'on consulte
   après avoir su quoi faire. */
/* PAS DE BARRE VERTICALE À GAUCHE.
   Un filet coloré sur le bord d'une carte est une convention d'alerte —
   bandeau d'erreur, encadré d'avertissement — et il en met une sur chaque
   carte importante jusqu'à ce qu'aucune ne le soit. Ce qui distingue cette
   carte, c'est sa place (la première) et son fond ; la couleur reste pour ce
   qui la mérite : le bouton, et un statut. */
.next-step { background: #f4f7f3; }
.next-step-waiting { background: #faf7ef; }
.next-step-done { background: var(--card); }
.next-step-steps {
  margin: 0 0 14px; padding-left: 1.2em;
  color: var(--fg); font-size: .94rem; line-height: 1.7;
}
.next-step-steps li { padding-left: 2px; }
.next-step-kicker,
.money-kicker {
  margin: 0 0 4px; font-size: .74rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em; font-weight: 600;
}
.next-step h2 { margin-top: 0; }
.next-step-body { margin: 0 0 14px; }
.next-step-action { margin: 0 0 6px; }
.next-step-action .btn { display: inline-block; }

/* Le montant est la réponse : il a la taille d'une réponse. */
.money-amount {
  margin: 0 0 14px;
  font-size: 2.4rem; line-height: 1.1; font-weight: 700;
  color: var(--accent-dark); font-variant-numeric: tabular-nums;
}
.money-settled .money-amount { font-size: 1.5rem; }
.money-detail > summary {
  cursor: pointer; font-size: .86rem; color: var(--muted);
  padding: 6px 0; list-style: revert;
}
.money-detail[open] > summary { margin-bottom: 6px; }
.money-lines { display: grid; grid-template-columns: 1fr auto; gap: 6px 16px; margin: 0; }
.money-lines dt { font-weight: 400; }
.money-lines dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
.money-line-detail { display: block; font-size: .78rem; color: var(--muted); }
.money-lines .money-total { font-weight: 700; border-top: 1px solid var(--fg); padding-top: 6px; }
.money-lines .money-paid { color: var(--muted); }

/* Le catalogue d'options, replié. Le résumé porte le titre, donc il doit se
   comporter comme un titre : pas de puce de <summary>, et le compte à droite
   dit ce qu'on trouve dedans sans l'ouvrir. */
.options-fold > summary { cursor: pointer; list-style: none; }
.options-fold > summary::-webkit-details-marker { display: none; }
.options-fold > summary h2 { display: inline; }
.options-fold > summary::after {
  content: "▾"; float: right; color: var(--muted); font-size: 1.1rem; line-height: 1.6;
}
.options-fold[open] > summary::after { content: "▴"; }
.options-fold-hint { display: block; font-size: .84rem; color: var(--muted); margin-top: 2px; }



@media (max-width: 560px) {
  .money-amount { font-size: 2rem; }
  .next-step-action .btn { display: block; width: 100%; text-align: center; }
}

/* ---------- La barre d'onglets dans le bandeau ----------
   Sur grand écran elle vit dans la carte du bandeau, au-dessus de ce qu'elle
   commande, ce qui rend au corps de page toute sa largeur et met la
   navigation là où on la cherche. Sur un téléphone elle reste en bas, sous le
   pouce (guestMobileBar), et celle-ci disparaît. */
/* UN MENU, ET QUI EN A L'AIR.
   Des mots posés sur une photo ne se lisent pas comme une barre de
   navigation : rien ne dit où commence et où finit ce qu'on peut cliquer. Un
   fond en dégradé sous toute la rangée le dit d'un coup d'œil, et chaque
   entrée devient une cellule rectangulaire dont la totalité est la cible —
   pas seulement le mot. Les cellules sont de largeur égale (display:table),
   ce qui aligne le menu au lieu de le laisser flotter au gré de la longueur
   des libellés. */
.hero-card .tab-bar-scroller {
  /* Les marges négatives annulent exactement le rembourrage de la carte : le
     bandeau doit toucher ses deux bords, sinon il flotte au milieu et ne se
     lit plus comme sa base. */
  margin: 20px calc(-1 * var(--hero-pad-x)) calc(-1 * var(--hero-pad-y));
  border-radius: 0 0 var(--radius) var(--radius);
  overflow: hidden;
  background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(0,0,0,.22));
  border-top: 1px solid rgba(255,255,255,.16);
}
/* DES CELLULES ÉGALES, EN GRILLE ET NON EN TABLEAU.
   `display: table` sur la barre et `table-cell` sur les entrées donne bien la
   mise en page voulue — sauf que les entrées sont des <button>, et qu'un
   navigateur refuse de transformer un contrôle de formulaire en cellule de
   tableau : Chrome les laisse en inline-block, et les cellules reprennent la
   largeur de leur texte. Une grille à colonnes égales dit la même chose et
   fonctionne sur des boutons, parce que c'est le conteneur qui décide de la
   largeur de ses éléments et non l'élément lui-même. */
.hero-card .tab-bar {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  width: 100%;
  border-bottom: none;
  gap: 0;
  mask-image: none;
  -webkit-mask-image: none;
}
.hero-card .tab-bar .tab {
  /* La cellule entière est la cible, pas seulement le mot. */
  width: 100%;
  height: 100%;
  text-align: center;
  vertical-align: middle;
  /* Petit espacement vertical : une rangée de menu, pas une rangée de
     boutons. La cible reste haute de 40 points, ce qui suffit à la souris. */
  padding: 9px 8px;
  color: rgba(255, 255, 255, .8);
  border-bottom: none;
  border-radius: 0;
  border-right: 1px solid rgba(255,255,255,.12);
  /* Les coins de la première et de la dernière cellule suivent ceux de la
     carte, sinon un angle droit dépasse de l'arrondi. */
  transition: background .15s ease, color .15s ease;
}
.hero-card .tab-bar .tab:first-child { border-bottom-left-radius: var(--radius); }
.hero-card .tab-bar .tab:last-child { border-right: none; border-bottom-right-radius: var(--radius); }
.hero-card .tab-bar .tab:hover { color: #fff; background: rgba(255,255,255,.10); }
/* L'onglet courant se signale par son fond et par un soulignement du mot,
   sans filet blanc sous la cellule : le trait redoublait le dégradé et, sur
   une carte aux angles arrondis, coupait la cellule de coin en travers de
   l'arrondi. Le soulignement porte sur le libellé seul — d'où le <span>
   autour, une décoration héritée par le bouton descendrait aussi sur
   l'icône. */
.hero-card .tab-bar .tab.is-active {
  color: #fff;
  background: linear-gradient(180deg, rgba(255,255,255,.20), rgba(255,255,255,.08));
}
.hero-card .tab-bar .tab.is-active .tab-label {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}
.hero-card .tab-bar .tab-icon { display: block; margin-bottom: 4px; font-size: 1rem; opacity: .9; }
.hero-card .tab-bar .tab-badge { background: rgba(255,255,255,.22); color: #fff; }

/* Ailleurs — la barre hors bandeau, si elle réapparaît un jour — l'icône
   n'appartient pas au dessin : elle y doublerait le libellé sans rien ajouter
   dans une rangée qui défile. */
.tab-bar-scroller:not(.hero-card *) .tab-icon { display: none; }
@media (max-width: 760px) {
  /* Sous cette largeur la barre du bas suffit : deux navigations pour le même
     jeu d'onglets, c'est une de trop et c'est celle qui prend la place du
     titre. */
  .hero-card .tab-bar-scroller { display: none; }
}

/* ---------- Les deux pièces du dossier ----------
   Le devis et le contrat sont deux documents du même dossier : ils se
   présentent ensemble, de même forme et de même taille, plutôt qu'en deux
   cartes empilées avec deux boutons de largeurs différentes. Chaque tuile est
   entièrement cliquable — l'icône, le nom, l'état, l'appel — parce qu'on vise
   un document, pas un mot. */
.doc-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin: 16px 0 12px;
}
.doc-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 18px 18px 16px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: #fff;
  text-decoration: none;
  color: inherit;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.doc-tile:hover {
  border-color: var(--accent);
  box-shadow: 0 6px 18px rgba(35, 79, 56, .12);
  transform: translateY(-1px);
}
.doc-tile-icon {
  font-size: 1.5rem; color: var(--accent);
  margin-bottom: 10px;
}
.doc-tile-name { font-weight: 700; font-size: 1.02rem; }
.doc-tile-note { color: var(--muted); font-size: .86rem; }
.doc-tile-cta {
  margin-top: 10px; font-weight: 600; font-size: .9rem; color: var(--accent-dark);
}
.doc-tile:hover .doc-tile-cta { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) { .doc-tile { transition: none; } .doc-tile:hover { transform: none; } }

/* ---------- Les options les plus demandées ----------
   Une rangée courte, un prix par ligne, un bouton par ligne. Pas de case à
   cocher : cocher puis valider, c'est deux gestes pour une envie, et une case
   pré-cochée serait à la fois malhonnête et illégale pour un supplément
   payant. Voir featuredOptions dans quote_options.go. */
.quick-options { display: grid; gap: 10px; margin: 16px 0 12px; }
.quick-option {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: #fff;
}
.quick-option-name { font-weight: 600; }
.quick-option-price { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.quick-option-unit { display: block; font-weight: 400; font-size: .78rem; color: var(--muted); }
.quick-option .quick-option-add { margin: 0; white-space: nowrap; }
@media (max-width: 560px) {
  /* Le nom sur sa ligne, le prix et le bouton sous lui : à 390 points les
     trois colonnes écrasaient le nom en quatre lignes. */
  .quick-option { grid-template-columns: 1fr auto; }
  .quick-option-name { grid-column: 1 / -1; }
  .quick-option .quick-option-add { width: auto; }
}

/* ---------- Le dialogue des options ----------
   <dialog> plutôt qu'une surcouche à la main : l'empilement, le piège à
   focus et la touche Échap sont au navigateur, qui les fait mieux que nous.
   Large mais pas plein écran sur un ordinateur, plein écran sur un téléphone
   — là-bas une fenêtre flottante n'est qu'une page plus étroite. */
.options-modal {
  width: min(760px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  padding: 0;
  border: none;
  border-radius: var(--radius);
  background: var(--card);
  color: var(--fg);
  box-shadow: 0 24px 64px rgba(0, 0, 0, .3);
}
.options-modal::backdrop { background: rgba(15, 20, 17, .55); }
.options-modal-head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 18px 20px 12px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
}
.options-modal-head h2 { margin: 0; }
.options-modal-close {
  border: none; background: none; cursor: pointer;
  font-size: 1.6rem; line-height: 1; color: var(--muted);
  padding: 0 6px; min-height: 44px; min-width: 44px;
}
.options-modal-close:hover { color: var(--fg); }
.options-modal-body { padding: 4px 20px 20px; overflow-y: auto; max-height: calc(100vh - 150px); }

@media (max-width: 560px) {
  .options-modal {
    width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh;
    margin: 0; border-radius: 0;
  }
  .options-modal-body { max-height: calc(100dvh - 72px); }
}

/* Le pictogramme d'une option d'ajout rapide. */
.quick-option { grid-template-columns: auto 1fr auto auto; }
.quick-option-icon { color: var(--accent); font-size: 1.1rem; width: 1.4em; text-align: center; }
@media (max-width: 560px) {
  .quick-option { grid-template-columns: auto 1fr auto; }
  .quick-option-name { grid-column: 2 / -1; }
  .quick-option-icon { grid-row: 1; }
}

/* ---------- Les deux tuiles du guide ----------
   Deux destinations de même rang, donc deux tuiles de même forme, côte à côte
   sur un écran large et l'une sous l'autre sur un téléphone. L'image porte la
   moitié du message : on choisit une balade sur une photo, pas sur un
   intitulé. */
.guide-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
  margin-top: 16px;
}
.guide-tile {
  display: block;
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
  background: #fff;
  text-decoration: none;
  color: inherit;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.guide-tile:hover {
  border-color: var(--accent);
  box-shadow: 0 6px 18px rgba(35, 79, 56, .12);
  transform: translateY(-1px);
}
.guide-tile-photo {
  display: flex; align-items: center; justify-content: center;
  height: 132px;
  background-size: cover; background-position: center;
  background-color: #e8ece7;
  color: #fff; font-size: 1.8rem;
}
/* Sur la tuile du guide la photo est un fond, pas le sujet : le pictogramme
   doit rester lisible par-dessus. */
/* Chemin relatif à cette feuille, donc juste quel que soit le préfixe de
   montage — voir le commentaire dans guest_shell.html. */
.guide-tile-photo-guide {
  background-image: url(images/hero.jpg?v=362c4ab44885);
  box-shadow: inset 0 0 0 999px rgba(20, 40, 30, .42);
}
.guide-tile-text { display: block; padding: 14px 16px 16px; }
.guide-tile-name { display: block; font-weight: 700; }
.guide-tile-note { display: block; color: var(--muted); font-size: .86rem; margin-top: 2px; }
@media (prefers-reduced-motion: reduce) { .guide-tile { transition: none; } .guide-tile:hover { transform: none; } }

/* WCAG 2.2, critère 2.5.8 : une cible d'au moins 24 points sur 24.
   .btn-link mesurait 23 — un pixel sous la règle, et bien plus sous ce qu'un
   pouce vise confortablement. Le rembourrage vertical le porte à 32 sans
   déplacer le texte, puisque la ligne reste la même. */
.btn-link { min-height: 24px; padding-top: 4px; padding-bottom: 4px; }

/* ---------- La carte du séjour, en deux colonnes ----------
   Les faits à gauche, ce qu'on peut ajouter à droite : on vérifie et on
   complète du même regard. Sous 900 points la grille redevient une colonne et
   les options passent en second, parce qu'on vérifie avant d'acheter. */
.stay-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px 32px; }
.stay-facts { margin: 0; }
.stay-options-title {
  margin: 0 0 10px; font-size: .78rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: .05em; font-weight: 600;
}
@media (max-width: 900px) {
  .stay-grid { grid-template-columns: 1fr; gap: 4px; }
  .stay-options { margin-top: 18px; }
}

/* La liste complète : même forme que les options, parce que c'est le même
   geste. Un bouton, pas un lien souligné sous une rangée de boutons. */
.quick-option-all {
  border: 1px dashed var(--border);
  background: none;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.quick-option-all:hover { border-color: var(--accent); background: #f7faf7; }
.quick-option-all .quick-option-add { pointer-events: none; }

/* Dans la colonne de droite il n'y a pas la place de tout mettre sur une
   ligne : « Linge de toilette (set serviettes) » y tenait sur quatre. Le nom
   prend donc sa ligne, le prix et le bouton la suivante, et l'icône tient la
   marge des deux. Des zones nommées plutôt qu'un compte de colonnes : on lit
   la disposition dans la règle. */
.stay-options .quick-option {
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    "icon name  name"
    "icon price add";
  align-items: center;
  row-gap: 6px;
}
.stay-options .quick-option-icon  { grid-area: icon; align-self: start; padding-top: 2px; }
.stay-options .quick-option-name  { grid-area: name; }
.stay-options .quick-option-price { grid-area: price; }
.stay-options .quick-option-add   { grid-area: add; }
.stay-options .quick-option-all .quick-option-price { display: none; }

/* Le pictogramme d'un intitulé : à largeur fixe, pour que les mots restent
   alignés d'une ligne à l'autre. */
.summary .dt-icon {
  display: inline-block; width: 1.3em;
  color: var(--accent); opacity: .75;
  margin-right: .1em;
}
/* Une note qui porte sur toute la liste, pas sur la cellule de droite. */
.summary .summary-note { grid-column: 1 / -1; margin: -2px 0 0; }

/* Les options au moment de signer : deux phrases, pas un tableau. Ce qu'elles
   deviennent dans le contrat, et jusqu'à quand on peut en ajouter. */
.decision-options { margin: 4px 0 18px; }
.decision-options-line {
  display: grid; grid-template-columns: 1.4em 1fr; gap: 10px;
  align-items: start; margin: 0 0 8px;
}
.decision-options-line i { color: var(--accent); margin-top: .18em; }
.decision-options-cta { margin: 12px 0 0; }
.decision-options-cta .btn { margin: 0; }

/* Une option déjà prise : la ligne reste, en retrait, et son bouton devient
   un état. Voir featuredOptions. */
/* Retenue : la ligne se teinte et porte sa coche, et le bouton devient la
   commande inverse. Le nom garde sa couleur de texte — grisé, il se lisait
   comme indisponible alors qu'il est justement ce qu'on a choisi. */
.quick-option.is-taken { background: #f4f7f3; border-color: #cbdccb; }
.option-taken-check { color: var(--accent-dark); margin-right: 6px; }
/* Retirer est une action mineure : elle doit être atteignable d'un doigt
   (2.5.8 : 24 px au moins) sans avoir le poids visuel d'Ajouter. */
.quick-option-remove {
  background: none;
  border-color: transparent;
  color: var(--muted);
  min-height: 32px;
}
.quick-option-remove:hover,
.quick-option-remove:focus-visible {
  color: #8a2b20;
  border-color: #e3c8c4;
  background: #fdf6f5;
}
.quick-option-add:disabled { opacity: 1; cursor: progress; }
/* La rangée revient du serveur : sans transition, elle clignote. */
.quick-option { transition: background-color .18s ease, border-color .18s ease; }

/* Pour les lecteurs d'écran seulement — l'annonce d'un ajout, et le mot qui
   dit qu'une ligne est retenue là où la coche ne parle qu'aux yeux. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- Les rubriques du guide, sous les deux tuiles ----------
   Des liens et non des tuiles : les deux du haut portent une image et sont
   des destinations, celles-ci sont une table des matières. Leur donner la
   même taille aurait fait cinq objets de même poids et plus de hiérarchie du
   tout. Une ligne chacune, l'icône dans la marge, le sous-titre dessous en
   petit — ce qui se lit d'un coup d'œil et se touche sans viser. */
.guide-links { list-style: none; margin: 14px 0 0; padding: 0; }
.guide-links li + li { border-top: 1px solid var(--border); }
.guide-links a {
  display: grid;
  grid-template-columns: 1.6em 1fr;
  gap: 12px;
  align-items: start;
  padding: 12px 2px;
  color: inherit;
  text-decoration: none;
  min-height: 44px;
}
.guide-links a:hover, .guide-links a:focus-visible { color: var(--accent-dark); }
.guide-links i { color: var(--accent); font-size: 1.05rem; padding-top: 2px; text-align: center; }
.guide-links strong { display: block; font-weight: 600; }
.guide-link-note { display: block; color: var(--muted); font-size: .85rem; }

/* ---------- L'attente qui n'est pas une étape ----------
   Sous la carte de l'étape en cours et détachée d'elle par un filet : elle ne
   concurrence pas l'action principale, et elle ne se lit pas comme sa note de
   bas de page non plus. Voir nextAside. */
.next-step-aside {
  display: grid;
  grid-template-columns: 1.3em 1fr;
  gap: 10px;
  align-items: start;
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: .92rem;
}
.next-step-aside i { color: var(--accent); padding-top: 2px; }
.next-step-aside-link {
  display: inline-block;
  margin-left: 4px;
  font-weight: 600;
  white-space: nowrap;
}

/* ---------- Répondre à une proposition de repas ----------
   Deux formulaires côte à côte, donc deux boutons sur une ligne, et le oui
   d'abord : c'est la réponse attendue, et l'ordre inverse fait hésiter sur
   celui qui engage. Sur un téléphone étroit ils passent l'un sous l'autre
   plutôt que de se comprimer. */
.cat-answer { display: flex; flex-wrap: wrap; align-items: stretch; gap: 10px; margin-top: 16px; }
.cat-answer form { margin: 0; display: flex; }
/* Les deux boutons sont une paire, et une paire se dessine pareil. .btn-primary
   porte un margin-top de 16 points et une hauteur de 50, .btn-secondary une de
   42 et un rembourrage plus court : côte à côte, l'un flottait au-dessus de
   l'autre et paraissait plus petit. Ici ils partagent leur métrique, et seule
   la couleur dit lequel engage. */
.cat-answer .btn { margin: 0; min-height: 48px; padding: 13px 20px; font-size: .95rem; }

/* ---------- Le lien d'évitement ----------
   Hors de l'écran jusqu'au focus, puis posé par-dessus tout le reste : il
   sert à une personne au clavier et ne doit rien coûter aux autres. Pas
   display:none, qui le retirerait aussi de l'ordre de tabulation — et il
   n'existe que pour cet ordre-là. */
.skip-link {
  position: absolute;
  left: 8px;
  top: -60px;
  z-index: 100;
  padding: 10px 16px;
  border-radius: 0 0 8px 8px;
  background: var(--accent-dark, #14532d);
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  transition: top .12s ease;
}
.skip-link:focus { top: 0; }
#contenu:focus { outline: none; }

/* ---------- La barre, sur les téléphones étroits ----------
   « Documents » demande 59 points et la cellule n'en offrait que 58 à 320 :
   un point de trop, que flex reprenait aux voisines — d'où des cellules
   inégales sur un iPhone SE, la plus longue volant sa marge aux autres. Le
   rembourrage horizontal ne sert à rien ici, le contenu étant centré : on le
   rend, et les cinq cellules redeviennent égales. */
@media (max-width: 380px) {
  .mobile-tab-bar-item { padding-left: 0; padding-right: 0; }
}

/* Une carte en attente, à côté de celle de l'étape : même famille, et le
   filet de gauche en plus pour qu'on voie d'un coup qu'il y en a deux. */
.pending-card { border-left: 3px solid var(--accent); }

/* ============================================================================
   Le devis à choix — les maisons mises en regard.

   Toutes les cartes portent les mêmes renseignements dans le même ordre :
   c'est ce qui rend une comparaison possible. On décide sur ce qui se compare
   — combien de personnes, combien de chambres, combien ça coûte — et pas sur
   une phrase différente par maison, qui donne à lire sans donner à comparer.
   ============================================================================ */
.house-cards {
  display: grid;
  /* 13rem : trois maisons tiennent de front dans la colonne de contenu du
     portail. Au-delà, la troisième passe seule à la ligne et la comparaison
     se fait en deux temps. */
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 16px;
  margin-top: 18px;
}
.house-card { min-width: 0; }
.house-card-link {
  display: flex;
  flex-direction: column;
  height: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--card);
  color: inherit;
  text-decoration: none;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.house-card-link:hover,
.house-card-link:focus-visible {
  border-color: var(--brand);
  box-shadow: 0 8px 24px rgba(28, 43, 34, 0.12);
  transform: translateY(-2px);
  text-decoration: none;
}
.house-card-photo {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  background: var(--border);
}
.house-card-photo-empty { aspect-ratio: 3 / 2; }
.house-card-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 16px 16px;
  flex: 1;
}
.house-card-name { font-weight: 700; font-size: 1.05rem; line-height: 1.25; }
.house-card-where { color: var(--muted); font-size: .86rem; }
.house-card-facts { display: flex; flex-wrap: wrap; gap: 12px; font-size: .86rem; color: var(--muted); }
.house-card-facts i { color: var(--brand); margin-right: 4px; }
/* Le prix en bas de carte, à la même hauteur d'une carte à l'autre : c'est le
   chiffre qu'on relit trois fois, et il ne doit pas se déplacer selon qu'un
   nom tient sur une ligne ou deux. */
.house-card-price { margin-top: auto; padding-top: 8px; font-weight: 700; font-size: 1.1rem; }
.house-card-price-note { font-weight: 400; font-size: .82rem; color: var(--muted); }
.house-card-cta { color: var(--brand); font-weight: 600; font-size: .9rem; }
.choice-intro { font-size: 1.05rem; line-height: 1.5; }
.choice-intro-by { margin-top: -6px; font-size: .86rem; }

/* Le bandeau de la comparaison ne montre aucune des trois maisons — c'est
   justement la question qu'elle pose — mais la côte, qui ne départage
   personne. Plus court que celui d'un devis pour autant : ce qu'on est venu
   lire est en dessous, et un demi-écran au-dessus de trois cartes retarde la
   seule chose que cette page a à faire.
   Le repli de fond reste le vert du bandeau, pour que l'attente du fichier
   ne soit pas un rectangle blanc. */
.hero.big.hero-choice {
  min-height: 32vh;
  min-height: 32dvh;
  /* Le repli, pour que l'attente du fichier ne soit pas un rectangle blanc. */
  background-color: var(--ink, #16231d);
  background-size: cover;
  /* Le fichier est déjà recadré à la forme du bandeau — l'horizon au premier
     cinquième, l'estran en dessous — donc rien à décaler ici. */
  background-position: center;
  background-image: linear-gradient(180deg, rgba(20, 26, 22, .30), rgba(15, 20, 17, .72)), var(--hero-photo);
}

/* Le téléphone prend la copie de 960px — 92 ko contre 360, sur la connexion
   qui les compte. Même seuil que le reste du portail. */
@media (max-width: 640px) {
  .hero.big.hero-choice {
    background-image: linear-gradient(180deg, rgba(20, 26, 22, .30), rgba(15, 20, 17, .72)), var(--hero-photo-mobile);
  }
}

/* Le retour vers les autres maisons : présent, discret, et jamais à côté de
   l'action principale — il n'en est pas le pendant. */
.switch-house { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }
.switch-house form { display: inline; margin: 0; }
/* Le lien de partage : le champ prend la largeur, le bouton reste à sa
   droite et passe dessous sur un téléphone étroit. Le champ est en lecture
   seule mais sélectionnable — sans JavaScript, copier à la main reste le
   chemin, et il doit marcher. */
.share-block { margin: 1rem 0; }
.share-block label { display: block; font-weight: 600; margin-bottom: .35rem; }
/* Le lien est long : il lui faut plus de place qu'à une adresse e-mail. */
.share-row { max-width: 34rem; }

/* ---------------------------------------------------------------------------
   La maquette de paiement par carte (checkout_mock.html).

   Une page à part, centrée, sans la navigation du séjour : c'est ce que fait
   un vrai Checkout, et le parcours ne se relit correctement que si la sortie
   du site se voit. Le bandeau reste au-dessus de tout et ne se ferme pas — la
   page ressemble assez au vrai pour qu'il faille le dire fort.
   --------------------------------------------------------------------------- */
body.checkout-mock { background: #f6f8f7; }

.mock-banner {
  position: sticky; top: 0; z-index: 10;
  background: #8a5a00; color: #fff; padding: .8rem 1rem;
  font-size: .95rem; line-height: 1.4; text-align: center;
}
.mock-banner strong { color: #fff; }

.checkout-wrap { display: flex; justify-content: center; padding: 2.5rem 1rem 4rem; }
.checkout-card {
  width: 100%; max-width: 26rem; background: #fff; border-radius: 12px;
  border: 1px solid #e2e8e5; padding: 1.75rem; box-shadow: 0 1px 3px rgba(20,30,25,.06);
}
.checkout-head { text-align: center; margin-bottom: 1.5rem; }
.checkout-merchant { margin: 0; font-size: .85rem; color: #5a6b62; letter-spacing: .02em; }
.checkout-label { margin: .5rem 0 0; color: #5a6b62; }
.checkout-amount { margin: .25rem 0 0; font-size: 2rem; font-weight: 700; }

.checkout-form label { display: block; margin-bottom: .9rem; font-size: .9rem; color: #3d4a44; }
.checkout-form input {
  display: block; width: 100%; margin-top: .3rem; padding: .65rem .75rem;
  border: 1px solid #cfd8d2; border-radius: 8px; font-size: 1rem; background: #fff;
}
.checkout-row { display: flex; gap: .75rem; margin-bottom: .9rem; }
.checkout-row input { margin-top: 0; }
.checkout-pay { width: 100%; margin-top: .5rem; }
.checkout-note, .checkout-hint { font-size: .85rem; color: #5a6b62; margin: .75rem 0 0; }
.checkout-cancel { text-align: center; margin: 1.25rem 0 0; font-size: .9rem; }

/* Le contexte sous le montant : au nom de qui, et qui a déjà versé. Plus
   petit que le chiffre — il l'explique, il ne le concurrence pas — et assez
   lisible pour être lu, ce qu'un gris pâle n'obtient jamais. */
.money-context {
  margin: .75rem 0 0; font-size: .92rem; line-height: 1.45; color: #3d4a44;
}

/* ---------------------------------------------------------------------------
   Le montant que l'on règle.

   Le champ et son signe € sur une ligne. Sans cette règle l'input héritait du
   « display:block; width:100% » des formulaires de cette feuille, et le €
   tombait seul sur la ligne suivante, à gauche, sans rapport visible avec le
   champ qu'il qualifie. Une unité séparée de son nombre n'est plus une unité.
   --------------------------------------------------------------------------- */
.pay-amount { margin: 1.25rem 0; }
.pay-amount > label { display: block; font-weight: 600; margin-bottom: .35rem; }
.pay-amount-field { display: flex; align-items: center; gap: .5rem; }
.pay-amount-field input {
  display: block; width: 9rem; margin: 0; padding: .6rem .75rem;
  font-size: 1.25rem; text-align: right;
  border: 1px solid #cfd8d2; border-radius: 8px; background: #fff;
}
.pay-amount-unit { font-size: 1.25rem; color: #3d4a44; }
.pay-amount p { margin-top: .6rem; }

/* ---------------------------------------------------------------------------
   La boîte de règlement.

   Ouverte depuis le séjour, pour que ce qui explique le montant — qui a déjà
   versé, ce qu'il reste, le lien à envoyer — reste derrière elle plutôt que
   d'être remplacé par elle.
   --------------------------------------------------------------------------- */
.pay-dialog {
  border: 0; border-radius: 14px; padding: 1.75rem;
  width: min(32rem, calc(100vw - 2rem)); max-height: calc(100vh - 4rem);
  color: inherit; background: #fff;
  box-shadow: 0 12px 40px rgba(20, 30, 25, .22);
}
.pay-dialog::backdrop { background: rgba(15, 25, 20, .45); }
.pay-dialog h2 { margin: 0 2rem .5rem 0; }
.pay-dialog .summary { margin-top: 0; }

/* La croix est un formulaire method="dialog" : elle ferme sans script, et
   reste donc une vraie commande quand celui-ci n'a pas pu s'exécuter. */
.pay-dialog-close { position: absolute; top: .6rem; right: .75rem; margin: 0; }
.pay-dialog-close button {
  border: 0; background: transparent; font-size: 1.6rem; line-height: 1;
  padding: .2rem .5rem; cursor: pointer; color: #5a6b62; border-radius: 6px;
}
.pay-dialog-close button:hover { background: #f0f4f2; color: #1d2b24; }

/* « sur 3 901,80 € » à côté du reste à régler.
   Nettement plus petit et plus pâle : c'est l'échelle du grand chiffre, pas
   un second montant. Sur la même ligne tant qu'elle tient, dessous ensuite —
   ce qui compte est qu'on lise les deux d'un même regard. */
.money-amount-of {
  margin-left: .5rem; font-size: .95rem; font-weight: 400; color: #5a6b62;
  white-space: nowrap;
}

/* Le bouton de règlement, sous le montant qu'il règle.
   Il vivait deux écrans plus bas, dans « Votre séjour » : on lisait le
   chiffre et on partait chercher le geste. Marge haute franche pour qu'il ne
   colle pas à la phrase de contexte, et pleine largeur sur téléphone, où un
   bouton centré au milieu d'une carte se rate. */
.money-cta { margin: 1.25rem 0 0; }
@media (max-width: 599px) {
  .money-cta .btn { display: block; width: 100%; text-align: center; }
}

/* ===========================================================================
   Le panneau « Paiements ».

   Refait après l'avoir enfin regardé dans un navigateur : ce qui était écrit
   ici tenait dans deux cartes, deux tableaux et deux paragraphes de prose
   chiffrée, et les classes du tableau venaient de la feuille du bureau, où
   elles existent — ici, non. Un <table> sans une seule règle.

   Trois composants, du général au détail : la jauge, le carnet des payeurs,
   le relevé des versements. Tous les trois en grille, parce que le fléau de
   cet écran sur un téléphone était le retour à la ligne : un bouton qui
   passe seul sous son libellé, une adresse coupée au milieu d'un mot.
   =========================================================================== */

/* La jauge. Le reste en gros, le total derrière, la barre dessous — la même
   lecture qu'en haut de page, pour que ce soit le même écran. */
.pot {
  margin: 0 0 1.5rem;
  padding: 1rem 1.1rem 1.1rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: #fbfaf6;
}
.pot.is-full { background: #f2f7f3; border-color: #cfe3d6; }
.pot-line { margin: 0 0 .6rem; display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; }
.pot-left {
  font-size: 1.7rem; font-weight: 700; color: var(--accent-dark);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.pot-of { font-size: .95rem; color: var(--muted); }
.pot-bar {
  height: 8px; border-radius: 999px; background: #e6e1d5; overflow: hidden;
}
.pot-fill { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.pot-note { margin: .55rem 0 0; font-size: .88rem; color: var(--muted); }
.pot-cta { margin: .9rem 0 0; }

/* Un sous-titre à l'intérieur d'une carte. Le panneau en compte trois ; sans
   eux, une liste de gens suivie d'une liste de versements est une seule
   longue liste. */
.card-sub {
  margin: 1.6rem 0 .5rem;
  font-size: .78rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}

/* Le carnet des payeurs. Quatre colonnes dont deux se rétrécissent : la
   pastille et le montant gardent leur place, l'identité prend ce qui reste
   et se coupe proprement au lieu de replier la ligne. */
.payer-list { list-style: none; margin: 0; padding: 0; }
.payer {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto auto;
  /* Alignée sur la PREMIÈRE ligne, pas au milieu du bloc : sur un téléphone
     le nom et ses étiquettes passent à deux lignes, et une pastille centrée
     verticalement se retrouvait à hauteur de l'adresse, en face de rien. */
  align-items: start;
  gap: .7rem;
  padding: .7rem 0;
  border-bottom: 1px solid var(--border);
}
.payer:last-child { border-bottom: none; }
.payer-avatar {
  width: 34px; height: 34px; flex: 0 0 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: #fff;
  font-size: .78rem; font-weight: 700; letter-spacing: .02em;
}
.payer.is-you .payer-avatar { background: var(--accent-dark); }
.payer-who { min-width: 0; display: flex; flex-direction: column; gap: .1rem; }
.payer-name { font-weight: 600; overflow-wrap: anywhere; }
/* L'adresse est une identité, pas une donnée : elle tient sur une ligne et
   s'abrège. En colonne de tableau elle se coupait en « fixture-5- » /
   « 91@example.com » et mangeait la moitié de l'écran. */
.payer-mail {
  font-size: .82rem; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.payer-chip {
  display: inline-block; margin-left: .3rem; padding: .05rem .4rem;
  border-radius: 999px; background: #eef2ee; color: var(--muted);
  font-size: .7rem; font-weight: 600; text-transform: lowercase;
  vertical-align: .05em;
}
.payer-chip-lead { background: #f6efe0; color: #8a611c; }
.payer-amount {
  font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap;
  padding-top: .35rem;
}
.payer-amount.is-none { color: var(--muted); font-weight: 400; }
.payer-act { display: flex; padding-top: .25rem; }

/* La note des parts égales suivait la dernière ligne du carnet sans respirer,
   collée sous le nom du dernier payeur comme si elle le concernait. */
.payer-list + .small-note { margin-top: .9rem; }
.payer-remove {
  border: none; background: none; cursor: pointer; padding: 0 .2rem;
  font-size: 1.15rem; line-height: 1; color: var(--muted);
}
.payer-remove:hover { color: #a4402f; }

/* Le relevé. Trois colonnes qui tiennent à 320 px parce que la date et le
   payeur passent SOUS le libellé au lieu de le suivre. */
.ledger-list { list-style: none; margin: 0; padding: 0; }
.ledger-line {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: .6rem .8rem;
  padding: .75rem 0;
  border-bottom: 1px solid var(--border);
}
.ledger-line:last-child { border-bottom: none; }
.ledger-what { min-width: 0; display: flex; flex-direction: column; gap: .1rem; font-weight: 600; }
.ledger-meta { font-size: .82rem; font-weight: 400; color: var(--muted); }
.ledger-amount { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.btn-sm { padding: .3rem .7rem; font-size: .85rem; }

.split-invite { margin-top: 1.2rem; }


.share-btn { white-space: nowrap; }

/* Les bandeaux d'information. Ils portaient depuis toujours les classes du
   bureau — `notice notice-ok` — dont aucune n'existe ici : cinq gabarits du
   portail voyageur affichaient leurs confirmations et leurs erreurs en texte
   nu. Les couleurs sont écrites en clair, la feuille du voyageur n'ayant pas
   les variables d'état du bureau. */
.notice {
  display: block; padding: .65rem 1rem; margin-bottom: 1rem;
  border-radius: 10px; font-size: .9rem; border: 1px solid transparent;
}
.notice code, .notice a { overflow-wrap: anywhere; }
.notice-ok { background: #eaf4ee; border-color: #cfe3d6; color: #1f5b3c; }
.notice-error { background: #fbeceb; border-color: #f0cfcb; color: #92312a; }

/* Trois classes restées sans règle, trouvées avec les précédentes : la tuile
   du guide et la carte de séjour se dessinaient en bloc nu, l'horodatage
   d'un message avait la taille de son texte, et le total d'un tableau de
   devis ne se distinguait pas d'une ligne ordinaire. */
.guide-card .guide-pair { margin-top: 1rem; }
.stay-card { margin-bottom: 1.25rem; }
.message-sender { font-weight: 600; }
.message-time { font-size: .8rem; color: var(--muted); }
.row-total td, .row-total th { font-weight: 700; border-top: 2px solid var(--border); }

/* De quoi le reste est fait, quand ce n'est plus une échéance : « ce sont vos
   options ». Sous le chiffre et discret — c'est une légende, pas une
   deuxième annonce. */
.money-rest-is { margin: .25rem 0 0; font-size: .9rem; color: var(--muted); }

/* Un champ et son bouton, en une seule pièce.
   Le bouton vit DANS la bordure du champ plutôt qu'à côté : c'est une seule
   action — une adresse, on l'ajoute — et deux boîtes accolées de hauteurs
   voisines mais pas égales sont ce qui donnait à cette ligne son air
   bricolé. La bordure est portée par le conteneur, les deux enfants n'en
   ont aucune, et le fond du bouton est ce qui l'en distingue. */
.inline-field {
  display: flex;
  align-items: stretch;
  max-width: 26rem;
  margin: 0 0 .4rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: #fff;
  overflow: hidden;
}
/* La mise au point se voit sur la pièce entière : un anneau autour du seul
   champ dessinerait une bordure à l'intérieur d'une autre. */
.inline-field:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(47, 111, 79, .15);
}
.inline-field input {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  background: none;
  padding: .6rem .8rem;
  font: inherit;
  color: inherit;
}
.inline-field input:focus { outline: none; }
.inline-field button {
  flex: 0 0 auto;
  margin: 0;
  border: none;
  border-left: 1px solid var(--border);
  border-radius: 0;
  padding: 0 1.1rem;
  background: #eef3ef;
  color: var(--accent-dark);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.inline-field button:hover { background: #e2ebe5; }
.inline-field + .small-note { margin-top: 0; }

/* Une ligne de remboursement dans le relevé : de l'argent reparti. Le mot et
   le signe le disent déjà ; la teinte évite qu'on les rate en parcourant une
   colonne de montants tous pareils. */
.ledger-line.is-refund .ledger-amount { color: #8a611c; }
