/* Brand typography — Plus Jakarta Sans, the Cyberesa house face, with Inter as
   the fallback. This reproduces the stack the rest of the suite runs on:
   cr.admin/css/style5.0.css and cr.resa/css/style5.0.css both end with
   `* { font-family: 'Plus Jakarta Sans', Trim, sans-serif !important }`, where
   `Trim` is the alias cr.fwk/css/myfonts.css maps onto the self-hosted Inter TTFs.
   Loaded with display=swap so text is never invisible; if offline / headless
   it silently falls back to the system stack in --font-sans / --font-head.
   NOTE: @import must precede every rule, so this line stays at the top of the
   file — the §36 brand block at the bottom only redeclares --font-sans/--font-head. */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap');

/* =============================================================================
   Cyberesa — Design System (cr.resa → ASP.NET MVC .NET 10)
   Single stylesheet, two densities (B2C + Agent) driven by :root[data-mode].
   All UI copy is French; class names / comments are English.
   Each component block below maps to a future Razor partial (noted in @partial).
   Palette "Nuit Méditerranée v3": indigo primary + coral accent (CTA).
   Base palette lives here; the final §26 BRAND IDENTITY block re-skins it.
   ========================================================================== */

/* -----------------------------------------------------------------------------
   1. DESIGN TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* Brand — primary (teal) */
  --c-primary-900: #06424A;
  --c-primary-700: #0A5D65;
  --c-primary-600: #0E7C86;   /* base */
  --c-primary-500: #159AA6;
  --c-primary-200: #B9E0E3;
  --c-primary-100: #E2F1F2;
  --c-primary-050: #F0F8F8;

  /* Brand — accent (orange, CTA) */
  --c-accent-700: #C75E07;
  --c-accent-600: #E06E0C;
  --c-accent-500: #F4801A;    /* base */
  --c-accent-300: #F9B072;
  --c-accent-100: #FDEBD8;

  /* Ink / text */
  --c-ink:        #14252B;
  --c-ink-2:      #2C3E44;
  --c-muted:      #5B6B70;
  --c-faint:      #8A979B;
  --c-on-dark:    #F4FAFA;

  /* Surfaces */
  --c-surface:    #FFFFFF;
  --c-sand:       #FBF7F1;
  --c-surface-2:  #F4F1EA;
  --c-surface-3:  #EEF3F3;
  --c-border:     #E4DED3;
  --c-border-2:   #DCE4E5;

  /* Status */
  --c-success:    #1E8E5A;  --c-success-bg: #E6F4EC;
  --c-danger:     #C0392B;  --c-danger-bg:  #FBEAE8;
  --c-warn:       #B0740A;  --c-warn-bg:    #FCF1DC;  /* "sur demande" */
  --c-info:       #1E5F9E;  --c-info-bg:    #E7F0F9;

  /* Provider / source colors */
  --c-src-direct:    #0E7C86;
  --c-src-tgx:       #6D4AB0;
  --c-src-cyberesa:  #1E5F9E;
  --c-src-dotw:      #B5651D;
  --c-src-lightresa: #3F8F3F;

  /* Spacing scale */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-7: 32px; --sp-8: 40px; --sp-9: 56px; --sp-10: 90px;

  /* Radii */
  --r-sm: 6px; --r-md: 10px; --r-lg: 16px; --r-xl: 22px; --r-pill: 999px;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(20,37,43,.06), 0 1px 3px rgba(20,37,43,.08);
  --shadow-md: 0 4px 12px rgba(20,37,43,.10);
  --shadow-lg: 0 12px 34px rgba(20,37,43,.16);
  --shadow-focus: 0 0 0 3px rgba(21,154,166,.35);

  /* Typography */
  --font-sans: "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --font-head: "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --fs-base: 15px;
  --fs-xs: 12px; --fs-sm: 13px; --fs-md: 15px; --fs-lg: 18px;
  --fs-xl: 22px; --fs-2xl: 28px; --fs-3xl: 36px;
  --lh: 1.5;

  /* Layout */
  --container: 1200px;
  --header-h: 116px;
  --z-header: 40; --z-popover: 60; --z-modal: 80;

  /* Component sizing (density-aware) */
  --control-h: 44px;
  --card-pad: var(--sp-5);
  --row-pad: var(--sp-4);
}

/* Agent (B2B) density — denser, smaller, data-first */
:root[data-mode="agent"] {
  --fs-base: 13px;
  --fs-sm: 12px; --fs-md: 13px; --fs-lg: 16px; --fs-xl: 19px; --fs-2xl: 23px;
  --control-h: 34px;
  --card-pad: var(--sp-4);
  --row-pad: var(--sp-3);
  --r-lg: 10px; --r-xl: 12px;
  --sp-hero: 0;
}

/* -----------------------------------------------------------------------------
   2. RESET & BASE
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh);
  color: var(--c-ink);
  background: var(--c-sand);
}
img { max-width: 100%; display: block; }
a { color: var(--c-primary-600); text-decoration: none; }
a:hover { text-decoration: underline; }
button { font-family: inherit; }
h1, h2, h3, h4 { font-family: var(--font-head); line-height: 1.2; margin: 0 0 var(--sp-3); color: var(--c-ink); }
h1 { font-size: var(--fs-3xl); font-weight: 800; letter-spacing: -.5px; }
h2 { font-size: var(--fs-2xl); font-weight: 700; }
h3 { font-size: var(--fs-xl); font-weight: 700; }
h4 { font-size: var(--fs-lg); font-weight: 700; }
p  { margin: 0 0 var(--sp-3); }
small { font-size: var(--fs-sm); }
code { font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: .92em;
  background: var(--c-surface-3); padding: 1px 5px; border-radius: 4px; color: var(--c-primary-700); }

/* Accessible focus ring */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: none; box-shadow: var(--shadow-focus); border-radius: var(--r-sm);
}
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* -----------------------------------------------------------------------------
   3. LAYOUT
   -------------------------------------------------------------------------- */
.container { width: min(100% - 2*var(--sp-4), var(--container)); margin-inline: auto; }
.page { padding-block: var(--sp-7); }
.section { margin-block: var(--sp-7); }
.stack > * + * { margin-top: var(--sp-3); }
.stack-lg > * + * { margin-top: var(--sp-5); }
.cluster { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }
.between { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3); }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-4); }
.layout-2col { display: grid; grid-template-columns: 288px 1fr; gap: var(--sp-6); align-items: start; }
.layout-detail { display: grid; grid-template-columns: 1fr 360px; gap: var(--sp-6); align-items: start; }
.muted { color: var(--c-muted); }
.faint { color: var(--c-faint); }
.eyebrow { text-transform: uppercase; letter-spacing: .1em; font-size: var(--fs-xs);
  font-weight: 700; color: var(--c-primary-600); }

/* -----------------------------------------------------------------------------
   4. BUTTONS   @partial: _Button.cshtml
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  height: var(--control-h); padding-inline: var(--sp-5);
  border: 1px solid transparent; border-radius: var(--r-pill);
  font-size: var(--fs-md); font-weight: 700; cursor: pointer; white-space: nowrap;
  transition: transform .05s ease, background .15s ease, box-shadow .15s ease;
}
.btn:active { transform: translateY(1px); }
.btn--accent { background: var(--c-accent-500); color: #fff; box-shadow: var(--shadow-sm); }
.btn--accent:hover { background: var(--c-accent-600); text-decoration: none; }
.btn--primary { background: var(--c-primary-600); color: #fff; }
.btn--primary:hover { background: var(--c-primary-700); text-decoration: none; }
.btn--ghost { background: transparent; color: var(--c-primary-700); border-color: var(--c-border-2); }
.btn--ghost:hover { background: var(--c-primary-050); text-decoration: none; }
.btn--subtle { background: var(--c-surface-3); color: var(--c-ink-2); }
.btn--subtle:hover { background: var(--c-border-2); text-decoration: none; }
.btn--sm { height: 32px; padding-inline: var(--sp-3); font-size: var(--fs-sm); }
.btn--lg { height: 52px; padding-inline: var(--sp-7); font-size: var(--fs-lg); }
.btn--block { width: 100%; }
.btn[disabled], .btn.is-disabled { opacity: .5; pointer-events: none; }
.linklike { background: none; border: 0; color: var(--c-primary-600); cursor: pointer; padding: 0;
  font-weight: 600; text-decoration: underline; }

/* -----------------------------------------------------------------------------
   5. BADGES / TAGS / STARS   @partial: _Badge.cshtml, _SourceBadge.cshtml
   -------------------------------------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: var(--r-pill);
  font-size: var(--fs-xs); font-weight: 700; line-height: 1.7; white-space: nowrap;
  background: var(--c-surface-3); color: var(--c-ink-2);
}
.badge__dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.badge--success   { background: var(--c-success-bg); color: var(--c-success); }
.badge--danger    { background: var(--c-danger-bg);  color: var(--c-danger); }
.badge--warn      { background: var(--c-warn-bg);    color: var(--c-warn); }
.badge--info      { background: var(--c-info-bg);    color: var(--c-info); }
.badge--outline   { background: #fff; border: 1px solid var(--c-border-2); color: var(--c-muted); }
.badge--promo     { background: var(--c-accent-100); color: var(--c-accent-700); }

/* Source / provider badges (consolidation multi-fournisseurs) */
.badge--src { color: #fff; }
.badge--src-direct    { background: var(--c-src-direct); }
.badge--src-tgx       { background: var(--c-src-tgx); }
.badge--src-cyberesa  { background: var(--c-src-cyberesa); }
.badge--src-dotw      { background: var(--c-src-dotw); }
.badge--src-lightresa { background: var(--c-src-lightresa); }

/* Cancellation / refund state pills */
.pill-free  { color: var(--c-success); font-weight: 700; }
.pill-nonref{ color: var(--c-danger);  font-weight: 700; }
.pill-onreq { color: var(--c-warn);    font-weight: 700; }

/* Star rating */
.stars { color: var(--c-accent-500); letter-spacing: 1px; font-size: 1em; }
.stars[aria-label] { cursor: default; }

/* -----------------------------------------------------------------------------
   6. FIELDS / INPUTS   @partial: _Field.cshtml
   -------------------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: 5px; }
.field__label { font-size: var(--fs-sm); font-weight: 600; color: var(--c-ink-2); }
.field__hint  { font-size: var(--fs-xs); color: var(--c-faint); }
.req { color: var(--c-danger); }
.input, .select, textarea.input {
  height: var(--control-h); width: 100%;
  padding: 0 var(--sp-3);
  border: 1px solid var(--c-border-2); border-radius: var(--r-md);
  background: #fff; color: var(--c-ink); font-size: var(--fs-md);
}
textarea.input { height: auto; padding: var(--sp-3); resize: vertical; }
.input::placeholder { color: var(--c-faint); }
.input:focus, .select:focus, textarea.input:focus { border-color: var(--c-primary-500); box-shadow: var(--shadow-focus); outline: none; }
.select { appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M2 4l4 4 4-4' fill='none' stroke='%235B6B70' stroke-width='1.6'/></svg>");
  background-repeat: no-repeat; background-position: right 12px center; padding-right: 30px; }
.input-icon { position: relative; }
.input-icon .input { padding-left: 38px; }
.input-icon > .ico { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--c-primary-600); }
.checkbox, .radio { display: inline-flex; gap: var(--sp-2); align-items: center; cursor: pointer; font-size: var(--fs-sm); }
/* flex: none — as a flex item the box would otherwise shrink when the label wraps onto two lines
   (e.g. "Breakfast + dinner or lunch included"), rendering smaller than its neighbours. */
.checkbox input, .radio input { width: 17px; height: 17px; flex: none; accent-color: var(--c-primary-600); }

/* -----------------------------------------------------------------------------
   7. SEGMENTED CONTROL + MODE / CURRENCY TOGGLE
   -------------------------------------------------------------------------- */
.segmented { display: inline-flex; background: var(--c-surface-3); border-radius: var(--r-pill); padding: 3px; }
.segmented__btn {
  border: 0; background: transparent; cursor: pointer; border-radius: var(--r-pill);
  padding: 4px 12px; font-size: var(--fs-sm); font-weight: 700; color: var(--c-muted);
}
.segmented__btn.is-active { background: #fff; color: var(--c-primary-700); box-shadow: var(--shadow-sm); }
.mode-toggle { display: inline-flex; background: rgba(255,255,255,.15); border-radius: var(--r-pill); padding: 3px; }
.mode-toggle__btn {
  border: 0; background: transparent; cursor: pointer; border-radius: var(--r-pill);
  padding: 4px 14px; font-size: var(--fs-sm); font-weight: 700; color: rgba(255,255,255,.85);
}
.mode-toggle__btn.is-active { background: var(--c-accent-500); color: #fff; }

/* -----------------------------------------------------------------------------
   8. HEADER / NAV   @partial: _SiteHeader.cshtml
   -------------------------------------------------------------------------- */
.site-header { position: sticky; top: 0; z-index: var(--z-header); }
.utility-bar { background: var(--c-primary-900); color: var(--c-on-dark); font-size: var(--fs-sm); }
.utility-bar__inner { display: flex; align-items: center; gap: var(--sp-4); min-height: 40px; }
.utility-bar a { color: rgba(244,250,250,.85); }
.utility-bar a:hover { color: #fff; }
.utility-links { display: flex; gap: var(--sp-4); margin-inline: auto var(--sp-4); }
.header-tools { display: flex; align-items: center; gap: var(--sp-3); margin-left: auto; }
.nav-bar { background: #fff; border-bottom: 1px solid var(--c-border); }
.nav-bar__inner { display: flex; align-items: center; gap: var(--sp-6); min-height: 72px; }
.brand { display: inline-flex; align-items: center; gap: var(--sp-2); font-weight: 800; font-size: var(--fs-xl); color: var(--c-primary-700); }
.brand:hover { text-decoration: none; }
.brand__mark { display: inline-grid; place-items: center; width: 34px; height: 34px; border-radius: 9px;
  background: linear-gradient(135deg, var(--c-primary-600), var(--c-primary-500)); color: #fff; font-size: 18px; }
.brand__name b { color: var(--c-accent-500); }
.brand__tag { font-size: var(--fs-xs); color: var(--c-muted); font-weight: 600; }
.main-nav { display: flex; gap: var(--sp-5); margin-left: var(--sp-4); }
.main-nav a { color: var(--c-ink-2); font-weight: 600; font-size: var(--fs-md); }
.main-nav a:hover { color: var(--c-primary-600); text-decoration: none; }
.header-phone { margin-left: auto; font-weight: 800; color: var(--c-primary-700); white-space: nowrap; }
.header-phone .ico { color: var(--c-accent-500); }

/* -----------------------------------------------------------------------------
   9. HERO + TABBED SEARCH BAR   @partial: _SearchBar.cshtml
   -------------------------------------------------------------------------- */
.hero { position: relative; color: #fff; background: #0b3b41; }
.hero__media { position: absolute; inset: 0; overflow: hidden; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; }
.hero::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(6,44,50,.30), rgba(6,44,50,.55)); }
.hero__inner { position: relative; z-index: 1; padding-block: var(--sp-9); }
.hero__title { color: #fff; text-shadow: 0 2px 12px rgba(0,0,0,.35); }
.hero__sub { color: rgba(255,255,255,.9); font-size: var(--fs-lg); margin-bottom: var(--sp-5); }

.searchbar { background: rgba(9, 40, 45, .72); backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.14); border-radius: var(--r-xl);
  padding: var(--sp-4); box-shadow: var(--shadow-lg); }
.search-tabs { display: flex; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.search-tab { display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 9px 18px; border-radius: var(--r-pill); border: 1px solid transparent;
  background: rgba(255,255,255,.10); color: rgba(255,255,255,.9);
  font-weight: 700; cursor: pointer; font-size: var(--fs-md); }
.search-tab.is-active { background: #fff; color: var(--c-primary-700); }
.search-tab[aria-disabled="true"] { opacity: .6; cursor: not-allowed; }
.search-tab .soon { font-size: var(--fs-xs); font-weight: 700; color: var(--c-accent-500); }

/* Date columns are `auto` (content-sized) so the full date label — e.g.
   "mer. 5 août 2026" — is always shown in full, never clipped. */
.search-fields { display: grid; grid-template-columns: minmax(0, 2.2fr) auto auto minmax(0, 1.4fr) auto; gap: var(--sp-2);
  background: #fff; border-radius: var(--r-lg); padding: var(--sp-2); align-items: stretch; }
.search-field { position: relative; display: flex; flex-direction: column; justify-content: center;
  padding: 8px 12px; border-radius: var(--r-md); min-width: 0; }
.search-field + .search-field { border-left: 1px solid var(--c-border); }
.search-field__label { font-size: var(--fs-xs); font-weight: 700; color: var(--c-muted);
  display: flex; align-items: center; gap: 6px; }
.search-field__value { border: 0; padding: 2px 0 0; font-size: var(--fs-md); font-weight: 600;
  color: var(--c-ink); background: transparent; width: 100%; }
.search-field__value::placeholder { color: var(--c-faint); font-weight: 500; }
.search-submit { display: flex; }
/* Full-height submit aligned with the search bar, but tightened: a short label
   ("Rechercher") shouldn't be dwarfed by btn--lg's extra padding + large type. */
.search-submit .btn { height: 100%; padding-inline: var(--sp-5); font-size: var(--fs-md); }
.search-advanced-toggle { margin-top: var(--sp-3); }
.search-advanced { display: none; margin-top: var(--sp-3); background: rgba(255,255,255,.08);
  border-radius: var(--r-md); padding: var(--sp-4); }
.search-advanced.is-open { display: block; }

/* Autocomplete + popover (destination, occupancy) */
.popover { position: absolute; top: calc(100% + 6px); left: 0; z-index: var(--z-popover);
  min-width: 300px; background: #fff; color: var(--c-ink); border: 1px solid var(--c-border);
  border-radius: var(--r-md); box-shadow: var(--shadow-lg); padding: var(--sp-3); }
.popover[hidden] { display: none; }
.ac-list { list-style: none; margin: 0; padding: 0; }
.ac-list li { padding: 8px 10px; border-radius: var(--r-sm); cursor: pointer; display: flex; gap: 8px; align-items: center; }
.ac-list li:hover { background: var(--c-primary-050); }
.ac-head { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .08em; color: var(--c-faint);
  font-weight: 700; padding: 4px 10px; }
/* Heading of the SECOND section of an autocomplete panel — the curated destinations, or the
   min-chars prompt, sitting under the remembered picks. It is an <li> and not a second .ac-head
   <p> because the panel holds ONE <ul>: the click handlers are delegated on it, and a list split
   in two would leave the lower half unwired. data-empty already keeps it from reading as a pick;
   these rules undo what .ac-list li gives every row, so it does not look like one. */
.ac-list li.ac-sep { cursor: default; padding: 10px 10px 4px; margin-top: 6px;
  border-top: 1px solid var(--c-border); border-radius: 0;
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .08em;
  color: var(--c-faint); font-weight: 700; }
.ac-list li.ac-sep:hover { background: none; }
.occ-row { display: grid; grid-template-columns: 1fr auto; gap: var(--sp-3); align-items: center;
  padding: var(--sp-2) 0; border-bottom: 1px dashed var(--c-border); }
.stepper-num { display: inline-flex; align-items: center; gap: var(--sp-2); }
.qty { display: inline-flex; align-items: center; gap: var(--sp-2); }
.qty button { width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--c-border-2);
  background: #fff; cursor: pointer; font-size: 16px; color: var(--c-primary-700); font-weight: 700; }
.qty output { min-width: 22px; text-align: center; font-weight: 700; }

/* -----------------------------------------------------------------------------
   10. STEPPER (5 steps)   @partial: _Stepper.cshtml
   -------------------------------------------------------------------------- */
.stepper { background: #fff; border-bottom: 1px solid var(--c-border); }
.stepper__list { list-style: none; display: flex; margin: 0; padding: var(--sp-3) 0; gap: var(--sp-2); }
.stepper__item { flex: 1; position: relative; }
.stepper__item > a, .stepper__item > span { display: flex; align-items: center; gap: var(--sp-2);
  justify-content: center; color: var(--c-faint); font-weight: 600; font-size: var(--fs-sm); }
.stepper__item > a:hover { text-decoration: none; }
.stepper__num { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 50%;
  background: var(--c-surface-3); color: var(--c-muted); font-weight: 800; font-size: var(--fs-sm); flex: none; }
.stepper__item::after { content: ""; position: absolute; top: 14px; left: calc(50% + 22px); right: calc(-50% + 22px);
  height: 2px; background: var(--c-border); }
.stepper__item:last-child::after { display: none; }
.stepper__item.is-done  .stepper__num { background: var(--c-primary-600); color: #fff; }
.stepper__item.is-done  > a, .stepper__item.is-done > span { color: var(--c-primary-700); }
.stepper__item.is-done::after { background: var(--c-primary-500); }
.stepper__item.is-active .stepper__num { background: var(--c-accent-500); color: #fff; box-shadow: 0 0 0 4px var(--c-accent-100); }
.stepper__item.is-active > a, .stepper__item.is-active > span { color: var(--c-ink); font-weight: 800; }
/* Upcoming (not-yet-reached) step — explicit rule so the class is always defined */
.stepper__item.is-todo .stepper__num { background: var(--c-surface-3); color: var(--c-faint); }
.stepper__item.is-todo > a, .stepper__item.is-todo > span { color: var(--c-faint); }

/* -----------------------------------------------------------------------------
   11. CARDS   @partial: _Card.cshtml, _HotelCard.cshtml
   -------------------------------------------------------------------------- */
.card { background: #fff; border: 1px solid var(--c-border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm); }
.card__body { padding: var(--card-pad); }
.card__header { padding: var(--card-pad); border-bottom: 1px solid var(--c-border); }
.card__footer { padding: var(--card-pad); border-top: 1px solid var(--c-border); }
.panel { background: #fff; border: 1px solid var(--c-border); border-radius: var(--r-lg); padding: var(--card-pad); }
.section-title { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.section-title::before { content: ""; width: 4px; height: 20px; border-radius: 2px; background: var(--c-accent-500); }

/* Hotel result card */
.hotel-card { display: grid; grid-template-columns: 280px 1fr; overflow: hidden; }
.hotel-card__media { position: relative; min-height: 220px; background: var(--c-surface-3); }
.hotel-card__media img { width: 100%; height: 100%; object-fit: cover; }
/* right: 54px bounds the overlay to the media column, clear of .hotel-card__fav (10 + 34 + 10).
   Without it the container shrink-wraps to its content and a long flag — a real promotion title
   such as "1er enfant - 6 ans dans la chambre des parents Gratuit" — spills across the card body
   and sits on top of the hotel name. */
.hotel-card__flags { position: absolute; top: 10px; left: 10px; right: 54px; display: flex; gap: 6px; flex-wrap: wrap; }
/* .badge is white-space:nowrap by design, which is right for short pills but means a sentence-long
   flag can never wrap. Inside this overlay only, let it wrap and clamp to 2 lines (the markup adds
   title="…" so the full text stays reachable). Short flags like "-10%" are unaffected: as flex
   items they still size to their content. */
.hotel-card__flags > .badge {
  max-width: 100%; white-space: normal; overflow-wrap: anywhere; text-align: left; line-height: 1.35;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
}
.hotel-card__fav { position: absolute; top: 10px; right: 10px; width: 34px; height: 34px; border-radius: 50%;
  background: rgba(255,255,255,.9); display: grid; place-items: center; border: 0; cursor: pointer; }
/* Hearted (List.cshtml, initResults favourites). The "Mes favoris" toggle reads as pressed while
   the list is restricted to them. */
.hotel-card__fav.is-fav { color: var(--c-danger); font-size: 1.15em; }
.fav-toggle.is-active { background: var(--c-danger-bg); color: var(--c-danger); border-color: var(--c-danger); }
.hotel-card__body { padding: var(--card-pad); display: flex; flex-direction: column; gap: var(--sp-2); }
.hotel-card__price { text-align: right; margin-top: auto; }
.price-old { color: var(--c-faint); text-decoration: line-through; font-size: var(--fs-sm); }
.price-now { font-size: var(--fs-2xl); font-weight: 800; color: var(--c-ink); }
.price-cur { font-size: var(--fs-md); font-weight: 700; color: var(--c-muted); }
.price-unit { font-size: var(--fs-xs); color: var(--c-faint); }
/* "à partir de" before a results-card price (blnHotelResultsOnlyBestPriceCombination). */
.price-from { font-size: var(--fs-xs); color: var(--c-muted); margin-right: 2px; }
.tags { display: flex; flex-wrap: wrap; gap: 6px; }
.tag { font-size: var(--fs-xs); background: var(--c-surface-3); color: var(--c-muted); padding: 2px 8px; border-radius: var(--r-sm); }

/* -----------------------------------------------------------------------------
   12. FILTERS SIDEBAR   @partial: _Filters.cshtml
   -------------------------------------------------------------------------- */
.filters { position: sticky; top: calc(var(--sp-4)); }
.filter-group { padding: var(--sp-4) 0; border-bottom: 1px solid var(--c-border); }
.filter-group:first-child { padding-top: 0; }
.filter-group h4 { font-size: var(--fs-md); margin-bottom: var(--sp-3); }
.filter-list { display: flex; flex-direction: column; gap: var(--sp-2); }
/* Hotel-name filter (/hotel/list sidebar): full-width field, submit button inset in its right
   edge. The sidebar is too narrow for a field and a separate button side by side. */
.name-filter { position: relative; }
.name-filter .input { padding-right: calc(var(--control-h) + var(--sp-1)); }
.name-filter__go {
  position: absolute; top: 4px; right: 4px;
  width: calc(var(--control-h) - 8px); height: calc(var(--control-h) - 8px);
  display: grid; place-items: center; padding: 0;
  border: 0; border-radius: calc(var(--r-md) - 2px);
  background: var(--c-primary-600); color: #fff; cursor: pointer;
}
.name-filter__go:hover { background: var(--c-primary-700); }
.name-filter__go:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.name-filter__go svg { width: 16px; height: 16px; }
.filter-list .checkbox { justify-content: space-between; width: 100%; }
.filter-count { color: var(--c-faint); font-size: var(--fs-xs); }
/* The airline row stays on one line: a long name is cut with an ellipsis (full name in its
   tooltip) instead of wrapping under the logo. The auto right margin keeps the name next to the
   checkbox and pushes the count to the right edge. */
.filter-list .checkbox .cluster { flex: 0 1 auto; flex-wrap: nowrap; min-width: 0; margin-right: auto; }
.filter-list .checkbox .filter-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filter-list .checkbox .airline-logo,
.filter-list .checkbox .filter-count { flex: none; }
.range { display: flex; gap: var(--sp-2); align-items: center; }

/* -----------------------------------------------------------------------------
   13. ROOM / BOARD ROWS + QUOTE MATRIX   @partial: _RoomRow.cshtml, _QuoteMatrix.cshtml
   -------------------------------------------------------------------------- */
.room-row { display: grid; grid-template-columns: 1.6fr 1fr 1.2fr 1fr auto; gap: var(--sp-3);
  align-items: center; padding: var(--row-pad); border-top: 1px solid var(--c-border); }
.room-row:first-of-type { border-top: 0; }
.room-name { font-weight: 700; }
.board-option { display: flex; align-items: center; gap: var(--sp-2); }
.occ-ico { color: var(--c-muted); font-size: var(--fs-sm); }
.quote-matrix { width: 100%; border-collapse: collapse; }
.quote-matrix thead th { text-align: left; font-size: var(--fs-xs); text-transform: uppercase;
  letter-spacing: .05em; color: var(--c-muted); padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--c-border); }
.quote-matrix td { padding: var(--sp-3); border-bottom: 1px solid var(--c-border); vertical-align: top; }
.quote-matrix tr.is-selected { background: var(--c-primary-050); }
.rate-net { color: var(--c-primary-700); font-weight: 700; }

/* -----------------------------------------------------------------------------
   14. PRICE RECAP   @partial: _PriceRecap.cshtml
   -------------------------------------------------------------------------- */
.recap { position: sticky; top: var(--sp-4); }
.recap__row { display: flex; justify-content: space-between; gap: var(--sp-3); padding: 6px 0; font-size: var(--fs-sm); }
.recap__total { display: flex; justify-content: space-between; align-items: baseline;
  padding-top: var(--sp-3); margin-top: var(--sp-2); border-top: 2px solid var(--c-border); }
.recap__total .amount { font-size: var(--fs-2xl); font-weight: 800; color: var(--c-primary-700); }
.recap__hotel-media { border-radius: var(--r-md); overflow: hidden; aspect-ratio: 16/9; }

/* -----------------------------------------------------------------------------
   15. TABLES   @partial: _Table.cshtml
   -------------------------------------------------------------------------- */
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-md); }
.table th, .table td { text-align: left; padding: var(--sp-3); border-bottom: 1px solid var(--c-border); vertical-align: top; }
.table th { font-size: var(--fs-sm); color: var(--c-muted); font-weight: 700; }
.table--compact th, .table--compact td { padding: 7px 10px; font-size: var(--fs-sm); }
.table--zebra tbody tr:nth-child(even) { background: var(--c-surface-2); }
.table-scroll { overflow-x: auto; }

/* -----------------------------------------------------------------------------
   16. MODAL   @partial: _Modal.cshtml
   -------------------------------------------------------------------------- */
.modal-backdrop { position: fixed; inset: 0; background: rgba(6,44,50,.55); z-index: var(--z-modal);
  display: grid; place-items: center; padding: var(--sp-4); }
.modal-backdrop[hidden] { display: none; }
.modal { background: #fff; border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  width: min(640px, 100%); max-height: 88vh; overflow: auto; }
.modal__head { display: flex; justify-content: space-between; align-items: center;
  padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--c-border); }
.modal__body { padding: var(--sp-5); }
.modal__close { border: 0; background: var(--c-surface-3); width: 34px; height: 34px; border-radius: 50%; cursor: pointer; }

/* -----------------------------------------------------------------------------
   17. SPEC PANEL (API annotation)   — dev-only, collapsible
   -------------------------------------------------------------------------- */
.spec-panel { border: 1px dashed var(--c-info); border-radius: var(--r-md);
  background: var(--c-info-bg); margin: var(--sp-4) 0; overflow: hidden; }
.spec-panel__toggle { width: 100%; display: flex; align-items: center; gap: var(--sp-2);
  padding: 10px var(--sp-4); background: transparent; border: 0; cursor: pointer;
  font-size: var(--fs-sm); font-weight: 700; color: var(--c-info); text-align: left; }
.spec-panel__toggle .chev { margin-left: auto; transition: transform .15s ease; }
.spec-panel__toggle[aria-expanded="true"] .chev { transform: rotate(180deg); }
.spec-panel__body { padding: 0 var(--sp-4) var(--sp-4); font-size: var(--fs-sm); }
.spec-panel__body table { width: 100%; border-collapse: collapse; background: #fff; border-radius: var(--r-sm); overflow: hidden; }
.spec-panel__body th, .spec-panel__body td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--c-border-2); font-size: var(--fs-xs); }
.spec-panel__body th { background: var(--c-surface-2); color: var(--c-ink-2); }
.spec-panel__body code { background: #fff; border: 1px solid var(--c-border-2); }

/* -----------------------------------------------------------------------------
   18. GALLERY   @partial: _Gallery.cshtml
   -------------------------------------------------------------------------- */
.gallery { display: grid; grid-template-columns: 2fr 1fr 1fr; grid-auto-rows: 130px; gap: var(--sp-2);
  border-radius: var(--r-lg); overflow: hidden; }
.gallery > * { overflow: hidden; }
.gallery img { width: 100%; height: 100%; object-fit: cover; }
.gallery > :first-child { grid-row: span 2; grid-column: span 2; }
.gallery__more { position: relative; display: grid; place-items: center; background: #0b3b41; color: #fff; font-weight: 700; }

/* Amenities grid */
/* A WRAPPING LIST, not a fixed grid. `auto-fill` sized every track to the widest label, so
   "Bar" and "Café" each held a ~250px column while the last row left two cells empty — the
   sparse, boxy look the border used to frame. Facility labels range from three characters to
   "Wifi gratuit dans le hall de réception", so letting each item take its own width and wrap
   packs them and drops the dead cells. The column gap is wide enough to keep them read as
   separate items without a rule between them. */
.amenities { display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5); }
.amenity { max-width: 100%; }
.amenity { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--c-ink-2); }
.amenity .ico { color: var(--c-primary-600); }
/* Inline SVG amenity icons. `flex: none` stops the icon being squeezed when the label wraps to
   two lines (Wifi gratuit dans le hall…), and align-self keeps it on the FIRST line rather than
   floating to the vertical middle of a two-line row. currentColor comes from .ico above. */
.amenity .ico { display: inline-flex; flex: none; align-self: flex-start; }
.amenity .ico-svg { width: 21px; height: 21px; display: block; }
/* --fs-md over the base rule's --fs-sm (:507). A token step, not a hardcoded px, so the row
   still tracks the two scales at :86 and :104. */
.amenity { align-items: flex-start; font-size: var(--fs-md); }

/* Reviews */
.review { padding: var(--sp-3) 0; border-bottom: 1px solid var(--c-border); }
.score-badge { display: inline-grid; place-items: center; min-width: 48px; height: 40px; padding: 0 8px;
  border-radius: var(--r-md); background: var(--c-primary-600); color: #fff; font-weight: 800; }

/* Map placeholder */
.map-embed { aspect-ratio: 16/9; border-radius: var(--r-md); background:
  repeating-linear-gradient(45deg, #dfeceb, #dfeceb 12px, #d3e5e4 12px, #d3e5e4 24px);
  display: grid; place-items: center; color: var(--c-primary-700); font-weight: 700; border: 1px solid var(--c-border); }

/* -----------------------------------------------------------------------------
   PHOTO PLACEHOLDERS — self-contained scenes (no external assets, offline-safe).
   Use on a container (fills parent). Add a <span class="ph__cap"> for a caption.
   In MVC these become real <img> bound to the provider's photo URLs.
   -------------------------------------------------------------------------- */
.ph { position: relative; width: 100%; height: 100%; min-height: 100%;
  background: var(--c-surface-3); overflow: hidden; }
.ph::after { content: ""; position: absolute; inset: 0;
  background: radial-gradient(120% 90% at 82% 8%, rgba(255,255,255,.45), transparent 45%); }
.ph__cap { position: absolute; left: 10px; bottom: 10px; z-index: 1;
  background: rgba(6,44,50,.55); color: #fff; font-size: var(--fs-xs); font-weight: 700;
  padding: 3px 9px; border-radius: var(--r-pill); backdrop-filter: blur(2px); }
.ph--sea    { background: linear-gradient(180deg,#8fd3e8 0%,#4bb4cf 42%,#0e9bb0 60%,#e9d9b6 60%,#f0e3c6 100%); }
.ph--pool   { background: linear-gradient(160deg,#bfeaf0 0%,#5bc4d6 45%,#159aa6 100%); }
.ph--sunset { background: linear-gradient(180deg,#ffcf8b 0%,#ff9a6b 40%,#e0627f 70%,#7a4a8f 100%); }
.ph--room   { background: linear-gradient(160deg,#f3e7d6 0%,#e3cdae 55%,#c9ac86 100%); }
.ph--city   { background: linear-gradient(180deg,#cfe0ea 0%,#9fb9cc 55%,#6d8aa1 100%); }
.ph--spa    { background: linear-gradient(160deg,#dff0e6 0%,#a9d9c4 50%,#5fae93 100%); }
.ph--coast  { background: linear-gradient(180deg,#a7dced 0%,#57bcd6 38%,#0e8fa6 55%,#f0e3c6 55%,#e8d3a6 100%); }

/* -----------------------------------------------------------------------------
   19. FOOTER   @partial: _SiteFooter.cshtml
   -------------------------------------------------------------------------- */
.site-footer { background: var(--c-primary-900); color: rgba(244,250,250,.8); margin-top: var(--sp-9); }
.site-footer__inner { padding-block: var(--sp-6); display: flex; flex-wrap: wrap; gap: var(--sp-4); justify-content: space-between; align-items: center; font-size: var(--fs-sm); }
.site-footer a { color: rgba(244,250,250,.85); }
.legend { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.legend .badge { transform: scale(.92); }

/* -----------------------------------------------------------------------------
   20. VOUCHER / PRINT
   -------------------------------------------------------------------------- */
.voucher { background: #fff; border: 1px solid var(--c-border); border-radius: var(--r-lg); }
.voucher__head { display: flex; justify-content: space-between; align-items: flex-start;
  padding: var(--sp-6); border-bottom: 3px solid var(--c-primary-600); }
.voucher__ref { font-family: ui-monospace, Consolas, monospace; font-size: var(--fs-lg); font-weight: 800; letter-spacing: 1px; }
.voucher__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-5); padding: var(--sp-6); }
.kv { display: grid; grid-template-columns: 150px 1fr; gap: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); }
.kv dt { color: var(--c-muted); font-weight: 600; }
.kv dd { margin: 0; font-weight: 600; }
.stamp { border: 2px solid var(--c-success); color: var(--c-success); border-radius: var(--r-md);
  padding: 6px 14px; font-weight: 800; transform: rotate(-4deg); display: inline-block; letter-spacing: 1px; }

@media print {
  .no-print, .site-header, .stepper, .site-footer, .spec-panel, .search-advanced-toggle { display: none !important; }
  body { background: #fff; }
  .page { padding: 0; }
  .voucher { border: 0; box-shadow: none; }
  a { color: #000; text-decoration: none; }
}

/* -----------------------------------------------------------------------------
   21. UTILITIES + DENSITY VISIBILITY
   -------------------------------------------------------------------------- */
.agent-only { display: none; }
:root[data-mode="agent"] .agent-only { display: revert; }
:root[data-mode="agent"] .b2c-only { display: none; }
[data-currency="tnd"] .cur-eur { display: none; }
[data-currency="eur"] .cur-tnd { display: none; }

.mt-0{margin-top:0}.mt-2{margin-top:var(--sp-2)}.mt-3{margin-top:var(--sp-3)}.mt-4{margin-top:var(--sp-4)}.mt-5{margin-top:var(--sp-5)}
.mb-0{margin-bottom:0}.mb-2{margin-bottom:var(--sp-2)}.mb-3{margin-bottom:var(--sp-3)}.mb-4{margin-bottom:var(--sp-4)}
.text-right{text-align:right}.text-center{text-align:center}
.nowrap{white-space:nowrap}.w-100{width:100%}
.divider{height:1px;background:var(--c-border);margin:var(--sp-4) 0}
.flex{display:flex}.flex-col{flex-direction:column}.gap-2{gap:var(--sp-2)}.gap-3{gap:var(--sp-3)}.gap-4{gap:var(--sp-4)}
.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}
.wrap{flex-wrap:wrap}.grow{flex:1}.fs-sm{font-size:var(--fs-sm)}.fs-xs{font-size:var(--fs-xs)}
.fw-700{font-weight:700}.fw-800{font-weight:800}
.notice { padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md); font-size: var(--fs-sm); }
.notice--info { background: var(--c-info-bg); color: var(--c-info); }
.notice--warn { background: var(--c-warn-bg); color: var(--c-warn); }
.notice--success { background: var(--c-success-bg); color: var(--c-success); }
.notice--danger { background: var(--c-danger-bg); color: var(--c-danger); }

/* -----------------------------------------------------------------------------
   22. RESPONSIVE
   -------------------------------------------------------------------------- */
@media (max-width: 1080px) {
  .layout-2col { grid-template-columns: 1fr; }
  .layout-detail { grid-template-columns: 1fr; }
  .search-fields { grid-template-columns: 1fr 1fr; }
  .search-field { border-left: 0 !important; border-top: 1px solid var(--c-border); }
  .recap, .filters { position: static; }
}
@media (max-width: 760px) {
  :root { --fs-3xl: 28px; --fs-2xl: 23px; }
  .nav-bar__inner { flex-wrap: wrap; min-height: 0; padding-block: var(--sp-3); gap: var(--sp-3); }
  .main-nav { display: none; }
  .utility-links { display: none; }
  .hotel-card { grid-template-columns: 1fr; }
  .search-fields { grid-template-columns: 1fr; }
  .grid-2, .grid-3, .voucher__grid { grid-template-columns: 1fr; }
  .room-row { grid-template-columns: 1fr 1fr; }
  .stepper__label { display: none; }
  .stepper__item::after { left: calc(50% + 18px); right: calc(-50% + 18px); }
  .gallery { grid-template-columns: 1fr 1fr; }
  .gallery > :first-child { grid-column: span 2; }
}

/* =============================================================================
   23. 2026 MODERN POLISH — v2 (appended; later rules win over the base above)
   Retunes tokens, adds a real-image media system, hides the temporarily
   disabled API spec panel, and elevates spacing / type / shadows.
   ========================================================================== */

/* 23.1 — Retuned design tokens (cleaner canvas, softer diffuse elevation) */
:root {
  --c-sand:      #F4F6F7;
  --c-surface-2: #F1F5F5;
  --c-surface-3: #E9EFEF;
  --c-border:    #E6EBEC;
  --c-border-2:  #D9E1E2;

  --shadow-sm: 0 1px 2px rgba(16,40,44,.05), 0 1px 3px rgba(16,40,44,.06);
  --shadow-md: 0 10px 26px -10px rgba(16,40,44,.18);
  --shadow-lg: 0 26px 60px -18px rgba(16,40,44,.30);

  --r-md: 12px; --r-lg: 18px; --r-xl: 26px;
  --container: 1220px;
}

/* 23.2 — Typography refinements */
body { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
h1 { letter-spacing: -.6px; }
h2 { letter-spacing: -.35px; }
h3, h4 { letter-spacing: -.2px; }
.eyebrow { letter-spacing: .14em; }
.section-title { margin-bottom: var(--sp-5); }
.section-title > h1, .section-title > h2 { margin: 0; }
.section-title::before { height: 26px; width: 4px; border-radius: 4px;
  background: linear-gradient(180deg, var(--c-accent-500), var(--c-accent-700)); }
.page { padding-block: var(--sp-8); }

/* 23.3 — Hide the API spec panel (temporarily disabled in the UI).
   Markup is PRESERVED on every page; delete this one rule to re-enable. */
.spec-panel { display: none !important; }

/* 23.4 — Real-image media system (replaces the gradient .ph placeholders) */
.media { position: relative; overflow: hidden; background: var(--c-surface-3); display: block; }
.media > img { display: block; width: 100%; height: 100%; object-fit: cover;
  transition: transform .55s cubic-bezier(.2,.6,.2,1); }
.media--16-9  { aspect-ratio: 16 / 9; }
.media--16-10 { aspect-ratio: 16 / 10; }
.media--4-3   { aspect-ratio: 4 / 3; }
.media--3-2   { aspect-ratio: 3 / 2; }
.media--1-1   { aspect-ratio: 1 / 1; }
.media__cap { position: absolute; left: 12px; bottom: 12px; z-index: 2;
  background: rgba(6,44,50,.58); color: #fff; font-size: var(--fs-xs); font-weight: 700;
  padding: 4px 10px; border-radius: var(--r-pill); backdrop-filter: blur(3px); }
.media--grad::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 42%, rgba(6,44,50,.45)); }
/* Subtle zoom on hover of any clickable card/media */
a:hover > .media > img,
.card:hover .media > img,
.hotel-card:hover .hotel-card__media img,
.hotel-card:hover .media > img { transform: scale(1.06); }
/* Ensure real <img> covers inside all existing media containers */
.hotel-card__media img,
.gallery img,
.hero__media img,
.recap__hotel-media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* 23.5 — Buttons: smoother and more tactile */
.btn { transition: transform .12s ease, background .18s ease, box-shadow .18s ease; }
.btn--accent  { box-shadow: 0 6px 16px -6px rgba(244,128,26,.55); }
.btn--accent:hover  { box-shadow: 0 12px 24px -6px rgba(244,128,26,.6); transform: translateY(-1px); }
.btn--primary:hover { box-shadow: 0 12px 24px -8px rgba(14,124,134,.55); transform: translateY(-1px); }
.btn--lg { border-radius: 14px; }

/* 23.6 — Cards: crisper, with hover elevation */
.card { border-radius: var(--r-lg); overflow: hidden;
  transition: box-shadow .2s ease, transform .2s ease, border-color .2s ease; }
a.card:hover { box-shadow: var(--shadow-md); transform: translateY(-3px); text-decoration: none; }
.panel { border-radius: var(--r-lg); }
.hotel-card { transition: box-shadow .2s ease, transform .2s ease; }
.hotel-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

/* 23.7 — Header: refined depth + animated nav underline */
.utility-bar { background: linear-gradient(90deg, var(--c-primary-900), #06393f); }
.site-header { box-shadow: var(--shadow-sm); }
.main-nav a { position: relative; padding-block: 6px; }
.main-nav a::after { content: ""; position: absolute; left: 0; right: 100%; bottom: 0; height: 2px;
  background: var(--c-accent-500); border-radius: 2px; transition: right .22s ease; }
.main-nav a:hover::after { right: 0; }
.brand__mark { box-shadow: 0 4px 10px -3px rgba(14,124,134,.6); }

/* 23.8 — Hero: taller, richer overlay, larger fluid headline */
.hero__inner { padding-block: calc(var(--sp-9) + 14px); }
.hero::after {
  background:
    linear-gradient(110deg, rgba(5,40,46,.66) 0%, rgba(5,40,46,.36) 46%, rgba(5,40,46,.16) 100%),
    linear-gradient(180deg, rgba(5,40,46,.05), rgba(5,40,46,.5)); }
.hero__title { font-size: clamp(30px, 4.4vw, 46px); line-height: 1.08; }
.hero__sub { max-width: 52ch; }
:root[data-mode="agent"] .hero__inner { padding-block: var(--sp-7); }

/* 23.9 — Search bar: a touch more premium */
.searchbar { box-shadow: 0 26px 64px -22px rgba(5,40,46,.55); border-radius: 24px; }
.search-fields { border-radius: 16px; }
.search-submit .btn { border-radius: 14px; }

/* 23.10 — Filters + recap: soft card treatment */
.filters { background: #fff; border: 1px solid var(--c-border); border-radius: var(--r-lg);
  padding: var(--sp-4); box-shadow: var(--shadow-sm); }
.filter-group:first-child { padding-top: 0; }
.stepper { box-shadow: inset 0 -1px 0 var(--c-border); }

/* =============================================================================
   24. FINAL POLISH — production review pass
   ========================================================================== */

/* 24.1 — Stepper: stack the circle ABOVE the label so the connector line never
   crosses the text (fixes the reported overlap). Connector links circle-to-circle.

   THE TOP PADDING IS NOT THE BOTTOM ONE, on purpose. Stacking put the label under the
   disc, so the band is no longer symmetrical about the connector line: below it sit the
   lower half of the disc (14), the gap (8), the label (~16) and the padding (16) — 54px —
   while above it there were only the padding and the upper half of the disc, 30px. The
   line, which is what the eye reads as the progress bar, sat a third of the way down
   instead of through the middle. 40 + 14 puts it back at 54 on both sides.

   A fixed value rather than a calc: the 54px below depends on the label's own font size,
   and a formula built on var(--fs-sm) would look self-adjusting while still being wrong
   the day the type scale moves. One number, one comment saying what it balances. */
.stepper__list { padding-block: 40px var(--sp-4); }
.stepper__item > a, .stepper__item > span { flex-direction: column; align-items: center;
  gap: 8px; text-align: center; padding-inline: 4px; }
.stepper__num { margin: 0; }
.stepper__label { line-height: 1.2; white-space: nowrap; }
.stepper__item::after { top: 14px; left: calc(50% + 24px); right: calc(-50% + 24px);
  height: 2px; border-radius: 2px; }
@media (max-width: 760px) {
  .stepper__item::after { top: 14px; left: calc(50% + 20px); right: calc(-50% + 20px); }
}

/* 24.2 — Sticky sub-panels (header removed) — stick near the top of the viewport */
.recap, .filters { top: var(--sp-4); }

/* 24.3 — Smooth anchored scrolling (header removed — small top offset) */
html { scroll-behavior: smooth; }
[id] { scroll-margin-top: var(--sp-4); }

/* 24.4 — Consistency + subtle hierarchy */
.panel { padding: var(--sp-5); }
.price-now { letter-spacing: -.4px; }
.site-footer { margin-top: var(--sp-8); }

/* =============================================================================
   25. FLIGHTS — components for the flight booking flow (reuse tokens + shell)
   @partials: _FlightSearch.cshtml, _FlightCard.cshtml, _Leg.cshtml,
              _PriceCalendar.cshtml, _SegmentTimeline.cshtml
   ========================================================================== */

/* 25.1 — Itinerary type pills (aller simple / aller-retour / multi-destinations) */
.trip-types { display: flex; gap: var(--sp-2); margin-bottom: var(--sp-3); flex-wrap: wrap; }
.trip-type { display: inline-flex; align-items: center; gap: 6px; padding: 7px 14px;
  border-radius: var(--r-pill); background: rgba(255,255,255,.12); color: #fff;
  font-weight: 700; font-size: var(--fs-sm); cursor: pointer; border: 1px solid transparent; }
.trip-type input { accent-color: var(--c-accent-500); }
.trip-type.is-active { background: #fff; color: var(--c-primary-700); }
.trip-types--light .trip-type { background: var(--c-surface-3); color: var(--c-ink-2); }
.trip-types--light .trip-type.is-active { background: var(--c-primary-600); color: #fff; }

/* 25.2 — Swap origin/destination */
.swap-btn { align-self: center; width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--c-border-2); background: #fff; color: var(--c-primary-700);
  cursor: pointer; display: grid; place-items: center; flex: none; }

/* 25.3 — Airline monogram (no real logos — IATA code on a brand-ish tint) */
.airline-logo { display: inline-grid; place-items: center; width: 40px; height: 40px;
  border-radius: 10px; background: var(--c-surface-3); color: #fff; font-weight: 800;
  font-size: var(--fs-sm); flex: none; letter-spacing: .5px; }
.airline-logo--tu { background: #C8102E; }  /* Tunisair */
.airline-logo--bj { background: #0098D8; }  /* Nouvelair */
.airline-logo--af { background: #002157; }  /* Air France */
.airline-logo--to { background: #00A94F; }  /* Transavia */
.airline-logo--other { background: #5B6B70; }

/* 25.4 — Flight result card */
.flight-card { background: #fff; border: 1px solid var(--c-border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm); overflow: hidden;
  transition: box-shadow .2s ease, transform .2s ease; }
.flight-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.flight-card__legs { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-4); }
.flight-card__buy { padding: var(--sp-4); display: flex; align-items: center;
  justify-content: space-between; gap: var(--sp-3); background: var(--c-surface-2); }
@media (min-width: 720px) {
  .flight-card { display: grid; grid-template-columns: 1fr 232px; }
  .flight-card__legs { border-right: 1px solid var(--c-border); }
  .flight-card__buy { flex-direction: column; align-items: stretch; justify-content: center; text-align: center; }
}

/* 25.5 — One leg (direction) inside a flight card */
.leg { display: grid; gap: 4px; }
.leg__dir { font-size: var(--fs-xs); color: var(--c-muted); font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; }
.leg__body { display: grid; grid-template-columns: 40px auto 1fr auto; gap: var(--sp-3); align-items: center; }
.leg__time { text-align: center; white-space: nowrap; }
.leg__time .h { font-size: var(--fs-lg); font-weight: 800; display: block; }
.leg__time .a { font-size: var(--fs-sm); color: var(--c-muted); }
/* Day rollover on the arrival time ("+1 j"), same accent as the details timeline. */
.leg__time .leg__day { margin-left: 2px; font-size: 11px; font-weight: 700; color: var(--c-accent-700);
  cursor: help; }
.leg__path { text-align: center; min-width: 90px; }
.leg__line { position: relative; height: 2px; background: var(--c-border-2); margin: 6px 8px; border-radius: 2px; }
.leg__line::before, .leg__line::after { content: ""; position: absolute; top: 50%;
  width: 7px; height: 7px; border-radius: 50%; background: var(--c-primary-500); transform: translateY(-50%); }
.leg__line::before { left: -2px; } .leg__line::after { right: -2px; }
.leg__stop { position: absolute; top: 50%; width: 6px; height: 6px; border-radius: 50%;
  background: var(--c-accent-500); transform: translate(-50%, -50%); }
.leg__meta { font-size: var(--fs-xs); color: var(--c-faint); }
.stops { font-size: var(--fs-xs); font-weight: 700; }
.stops--direct { color: var(--c-success); }
.stops--via   { color: var(--c-warn); }

/* 25.6 — Price calendar / flexible-dates matrix */
.price-cal { display: grid; grid-auto-flow: column; gap: 6px; overflow-x: auto; padding-bottom: var(--sp-2); }
.price-cal__cell { min-width: 96px; text-align: center; padding: 8px 6px;
  border: 1px solid var(--c-border); border-radius: var(--r-md); background: #fff; cursor: pointer; }
.price-cal__cell .d { font-size: var(--fs-xs); color: var(--c-muted); display: block; }
.price-cal__cell .p { font-weight: 800; }
.price-cal__cell.is-cheapest { border-color: var(--c-success); background: var(--c-success-bg); }
.price-cal__cell.is-active { border-color: var(--c-accent-500); box-shadow: 0 0 0 2px var(--c-accent-100); }

/* 25.7 — Segment timeline (flight-detail) */
.timeline { position: relative; padding-left: 26px; }
.timeline::before { content: ""; position: absolute; left: 9px; top: 6px; bottom: 6px;
  width: 2px; background: var(--c-border-2); }
.tl-point { position: relative; padding-bottom: var(--sp-4); }
.tl-point::before { content: ""; position: absolute; left: -22px; top: 3px; width: 12px; height: 12px;
  border-radius: 50%; background: #fff; border: 2px solid var(--c-primary-500); }
.tl-point:last-child { padding-bottom: 0; }
.tl-layover { margin: var(--sp-2) 0 var(--sp-2) -6px; padding: 6px 12px; background: var(--c-warn-bg);
  color: var(--c-warn); border-radius: var(--r-md); font-size: var(--fs-sm); font-weight: 700; display: inline-block; }

/* 25.8 — Cabin / pax badges + async search state */
.cabin-badge { background: var(--c-primary-100); color: var(--c-primary-700); }
.pax-badge { background: var(--c-surface-3); color: var(--c-ink-2); }
.search-progress { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-4);
  background: var(--c-info-bg); color: var(--c-info); border-radius: var(--r-lg); font-weight: 600; }
.spinner { width: 20px; height: 20px; border-radius: 50%; border: 3px solid var(--c-primary-200);
  border-top-color: var(--c-primary-600); animation: spin 0.8s linear infinite; flex: none; }
@keyframes spin { to { transform: rotate(360deg); } }
.progress-track { height: 6px; border-radius: var(--r-pill); background: var(--c-primary-100); overflow: hidden; }
.progress-track > i { display: block; height: 100%; background: var(--c-primary-500); border-radius: inherit; }

/* Legible labels + controls inside the dark advanced search panel (hotels +
   flights). Field labels are solid white; checkbox body text is bright and stays
   light on hover/focus. NB: the base `.checkbox:hover → --c-primary-700` is built
   for the LIGHT filter lists and would darken (hide) text on this dark panel. */
.search-advanced .field__label { color: #fff; }
.search-advanced .checkbox { color: rgba(255,255,255,.95); }
.search-advanced .checkbox:hover,
.search-advanced .checkbox:focus-within { color: #fff; }

/* Flight source badges (extend the hotel source-badge system) */
.badge--src-gds { background: #1F5FA8; }  /* GDS · Amadeus */
.badge--src-lcc { background: #2E9E6B; }  /* Low-cost carrier */

/* =============================================================================
   26. FINAL POLISH — production QA pass (responsive fixes + micro-interactions)
   ========================================================================== */

/* 26.1 — Mobile overflow guards (clip does not break position:sticky) */
html, body { max-width: 100%; }
body { overflow-x: clip; }

/* Flight search form: 7-column desktop layout as a CLASS (was an inline style
   that overrode the responsive media queries and broke mobile). */
/* Date + passengers columns are content-sized so their labels never wrap to a
   second line — keeping the row (and the full-height submit button) the same
   height as the hôtels bar. */
.search-fields--flights { grid-template-columns: minmax(0, 1.5fr) auto minmax(0, 1.5fr) auto auto auto auto; }
@media (max-width: 1080px) { .search-fields--flights { grid-template-columns: 1fr 1fr; } }
@media (max-width: 760px)  { .search-fields--flights { grid-template-columns: 1fr; } }

/* Occupant/passenger field rows (was inline 5-column grid in hotel-booking) */
.pax-grid { display: grid; grid-template-columns: .8fr 1.3fr 1.3fr 1fr .7fr;
  gap: var(--sp-3); align-items: end; }
@media (max-width: 900px) { .pax-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .pax-grid { grid-template-columns: 1fr; } }

/* 26.2 — Filter rows: label left-aligned, count pushed right (3-child rows
   previously center-floated the label via space-between) */
.filter-list .checkbox { justify-content: flex-start; }
.filter-list .checkbox .filter-count { margin-left: auto; }

/* 26.3 — Small-screen refinements */
@media (max-width: 760px) {
  .between { flex-wrap: wrap; }
  .searchbar { padding: var(--sp-3); border-radius: var(--r-lg); }
  .hero__inner { padding-block: var(--sp-7); }
  .table, .quote-matrix { display: block; overflow-x: auto; }
  .page { padding-block: var(--sp-6); }
  .voucher__head { flex-wrap: wrap; gap: var(--sp-4); }
}
@media (max-width: 560px) {
  .utility-bar__inner > span { display: none; }  /* tagline; tools keep the row */
  .price-now { font-size: var(--fs-xl); }
}

/* 26.4 — Micro-interactions (subtle, disabled under prefers-reduced-motion) */
/* Page-load fade removed on purpose: in a multi-page funnel a fade on every
   navigation reads as lag. Only interaction-triggered animations remain. */
@keyframes popIn { from { opacity: 0; transform: translateY(-4px) scale(.98); } to { opacity: 1; transform: none; } }
.popover:not([hidden]) { animation: popIn .16s ease both; }
.modal-backdrop:not([hidden]) .modal { animation: popIn .2s ease both; }

.input, .select, textarea.input { transition: border-color .15s ease, box-shadow .15s ease; }
.input:hover, .select:hover, textarea.input:hover { border-color: var(--c-primary-500); }
.checkbox:hover, .radio:hover { color: var(--c-primary-700); }
.table tbody tr, .quote-matrix tbody tr { transition: background .15s ease; }
.table tbody tr:hover { background: var(--c-primary-050); }
.quote-matrix tbody tr:hover:not(.is-selected) { background: var(--c-surface-2); }
.price-cal__cell { transition: border-color .15s ease, box-shadow .15s ease, transform .12s ease; }
.price-cal__cell:hover { border-color: var(--c-primary-500); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.trip-type, .search-tab, .segmented__btn { transition: background .15s ease, color .15s ease; }
details > summary { cursor: pointer; list-style-position: inside; }
details > summary:hover { color: var(--c-primary-700); }

/* 26.5 — Premium finishing touches */
::selection { background: var(--c-primary-200); color: var(--c-primary-900); }
* { scrollbar-width: thin; scrollbar-color: var(--c-border-2) transparent; }
*::-webkit-scrollbar { height: 8px; width: 10px; }
*::-webkit-scrollbar-thumb { background: var(--c-border-2); border-radius: 8px; }
*::-webkit-scrollbar-thumb:hover { background: var(--c-faint); }
.badge { letter-spacing: .01em; }
.stepper__num { transition: box-shadow .2s ease, background .2s ease; }
.site-footer a:hover { color: #fff; }

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

/* =============================================================================
   27. HOME — full-height hero (search fills the viewport, no empty gap below)
   Scoped to <body class="home"> so only the Accueil page becomes a flex column;
   the stepper keeps its natural height and the hero grows to fill the rest.
   ========================================================================== */
body.home { min-height: 100vh; display: flex; flex-direction: column; }
body.home .hero { flex: 1 0 auto; display: flex; flex-direction: column; justify-content: center; }

/* 28.1 — Home hero: make the "options avancées" toggle a high-contrast,
   clearly interactive link (the teal .linklike is unreadable on the dark hero). */
.hero .search-advanced-toggle {
  color: var(--c-accent-100);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  transition: color .15s ease;
}
.hero .search-advanced-toggle:hover,
.hero .search-advanced-toggle:focus-visible { color: #fff; }

/* =============================================================================
   28. TOP STEPPER BAR — currency picker
   The site header was removed; the stepper is now the top band and hosts the
   currency selector at its right end. Replaces the old segmented toggle with a
   native <select> styled to match the design system (.currency-select).
   @partial: _Stepper.cshtml (currency = _CurrencyPicker.cshtml view component)
   ========================================================================== */
.stepper__bar { display: flex; align-items: center; gap: var(--sp-4); }
.stepper__bar > .stepper__list { flex: 1 1 auto; min-width: 0; }

.currency-picker { flex: 0 0 auto; display: inline-flex; align-items: center; }
.currency-select {
  appearance: none; -webkit-appearance: none;
  height: 36px; padding: 0 32px 0 14px;
  font-size: var(--fs-sm); font-weight: 700; color: var(--c-ink);
  background-color: #fff; border: 1px solid var(--c-border-2); border-radius: var(--r-pill);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M2 4l4 4 4-4' fill='none' stroke='%235B6B70' stroke-width='1.6'/></svg>");
  background-repeat: no-repeat; background-position: right 12px center;
  cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease;
}
.currency-select:hover { border-color: var(--c-primary-500); }
.currency-select:focus { border-color: var(--c-primary-500); box-shadow: var(--shadow-focus); outline: none; }

@media (max-width: 560px) {
  .stepper__bar { gap: var(--sp-2); }
  .currency-select { height: 34px; padding: 0 28px 0 10px; }
}

/* =============================================================================
   29. HOTEL RESULTS — inline "Chambres & tarifs" expansion (accordion)
   Clicking "Chambres & tarifs" expands the rooms/rates INSIDE the result card
   (no page change). Rooms are chosen with a Choice-Chip grid (see §30); a single
   bottom summary bar totals every checked rate and drives one "Réserver" CTA.
   @partial: _HotelRooms.cshtml — inline load of HotelDetailRS.Rooms[] for the card.
   ========================================================================== */

/* Hotel image / name are now links to the detail page */
.hotel-card__media-link { position: absolute; inset: 0; display: block; z-index: 1; }
.hotel-card__flags, .hotel-card__fav { z-index: 2; }
.hotel-card__name-link { color: inherit; text-decoration: none; }
.hotel-card__name-link:hover { color: var(--c-primary-700); text-decoration: underline; }

/* "Chambres & tarifs" toggle — active state while its card is expanded */
.hotel-card.is-open .rooms-toggle {
  background: var(--c-accent-600);
  box-shadow: 0 0 0 3px var(--c-accent-100), var(--shadow-sm);
}

/* Expanding panel: animate height via grid-template-rows 0fr → 1fr */
.hotel-card__rooms {
  grid-column: 1 / -1;
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .3s ease;
}
.hotel-card.is-open .hotel-card__rooms { grid-template-rows: 1fr; }
.hotel-card__rooms > .rooms-panel {
  overflow: hidden; min-height: 0;
  visibility: hidden; transition: visibility .3s ease;
}
.hotel-card.is-open .hotel-card__rooms > .rooms-panel { visibility: visible; }

/* =============================================================================
   30. ROOMS & RATES — Choice-Chip selector (shared: results + detail pages)
   Blended UX: a formule choice-chip bar filters rates by board across all rooms;
   every room exposes its boards as multi-select toggle "rate-chips" (checkbox),
   so several rooms/boards can be booked at once. Built by CR.roomsPanelHTML().
   ========================================================================== */
.rooms-panel {
  border-top: 1px solid var(--c-border);
  background: var(--c-surface-2);
  padding: var(--sp-4) var(--card-pad);
}

/* --- Formule choice-chip bar (filter rates by board) --------------------- */
.rooms-formule {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  padding-bottom: var(--sp-3); margin-bottom: var(--sp-3);
  border-bottom: 1px solid var(--c-border);
}
.rooms-formule__label { font-weight: 800; color: var(--c-ink); margin-right: var(--sp-2); }
.formule-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.formule-chip {
  border: 1.5px solid var(--c-border-2); background: var(--c-surface);
  color: var(--c-ink-2); font-weight: 600; font-size: var(--fs-sm);
  padding: 7px 16px; border-radius: var(--r-pill); cursor: pointer;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.formule-chip:hover { border-color: var(--c-primary-500); color: var(--c-primary-700); }
.formule-chip.is-active {
  background: var(--c-primary-600); border-color: var(--c-primary-600); color: #fff;
  box-shadow: var(--shadow-sm);
}

/* --- Rate table (one selectable row per room × board) ------------------- */
.rooms-table {
  border: 1px solid var(--c-border); border-radius: var(--r-md);
  overflow: hidden; background: var(--c-surface);
}
.rooms-min { font-weight: 700; }

.rate-row {
  position: relative; display: grid; align-items: center;
  grid-template-columns: 26px minmax(150px, 1.15fr) minmax(190px, 1.5fr) minmax(120px, auto);
  gap: var(--sp-2) var(--sp-4);
  padding: var(--sp-3) var(--sp-4); cursor: pointer;
  background: var(--c-surface); border-top: 1px solid var(--c-border-2);
  transition: background .12s ease, box-shadow .12s ease;
}
.rate-row:first-child { border-top: 0; }
.rate-row--start { border-top: 1px solid var(--c-border); }   /* stronger divider between room groups */
.rooms-table > .rate-row--start:first-child { border-top: 0; }
.rate-row[data-group="odd"] { background: var(--c-surface-2); }
.rate-row:hover { background: var(--c-primary-050); }
.rate-row:focus-within { box-shadow: var(--shadow-focus); z-index: 1; }
.rate-row__check { position: absolute; opacity: 0; width: 0; height: 0; }

/* Custom checkbox */
.rate-row__box {
  width: 20px; height: 20px; border-radius: 5px;
  border: 1.5px solid var(--c-border-2); background: var(--c-surface);
  position: relative; transition: background .12s ease, border-color .12s ease;
}
.rate-row__box::after {
  content: "✔"; position: absolute; inset: 0; color: #fff; font-size: 13px;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transform: scale(.5); transition: opacity .12s ease, transform .12s ease;
}
.rate-row.is-checked .rate-row__box,
.rate-row:has(.rate-row__check:checked) .rate-row__box {
  background: var(--c-accent-500); border-color: var(--c-accent-500);
}
.rate-row.is-checked .rate-row__box::after,
.rate-row:has(.rate-row__check:checked) .rate-row__box::after { opacity: 1; transform: scale(1); }

/* Selected row emphasis (JS .is-checked + native :has fallback) */
.rate-row.is-checked,
.rate-row:has(.rate-row__check:checked) {
  background: var(--c-primary-050); box-shadow: inset 3px 0 0 var(--c-primary-600);
}

/* Room column */
.rate-row__room { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rate-row__room-name {
  font-weight: 400; color: var(--c-ink); font-size: var(--fs-md);
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
}
.rate-row__occ { color: var(--c-muted); font-size: var(--fs-sm); }
.rate-row__scarcity { color: var(--c-danger); font-size: var(--fs-sm); font-weight: 700; margin-top: 2px; }

/* Board column */
.rate-row__board { display: flex; flex-direction: column; gap: var(--sp-2); align-items: flex-start; min-width: 0; }
.rate-row__board-name {
  font-weight: 400; color: var(--c-accent-700); font-size: var(--fs-sm);
  display: flex; align-items: center; gap: 6px;
}
.rate-row__board-ico { filter: grayscale(.2); }
.rate-row__cancel {
  font-size: var(--fs-xs); font-weight: 400; border-radius: var(--r-pill);
  padding: 3px 10px; line-height: 1.3;
}
.rate-row__cancel.is-free { color: var(--c-success); background: var(--c-success-bg); }
.rate-row__cancel.is-nonref { color: var(--c-danger); background: var(--c-danger-bg); }

/* End column (availability + price, right-aligned) */
.rate-row__end { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; text-align: right; }
.rate-row__avail {
  font-size: var(--fs-xs); font-weight: 400; border-radius: var(--r-sm); padding: 2px 9px;
}
.rate-row__avail.is-ok { color: #fff; background: var(--c-success); }
.rate-row__avail.is-request { color: #fff; background: var(--c-warn); }
.rate-row__price .price-now { font-size: var(--fs-lg); font-weight: 800; color: var(--c-ink); }
.rate-row__price .price-old { font-size: var(--fs-xs); margin-right: 2px; }

/* Formule filter: hide rows whose board ≠ active chip. A CHECKED row is never
   hidden (:not(.is-checked)) so a selection always stays visible & consistent. */
.rooms-panel[data-filter="lpd"]  .rate-row:not([data-board="lpd"]):not(.is-checked),
.rooms-panel[data-filter="dp"]   .rate-row:not([data-board="dp"]):not(.is-checked),
.rooms-panel[data-filter="aisd"] .rate-row:not([data-board="aisd"]):not(.is-checked),
.rooms-panel[data-filter="ai"]   .rate-row:not([data-board="ai"]):not(.is-checked) { display: none; }

/* --- Bottom summary bar — total of all checked rates + one Réserver CTA -- */
.rooms-summary {
  display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
  margin-top: var(--sp-4); padding: var(--sp-3) var(--sp-4);
  background: var(--c-primary-050); border: 1px solid var(--c-primary-100);
  border-radius: var(--r-md);
}
.rooms-summary__meta { display: flex; flex-direction: column; }
.rooms-summary__label { font-weight: 800; color: var(--c-ink-2); }
.rooms-summary__count { font-size: var(--fs-sm); color: var(--c-muted); }
.rooms-summary__price { margin-left: auto; }
.rooms-summary__price .price-now { font-size: var(--fs-2xl); font-weight: 800; color: var(--c-primary-700); }
[data-summary-book].is-disabled, [data-summary-book][aria-disabled="true"] {
  opacity: .45; pointer-events: none;
}

@media (max-width: 760px) {
  .rate-row {
    grid-template-columns: 22px 1fr; gap: var(--sp-1) var(--sp-3); align-items: start;
  }
  .rate-row__box  { grid-column: 1; grid-row: 1; align-self: start; margin-top: 2px; }
  .rate-row__room { grid-column: 2; }
  .rate-row__board { grid-column: 2; margin-top: 2px; }
  .rate-row__end  {
    grid-column: 2; flex-direction: row; align-items: center; text-align: left;
    gap: var(--sp-2); flex-wrap: wrap; margin-top: 2px;
  }
  .rooms-summary__price { margin-left: 0; }
}

/* =============================================================================
   31. HOTEL DETAIL — search-first availability (recompute rates in place)
   The detail page opens on a "Tarifs & Réservation" search bar (no rooms shown).
   Submitting reveals the Choice-Chip rooms panel below WITHOUT leaving the page.
   ========================================================================== */
.detail-search__form {
  display: grid; grid-template-columns: auto auto minmax(0, 1.5fr) auto;
  gap: var(--sp-3); align-items: end;
}
.detail-search__form .field { margin: 0; }
.detail-search__submit { align-self: end; }
.detail-rooms { margin-top: var(--sp-4); }
.detail-rooms .rooms-panel {
  border: 1px solid var(--c-border); border-radius: var(--r-lg);
  animation: detail-rooms-in .35s ease both;
}
@keyframes detail-rooms-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (max-width: 720px) {
  .detail-search__form { grid-template-columns: 1fr 1fr; }
  .detail-search__submit { grid-column: 1 / -1; }
  .detail-search__submit .btn { width: 100%; }
}

/* =============================================================================
   26. MINIMALIST REFRESH — "Ardoise" (clean · restrained · professional)
   -----------------------------------------------------------------------------
   PURELY VISUAL re-skin (no markup / no JS / no layout / no flow changes).
   Appended last so it overrides §1 and §23–24. Replaces the previous
   indigo+coral, gradient-heavy look with a restrained, near-monochrome system:
   neutral greys + a SINGLE muted-navy accent, flat fills (NO gradients), hairline
   borders with subtle neutral elevation, quiet interactions, and Inter for a
   clean typographic hierarchy. Hierarchy comes from weight, size, and whitespace
   — not colour or effects. Functional status colours (success / danger / warn)
   are kept but muted; hotel-rating stars stay a conventional muted gold.
   ========================================================================== */
:root {
  /* Single accent — muted, professional navy. Primary AND accent point here,
     so the whole UI resolves to ONE restrained colour + neutrals. */
  --c-primary-900: #16324B;
  --c-primary-700: #1E4364;
  --c-primary-600: #2C5A86;   /* base */
  --c-primary-500: #3F73A2;
  --c-primary-200: #BACDDE;
  --c-primary-100: #E6EDF3;
  --c-primary-050: #F4F8FB;

  --c-accent-700: #1E4364;
  --c-accent-600: #24507A;
  --c-accent-500: #2C5A86;    /* same navy — no second brand colour */
  --c-accent-300: #7FA0BE;
  --c-accent-100: #E6EDF3;

  /* Ink / text — neutral grey scale (no colour cast) */
  --c-ink:     #191C21;
  --c-ink-2:   #3B3F45;
  --c-muted:   #696E75;
  --c-faint:   #9AA0A7;
  --c-on-dark: #F6F7F8;

  /* Surfaces — airy neutral canvas, hairline borders */
  --c-surface:   #FFFFFF;
  --c-sand:      #FAFBFC;   /* page canvas */
  --c-surface-2: #F3F4F6;
  --c-surface-3: #EBEDEF;
  --c-border:    #E6E8EB;
  --c-border-2:  #DADDE1;

  /* Status — muted but legible; info/direct fold onto the navy accent */
  --c-success: #2E7D52;  --c-success-bg: #EAF3EE;
  --c-danger:  #B23B32;  --c-danger-bg:  #FBECEA;
  --c-warn:    #8A6D1F;  --c-warn-bg:    #F4EEDD;
  --c-info:    #2C5A86;  --c-info-bg:    #E9EFF4;
  --c-src-direct:   #2C5A86;
  --c-src-cyberesa: #2C5A86;

  --c-hero-ink: #1B2530;   /* neutral dark slate for hero / header / overlays */

  /* Subtle neutral elevation — borders do most of the work */
  --shadow-sm:    0 1px 2px rgba(20,24,31,.05), 0 1px 3px rgba(20,24,31,.05);
  --shadow-md:    0 4px 14px rgba(20,24,31,.07);
  --shadow-lg:    0 12px 32px rgba(20,24,31,.10);
  --shadow-focus: 0 0 0 3px rgba(44,90,134,.28);

  /* One clean, neutral type family (system fallback if the webfont is offline) */
  --font-sans: "Inter", "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --font-head: "Inter", "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
}

/* Headings: refined weights + tight tracking — hierarchy via size/weight, not colour */
h1 { font-weight: 700; letter-spacing: -.4px; }
h2 { font-weight: 700; letter-spacing: -.3px; }
h3, h4 { font-weight: 600; letter-spacing: -.1px; }

/* Buttons: flat solid fills, no gradient, no coloured glow, no hover lift */
.btn--accent  { background: var(--c-accent-500); box-shadow: var(--shadow-sm); }
.btn--accent:hover  { background: var(--c-accent-700); box-shadow: var(--shadow-sm); transform: none; }
.btn--primary { background: var(--c-primary-600); }
.btn--primary:hover { background: var(--c-primary-700); box-shadow: var(--shadow-sm); transform: none; }

/* Chips: active state is a flat navy fill */
.formule-chip.is-active { background: var(--c-primary-600); border-color: var(--c-primary-600); color: #fff; }
.trip-types--light .trip-type.is-active { background: var(--c-primary-600); }

/* Section-title accent: a thin solid navy rule (was a gradient) */
.section-title::before { background: var(--c-primary-600); }

/* Hero / search bar / media overlays: neutral dark, single flat scrim for legibility */
.hero { background: var(--c-hero-ink); }
.hero::after { background: linear-gradient(180deg, rgba(20,28,36,.30), rgba(20,28,36,.55)); }
.searchbar { background: rgba(23,31,40,.62); border-color: rgba(255,255,255,.14);
  box-shadow: var(--shadow-lg); }
.media__cap { background: rgba(23,31,40,.62); }
.media--grad::after { background: linear-gradient(180deg, transparent 45%, rgba(23,31,40,.5)); }

/* Header: flat neutral-dark bar, quiet brand mark */
.utility-bar { background: var(--c-hero-ink); }
.brand__mark { box-shadow: var(--shadow-sm); }

/* Map placeholder + gallery tile + modal scrim → neutral */
.map-embed {
  background: repeating-linear-gradient(45deg, #EEF0F2, #EEF0F2 12px, #E4E7EA 12px, #E4E7EA 24px);
  color: var(--c-ink-2); }
.gallery__more { background: var(--c-hero-ink); }
.modal-backdrop { background: rgba(23,31,40,.5); }

/* Hotel-rating stars keep a conventional, muted gold (functional, not decorative) */
.stars { color: #C99A2E; }

/* Calm interactions: gentle shadow on hover, no card "pop", minimal image zoom */
a.card:hover, .hotel-card:hover { transform: none; box-shadow: var(--shadow-md); }
a:hover > .media > img,
.card:hover .media > img,
.hotel-card:hover .hotel-card__media img,
.hotel-card:hover .media > img { transform: scale(1.03); }

/* -----------------------------------------------------------------------------
   26.1 — EXTRA RESTRAINT ("sobre, sans beaucoup de couleurs")
   Pulls the palette down to navy + neutral greys. The ONLY remaining hues are a
   muted green (positive) and a muted red (caution) for essential functional
   signals; every other decorative colour (provider purples/browns, promo/on-
   request ambers, gold stars) collapses to neutral grey.
   -------------------------------------------------------------------------- */
:root {
  /* "Sur demande" / "Min. N nuitées" etc. → sober grey instead of amber */
  --c-warn: #5B6169;  --c-warn-bg: #EEF0F2;
  /* All provider / source badges → one neutral grey (no purple / brown / green) */
  --c-src-direct: #3B3F45;  --c-src-tgx: #3B3F45;  --c-src-cyberesa: #3B3F45;
  --c-src-dotw: #3B3F45;     --c-src-lightresa: #3B3F45;
}
/* Rating stars → neutral (was gold) */
.stars { color: var(--c-ink-2); }
/* Promo + provider badges → quiet neutral chips */
.badge--promo { background: var(--c-surface-3); color: var(--c-ink-2); }
.badge--src   { background: var(--c-surface-3); color: var(--c-ink-2); }
.badge--src-direct, .badge--src-tgx, .badge--src-cyberesa,
.badge--src-dotw, .badge--src-lightresa { background: var(--c-surface-3); }
/* Availability badges → subtle tint instead of a solid colour fill */
.rate-row__avail.is-ok      { background: var(--c-success-bg); color: var(--c-success); }
.rate-row__avail.is-request { background: var(--c-surface-3);  color: var(--c-muted); }

/* =============================================================================
   32. IBE HÔTEL (« Hotel Only ») — site mono-hôtel
   -----------------------------------------------------------------------------
   Réutilise entièrement le parcours hôtel. Ces règles ne font qu'ADAPTER la
   barre de recherche (hôtel fixe : pas de destination, pas d'étoiles) et
   l'en-tête « un seul hôtel » de la page résultats. Aucune logique nouvelle.
   ========================================================================== */

/* Bandeau d'identité de l'hôtel (remplace les onglets Hôtels/Vols du hero) */
.ibe-hotelbar { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2) var(--sp-4);
  margin-bottom: var(--sp-4); color: rgba(255,255,255,.94); }
.ibe-hotelbar__name { font-size: var(--fs-lg); font-weight: 700; display: inline-flex; align-items: center; gap: var(--sp-2); }
.ibe-hotelbar__name .stars { color: var(--c-accent-100); }
.ibe-hotelbar__hint { font-size: var(--fs-sm); color: rgba(255,255,255,.78); }

/* Grille de la barre IBE : établissement (verrouillé) · arrivée · départ · occupation · CTA */
.search-fields--ibe { grid-template-columns: 1.7fr 1fr 1fr 1.4fr auto; }

/* Champ « établissement » en lecture seule (hôtel imposé par le site) */
.search-field--locked { background: var(--c-surface-2); border-radius: var(--r-md); }
.search-field--locked .search-field__value { font-weight: 700; color: var(--c-ink); white-space: normal; }

/* En-tête « un seul hôtel » de ibe-results.html */
.ibe-single-hotel { display: block; }
.ibe-note { font-size: var(--fs-sm); color: var(--c-muted);
  background: var(--c-surface-2); border: 1px dashed var(--c-border);
  border-radius: var(--r-md); padding: 10px 12px; }

@media (max-width: 900px) {
  .search-fields--ibe { grid-template-columns: 1fr 1fr; }
  .search-fields--ibe .search-field--locked { grid-column: 1 / -1; }
  .search-fields--ibe .search-submit { grid-column: 1 / -1; }
  /* keep the "border-left divider" from looking broken when wrapped */
  .search-fields--ibe .search-field + .search-field { border-left: 0; border-top: 1px solid var(--c-border); }
}

/* =============================================================================
   33. IBE PREMIUM SKIN — "Rivage"  (scoped to [data-skin="ibe"] ONLY)
   -----------------------------------------------------------------------------
   A premium, minimal, elegant reskin for the hotel-owned IBE, inspired by the
   best patterns of Profitroom / iHotelier / SynXis (guided search + price
   calendar, hero shopfront, inline offer expansion, sticky booking summary).
   PURELY VISUAL + additive. Gated by [data-skin="ibe"] so it NEVER touches the
   validated multi-hotel flow. No JS/logic/flow changes; same operations, fields,
   filters and journey. Premium comes from WHITESPACE + TYPE + refined
   components, not new colour — it rides on the §26 Ardoise navy+neutral tokens.
   ========================================================================== */
[data-skin="ibe"] {
  --ibe-radius: 16px;
  --ibe-radius-sm: 12px;
  --ibe-gap: clamp(20px, 3vw, 36px);
  --ibe-pad: clamp(20px, 2.4vw, 34px);
  --ibe-shadow: 0 18px 48px -24px rgba(20,24,31,.34);
  --ibe-shadow-sm: 0 2px 10px rgba(20,24,31,.06);
  --ibe-ink: var(--c-ink);
}

/* Airier canvas + calmer body rhythm */
[data-skin="ibe"] body,
body[data-skin] { background: var(--c-sand); }
[data-skin="ibe"] .page { padding-top: var(--ibe-gap); padding-bottom: calc(var(--ibe-gap) * 1.6); }

/* Elegant type — lighter, larger, tighter headings */
[data-skin="ibe"] h1 { font-weight: 600; letter-spacing: -.6px; line-height: 1.08; }
[data-skin="ibe"] h2 { font-weight: 600; letter-spacing: -.4px; }
[data-skin="ibe"] .eyebrow { letter-spacing: .18em; font-weight: 700; text-transform: uppercase; font-size: 12px; }

/* Panels/cards → premium: hairline border, soft elevation, generous padding */
[data-skin="ibe"] .panel,
[data-skin="ibe"] .card { border: 1px solid var(--c-border); border-radius: var(--ibe-radius);
  box-shadow: var(--ibe-shadow-sm); }
[data-skin="ibe"] .panel { padding: var(--ibe-pad); }
[data-skin="ibe"] .section-title h2 { font-size: clamp(18px, 2vw, 22px); }

/* Buttons → refined, quiet, letter-spaced labels */
[data-skin="ibe"] .btn { border-radius: 999px; font-weight: 600; letter-spacing: .01em; }
[data-skin="ibe"] .btn--lg { padding: 14px 26px; }

/* ---------------------------------------------------------------------------
   33.1  HERO — the hotel shopfront (SynXis-inspired, deepened)
   ------------------------------------------------------------------------- */
[data-skin="ibe"] .hero { min-height: clamp(520px, 74vh, 760px); }
[data-skin="ibe"] .hero::after { background:
  linear-gradient(180deg, rgba(18,26,34,.28) 0%, rgba(18,26,34,.22) 42%, rgba(18,26,34,.72) 100%); }
[data-skin="ibe"] .hero__inner { padding-bottom: clamp(28px, 5vh, 60px); }
[data-skin="ibe"] .hero__title { font-size: clamp(38px, 6vw, 66px); font-weight: 600; letter-spacing: -1.2px; }
[data-skin="ibe"] .hero__sub { font-size: clamp(15px, 1.4vw, 18px); max-width: 54ch; opacity: .94; }

/* 33.1a — Unified search card. Like the Hôtel / Vol .searchbar, the guided pill
   search AND the advanced options live inside ONE solid teal card so they read as
   a single panel (the pill bar was previously floating on the hero, separate from
   the advanced panel). The card reads clearly over the hero photo and shares the
   IBE radius/shadow with the rest of the skin. */
[data-skin="ibe"] .ibe-searchbar {
  background: rgba(9, 40, 45, .72);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: var(--ibe-radius);
  box-shadow: var(--ibe-shadow);
  padding: var(--sp-4);
}
/* The pill bar now nests inside the card, so drop its heavy drop-shadow to a
   subtle one — it shouldn't look like it floats above the card. */
[data-skin="ibe"] .ibe-searchbar .ibe-search { box-shadow: var(--ibe-shadow-sm); }

/* Advanced options: now a subtle inner surface within the card (same treatment
   as the Hôtel / Vol .search-advanced), full width of the card. The base toggle
   just flips display:none↔block (instant); here we keep the panel in flow and
   animate grid-template-rows 0fr→1fr (auto-height, so it stays smooth at any
   content height — incl. the mobile 1-column layout) plus opacity/padding, for a
   smooth slide-open. The inner wrapper does the clipping. Purely visual — fields,
   white labels and toggle logic unchanged. */
[data-skin="ibe"] .search-advanced {
  display: grid;                 /* keep in flow (base = display:none) so it animates */
  grid-template-rows: 0fr;       /* collapsed */
  margin-top: 0;
  padding: 0 var(--sp-4);        /* vertical padding grows on open; horizontal stays */
  opacity: 0;
  background: rgba(255,255,255,.08);
  border-radius: var(--r-md);
  transition: grid-template-rows .4s ease, opacity .3s ease,
              padding .4s ease, margin-top .4s ease;
}
[data-skin="ibe"] .search-advanced.is-open {
  grid-template-rows: 1fr;       /* expands to the content's natural height */
  margin-top: var(--sp-3);
  padding: var(--sp-4);
  opacity: 1;
}
[data-skin="ibe"] .search-advanced__inner { overflow: hidden; min-height: 0; }

/* ---------------------------------------------------------------------------
   33.2  GUIDED SEARCH — pill stepper (Arrivée › Départ › Occupation) + CTA
   Profitroom-style guided search; drives the SAME fromdate/todate/rooms.
   ------------------------------------------------------------------------- */
/* FLEX, not a fixed 6-column grid. The bar carries three pills on a mono-hotel
   site (Arrivée / Départ / Occupation) but FOUR when the deployment pins no
   establishment and the hotel picker is rendered (see IbeSearch.cshtml). A
   `grid-template-columns: 1fr auto 1fr auto 1.1fr auto` sized for three wrapped
   the 4-pill variant onto a second implicit row — the CTA ended up under the
   pills, misaligned with the mono-hotel page. Flex sizes the row from whatever
   children are actually present, so BOTH variants read as one aligned line. */
.ibe-search { position: relative; background: rgba(255,255,255,.97); border-radius: var(--ibe-radius);
  box-shadow: var(--ibe-shadow); padding: 10px; display: flex; flex-wrap: nowrap;
  align-items: stretch; gap: 6px; }
.ibe-search__pill { position: relative; display: flex; flex-direction: column; justify-content: center;
  gap: 3px; padding: 12px 18px; border-radius: var(--ibe-radius-sm); background: #fff;
  border: 1px solid transparent; cursor: pointer; text-align: left; min-width: 0;
  flex: 1 1 0; }
/* The 1.1 ratio the grid gave the occupancy column, kept so the 3-pill bar has
   exactly the proportions it has today. */
.ibe-search__pill[data-ibe-pill="pop-occ"] { flex-grow: 1.1; }
.ibe-search__pill:hover { background: var(--c-primary-050); }
.ibe-search__pill.is-open { border-color: var(--c-primary-200); background: var(--c-primary-050); }
.ibe-search__label { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--c-muted); display: inline-flex; align-items: center; gap: 6px; }
.ibe-search__value { font-size: 16px; font-weight: 600; color: var(--c-ink); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.ibe-search__value small { font-weight: 500; color: var(--c-muted); }
.ibe-search__sep { display: flex; align-items: center; justify-content: center; color: var(--c-faint);
  flex: 0 0 auto; }
.ibe-search__sep svg, .ibe-search__sep span { width: 26px; height: 26px; display: grid; place-items: center;
  border-radius: 50%; background: var(--c-surface-2); font-size: 13px; }
.ibe-search__cta { display: flex; flex: 0 0 auto; }
.ibe-search__cta .btn { height: 100%; white-space: nowrap; }

/* Dropdown panel shared by calendar + occupancy */
.ibe-pop { position: absolute; z-index: var(--z-popover); top: calc(100% + 10px); left: 0;
  background: #fff; color: var(--c-ink); border: 1px solid var(--c-border); border-radius: var(--ibe-radius);
  box-shadow: var(--ibe-shadow); padding: 18px; }
.ibe-pop[hidden] { display: none; }
.ibe-pop--occ { min-width: min(340px, 92vw); }
/* 33.2b — Hotel picker in LISTBOX mode (blnIBESupported): the whole inventory is
   rendered at once instead of an autocomplete, so the panel scrolls rather than
   running past the fold on a deployment that sells more than a handful. */
.ibe-pop--list { min-width: min(360px, 92vw); }
.ibe-pop--list .ac-list { max-height: 320px; overflow-y: auto; }

/* ---------------------------------------------------------------------------
   33.3  DATE-RANGE CALENDAR — clean check-in → check-out picker (no prices).
   The whole stay is highlighted: navy endpoints (is-start/is-end) + a light
   connected band for every night in between (is-inrange).
   ------------------------------------------------------------------------- */
.pcal { min-width: min(660px, 92vw); }
.pcal__head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 14px; }
.pcal__nav { width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--c-border);
  background: #fff; cursor: pointer; display: grid; place-items: center; color: var(--c-ink-2); flex: none; }
.pcal__nav:hover { background: var(--c-primary-050); border-color: var(--c-primary-200); }
.pcal__months { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; }
.pcal__mname { text-align: center; font-weight: 600; letter-spacing: -.2px; margin-bottom: 12px; }
.pcal__dow, .pcal__grid { display: grid; grid-template-columns: repeat(7, 1fr); }
.pcal__dow span { text-align: center; font-size: 11px; font-weight: 700; letter-spacing: .06em;
  color: var(--c-faint); text-transform: uppercase; padding-bottom: 8px; }
.pcal__grid { gap: 0; }
.pcal__day { aspect-ratio: 1 / 1; border: 0; background: transparent; display: grid; place-items: center;
  cursor: pointer; transition: background .1s ease; }
.pcal__day .d { display: grid; place-items: center; width: 82%; height: 82%; border-radius: 50%;
  font-size: 14px; font-weight: 600; color: var(--c-ink); line-height: 1; transition: background .1s ease; }
.pcal__day:hover:not(.is-selected):not(.is-unavail) .d { background: var(--c-primary-050); }
.pcal__day.is-out { visibility: hidden; }
.pcal__day.is-unavail { cursor: not-allowed; }
.pcal__day.is-unavail .d { color: var(--c-faint); text-decoration: line-through; }
/* connected in-range band (square fill) + rounded outer endpoints — a clearly
   visible medium-blue band so the whole selected stay reads at a glance */
.pcal__day.is-inrange { background: var(--c-primary-200); }
.pcal__day.is-inrange .d { color: var(--c-primary-900); font-weight: 700; }
.pcal__day.is-selected { background: var(--c-primary-200); }
.pcal__day.is-start { border-top-left-radius: 999px; border-bottom-left-radius: 999px; }
.pcal__day.is-end { border-top-right-radius: 999px; border-bottom-right-radius: 999px; }
.pcal__day.is-selected .d { background: var(--c-primary-700); color: #fff; width: 88%; height: 88%; }
.pcal__foot { display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: center; justify-content: space-between;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--c-border); }
.pcal__nights { font-size: 14px; font-weight: 600; color: var(--c-ink); }

/* ---------------------------------------------------------------------------
   33.4  RESULTS — sticky editable search summary (Profitroom/SynXis)
   ------------------------------------------------------------------------- */
.ibe-topbar { position: sticky; top: 8px; z-index: 20; margin-bottom: var(--ibe-gap);
  background: rgba(255,255,255,.9); backdrop-filter: blur(10px);
  border: 1px solid var(--c-border); border-radius: 999px; box-shadow: var(--ibe-shadow-sm);
  display: flex; align-items: center; gap: 6px; padding: 8px 8px 8px 20px; }
.ibe-topbar__brand { font-weight: 700; letter-spacing: -.2px; white-space: nowrap; padding-right: 6px; }
.ibe-topbar__brand .stars { font-size: 12px; margin-left: 4px; }
.ibe-topbar__pills { display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0; overflow: hidden; }
.ibe-topbar__pill { display: inline-flex; align-items: baseline; gap: 6px; padding: 7px 14px;
  border-radius: 999px; background: var(--c-surface-2); font-size: 14px; white-space: nowrap; }
.ibe-topbar__pill b { font-weight: 600; }
.ibe-topbar__edit { margin-left: auto; white-space: nowrap; }

/* ---------------------------------------------------------------------------
   33.5  ROOMS — ROOM-TYPE CARDS with photo + expandable rate options
   (Profitroom "SELECT OFFER" · iHotelier "+ PLUS DE TARIFS" · SynXis rate rows)
   One card per room type: big photo · specs · one "à partir de" price · expand
   to reveal that room's meal-plan rates. Selection still drives the shared
   .rate-row__check → CR.updateRoomsSummary summary (functionality unchanged).
   ------------------------------------------------------------------------- */
[data-skin="ibe"] .ibe-roomsbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 16px; }
[data-skin="ibe"] .ibe-roomsbar .sort { display: inline-flex; align-items: center; gap: 8px; }
[data-skin="ibe"] .rooms-panel { background: transparent; border: 0; padding: 0; box-shadow: none; }
[data-skin="ibe"] .rooms-list { display: flex; flex-direction: column; gap: 20px; }

/* ---- the room card ---- */
.rmc { display: grid; grid-template-columns: 300px 1fr 232px; background: #fff;
  border: 1px solid var(--c-border); border-radius: var(--ibe-radius); box-shadow: var(--ibe-shadow-sm);
  overflow: hidden; transition: box-shadow .15s ease, border-color .15s ease; }
.rmc:hover { box-shadow: var(--ibe-shadow); }
.rmc.is-open { box-shadow: var(--ibe-shadow); border-color: var(--c-border-2); }
.rmc__media { position: relative; min-height: 220px; overflow: hidden; }
.rmc__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .4s ease; }
.rmc:hover .rmc__media img { transform: scale(1.04); }
.rmc__badge { position: absolute; top: 12px; left: 12px; background: var(--c-hero-ink); color: #fff;
  font-size: 12px; font-weight: 600; padding: 5px 12px; border-radius: 999px; letter-spacing: .01em; }
.rmc__count { position: absolute; bottom: 12px; left: 12px; background: rgba(23,31,40,.72); color: #fff;
  font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 999px; display: inline-flex; gap: 6px; }
.rmc__body { padding: 22px 24px; display: flex; flex-direction: column; gap: 11px; min-width: 0; }
.rmc__name { font-size: 20px; font-weight: 600; letter-spacing: -.3px; margin: 0; }
.rmc__specs { display: flex; flex-wrap: wrap; gap: 8px 20px; font-size: 14px; color: var(--c-ink-2); }
.rmc__specs span { display: inline-flex; align-items: center; gap: 7px; }

/* ---- capacity badge (MinPax/MaxPax of HotelDetailRS) ----
   A pill rather than the bare "👤 2 Adultes" line it replaces: it states a property of the ROOM,
   not of the search, so it is the one spec on the card worth framing.

   gap = the ONLY separation inside the pill, and it is deliberately WIDE. The glyph block can end
   with "+1" and the wording can start with "2", so a normal 8-10px would put two unrelated digits
   next to each other and the eye reads "+1 2 à 6".

   SCOPED UNDER .rmc__specs ON PURPOSE, here and on .rmc__pax-icons below. The rule right above is
   a descendant selector, so it scores (0,1,1) and BEATS a lone .rmc__pax at (0,1,0) — every gap
   set on a single class here would be silently discarded, whatever its value. Both blocks of this
   badge are spans inside .rmc__specs, so both need the prefix. */
.rmc__specs .rmc__pax { gap: 15px; padding: 5px 12px 5px 10px; border-radius: 999px;
  background: var(--c-primary-050); border: 1px solid var(--c-border); color: var(--c-ink-2);
  cursor: default; }
/* The glyphs sit tight against each other rather than spaced, so the strip reads as ONE group of
   guests instead of N separate marks. It takes a NEGATIVE margin, not gap:0 — the icon is a 24-unit
   viewBox holding 14.4 units of ink (circle cx=12 r=3.6, shoulders 4.8→19.2), so each box carries
   ~3px of built-in padding per side at 15px and two touching boxes still leave a 6px hole.
   --rmc-pax-tighten is that hole: 6px closes it exactly, more overlaps the outlines, 0 restores
   plain adjacency. */
.rmc__specs .rmc__pax-icons { --rmc-pax-tighten: 6px;
  display: inline-flex; align-items: center; gap: 5px; color: var(--c-primary-600); }
.rmc__pax-ico + .rmc__pax-ico { margin-left: calc(-1 * var(--rmc-pax-tighten)); }
.rmc__pax-ico { width: 15px; height: 15px; flex: none;
  /* Outlined = capacity still available above the minimum; filled = a guest the room is sold for.
     Opacity instead of a second colour keeps one hue in the pill. */
  opacity: .42; }
.rmc__pax-ico.is-required { opacity: 1; }
/* The guests the strip could not draw, as a FILLED DISC the size of a glyph — the avatar-stack
   convention. A disc rather than bare text for one reason: "+1" is a number and so is the "2" that
   opens the wording next to it, and two numbers side by side get read as one. Wrapped in a shape
   of its own, it stops being text and becomes the last element of the strip. */
.rmc__pax-more { display: inline-flex; align-items: center; justify-content: center;
  min-width: 15px; height: 15px; flex: none; padding: 0 3px; border-radius: 999px;
  /* Same compensation as between two glyphs, and through the SAME variable: whatever gap the
     strip is tuned to, the disc keeps the exact spacing the icons have. min-width + padding
     rather than a fixed width so "+12" stretches into a short pill instead of overflowing. */
  margin-left: calc(-1 * var(--rmc-pax-tighten));
  background: var(--c-primary-600); color: #fff;
  font-size: 9px; font-weight: 700; line-height: 1; letter-spacing: -.02em; }
.rmc__pax-label { font-weight: 600; white-space: nowrap; }
.rmc__amen { display: flex; flex-wrap: wrap; gap: 14px; font-size: 13px; color: var(--c-muted); }
.rmc__amen span { display: inline-flex; align-items: center; gap: 6px; }
.rmc__scarcity { font-size: 13px; font-weight: 600; color: var(--c-danger); margin: 0; }
.rmc__details { font-size: 14px; font-weight: 600; color: var(--c-primary-600); cursor: pointer;
  align-self: flex-start; background: none; border: 0; padding: 0; margin-top: auto; }
.rmc__details:hover { text-decoration: underline; }
.rmc__aside { padding: 22px 24px; border-left: 1px solid var(--c-border); display: flex; flex-direction: column;
  align-items: flex-end; justify-content: center; gap: 3px; text-align: right; }
.rmc__from { font-size: 11px; text-transform: uppercase; letter-spacing: .11em; color: var(--c-muted); font-weight: 700; }
.rmc__price .price-old { font-size: 14px; }
.rmc__price .price-now { font-size: 26px; font-weight: 700; letter-spacing: -.6px; }
.rmc__unit { font-size: 12px; color: var(--c-muted); margin-bottom: 8px; }
.rmc__toggle { width: 100%; white-space: nowrap; }

/* ---- expandable rate options ---- */
.rmc__rates { grid-column: 1 / -1; border-top: 1px solid var(--c-border); background: var(--c-primary-050);
  padding: 4px 24px 16px; }
.rmc:not(.is-open) .rmc__rates { display: none; }
.rmc__rates-desc { font-size: 14px; color: var(--c-ink-2); padding: 15px 0 13px; border-bottom: 1px solid var(--c-border); }
.rmc__rate { position: relative; display: grid; grid-template-columns: 24px 1fr auto; align-items: center;
  gap: 16px; padding: 15px 4px; border-bottom: 1px solid var(--c-border); cursor: pointer; }
.rmc__rate:last-child { border-bottom: 0; }
.rmc__rate .rate-row__check { position: absolute; opacity: 0; width: 0; height: 0; }
.rmc__box { width: 24px; height: 24px; border: 2px solid var(--c-border-2); border-radius: 7px;
  display: grid; place-items: center; background: #fff; transition: all .12s ease; }
.rmc__rate:hover .rmc__box { border-color: var(--c-primary-500); }
/* selection styled from the checkbox state (no dependency on JS is-checked class) */
.rmc__rate:has(.rate-row__check:checked) { background: #fff; border-radius: 10px; box-shadow: var(--ibe-shadow-sm); }
.rmc__rate:has(.rate-row__check:checked) .rmc__box { background: var(--c-primary-600); border-color: var(--c-primary-600); }
.rmc__rate:has(.rate-row__check:checked) .rmc__box::after { content: "✔"; color: #fff; font-size: 13px; line-height: 1; }
.rmc__rate-main { min-width: 0; }
.rmc__rate-name { font-weight: 600; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rmc__rate-flag { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--c-success); background: var(--c-success-bg); padding: 2px 8px; border-radius: 999px; }
.rmc__rate-cancel { font-size: 13px; color: var(--c-muted); margin-top: 4px; }
.rmc__rate-cancel.is-free { color: var(--c-success); }
.rmc__rate-cancel.is-nonref { color: var(--c-muted); }
.rmc__rate-price { text-align: right; white-space: nowrap; }
.rmc__rate-price .price-old { font-size: 13px; }
.rmc__rate-price .price-now { font-size: 19px; font-weight: 700; letter-spacing: -.3px; }
.rmc__rate-unit { font-size: 11px; color: var(--c-faint); }

/* ---------------------------------------------------------------------------
   33.6  STICKY BOOKING SUMMARY (iHotelier bottom bar / SynXis cart)
   Restyles the existing .rooms-summary — same [data-summary-*] hooks + CTA.
   ------------------------------------------------------------------------- */
[data-skin="ibe"] .rooms-summary { position: sticky; bottom: 14px; z-index: 15; margin-top: 20px;
  background: #fff; border: 1px solid var(--c-border-2); border-radius: 999px;
  box-shadow: var(--ibe-shadow); padding: 12px 12px 12px 26px; gap: 18px; }
[data-skin="ibe"] .rooms-summary__label { font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--c-muted); font-weight: 700; }
[data-skin="ibe"] .rooms-summary__price .price-now { font-size: 24px; font-weight: 700; letter-spacing: -.6px; }
[data-skin="ibe"] .rooms-summary .btn { padding: 14px 30px; }

/* ---------------------------------------------------------------------------
   33.7  GALLERY refinement + reassurance strip
   ------------------------------------------------------------------------- */
[data-skin="ibe"] .gallery { border-radius: var(--ibe-radius); overflow: hidden; grid-auto-rows: 150px; }
[data-skin="ibe"] .ibe-trust { display: flex; flex-wrap: wrap; gap: 10px 26px; }
[data-skin="ibe"] .ibe-trust span { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; color: var(--c-ink-2); }

/* ---------------------------------------------------------------------------
   33.9  RESULTS-PAGE INLINE SEARCH + top quick filters
   The home search form embedded at the top of the results page (replaces the
   old "✎ Modifier la recherche"). Dark band = same visual language as the home
   hero, so the reused white search card + white-on-dark controls render right.
   NOT gated by [data-skin] — hotel-list & flight-results reuse .results-search.
   ------------------------------------------------------------------------- */
/* Refined LIGHT search panel: soft cool tint + hairline border + gentle shadow,
   so the white search cards pop and it feels elegant (no dark block). */
.results-search { background: var(--c-primary-100); border: 1px solid var(--c-border-2);
  border-radius: var(--ibe-radius); box-shadow: var(--ibe-shadow-sm);
  padding: 18px 20px 20px; margin-bottom: 22px; }
.results-search__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; margin-bottom: 14px; }
.results-search__brand { font-weight: 700; font-size: var(--fs-lg); color: var(--c-ink); }
.results-search__brand .stars { font-size: 13px; }
.results-search__hint { font-size: var(--fs-sm); color: var(--c-muted); }
/* Quick controls sitting DIRECTLY on the panel (flight results: flexibility, direct, baggage) —
   not in the white search card and not in the advanced drawer, so they take the panel's own
   contrast. Held here rather than inline on the row so a client sheet that repaints .results-search
   can retint them; an inline style would need !important to reach. */
.results-search__quick { color: var(--c-ink-2); }
/* the embedded home search form (white card) sits on the light panel */
.results-search .search-fields { margin: 0; }
.results-search .search-advanced-toggle { color: var(--c-primary-600); }
/* Flight trip-type + advanced controls, re-tinted dark-on-light for the light band */
.results-search .trip-type { background: #fff; border: 1px solid var(--c-border-2); color: var(--c-ink-2); }
.results-search .trip-type.is-active { background: var(--c-primary-600); border-color: var(--c-primary-600); color: #fff; }
.results-search .search-advanced { background: var(--c-surface); border: 1px solid var(--c-border); }
.results-search .search-advanced .field__label { color: var(--c-ink-2); }
/* Companion to the line above. .search-advanced is styled for the dark hero, where BOTH
   .field__label and .checkbox are white (§ .search-advanced .checkbox). On this light band the
   panel background is repainted white, so the checkbox labels of "Filtres rapides" were rendering
   white-on-white — present in the DOM, invisible on screen. */
.results-search .search-advanced .checkbox { color: var(--c-ink-2); }
/* Same for the hover/focus pair (§ .search-advanced .checkbox:hover, :focus-within → #fff), which
   would otherwise blank the label again the moment it is hovered or tabbed to. Restores the normal
   light-context affordance used by the filter lists (.checkbox:hover → --c-primary-700). */
.results-search .search-advanced .checkbox:hover,
.results-search .search-advanced .checkbox:focus-within { color: var(--c-primary-700); }

/* Top quick-filters strip (IBE) — the only filters kept, moved above results */
.ibe-quickfilters { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 26px; }
.ibe-quickfilters__label { font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--c-muted); margin-right: 4px; }
.qf-chip { position: relative; display: inline-flex; align-items: center; gap: 8px; padding: 9px 16px;
  border-radius: 999px; border: 1px solid var(--c-border-2); background: #fff; font-size: 14px; font-weight: 600;
  color: var(--c-ink-2); cursor: pointer; transition: background .12s ease, border-color .12s ease, color .12s ease; }
.qf-chip:hover { border-color: var(--c-primary-500); }
.qf-chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.qf-chip::before { content: "＋"; font-weight: 700; color: var(--c-faint); }
.qf-chip:has(input:checked) { background: var(--c-primary-600); border-color: var(--c-primary-600); color: #fff; }
.qf-chip:has(input:checked)::before { content: "✔"; color: #fff; }
.ibe-quickfilters__reset { margin-left: auto; font-size: 14px; }

/* ---------------------------------------------------------------------------
   33.8  RESPONSIVE
   ------------------------------------------------------------------------- */
@media (max-width: 860px) {
  .results-search { padding: 14px; }
  .ibe-quickfilters__reset { margin-left: 0; }
  /* Two pills per row, CTA full width — the flex equivalent of the 2-column
     grid this block used to override. */
  .ibe-search { flex-wrap: wrap; padding: 8px; }
  .ibe-search__sep { display: none; }
  .ibe-search__pill, .ibe-search__pill[data-ibe-pill="pop-occ"] { flex: 1 1 calc(50% - 6px); }
  .ibe-search__cta { flex: 1 1 100%; }
  .ibe-search__cta .btn { width: 100%; }
  .pcal__months { grid-template-columns: 1fr; gap: 22px; }
  .ibe-topbar { flex-wrap: wrap; border-radius: var(--ibe-radius); padding: 12px 16px; }
  .ibe-topbar__pills { order: 3; width: 100%; flex: none; overflow: visible; flex-wrap: wrap; }
  .ibe-topbar__edit { order: 2; margin-left: auto; }
  [data-skin="ibe"] .rooms-summary { border-radius: var(--ibe-radius); flex-wrap: wrap; padding: 16px; }
}

/* Room cards: photo + info side-by-side, price/CTA drops below (tablet) */
@media (max-width: 1024px) {
  .rmc { grid-template-columns: 240px 1fr; }
  .rmc__aside { grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--c-border);
    flex-direction: row; align-items: center; justify-content: space-between; text-align: left; }
  .rmc__aside .rmc__toggle { width: auto; }
  .rmc__price-wrap { display: flex; align-items: baseline; gap: 8px; }
}
/* Full stack (mobile): photo on top */
@media (max-width: 620px) {
  .rmc { grid-template-columns: 1fr; }
  .rmc__media { min-height: 180px; }
  .rmc__aside { flex-wrap: wrap; gap: 12px; }
  .rmc__rate { grid-template-columns: 24px 1fr; }
  .rmc__rate-price { grid-column: 2; text-align: left; padding-left: 0; }
}

/* =============================================================================
   34. FLIGHTS — sober alignment with the hotel design system
   -----------------------------------------------------------------------------
   PURELY VISUAL (no markup / JS / layout / flow changes). Appended last so it
   overrides §25. Brings the flight flow to the SAME restraint as the hotel
   pages: carrier and flight-source identity are carried by TEXT (IATA code +
   airline name), NOT colour — mirroring the neutral provider-chip treatment
   applied to hotels in §26.1. Removes the saturated hues that were specific to
   the flight screens, so the whole product reads as one navy + neutral system.
   ========================================================================== */

/* Airline monograms → ONE neutral chip for every carrier (was per-airline brand
   red / blue / navy / green). Same grey family as the hotel source badges; the
   IATA code stays bold and high-contrast, with a hairline border for a clean
   "logo" edge. Real carrier logos replace these placeholders in production. */
.airline-logo {
  background: var(--c-surface-3); color: var(--c-ink);
  border: 1px solid var(--c-border-2); font-weight: 800;
}
.airline-logo--tu, .airline-logo--bj, .airline-logo--af,
.airline-logo--to, .airline-logo--other {
  background: var(--c-surface-3); color: var(--c-ink); border-color: var(--c-border-2);
}

/* Flight source badges (GDS · Amadeus / LCC) → the same neutral grey as every
   other provider badge (§26.1), rather than blue / green. */
.badge--src-gds, .badge--src-lcc { background: var(--c-surface-3); color: var(--c-ink-2); }

/* =============================================================================
   35. FLIGHTS — real carrier logos (replace the IATA-code placeholders)
   -----------------------------------------------------------------------------
   Supervisor request (« vol · résultats · icones des compagnies »). Turns the
   neutral IATA monograms of §25.3 / §34 into the carriers' REAL logos. Keyed by
   the SAME .airline-logo--{iata} modifiers, so NO markup / JS change is needed
   and it applies everywhere a monogram appears: résultats, détail, paiement,
   voucher, and the « Compagnies » filter list. PURELY VISUAL — the §34 sober
   frame is kept (white chip + hairline border, neutral source badges); only the
   carrier's own mark carries colour.
   Assets: images/airlines/{IATA}.gif — a copy of cr.fwk/images/airlines, the
   same per-IATA library the production MVC app serves. The rules below cover
   EVERY 2-character code that library ships (428 of them), not a hand-picked
   few: the views emit .airline-logo--{iata} for whatever carrier the GDS
   returns, and a carrier with no .gif simply matches no rule here and keeps the
   neutral chip with its IATA monogram visible.
   ========================================================================== */
/* --- GENERATED: shared chip treatment for every carrier that HAS a logo file ---
   Source: wwwroot/images/airlines/<IATA>.gif (2-character codes only).
   Regenerate this whole block after adding or removing logo files: a carrier
   listed here MUST have its .gif, because these rules set color:transparent and
   would otherwise leave an empty white chip instead of the IATA monogram. */
.airline-logo--0b,
.airline-logo--2j,
.airline-logo--2k,
.airline-logo--2l,
.airline-logo--2m,
.airline-logo--2n,
.airline-logo--2p,
.airline-logo--2u,
.airline-logo--3k,
.airline-logo--3l,
.airline-logo--3r,
.airline-logo--3u,
.airline-logo--4g,
.airline-logo--4m,
.airline-logo--4r,
.airline-logo--4t,
.airline-logo--4u,
.airline-logo--5g,
.airline-logo--5j,
.airline-logo--5n,
.airline-logo--5o,
.airline-logo--5p,
.airline-logo--5r,
.airline-logo--5t,
.airline-logo--6a,
.airline-logo--6b,
.airline-logo--6e,
.airline-logo--6h,
.airline-logo--7b,
.airline-logo--7d,
.airline-logo--7e,
.airline-logo--7f,
.airline-logo--7h,
.airline-logo--7i,
.airline-logo--7k,
.airline-logo--7n,
.airline-logo--8m,
.airline-logo--8p,
.airline-logo--8r,
.airline-logo--8u,
.airline-logo--91,
.airline-logo--92,
.airline-logo--94,
.airline-logo--9c,
.airline-logo--9h,
.airline-logo--9k,
.airline-logo--9m,
.airline-logo--9u,
.airline-logo--9w,
.airline-logo--a3,
.airline-logo--a5,
.airline-logo--a6,
.airline-logo--a7,
.airline-logo--a9,
.airline-logo--aa,
.airline-logo--ab,
.airline-logo--ac,
.airline-logo--ad,
.airline-logo--ae,
.airline-logo--af,
.airline-logo--ah,
.airline-logo--ai,
.airline-logo--aj,
.airline-logo--ak,
.airline-logo--am,
.airline-logo--ap,
.airline-logo--aq,
.airline-logo--ar,
.airline-logo--as,
.airline-logo--at,
.airline-logo--au,
.airline-logo--av,
.airline-logo--ax,
.airline-logo--ay,
.airline-logo--az,
.airline-logo--b2,
.airline-logo--b6,
.airline-logo--b7,
.airline-logo--b8,
.airline-logo--ba,
.airline-logo--bd,
.airline-logo--be,
.airline-logo--bg,
.airline-logo--bh,
.airline-logo--bi,
.airline-logo--bj,
.airline-logo--bl,
.airline-logo--bn,
.airline-logo--bp,
.airline-logo--br,
.airline-logo--bt,
.airline-logo--bu,
.airline-logo--bv,
.airline-logo--bw,
.airline-logo--by,
.airline-logo--c2,
.airline-logo--c9,
.airline-logo--ca,
.airline-logo--cb,
.airline-logo--ce,
.airline-logo--cf,
.airline-logo--ci,
.airline-logo--cj,
.airline-logo--cm,
.airline-logo--co,
.airline-logo--cu,
.airline-logo--cx,
.airline-logo--cy,
.airline-logo--cz,
.airline-logo--d6,
.airline-logo--d7,
.airline-logo--d9,
.airline-logo--db,
.airline-logo--de,
.airline-logo--dj,
.airline-logo--dl,
.airline-logo--do,
.airline-logo--dp,
.airline-logo--dt,
.airline-logo--dx,
.airline-logo--dy,
.airline-logo--e5,
.airline-logo--ec,
.airline-logo--eg,
.airline-logo--ei,
.airline-logo--ek,
.airline-logo--el,
.airline-logo--en,
.airline-logo--eq,
.airline-logo--et,
.airline-logo--ew,
.airline-logo--ey,
.airline-logo--f7,
.airline-logo--f9,
.airline-logo--fb,
.airline-logo--fd,
.airline-logo--fi,
.airline-logo--fj,
.airline-logo--fl,
.airline-logo--fm,
.airline-logo--fr,
.airline-logo--fs,
.airline-logo--fv,
.airline-logo--fz,
.airline-logo--g3,
.airline-logo--g9,
.airline-logo--ga,
.airline-logo--ge,
.airline-logo--gf,
.airline-logo--gi,
.airline-logo--gl,
.airline-logo--gn,
.airline-logo--gr,
.airline-logo--gt,
.airline-logo--gu,
.airline-logo--gw,
.airline-logo--h2,
.airline-logo--h9,
.airline-logo--ha,
.airline-logo--hf,
.airline-logo--hg,
.airline-logo--hm,
.airline-logo--hp,
.airline-logo--hq,
.airline-logo--hr,
.airline-logo--hs,
.airline-logo--hu,
.airline-logo--hv,
.airline-logo--hx,
.airline-logo--hy,
.airline-logo--i7,
.airline-logo--i9,
.airline-logo--ib,
.airline-logo--ic,
.airline-logo--ie,
.airline-logo--ig,
.airline-logo--ik,
.airline-logo--in,
.airline-logo--iq,
.airline-logo--ir,
.airline-logo--iv,
.airline-logo--iy,
.airline-logo--iz,
.airline-logo--j2,
.airline-logo--j8,
.airline-logo--jb,
.airline-logo--jj,
.airline-logo--jk,
.airline-logo--jl,
.airline-logo--jm,
.airline-logo--jo,
.airline-logo--jp,
.airline-logo--jq,
.airline-logo--jt,
.airline-logo--ju,
.airline-logo--jv,
.airline-logo--jz,
.airline-logo--k5,
.airline-logo--ka,
.airline-logo--kc,
.airline-logo--kd,
.airline-logo--ke,
.airline-logo--kf,
.airline-logo--kk,
.airline-logo--kl,
.airline-logo--km,
.airline-logo--kq,
.airline-logo--ks,
.airline-logo--ku,
.airline-logo--kx,
.airline-logo--l6,
.airline-logo--la,
.airline-logo--lb,
.airline-logo--lf,
.airline-logo--lg,
.airline-logo--lh,
.airline-logo--li,
.airline-logo--lj,
.airline-logo--lm,
.airline-logo--ln,
.airline-logo--lo,
.airline-logo--lp,
.airline-logo--lr,
.airline-logo--ls,
.airline-logo--lt,
.airline-logo--lw,
.airline-logo--lx,
.airline-logo--ly,
.airline-logo--m5,
.airline-logo--ma,
.airline-logo--md,
.airline-logo--me,
.airline-logo--mf,
.airline-logo--mh,
.airline-logo--mi,
.airline-logo--mk,
.airline-logo--mo,
.airline-logo--mp,
.airline-logo--ms,
.airline-logo--mt,
.airline-logo--mu,
.airline-logo--mw,
.airline-logo--mx,
.airline-logo--n8,
.airline-logo--na,
.airline-logo--nc,
.airline-logo--ne,
.airline-logo--nf,
.airline-logo--ng,
.airline-logo--nh,
.airline-logo--ni,
.airline-logo--nk,
.airline-logo--nm,
.airline-logo--nn,
.airline-logo--no,
.airline-logo--nt,
.airline-logo--nu,
.airline-logo--nw,
.airline-logo--nx,
.airline-logo--nz,
.airline-logo--o6,
.airline-logo--oa,
.airline-logo--ok,
.airline-logo--ol,
.airline-logo--om,
.airline-logo--on,
.airline-logo--or,
.airline-logo--os,
.airline-logo--ot,
.airline-logo--ou,
.airline-logo--ov,
.airline-logo--oz,
.airline-logo--p2,
.airline-logo--pa,
.airline-logo--pb,
.airline-logo--pc,
.airline-logo--pd,
.airline-logo--pg,
.airline-logo--pk,
.airline-logo--pr,
.airline-logo--ps,
.airline-logo--pu,
.airline-logo--pw,
.airline-logo--px,
.airline-logo--py,
.airline-logo--pz,
.airline-logo--qa,
.airline-logo--qc,
.airline-logo--qf,
.airline-logo--qh,
.airline-logo--qi,
.airline-logo--qm,
.airline-logo--qr,
.airline-logo--qs,
.airline-logo--qv,
.airline-logo--qz,
.airline-logo--r2,
.airline-logo--r3,
.airline-logo--r7,
.airline-logo--ra,
.airline-logo--rb,
.airline-logo--rc,
.airline-logo--re,
.airline-logo--rj,
.airline-logo--ro,
.airline-logo--ru,
.airline-logo--s2,
.airline-logo--s3,
.airline-logo--s4,
.airline-logo--s7,
.airline-logo--sa,
.airline-logo--sb,
.airline-logo--sc,
.airline-logo--sd,
.airline-logo--se,
.airline-logo--sg,
.airline-logo--si,
.airline-logo--sk,
.airline-logo--sm,
.airline-logo--sn,
.airline-logo--sp,
.airline-logo--sq,
.airline-logo--ss,
.airline-logo--st,
.airline-logo--su,
.airline-logo--sv,
.airline-logo--sw,
.airline-logo--sx,
.airline-logo--sy,
.airline-logo--t3,
.airline-logo--t7,
.airline-logo--ta,
.airline-logo--tb,
.airline-logo--tc,
.airline-logo--tf,
.airline-logo--tg,
.airline-logo--tk,
.airline-logo--tl,
.airline-logo--tm,
.airline-logo--tn,
.airline-logo--to,
.airline-logo--tp,
.airline-logo--tr,
.airline-logo--ts,
.airline-logo--tt,
.airline-logo--tu,
.airline-logo--tv,
.airline-logo--tx,
.airline-logo--tz,
.airline-logo--u2,
.airline-logo--u5,
.airline-logo--u6,
.airline-logo--u8,
.airline-logo--ua,
.airline-logo--ud,
.airline-logo--uf,
.airline-logo--ug,
.airline-logo--ul,
.airline-logo--um,
.airline-logo--un,
.airline-logo--uo,
.airline-logo--up,
.airline-logo--us,
.airline-logo--ut,
.airline-logo--uu,
.airline-logo--ux,
.airline-logo--uy,
.airline-logo--v3,
.airline-logo--v7,
.airline-logo--va,
.airline-logo--ve,
.airline-logo--vf,
.airline-logo--vg,
.airline-logo--vk,
.airline-logo--vn,
.airline-logo--vo,
.airline-logo--vr,
.airline-logo--vs,
.airline-logo--vt,
.airline-logo--vv,
.airline-logo--vw,
.airline-logo--vx,
.airline-logo--vy,
.airline-logo--w3,
.airline-logo--w5,
.airline-logo--w6,
.airline-logo--wa,
.airline-logo--wb,
.airline-logo--wf,
.airline-logo--wj,
.airline-logo--wk,
.airline-logo--wm,
.airline-logo--wp,
.airline-logo--ws,
.airline-logo--wu,
.airline-logo--ww,
.airline-logo--wx,
.airline-logo--wy,
.airline-logo--x3,
.airline-logo--x6,
.airline-logo--x9,
.airline-logo--xc,
.airline-logo--xf,
.airline-logo--xg,
.airline-logo--xk,
.airline-logo--xl,
.airline-logo--xm,
.airline-logo--xp,
.airline-logo--xq,
.airline-logo--xr,
.airline-logo--xy,
.airline-logo--y4,
.airline-logo--y7,
.airline-logo--yc,
.airline-logo--yd,
.airline-logo--yk,
.airline-logo--ym,
.airline-logo--yo,
.airline-logo--yq,
.airline-logo--yv,
.airline-logo--yx,
.airline-logo--z2,
.airline-logo--z6,
.airline-logo--zb,
.airline-logo--zh,
.airline-logo--zi,
.airline-logo--zk,
.airline-logo--zl {
  background-color: #fff;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  border: 1px solid var(--c-border-2);
  color: transparent;   /* hide the placeholder IATA text sitting under the logo */
  overflow: hidden;
}

/* One image per carrier. The URL keeps the on-disk filename casing (a couple of
   files are lower-case), so it resolves on a case-sensitive host too. */
.airline-logo--0b { background-image: url("images/airlines/0B.gif"); }
.airline-logo--2j { background-image: url("images/airlines/2J.gif"); }
.airline-logo--2k { background-image: url("images/airlines/2K.gif"); }
.airline-logo--2l { background-image: url("images/airlines/2L.gif"); }
.airline-logo--2m { background-image: url("images/airlines/2M.gif"); }
.airline-logo--2n { background-image: url("images/airlines/2N.gif"); }
.airline-logo--2p { background-image: url("images/airlines/2P.gif"); }
.airline-logo--2u { background-image: url("images/airlines/2U.gif"); }
.airline-logo--3k { background-image: url("images/airlines/3K.gif"); }
.airline-logo--3l { background-image: url("images/airlines/3L.gif"); }
.airline-logo--3r { background-image: url("images/airlines/3R.gif"); }
.airline-logo--3u { background-image: url("images/airlines/3U.gif"); }
.airline-logo--4g { background-image: url("images/airlines/4G.gif"); }
.airline-logo--4m { background-image: url("images/airlines/4M.gif"); }
.airline-logo--4r { background-image: url("images/airlines/4R.gif"); }
.airline-logo--4t { background-image: url("images/airlines/4T.gif"); }
.airline-logo--4u { background-image: url("images/airlines/4U.gif"); }
.airline-logo--5g { background-image: url("images/airlines/5G.gif"); }
.airline-logo--5j { background-image: url("images/airlines/5J.gif"); }
.airline-logo--5n { background-image: url("images/airlines/5N.gif"); }
.airline-logo--5o { background-image: url("images/airlines/5o.gif"); }
.airline-logo--5p { background-image: url("images/airlines/5P.gif"); }
.airline-logo--5r { background-image: url("images/airlines/5R.gif"); }
.airline-logo--5t { background-image: url("images/airlines/5T.gif"); }
.airline-logo--6a { background-image: url("images/airlines/6A.gif"); }
.airline-logo--6b { background-image: url("images/airlines/6B.gif"); }
.airline-logo--6e { background-image: url("images/airlines/6E.gif"); }
.airline-logo--6h { background-image: url("images/airlines/6H.gif"); }
.airline-logo--7b { background-image: url("images/airlines/7B.gif"); }
.airline-logo--7d { background-image: url("images/airlines/7D.gif"); }
.airline-logo--7e { background-image: url("images/airlines/7E.gif"); }
.airline-logo--7f { background-image: url("images/airlines/7F.gif"); }
.airline-logo--7h { background-image: url("images/airlines/7H.gif"); }
.airline-logo--7i { background-image: url("images/airlines/7I.gif"); }
.airline-logo--7k { background-image: url("images/airlines/7K.gif"); }
.airline-logo--7n { background-image: url("images/airlines/7N.gif"); }
.airline-logo--8m { background-image: url("images/airlines/8M.gif"); }
.airline-logo--8p { background-image: url("images/airlines/8P.gif"); }
.airline-logo--8r { background-image: url("images/airlines/8R.gif"); }
.airline-logo--8u { background-image: url("images/airlines/8U.gif"); }
.airline-logo--91 { background-image: url("images/airlines/91.gif"); }
.airline-logo--92 { background-image: url("images/airlines/92.gif"); }
.airline-logo--94 { background-image: url("images/airlines/94.gif"); }
.airline-logo--9c { background-image: url("images/airlines/9C.gif"); }
.airline-logo--9h { background-image: url("images/airlines/9H.gif"); }
.airline-logo--9k { background-image: url("images/airlines/9K.gif"); }
.airline-logo--9m { background-image: url("images/airlines/9M.gif"); }
.airline-logo--9u { background-image: url("images/airlines/9U.gif"); }
.airline-logo--9w { background-image: url("images/airlines/9W.gif"); }
.airline-logo--a3 { background-image: url("images/airlines/A3.gif"); }
.airline-logo--a5 { background-image: url("images/airlines/A5.gif"); }
.airline-logo--a6 { background-image: url("images/airlines/A6.gif"); }
.airline-logo--a7 { background-image: url("images/airlines/A7.gif"); }
.airline-logo--a9 { background-image: url("images/airlines/A9.gif"); }
.airline-logo--aa { background-image: url("images/airlines/AA.gif"); }
.airline-logo--ab { background-image: url("images/airlines/AB.gif"); }
.airline-logo--ac { background-image: url("images/airlines/AC.gif"); }
.airline-logo--ad { background-image: url("images/airlines/AD.gif"); }
.airline-logo--ae { background-image: url("images/airlines/AE.gif"); }
.airline-logo--af { background-image: url("images/airlines/AF.gif"); }
.airline-logo--ah { background-image: url("images/airlines/AH.gif"); }
.airline-logo--ai { background-image: url("images/airlines/AI.gif"); }
.airline-logo--aj { background-image: url("images/airlines/AJ.gif"); }
.airline-logo--ak { background-image: url("images/airlines/AK.gif"); }
.airline-logo--am { background-image: url("images/airlines/AM.gif"); }
.airline-logo--ap { background-image: url("images/airlines/AP.gif"); }
.airline-logo--aq { background-image: url("images/airlines/AQ.gif"); }
.airline-logo--ar { background-image: url("images/airlines/AR.gif"); }
.airline-logo--as { background-image: url("images/airlines/AS.gif"); }
.airline-logo--at { background-image: url("images/airlines/AT.gif"); }
.airline-logo--au { background-image: url("images/airlines/AU.gif"); }
.airline-logo--av { background-image: url("images/airlines/AV.gif"); }
.airline-logo--ax { background-image: url("images/airlines/AX.gif"); }
.airline-logo--ay { background-image: url("images/airlines/AY.gif"); }
.airline-logo--az { background-image: url("images/airlines/AZ.gif"); }
.airline-logo--b2 { background-image: url("images/airlines/B2.gif"); }
.airline-logo--b6 { background-image: url("images/airlines/B6.gif"); }
.airline-logo--b7 { background-image: url("images/airlines/B7.gif"); }
.airline-logo--b8 { background-image: url("images/airlines/B8.gif"); }
.airline-logo--ba { background-image: url("images/airlines/BA.gif"); }
.airline-logo--bd { background-image: url("images/airlines/BD.gif"); }
.airline-logo--be { background-image: url("images/airlines/BE.gif"); }
.airline-logo--bg { background-image: url("images/airlines/BG.gif"); }
.airline-logo--bh { background-image: url("images/airlines/BH.gif"); }
.airline-logo--bi { background-image: url("images/airlines/BI.gif"); }
.airline-logo--bj { background-image: url("images/airlines/BJ.gif"); }
.airline-logo--bl { background-image: url("images/airlines/BL.gif"); }
.airline-logo--bn { background-image: url("images/airlines/BN.gif"); }
.airline-logo--bp { background-image: url("images/airlines/BP.gif"); }
.airline-logo--br { background-image: url("images/airlines/BR.gif"); }
.airline-logo--bt { background-image: url("images/airlines/BT.gif"); }
.airline-logo--bu { background-image: url("images/airlines/BU.gif"); }
.airline-logo--bv { background-image: url("images/airlines/BV.gif"); }
.airline-logo--bw { background-image: url("images/airlines/BW.gif"); }
.airline-logo--by { background-image: url("images/airlines/BY.gif"); }
.airline-logo--c2 { background-image: url("images/airlines/C2.gif"); }
.airline-logo--c9 { background-image: url("images/airlines/C9.gif"); }
.airline-logo--ca { background-image: url("images/airlines/CA.gif"); }
.airline-logo--cb { background-image: url("images/airlines/CB.gif"); }
.airline-logo--ce { background-image: url("images/airlines/CE.gif"); }
.airline-logo--cf { background-image: url("images/airlines/CF.gif"); }
.airline-logo--ci { background-image: url("images/airlines/CI.gif"); }
.airline-logo--cj { background-image: url("images/airlines/CJ.gif"); }
.airline-logo--cm { background-image: url("images/airlines/CM.gif"); }
.airline-logo--co { background-image: url("images/airlines/CO.gif"); }
.airline-logo--cu { background-image: url("images/airlines/CU.gif"); }
.airline-logo--cx { background-image: url("images/airlines/CX.gif"); }
.airline-logo--cy { background-image: url("images/airlines/CY.gif"); }
.airline-logo--cz { background-image: url("images/airlines/CZ.gif"); }
.airline-logo--d6 { background-image: url("images/airlines/D6.gif"); }
.airline-logo--d7 { background-image: url("images/airlines/D7.gif"); }
.airline-logo--d9 { background-image: url("images/airlines/D9.gif"); }
.airline-logo--db { background-image: url("images/airlines/DB.gif"); }
.airline-logo--de { background-image: url("images/airlines/DE.gif"); }
.airline-logo--dj { background-image: url("images/airlines/DJ.gif"); }
.airline-logo--dl { background-image: url("images/airlines/DL.gif"); }
.airline-logo--do { background-image: url("images/airlines/DO.gif"); }
.airline-logo--dp { background-image: url("images/airlines/DP.gif"); }
.airline-logo--dt { background-image: url("images/airlines/DT.gif"); }
.airline-logo--dx { background-image: url("images/airlines/DX.gif"); }
.airline-logo--dy { background-image: url("images/airlines/DY.gif"); }
.airline-logo--e5 { background-image: url("images/airlines/E5.gif"); }
.airline-logo--ec { background-image: url("images/airlines/EC.gif"); }
.airline-logo--eg { background-image: url("images/airlines/EG.gif"); }
.airline-logo--ei { background-image: url("images/airlines/EI.gif"); }
.airline-logo--ek { background-image: url("images/airlines/EK.gif"); }
.airline-logo--el { background-image: url("images/airlines/EL.gif"); }
.airline-logo--en { background-image: url("images/airlines/EN.gif"); }
.airline-logo--eq { background-image: url("images/airlines/EQ.gif"); }
.airline-logo--et { background-image: url("images/airlines/ET.gif"); }
.airline-logo--ew { background-image: url("images/airlines/EW.gif"); }
.airline-logo--ey { background-image: url("images/airlines/EY.gif"); }
.airline-logo--f7 { background-image: url("images/airlines/F7.gif"); }
.airline-logo--f9 { background-image: url("images/airlines/F9.gif"); }
.airline-logo--fb { background-image: url("images/airlines/FB.gif"); }
.airline-logo--fd { background-image: url("images/airlines/FD.gif"); }
.airline-logo--fi { background-image: url("images/airlines/FI.gif"); }
.airline-logo--fj { background-image: url("images/airlines/FJ.gif"); }
.airline-logo--fl { background-image: url("images/airlines/FL.gif"); }
.airline-logo--fm { background-image: url("images/airlines/FM.gif"); }
.airline-logo--fr { background-image: url("images/airlines/FR.gif"); }
.airline-logo--fs { background-image: url("images/airlines/FS.gif"); }
.airline-logo--fv { background-image: url("images/airlines/FV.gif"); }
.airline-logo--fz { background-image: url("images/airlines/FZ.gif"); }
.airline-logo--g3 { background-image: url("images/airlines/G3.gif"); }
.airline-logo--g9 { background-image: url("images/airlines/G9.gif"); }
.airline-logo--ga { background-image: url("images/airlines/GA.gif"); }
.airline-logo--ge { background-image: url("images/airlines/GE.gif"); }
.airline-logo--gf { background-image: url("images/airlines/GF.gif"); }
.airline-logo--gi { background-image: url("images/airlines/GI.gif"); }
.airline-logo--gl { background-image: url("images/airlines/GL.gif"); }
.airline-logo--gn { background-image: url("images/airlines/GN.gif"); }
.airline-logo--gr { background-image: url("images/airlines/GR.gif"); }
.airline-logo--gt { background-image: url("images/airlines/GT.gif"); }
.airline-logo--gu { background-image: url("images/airlines/GU.gif"); }
.airline-logo--gw { background-image: url("images/airlines/GW.gif"); }
.airline-logo--h2 { background-image: url("images/airlines/H2.gif"); }
.airline-logo--h9 { background-image: url("images/airlines/H9.gif"); }
.airline-logo--ha { background-image: url("images/airlines/HA.gif"); }
.airline-logo--hf { background-image: url("images/airlines/HF.gif"); }
.airline-logo--hg { background-image: url("images/airlines/HG.gif"); }
.airline-logo--hm { background-image: url("images/airlines/HM.gif"); }
.airline-logo--hp { background-image: url("images/airlines/HP.gif"); }
.airline-logo--hq { background-image: url("images/airlines/HQ.gif"); }
.airline-logo--hr { background-image: url("images/airlines/HR.gif"); }
.airline-logo--hs { background-image: url("images/airlines/HS.gif"); }
.airline-logo--hu { background-image: url("images/airlines/HU.gif"); }
.airline-logo--hv { background-image: url("images/airlines/HV.gif"); }
.airline-logo--hx { background-image: url("images/airlines/HX.gif"); }
.airline-logo--hy { background-image: url("images/airlines/HY.gif"); }
.airline-logo--i7 { background-image: url("images/airlines/I7.gif"); }
.airline-logo--i9 { background-image: url("images/airlines/I9.gif"); }
.airline-logo--ib { background-image: url("images/airlines/IB.gif"); }
.airline-logo--ic { background-image: url("images/airlines/IC.gif"); }
.airline-logo--ie { background-image: url("images/airlines/IE.gif"); }
.airline-logo--ig { background-image: url("images/airlines/IG.gif"); }
.airline-logo--ik { background-image: url("images/airlines/IK.gif"); }
.airline-logo--in { background-image: url("images/airlines/IN.gif"); }
.airline-logo--iq { background-image: url("images/airlines/IQ.gif"); }
.airline-logo--ir { background-image: url("images/airlines/IR.gif"); }
.airline-logo--iv { background-image: url("images/airlines/IV.gif"); }
.airline-logo--iy { background-image: url("images/airlines/IY.gif"); }
.airline-logo--iz { background-image: url("images/airlines/IZ.gif"); }
.airline-logo--j2 { background-image: url("images/airlines/J2.gif"); }
.airline-logo--j8 { background-image: url("images/airlines/J8.gif"); }
.airline-logo--jb { background-image: url("images/airlines/JB.gif"); }
.airline-logo--jj { background-image: url("images/airlines/JJ.gif"); }
.airline-logo--jk { background-image: url("images/airlines/JK.gif"); }
.airline-logo--jl { background-image: url("images/airlines/JL.gif"); }
.airline-logo--jm { background-image: url("images/airlines/JM.gif"); }
.airline-logo--jo { background-image: url("images/airlines/JO.gif"); }
.airline-logo--jp { background-image: url("images/airlines/JP.gif"); }
.airline-logo--jq { background-image: url("images/airlines/JQ.gif"); }
.airline-logo--jt { background-image: url("images/airlines/JT.gif"); }
.airline-logo--ju { background-image: url("images/airlines/JU.gif"); }
.airline-logo--jv { background-image: url("images/airlines/JV.gif"); }
.airline-logo--jz { background-image: url("images/airlines/JZ.gif"); }
.airline-logo--k5 { background-image: url("images/airlines/K5.gif"); }
.airline-logo--ka { background-image: url("images/airlines/KA.gif"); }
.airline-logo--kc { background-image: url("images/airlines/KC.gif"); }
.airline-logo--kd { background-image: url("images/airlines/KD.gif"); }
.airline-logo--ke { background-image: url("images/airlines/KE.gif"); }
.airline-logo--kf { background-image: url("images/airlines/KF.gif"); }
.airline-logo--kk { background-image: url("images/airlines/KK.gif"); }
.airline-logo--kl { background-image: url("images/airlines/KL.gif"); }
.airline-logo--km { background-image: url("images/airlines/KM.gif"); }
.airline-logo--kq { background-image: url("images/airlines/KQ.gif"); }
.airline-logo--ks { background-image: url("images/airlines/KS.gif"); }
.airline-logo--ku { background-image: url("images/airlines/KU.gif"); }
.airline-logo--kx { background-image: url("images/airlines/KX.gif"); }
.airline-logo--l6 { background-image: url("images/airlines/L6.gif"); }
.airline-logo--la { background-image: url("images/airlines/LA.gif"); }
.airline-logo--lb { background-image: url("images/airlines/LB.gif"); }
.airline-logo--lf { background-image: url("images/airlines/LF.gif"); }
.airline-logo--lg { background-image: url("images/airlines/LG.gif"); }
.airline-logo--lh { background-image: url("images/airlines/LH.gif"); }
.airline-logo--li { background-image: url("images/airlines/LI.gif"); }
.airline-logo--lj { background-image: url("images/airlines/LJ.gif"); }
.airline-logo--lm { background-image: url("images/airlines/LM.gif"); }
.airline-logo--ln { background-image: url("images/airlines/LN.gif"); }
.airline-logo--lo { background-image: url("images/airlines/LO.gif"); }
.airline-logo--lp { background-image: url("images/airlines/LP.gif"); }
.airline-logo--lr { background-image: url("images/airlines/LR.gif"); }
.airline-logo--ls { background-image: url("images/airlines/LS.gif"); }
.airline-logo--lt { background-image: url("images/airlines/LT.gif"); }
.airline-logo--lw { background-image: url("images/airlines/LW.gif"); }
.airline-logo--lx { background-image: url("images/airlines/LX.gif"); }
.airline-logo--ly { background-image: url("images/airlines/LY.gif"); }
.airline-logo--m5 { background-image: url("images/airlines/M5.gif"); }
.airline-logo--ma { background-image: url("images/airlines/MA.gif"); }
.airline-logo--md { background-image: url("images/airlines/MD.gif"); }
.airline-logo--me { background-image: url("images/airlines/ME.gif"); }
.airline-logo--mf { background-image: url("images/airlines/MF.gif"); }
.airline-logo--mh { background-image: url("images/airlines/MH.gif"); }
.airline-logo--mi { background-image: url("images/airlines/MI.gif"); }
.airline-logo--mk { background-image: url("images/airlines/MK.gif"); }
.airline-logo--mo { background-image: url("images/airlines/MO.gif"); }
.airline-logo--mp { background-image: url("images/airlines/MP.gif"); }
.airline-logo--ms { background-image: url("images/airlines/MS.gif"); }
.airline-logo--mt { background-image: url("images/airlines/MT.gif"); }
.airline-logo--mu { background-image: url("images/airlines/MU.gif"); }
.airline-logo--mw { background-image: url("images/airlines/MW.gif"); }
.airline-logo--mx { background-image: url("images/airlines/MX.gif"); }
.airline-logo--n8 { background-image: url("images/airlines/N8.gif"); }
.airline-logo--na { background-image: url("images/airlines/NA.gif"); }
.airline-logo--nc { background-image: url("images/airlines/NC.gif"); }
.airline-logo--ne { background-image: url("images/airlines/NE.gif"); }
.airline-logo--nf { background-image: url("images/airlines/NF.gif"); }
.airline-logo--ng { background-image: url("images/airlines/NG.gif"); }
.airline-logo--nh { background-image: url("images/airlines/NH.gif"); }
.airline-logo--ni { background-image: url("images/airlines/NI.gif"); }
.airline-logo--nk { background-image: url("images/airlines/NK.gif"); }
.airline-logo--nm { background-image: url("images/airlines/NM.gif"); }
.airline-logo--nn { background-image: url("images/airlines/NN.gif"); }
.airline-logo--no { background-image: url("images/airlines/NO.gif"); }
.airline-logo--nt { background-image: url("images/airlines/NT.gif"); }
.airline-logo--nu { background-image: url("images/airlines/NU.gif"); }
.airline-logo--nw { background-image: url("images/airlines/NW.gif"); }
.airline-logo--nx { background-image: url("images/airlines/NX.gif"); }
.airline-logo--nz { background-image: url("images/airlines/NZ.gif"); }
.airline-logo--o6 { background-image: url("images/airlines/O6.gif"); }
.airline-logo--oa { background-image: url("images/airlines/OA.gif"); }
.airline-logo--ok { background-image: url("images/airlines/OK.gif"); }
.airline-logo--ol { background-image: url("images/airlines/OL.gif"); }
.airline-logo--om { background-image: url("images/airlines/OM.gif"); }
.airline-logo--on { background-image: url("images/airlines/ON.gif"); }
.airline-logo--or { background-image: url("images/airlines/OR.gif"); }
.airline-logo--os { background-image: url("images/airlines/OS.gif"); }
.airline-logo--ot { background-image: url("images/airlines/OT.gif"); }
.airline-logo--ou { background-image: url("images/airlines/OU.gif"); }
.airline-logo--ov { background-image: url("images/airlines/OV.gif"); }
.airline-logo--oz { background-image: url("images/airlines/OZ.gif"); }
.airline-logo--p2 { background-image: url("images/airlines/P2.gif"); }
.airline-logo--pa { background-image: url("images/airlines/PA.gif"); }
.airline-logo--pb { background-image: url("images/airlines/PB.gif"); }
.airline-logo--pc { background-image: url("images/airlines/PC.gif"); }
.airline-logo--pd { background-image: url("images/airlines/PD.gif"); }
.airline-logo--pg { background-image: url("images/airlines/PG.gif"); }
.airline-logo--pk { background-image: url("images/airlines/PK.gif"); }
.airline-logo--pr { background-image: url("images/airlines/PR.gif"); }
.airline-logo--ps { background-image: url("images/airlines/PS.gif"); }
.airline-logo--pu { background-image: url("images/airlines/PU.gif"); }
.airline-logo--pw { background-image: url("images/airlines/PW.gif"); }
.airline-logo--px { background-image: url("images/airlines/PX.gif"); }
.airline-logo--py { background-image: url("images/airlines/PY.gif"); }
.airline-logo--pz { background-image: url("images/airlines/PZ.gif"); }
.airline-logo--qa { background-image: url("images/airlines/QA.gif"); }
.airline-logo--qc { background-image: url("images/airlines/QC.gif"); }
.airline-logo--qf { background-image: url("images/airlines/QF.gif"); }
.airline-logo--qh { background-image: url("images/airlines/QH.gif"); }
.airline-logo--qi { background-image: url("images/airlines/QI.gif"); }
.airline-logo--qm { background-image: url("images/airlines/QM.gif"); }
.airline-logo--qr { background-image: url("images/airlines/QR.gif"); }
.airline-logo--qs { background-image: url("images/airlines/QS.gif"); }
.airline-logo--qv { background-image: url("images/airlines/QV.gif"); }
.airline-logo--qz { background-image: url("images/airlines/QZ.gif"); }
.airline-logo--r2 { background-image: url("images/airlines/R2.gif"); }
.airline-logo--r3 { background-image: url("images/airlines/R3.gif"); }
.airline-logo--r7 { background-image: url("images/airlines/R7.gif"); }
.airline-logo--ra { background-image: url("images/airlines/RA.gif"); }
.airline-logo--rb { background-image: url("images/airlines/RB.gif"); }
.airline-logo--rc { background-image: url("images/airlines/RC.gif"); }
.airline-logo--re { background-image: url("images/airlines/RE.gif"); }
.airline-logo--rj { background-image: url("images/airlines/RJ.gif"); }
.airline-logo--ro { background-image: url("images/airlines/RO.gif"); }
.airline-logo--ru { background-image: url("images/airlines/RU.gif"); }
.airline-logo--s2 { background-image: url("images/airlines/S2.gif"); }
.airline-logo--s3 { background-image: url("images/airlines/S3.gif"); }
.airline-logo--s4 { background-image: url("images/airlines/S4.gif"); }
.airline-logo--s7 { background-image: url("images/airlines/S7.gif"); }
.airline-logo--sa { background-image: url("images/airlines/SA.gif"); }
.airline-logo--sb { background-image: url("images/airlines/SB.gif"); }
.airline-logo--sc { background-image: url("images/airlines/SC.gif"); }
.airline-logo--sd { background-image: url("images/airlines/SD.gif"); }
.airline-logo--se { background-image: url("images/airlines/SE.gif"); }
.airline-logo--sg { background-image: url("images/airlines/SG.gif"); }
.airline-logo--si { background-image: url("images/airlines/SI.gif"); }
.airline-logo--sk { background-image: url("images/airlines/SK.gif"); }
.airline-logo--sm { background-image: url("images/airlines/SM.gif"); }
.airline-logo--sn { background-image: url("images/airlines/SN.gif"); }
.airline-logo--sp { background-image: url("images/airlines/SP.gif"); }
.airline-logo--sq { background-image: url("images/airlines/SQ.gif"); }
.airline-logo--ss { background-image: url("images/airlines/SS.gif"); }
.airline-logo--st { background-image: url("images/airlines/ST.gif"); }
.airline-logo--su { background-image: url("images/airlines/SU.gif"); }
.airline-logo--sv { background-image: url("images/airlines/SV.gif"); }
.airline-logo--sw { background-image: url("images/airlines/SW.gif"); }
.airline-logo--sx { background-image: url("images/airlines/SX.gif"); }
.airline-logo--sy { background-image: url("images/airlines/SY.gif"); }
.airline-logo--t3 { background-image: url("images/airlines/T3.gif"); }
.airline-logo--t7 { background-image: url("images/airlines/T7.gif"); }
.airline-logo--ta { background-image: url("images/airlines/TA.gif"); }
.airline-logo--tb { background-image: url("images/airlines/TB.gif"); }
.airline-logo--tc { background-image: url("images/airlines/TC.gif"); }
.airline-logo--tf { background-image: url("images/airlines/TF.gif"); }
.airline-logo--tg { background-image: url("images/airlines/TG.gif"); }
.airline-logo--tk { background-image: url("images/airlines/TK.gif"); }
.airline-logo--tl { background-image: url("images/airlines/TL.gif"); }
.airline-logo--tm { background-image: url("images/airlines/TM.gif"); }
.airline-logo--tn { background-image: url("images/airlines/TN.gif"); }
.airline-logo--to { background-image: url("images/airlines/TO.gif"); }
.airline-logo--tp { background-image: url("images/airlines/TP.gif"); }
.airline-logo--tr { background-image: url("images/airlines/TR.gif"); }
.airline-logo--ts { background-image: url("images/airlines/TS.gif"); }
.airline-logo--tt { background-image: url("images/airlines/TT.gif"); }
.airline-logo--tu { background-image: url("images/airlines/TU.gif"); }
.airline-logo--tv { background-image: url("images/airlines/tv.gif"); }
.airline-logo--tx { background-image: url("images/airlines/TX.gif"); }
.airline-logo--tz { background-image: url("images/airlines/TZ.gif"); }
.airline-logo--u2 { background-image: url("images/airlines/U2.gif"); }
.airline-logo--u5 { background-image: url("images/airlines/U5.gif"); }
.airline-logo--u6 { background-image: url("images/airlines/U6.gif"); }
.airline-logo--u8 { background-image: url("images/airlines/U8.gif"); }
.airline-logo--ua { background-image: url("images/airlines/UA.gif"); }
.airline-logo--ud { background-image: url("images/airlines/UD.gif"); }
.airline-logo--uf { background-image: url("images/airlines/UF.gif"); }
.airline-logo--ug { background-image: url("images/airlines/UG.gif"); }
.airline-logo--ul { background-image: url("images/airlines/UL.gif"); }
.airline-logo--um { background-image: url("images/airlines/UM.gif"); }
.airline-logo--un { background-image: url("images/airlines/UN.gif"); }
.airline-logo--uo { background-image: url("images/airlines/UO.gif"); }
.airline-logo--up { background-image: url("images/airlines/UP.gif"); }
.airline-logo--us { background-image: url("images/airlines/US.gif"); }
.airline-logo--ut { background-image: url("images/airlines/UT.gif"); }
.airline-logo--uu { background-image: url("images/airlines/UU.gif"); }
.airline-logo--ux { background-image: url("images/airlines/UX.gif"); }
.airline-logo--uy { background-image: url("images/airlines/UY.gif"); }
.airline-logo--v3 { background-image: url("images/airlines/V3.gif"); }
.airline-logo--v7 { background-image: url("images/airlines/V7.gif"); }
.airline-logo--va { background-image: url("images/airlines/VA.gif"); }
.airline-logo--ve { background-image: url("images/airlines/VE.gif"); }
.airline-logo--vf { background-image: url("images/airlines/VF.gif"); }
.airline-logo--vg { background-image: url("images/airlines/VG.gif"); }
.airline-logo--vk { background-image: url("images/airlines/VK.gif"); }
.airline-logo--vn { background-image: url("images/airlines/VN.gif"); }
.airline-logo--vo { background-image: url("images/airlines/VO.gif"); }
.airline-logo--vr { background-image: url("images/airlines/VR.gif"); }
.airline-logo--vs { background-image: url("images/airlines/VS.gif"); }
.airline-logo--vt { background-image: url("images/airlines/VT.gif"); }
.airline-logo--vv { background-image: url("images/airlines/VV.gif"); }
.airline-logo--vw { background-image: url("images/airlines/VW.gif"); }
.airline-logo--vx { background-image: url("images/airlines/VX.gif"); }
.airline-logo--vy { background-image: url("images/airlines/VY.gif"); }
.airline-logo--w3 { background-image: url("images/airlines/W3.gif"); }
.airline-logo--w5 { background-image: url("images/airlines/W5.gif"); }
.airline-logo--w6 { background-image: url("images/airlines/W6.gif"); }
.airline-logo--wa { background-image: url("images/airlines/WA.gif"); }
.airline-logo--wb { background-image: url("images/airlines/WB.gif"); }
.airline-logo--wf { background-image: url("images/airlines/WF.gif"); }
.airline-logo--wj { background-image: url("images/airlines/WJ.gif"); }
.airline-logo--wk { background-image: url("images/airlines/WK.gif"); }
.airline-logo--wm { background-image: url("images/airlines/WM.gif"); }
.airline-logo--wp { background-image: url("images/airlines/WP.gif"); }
.airline-logo--ws { background-image: url("images/airlines/WS.gif"); }
.airline-logo--wu { background-image: url("images/airlines/WU.gif"); }
.airline-logo--ww { background-image: url("images/airlines/WW.gif"); }
.airline-logo--wx { background-image: url("images/airlines/WX.gif"); }
.airline-logo--wy { background-image: url("images/airlines/WY.gif"); }
.airline-logo--x3 { background-image: url("images/airlines/X3.gif"); }
.airline-logo--x6 { background-image: url("images/airlines/X6.gif"); }
.airline-logo--x9 { background-image: url("images/airlines/X9.gif"); }
.airline-logo--xc { background-image: url("images/airlines/XC.gif"); }
.airline-logo--xf { background-image: url("images/airlines/XF.gif"); }
.airline-logo--xg { background-image: url("images/airlines/XG.gif"); }
.airline-logo--xk { background-image: url("images/airlines/XK.gif"); }
.airline-logo--xl { background-image: url("images/airlines/XL.gif"); }
.airline-logo--xm { background-image: url("images/airlines/XM.gif"); }
.airline-logo--xp { background-image: url("images/airlines/XP.gif"); }
.airline-logo--xq { background-image: url("images/airlines/XQ.gif"); }
.airline-logo--xr { background-image: url("images/airlines/XR.gif"); }
.airline-logo--xy { background-image: url("images/airlines/XY.gif"); }
.airline-logo--y4 { background-image: url("images/airlines/Y4.gif"); }
.airline-logo--y7 { background-image: url("images/airlines/Y7.gif"); }
.airline-logo--yc { background-image: url("images/airlines/YC.gif"); }
.airline-logo--yd { background-image: url("images/airlines/YD.gif"); }
.airline-logo--yk { background-image: url("images/airlines/YK.gif"); }
.airline-logo--ym { background-image: url("images/airlines/YM.gif"); }
.airline-logo--yo { background-image: url("images/airlines/YO.gif"); }
.airline-logo--yq { background-image: url("images/airlines/YQ.gif"); }
.airline-logo--yv { background-image: url("images/airlines/YV.gif"); }
.airline-logo--yx { background-image: url("images/airlines/YX.gif"); }
.airline-logo--z2 { background-image: url("images/airlines/Z2.gif"); }
.airline-logo--z6 { background-image: url("images/airlines/Z6.gif"); }
.airline-logo--zb { background-image: url("images/airlines/ZB.gif"); }
.airline-logo--zh { background-image: url("images/airlines/ZH.gif"); }
.airline-logo--zi { background-image: url("images/airlines/ZI.gif"); }
.airline-logo--zk { background-image: url("images/airlines/ZK.gif"); }
.airline-logo--zl { background-image: url("images/airlines/ZL.gif"); }

/* =============================================================================
   36. CYBERESA BRAND IDENTITY  (« Font family, CSS couleurs, Cyberesa »)
   -----------------------------------------------------------------------------
   PURELY VISUAL re-skin — no markup, no JS, no layout, no flow, no copy changes.
   Aligns cr.resa.core with the rest of the suite (cr.admin / cr.resa), whose
   shared house style is:

     font    * { font-family: 'Plus Jakarta Sans', Trim, sans-serif !important }
             where `Trim` is the cr.fwk/css/myfonts.css alias for Inter.
     colour  --main-color / --header-color / --button-color : #630005  (oxblood)
             --hover-color / --secondary-color / --bar-color: #ffa500  (orange)
     shape   border-radius 3-4px dominant; small, low-alpha shadows.

   The official logo (cr.resa/images/official_logo.png) is the same three notes:
   a white CYBERESA wordmark on a solid oxblood field, with an orange monogram.

   Appended last so it overrides §1, §23-24 and §26 "Ardoise" by source order.
   Tokens are declared on `:root, :root[data-mode]` so they also beat the dormant
   `:root[data-mode="agent"]` block (§2), which redefines --r-lg / --r-xl.
   Component overrides are prefixed `:root ` to reach specificity (0,2,0) — enough
   to beat the `[data-skin="ibe"] .x` rules of §33 without resorting to !important.

   PRESERVED ON PURPOSE: the 15px type scale (changing it would reflow every
   page — a layout change, not a styling one), the §26.1 "restraint" decision to
   keep provider badges neutral, and every component's geometry, order and state
   logic. Only colour, typeface, radius and elevation move.
   ========================================================================== */
:root, :root[data-mode] {

  /* --- Brand: oxblood burgundy ladder, built around #630005 ---------------- */
  --c-primary-900: #3A0003;
  --c-primary-700: #4E0004;
  --c-primary-600: #630005;   /* base — cr.admin/cr.resa --main-color */
  --c-primary-500: #7A0008;   /* the lighter shade cr.resa already ships raw */
  --c-primary-200: #E8C9CB;
  --c-primary-100: #F6E9EA;
  --c-primary-050: #FCF5F5;

  /* Accent stays on the burgundy ladder ON PURPOSE. `.btn--accent` hardcodes
     `color:#fff` and is the app's main CTA; white on #ffa500 is ~2.1:1, an
     accessibility failure. Orange is routed through the --c-brand-orange-*
     tokens below and applied explicitly to interaction/highlight surfaces —
     exactly the reference's own split (--button-color vs --hover-color).
     --c-accent-100 must stay very light: it is used as TEXT on the dark hero. */
  --c-accent-700: #4E0004;
  --c-accent-600: #7A0008;    /* CTA hover — lifts rather than darkens */
  --c-accent-500: #630005;
  --c-accent-300: #B9686B;
  --c-accent-100: #F6E9EA;

  /* --- Cyberesa orange (--hover-color / --secondary-color / --bar-color) --- */
  --c-brand-orange:     #FFA500;   /* the brand orange, for fills on dark      */
  --c-brand-orange-600: #C46A00;   /* mid — icons, borders, rules              */
  --c-brand-orange-700: #A85400;   /* deep — AA-safe text on white or on -100  */
  --c-brand-orange-100: #FFF0D6;   /* tint — promo / warn backgrounds          */

  /* --- Ink & neutrals: the reference's grey ramp, warmed a shade ----------- */
  --c-ink:     #1A1315;
  --c-ink-2:   #3D3436;
  --c-muted:   #666666;   /* cr.admin --dirtyWhiteShadow-color */
  --c-faint:   #7B7B7B;   /* cr.resa --background-sixth-degree  */
  --c-on-dark: #FFFFFF;

  /* --- Surfaces: the "degree" scale (#fff → #F3F3F3 → #efefef → #E2E2E2) --- */
  --c-surface:   #FFFFFF;
  --c-sand:      #F7F5F5;   /* page canvas */
  --c-surface-2: #EFEFEF;
  --c-surface-3: #E2E2E2;
  --c-border:    #DCD6D7;
  --c-border-2:  #CCCCCC;   /* cr.resa --background-fifth-degree */

  /* --- Status: the reference's own alert colours, tuned to clear AA -------- */
  --c-success: #2E7D32;  --c-success-bg: #CCEEDD;  /* --room-available-color   */
  --c-danger:  #C6001C;  --c-danger-bg:  #FBE7E9;  /* cr.admin alert red       */
  --c-warn:    #A85400;  --c-warn-bg:    #FFF0D6;  /* amber restored (§26.1
                                                      had greyed it out)       */
  --c-info:    #630005;  --c-info-bg:    #F6E9EA;

  /* Provider badges stay neutral — §26.1's restraint decision is preserved,
     the grey is only warmed so it sits with the burgundy instead of the navy. */
  --c-src-direct: #4A4244;  --c-src-tgx: #4A4244;  --c-src-cyberesa: #4A4244;
  --c-src-dotw:   #4A4244;  --c-src-lightresa: #4A4244;

  /* Hero / header / overlay ground — burgundy instead of neutral slate. This
     single token repaints .hero, .utility-bar, .gallery__more and .rmc__badge. */
  --c-hero-ink: #4A0004;

  /* --- Shape: tightened toward the reference's crisp 3-4px language -------- */
  --r-sm:   3px;
  --r-md:   6px;
  --r-lg:  10px;
  --r-xl:  12px;
  --r-pill: 6px;   /* buttons, badges and chips square off (cr.admin has no pills) */
  --ibe-radius:    10px;
  --ibe-radius-sm:  6px;

  /* --- Elevation: small and low-alpha, tinted with the brand ---------------- */
  --shadow-sm:    0 1px 2px rgba(60,10,12,.08);
  --shadow-md:    0 2px 6px rgba(60,10,12,.10);
  --shadow-lg:    0 4px 14px rgba(60,10,12,.14);
  --shadow-focus: 0 0 0 3px rgba(255,165,0,.45);   /* ORANGE focus ring — the
     single most visible Cyberesa cue, and it reaches every focusable element
     through the global :focus-visible rule in §2. */
  --ibe-shadow:    0 6px 18px -8px rgba(60,10,12,.30);
  --ibe-shadow-sm: 0 1px 4px rgba(60,10,12,.08);

  /* --- Typography: the house stack, Inter kept as the offline fallback ----- */
  --font-sans: "Plus Jakarta Sans", "Inter", "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --font-head: "Plus Jakarta Sans", "Inter", "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
}

/* -----------------------------------------------------------------------------
   36.1  TYPOGRAPHY — Plus Jakarta Sans is geometric and reads a touch wider than
   Inter, so headings take slightly tighter tracking. Sizes are UNCHANGED.
   -------------------------------------------------------------------------- */
:root h1 { font-weight: 800; letter-spacing: -.5px; }
:root h2 { font-weight: 700; letter-spacing: -.4px; }
:root h3, :root h4 { font-weight: 700; letter-spacing: -.2px; }

/* Section headers echo cr.admin's `.headerUn`: uppercase, tracked, hairline rule */
:root .eyebrow { text-transform: uppercase; letter-spacing: .16em; font-weight: 700;
  color: var(--c-primary-600); }

/* -----------------------------------------------------------------------------
   36.2  DARK SURFACES — every cool-slate scrim becomes burgundy. These rules
   hardcode rgba() and so do NOT follow the token swap on their own.
   -------------------------------------------------------------------------- */
:root .hero { background: var(--c-hero-ink); }
:root .hero::after {
  background:
    linear-gradient(110deg, rgba(45,0,3,.74) 0%, rgba(45,0,3,.44) 46%, rgba(45,0,3,.18) 100%),
    linear-gradient(180deg, rgba(45,0,3,.10), rgba(45,0,3,.58)); }
:root .searchbar { background: rgba(58,0,4,.68); border-color: rgba(255,255,255,.16); }
:root .media__cap { background: rgba(45,0,3,.66); }
:root .media--grad::after { background: linear-gradient(180deg, transparent 45%, rgba(45,0,3,.55)); }
:root .modal-backdrop { background: rgba(30,0,2,.55); }
:root .utility-bar { background: var(--c-hero-ink); }

/* The map placeholder's diagonal stripes were cool grey — warm them. */
:root .map-embed {
  background: repeating-linear-gradient(45deg, #EFEDED, #EFEDED 12px, #E2DEDF 12px, #E2DEDF 24px);
  color: var(--c-ink-2); }

/* Brand mark: flat oxblood tile with the logo's orange initial (was a gradient). */
:root .brand__mark { background: var(--c-primary-600); border-radius: var(--r-md);
  color: var(--c-brand-orange); box-shadow: var(--shadow-sm); }
:root .brand__name b { color: var(--c-brand-orange-700); }

/* -----------------------------------------------------------------------------
   36.3  ORANGE ACCENTS — #ffa500 is the reference's --hover-color / --bar-color.
   Applied to interaction, progress and highlight only; never as a large fill
   behind white text.
   -------------------------------------------------------------------------- */
/* Progress + stepper rail: the "bar" role, straight from --bar-color */
:root .progress-track > i { background: var(--c-brand-orange); }
:root .stepper__item.is-done::after { background: var(--c-brand-orange); }
/* Current step: orange disc with dark ink (white on orange would be ~2.1:1) */
:root .stepper__item.is-active .stepper__num {
  background: var(--c-brand-orange); color: #2A1A00;
  box-shadow: 0 0 0 4px var(--c-brand-orange-100); }
/* Active search tab keeps its white pill, gains an orange underline */
:root .search-tab.is-active { color: var(--c-primary-700); box-shadow: inset 0 -3px 0 var(--c-brand-orange); }
:root .search-tab .soon { color: var(--c-brand-orange); }
/* Section rule + nav underline */
:root .section-title::before { background: var(--c-brand-orange); }
:root .main-nav a::after { background: var(--c-brand-orange); }
/* Promo chips return to orange (§26.1 had neutralised them) */
:root .badge--promo { background: var(--c-brand-orange-100); color: var(--c-brand-orange-700); }
/* Hotel rating stars return to a warm amber, tying back to the logo monogram */
:root .stars { color: var(--c-brand-orange-600); }
:root .hero .stars, :root .ibe-hotelbar__name .stars { color: var(--c-brand-orange); }
/* Link hover — scoped to content areas so the @media print `a { color:#000 }`
   guard for the voucher is never overridden. */
:root main a:hover, :root .card a:hover, :root .hotel-card__name-link:hover { color: var(--c-brand-orange-700); }

/* -----------------------------------------------------------------------------
   36.4  SHAPE — literals that bypass the --r-* tokens and would otherwise keep
   the old soft geometry. Sizes and paddings are untouched.
   -------------------------------------------------------------------------- */
:root .btn--lg { border-radius: 8px; }
:root .searchbar { border-radius: var(--r-xl); box-shadow: var(--shadow-lg); }
:root .search-fields { border-radius: var(--r-lg); }
:root .search-submit .btn { border-radius: 8px; }
:root .airline-logo { border-radius: var(--r-md); }
:root .rate-row__box { border-radius: var(--r-sm); }
:root code, :root .voucher__ref { border-radius: var(--r-sm); }
:root *::-webkit-scrollbar-thumb { border-radius: var(--r-sm); }

/* The IBE skin (§33) hardcodes `999px` in ten places. They MUST all move together
   or the two IBE pages come out half-square, half-pill. Genuine circles
   (.pcal__nav, .pcal__day .d, .ibe-search__sep — all `50%`) are left alone. */
:root[data-skin="ibe"] .btn { border-radius: var(--r-pill); }
:root .ibe-topbar { border-radius: var(--r-lg); }
:root .ibe-topbar__pill { border-radius: var(--r-pill); }
:root .rmc__badge { border-radius: var(--r-pill); }
:root .rmc__count { border-radius: var(--r-pill); background: rgba(45,0,3,.72); }
:root .rmc__rate-flag { border-radius: var(--r-pill); }
:root .rmc__box { border-radius: 4px; }
:root .rmc__rate:has(.rate-row__check:checked) { border-radius: var(--r-md); }
:root[data-skin="ibe"] .rooms-summary { border-radius: var(--r-lg); }
:root .qf-chip { border-radius: var(--r-pill); }
:root .pcal__day.is-start { border-top-left-radius: var(--r-pill); border-bottom-left-radius: var(--r-pill); }
:root .pcal__day.is-end   { border-top-right-radius: var(--r-pill); border-bottom-right-radius: var(--r-pill); }

/* §33's unified IBE search card was still teal (rgba(9,40,45,.72)) — a leftover
   from the pre-Ardoise palette that clashes hard with burgundy. */
:root[data-skin="ibe"] .ibe-searchbar { background: rgba(58,0,4,.68); border-color: rgba(255,255,255,.16); }
:root[data-skin="ibe"] .hero::after { background:
  linear-gradient(180deg, rgba(45,0,3,.30) 0%, rgba(45,0,3,.24) 42%, rgba(45,0,3,.74) 100%); }

/* -----------------------------------------------------------------------------
   36.5  ELEVATION — §23.5's coloured button glows still name the old teal/coral.
   -------------------------------------------------------------------------- */
:root .btn--accent { box-shadow: var(--shadow-sm); }
:root .btn--accent:hover  { box-shadow: var(--shadow-md); transform: none; }
:root .btn--primary:hover { box-shadow: var(--shadow-md); transform: none; }

/* -----------------------------------------------------------------------------
   36.6  BRAND LOGO — the official Cyberesa lockup, ported into wwwroot/images.

   Two variants, picked by background rather than by page:
     official_logo.png  666x189, white wordmark on an opaque #630005 field
                        -> the white .stepper top band, where the oxblood block
                           carries the identity on its own.
     logo_print.gif     300x53,  burgundy wordmark on white
                        -> white documents (voucher, error page), and anything
                           that gets printed — a solid burgundy field would
                           flood the page with ink.

   The logo sits as the first child of .stepper__bar, which is already
   `display:flex; align-items:center; gap:var(--sp-4)` with `.stepper__list`
   on `flex:1 1 auto` — so it drops into the empty left space the bar already
   had. No step, control or column moves.
   -------------------------------------------------------------------------- */
.brand-logo {
  flex: none;                    /* never squeezed by the stepper's flex:1 list */
  display: block;
  height: auto; width: auto;
  max-height: 100px;             /* THE DEFAULT CEILING FOR EVERY CLIENT, the same figure the site
                                    header uses (§43.2) — the two never show at once (§43 hides
                                    this one whenever the header rendered), so a deployment gets
                                    the same lockup size whether HeaderFooterSupported is on or
                                    off. Sizing it here rather than per client keeps a skin to
                                    what a skin is for: colours.
                                    max-height, NOT height: a client logo is whatever file the
                                    client sent, and forcing 100px on a 150x55 file upscales it
                                    1.8x into a staircase-edged blur. Assets that have the pixels
                                    get the full 100px; the smaller ones stay native and crisp.
                                    The bar pays for it. A step measures 51.6px — the 28px disc,
                                    an 8px gap, and a 13px label at 1.2 — and .stepper__bar is
                                    as tall as its tallest item, so a full-height LOGO sets the
                                    band's height. Accepted: nothing in the row is positioned
                                    against a fixed bar height, the steps stay centred. */
  border-radius: var(--r-sm);    /* softens the oxblood block's hard corners */
}
/* Document variant: white-backed artwork, so no tile radius and a touch smaller. */
.brand-logo--doc { height: 32px; border-radius: 0; }

/* Where the lockup is a link back to the search (Booking.cshtml), the anchor takes over
   the flex role the image had — otherwise it is the anchor, not the image, that the bar
   lays out, and .brand-logo's `flex:none` applies to nothing.

   THE HIT AREA IS THE WHOLE BAND, not the artwork. .stepper__list sets `padding-block:
   40px var(--sp-4)` (§22), so the bar stands ~110px tall while the lockup is 60px: an
   anchor sized to the image alone leaves ~50px of bar around it that looks like the logo
   and does nothing, and a click a few pixels high or low simply misses. `align-self:
   stretch` takes the full bar height instead — it cannot grow the bar, since the list is
   what sets that height. Being a flex container also removes the inline box under the
   image, which is what `line-height: 0` was there for.

   No colour or underline of its own: the artwork IS the affordance. */
.stepper__brand {
  flex: none; align-self: stretch;
  display: flex; align-items: center;
  border-radius: var(--r-sm);
}
.stepper__brand:hover .brand-logo { opacity: .85; }
.stepper__brand .brand-logo { transition: opacity .15s ease; }

/* The voucher's brand link wrapped a text mark before; drop the text-mark spacing
   so the image sits flush, and keep it from inheriting the old 800-weight rules. */
:root .voucher .brand { gap: 0; line-height: 0; }

/* Narrow screens: the stepper already tightens its gap at 760px (§22); shrink the
   lockup with it so the five step labels keep the room they have today. */
@media (max-width: 760px) {
  /* Raised far less than the desktop size did. Height buys width here — at 3.5:1 every extra
     pixel of height takes 3.5 from the five step labels sharing the same row — so this one is
     nudged rather than scaled with it. */
  .brand-logo { max-height: 28px; }
  .brand-logo--doc { height: 26px; }
}
/* Below the stepper's own breakpoint the five steps need the full width, so the
   lockup steps aside rather than pushing them — the brand is still in the tab
   icon and on the voucher. */
@media (max-width: 560px) {
  /* The wrapping anchor goes with it: hiding only the image would leave an empty flex
     item, and the bar's `gap` would still reserve a column for it. */
  .stepper__bar .brand-logo,
  .stepper__bar .stepper__brand { display: none; }
}

/* =============================================================================
   37. FLIGHTS — « Détails du vol » : per-leg journey timeline
   -----------------------------------------------------------------------------
   Replaces the flat two-column table the results card used to open, which listed
   every segment of every leg in ONE undifferentiated list: on a 3-leg itinerary
   four rows appeared with nothing saying which leg each belonged to, no times at
   all, and terminals wedged into parentheses beside the aircraft type.

   Layout is a 3-column grid — time | rail | content — repeated per leg:

       09:50   ●    TUN  Tunis-Carthage
                    Terminal M
               ✈    AF 1085 · 320 · Économique
       13:25   ●    CDG  Paris-Charles de Gaulle

   A CONNECTION IS ONE ROW, not three. The earlier version emitted an arrival row,
   a layover row and a departure row, which printed the connecting airport's name
   three times and doubled the height of every non-direct leg. Here both times
   stack in the time column against a single ring marker, the airport is named
   once, and a terminal change at the connection — real information that was
   buried before — is spelled out:

       07:05
        2h15  ◍    FCO  Rome-Fiumicino
       09:20        Escale · Terminal 1 → 3

   ADDITIVE ONLY. Every selector is new and namespaced .fdet*; no existing rule is
   touched, so nothing outside the flight results card can be affected. Colours
   come exclusively from tokens, so the §36 brand re-skin still applies even
   though this section sits after it.
   ========================================================================== */

.fdet { margin-top: var(--sp-3); border-top: 1px solid var(--c-border); padding-top: var(--sp-2); }

/* Disclosure: the default triangle is replaced by a rotating chevron so the
   open/closed state reads at the weight of the rest of the card. */
.fdet > summary { cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); font-weight: 700; color: var(--c-primary-600); }
.fdet > summary::marker { content: ""; }
.fdet > summary::before { content: "\25B8"; font-size: 11px; line-height: 1; transition: transform .15s ease; }
.fdet[open] > summary::before { transform: rotate(90deg); }
.fdet > summary:hover { color: var(--c-primary-700); }
.fdet__body { margin-top: var(--sp-3); }

/* ---- one leg ------------------------------------------------------------- */
.fdet__leg + .fdet__leg { margin-top: 18px; padding-top: 18px; border-top: 1px dashed var(--c-border-2); }
.fdet__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; margin-bottom: 12px; }
.fdet__no { font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--c-primary-700); background: var(--c-primary-100); border-radius: 999px; padding: 3px 10px; }
.fdet__date { font-size: var(--fs-sm); font-weight: 700; color: var(--c-ink-2); }
/* margin-left:auto trails the duration; collapses harmlessly when the head wraps. */
.fdet__facts { margin-left: auto; font-size: var(--fs-xs); color: var(--c-muted);
  font-variant-numeric: tabular-nums; }

/* ---- the rail: time | marker | content ---------------------------------- */
.fdet__tl { list-style: none; margin: 0; padding: 0; }
.fdet__r { display: grid; grid-template-columns: 64px 16px minmax(0, 1fr); column-gap: 12px; }

.fdet__t { text-align: right; font-size: var(--fs-sm); font-weight: 700; color: var(--c-ink);
  line-height: 1.35; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
/* Day rollover ("+1 j"): accent-coloured because it changes what the adjacent time
   means, superscripted so it cannot be misread as part of the clock. */
.fdet__t sup { margin-left: 1px; font-size: 9px; font-weight: 400; color: var(--c-accent-700);
  white-space: nowrap; }
/* Ground time, between the two stacked times of a connection row. */
.fdet__t .fdet__wait { display: block; font-size: 10px; font-weight: 700; color: var(--c-accent-700);
  letter-spacing: 0; }

.fdet__m { position: relative; }
.fdet__m::before { content: ""; position: absolute; left: 50%; transform: translateX(-50%);
  top: 0; bottom: 0; width: 2px; background: var(--c-border-2); }
/* The rail starts and stops AT the dots rather than overhanging the block. Dots sit
   on the optical centre of the first line of the time text. */
.fdet__r:first-child .fdet__m::before { top: 8px; }
.fdet__r:last-child .fdet__m::before { bottom: calc(100% - 8px); }

.fdet__m i { position: absolute; left: 50%; top: 8px; transform: translate(-50%, -50%);
  width: 9px; height: 9px; border-radius: 50%; background: var(--c-primary-600);
  box-shadow: 0 0 0 2px #fff; }

/* In-flight row: a plane on the rail orients the eye down the journey and tells the
   two row kinds apart at a glance. Rotated to point along the rail; the solid
   background masks the line behind it so the glyph stays crisp. */
.fdet__m--fly::after { content: "\2708"; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%) rotate(90deg); font-size: 12px; line-height: 1;
  color: var(--c-primary-600); background: #fff; padding: 3px 0; }

/* Connection row: dashed rail (on the ground) and a hollow ring centred on the
   taller, three-line time cell. */
.fdet__m--wait::before {
  background: repeating-linear-gradient(to bottom, var(--c-border-2) 0 3px, transparent 3px 7px); }
.fdet__m--wait i { top: 50%; width: 9px; height: 9px; background: #fff;
  border: 2px solid var(--c-accent-500); box-shadow: none; }

/* ---- content ------------------------------------------------------------- */
.fdet__c { padding-bottom: 16px; font-size: var(--fs-sm); color: var(--c-ink); line-height: 1.35; }
.fdet__r:last-child .fdet__c { padding-bottom: 0; }
/* Code first and heaviest, name second and quieter: the code is what the traveller
   matches against a boarding pass, the name is what tells them where that is. */
.fdet__ap { font-weight: 700; letter-spacing: .02em; }
.fdet__city { color: var(--c-ink-2); }
.fdet__sub { display: block; margin-top: 1px; font-size: var(--fs-xs); color: var(--c-muted); }
.fdet__sub b { font-weight: 700; color: var(--c-accent-700); }

/* Carrier / aircraft / cabin, as quiet chips rather than a run of separators. */
.fdet__hop { display: flex; flex-wrap: wrap; align-items: center; gap: 5px 7px;
  font-size: var(--fs-xs); color: var(--c-muted); padding-bottom: 16px; }
.fdet__hop .airline-logo { width: 22px; height: 22px; font-size: 10px; border-radius: 6px; flex: none; }
.fdet__fno { font-weight: 700; font-size: var(--fs-sm); color: var(--c-ink-2);
  font-variant-numeric: tabular-nums; }
.fdet__chip { border: 1px solid var(--c-border-2); border-radius: var(--r-sm);
  padding: 1px 7px; background: #fff; white-space: nowrap; }

/* Technical footer (OfficeId). .agent-only already hides it at B2C density. */
.fdet__tech { margin-top: 14px; padding-top: 10px; border-top: 1px dashed var(--c-border-2);
  display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: var(--fs-xs); color: var(--c-faint);
  font-variant-numeric: tabular-nums; }

@media (max-width: 640px) {
  /* Narrow cards: tighten the time column and let the duration drop to its own line. */
  .fdet__r { grid-template-columns: 58px 14px minmax(0, 1fr); column-gap: 9px; }
  .fdet__facts { margin-left: 0; flex-basis: 100%; }
  .fdet__c, .fdet__hop { padding-bottom: 13px; }
}

/* =============================================================================
   38. SEARCH PROGRESS — /flight/list, /hotel/list and /hotel/ibe/list
   -----------------------------------------------------------------------------
   Sits between the search bar and the results while the remote search runs — the
   GDS call on the flights page, HotelSearchREQ on the two hotel ones. Deliberately
   NOT a full-page loader and not centred: it occupies the space the results are
   about to fill, so the page never blanks and nothing below it moves when it goes.

   ONE component for all three pages, and only the glyph differs. .srch--hotel is
   the whole of that difference: a building does not bank like an aircraft, so the
   modifier keeps the lift and drops the rotation. Nothing else is per-product.

   The visual language is this design system's, not the reference screenshot's:
   the brand teal on a tinted surface, the same panel radius and border as every
   other block on the page, and one moving highlight rather than a striped
   barber-pole. Restraint is the point — it is furniture the user looks past once
   the fares arrive.

   Collapsing is animated on height AND opacity, from an inline height the script
   pins first, so the results glide up instead of snapping.

   ADDITIVE ONLY. Every selector is new and namespaced .srch*.
   ========================================================================== */

.srch {
  margin-top: var(--sp-4);
  padding: 16px 18px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  overflow: hidden;
  transition: height .38s cubic-bezier(.4, 0, .2, 1),
              opacity .3s ease,
              margin .38s cubic-bezier(.4, 0, .2, 1);
}
.srch[hidden] { display: none; }

.srch__row { display: flex; align-items: center; gap: 14px; margin-bottom: 12px; }

/* The plane travels with the fill rather than spinning in place: the subject's own
   motion, and it reads as progress even before the numbers are legible. */
.srch__plane {
  flex: none;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--c-primary-100);
  color: var(--c-primary-600);
  font-size: 15px;
  animation: srch-lift 2.4s ease-in-out infinite;
}

.srch__text { min-width: 0; flex: 1; }

.srch__stage {
  margin: 0;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--c-ink);
  letter-spacing: -.01em;
}

.srch__hint {
  margin: 2px 0 0;
  font-size: var(--fs-xs);
  color: var(--c-muted);
}

.srch__pct {
  flex: none;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--c-primary-600);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}

/* ---- the bar ------------------------------------------------------------- */
.srch__track {
  position: relative;
  height: 6px;
  border-radius: 999px;
  background: var(--c-primary-100);
  overflow: hidden;
}

.srch__fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--c-primary-600), var(--c-primary-500, var(--c-primary-600)));
  /* Matches the script's tick so the fill glides between values instead of stepping. */
  transition: width .18s linear;
}

/* A highlight sweeping the FILLED portion — motion that says "still working"
   without implying progress the API never reported. */
.srch__fill::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
              transparent 0%,
              rgba(255, 255, 255, .45) 50%,
              transparent 100%);
  animation: srch-sweep 1.5s linear infinite;
}

@keyframes srch-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

@keyframes srch-lift {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-3px) rotate(8deg); }
}

/* Hotels: the same lift without the bank — a building that tilts 8° reads as a
   glitch, where a plane that does reads as flight. Only the animation changes;
   the chip, its colours and its size stay the shared ones. */
.srch--hotel .srch__plane { animation-name: srch-hover; }

@keyframes srch-hover {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3px); }
}

/* Motion is decoration here; the percentage and the caption carry the meaning. */
@media (prefers-reduced-motion: reduce) {
  .srch, .srch__fill { transition: none; }
  .srch__plane, .srch__fill::after { animation: none; }
}

@media (max-width: 640px) {
  .srch { padding: 13px 14px; }
  .srch__row { gap: 11px; margin-bottom: 10px; }
  .srch__plane { width: 30px; height: 30px; font-size: 13px; }
  .srch__hint { display: none; }   /* the caption above it already says enough */
}

/* =============================================================================
   39. FLIGHTS — « Devis » : confirmation window + fare-rules accordion
   -----------------------------------------------------------------------------
   Two components for /flight/detail. Everything else on that page reuses what the
   stylesheet already has — .layout-detail, .panel, .timeline / .tl-point /
   .tl-layover, .table--compact, .recap and .amount — because the hotel detail and
   quote pages established all of it.

   .fwin — the 8-minute confirmation window. FlightDetailRS opens a window to
   confirm the booking (PDF p.28) and states no deadline of its own, so the page
   counts down from the instant the response landed. It sits directly under the
   page heading, above the itinerary, because it governs everything below it.

   Deliberately NOT an alarm. For seven of its eight minutes this is ordinary
   reassurance — a guaranteed fare — so it wears the informational tint, and only
   the expired state switches to the warning palette. A red bar counting down from
   8:00 would rush a decision the traveller is entitled to take calmly, and
   expiring is not even a failure here: re-pricing is one button and always works.

   .frule — the fare-rules accordion. Amadeus returns column-aligned plain text
   running to a hundred lines or more per category, so the body is a <pre> with its
   own scroll rather than reflowed prose: re-wrapping turns a fare table into
   noise. Collapsed by default — reference text must not bury the price.

   ADDITIVE ONLY. Every selector is new and namespaced .fwin* / .frule*.
   ========================================================================== */

.fwin {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
  padding: 12px 16px;
  background: var(--c-info-bg);
  border: 1px solid var(--c-border);
  border-left: 3px solid var(--c-info);
  border-radius: var(--r-md);
}

.fwin__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Takes the slack so the clock and the refresh button stay hard right. */
  flex: 1 1 240px;
  min-width: 0;
}

.fwin__title {
  font-size: var(--fs-sm);
  color: var(--c-ink);
}

.fwin__note {
  font-size: var(--fs-xs);
  color: var(--c-muted);
}

/* Tabular figures so the seconds do not jitter the layout as they tick down. */
.fwin__clock {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--c-info);
  font-variant-numeric: tabular-nums;
  letter-spacing: .5px;
}

.fwin__clock:empty { display: none; }

.fwin__refresh { margin: 0; }

/* Expired: the fare is no longer guaranteed, so the tint changes — but the copy
   below it says the remedy is one button, not a lost booking. */
.fwin--expired {
  background: var(--c-warn-bg);
  border-left-color: var(--c-warn);
}

.fwin--expired .fwin__clock { color: var(--c-warn); }

/* The stepper-bar language + currency pickers. On /flight/list these are links;
   here they submit a form, because the selected flight lives in the POST body and
   a navigation would arrive with nothing to price. Laid out as one row so the two
   selects read as a single control group rather than two stray dropdowns. */
.ctx-switch {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
}

/* The hidden fields carrying the selection must never take part in the layout —
   there are up to twenty of them and a flex container would give each one a slot. */
.ctx-switch input[type="hidden"] { display: none; }

/* ---- fare rules ----------------------------------------------------------
   Separate cards rather than one flat striped list. The first version looked
   unfinished for three reasons, all fixed here: the headings were raw
   teletype-era ALL CAPS straight from Amadeus ("ADVANCE RES/TKT"), they were
   set at --fs-sm, and every row was the same weight, so nothing said which of
   the six mattered. Now the category is a coloured code chip, the heading is a
   translated sentence in the heading face at body size, the airline's own
   wording sits under it, and the line count is on the right — so PENALTIES
   announces its 132 lines before it is opened.
   -------------------------------------------------------------------------- */

.frule {
  margin-top: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.frule__item {
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  background: var(--c-surface);
  overflow: hidden;
  transition: border-color .18s ease, box-shadow .18s ease;
}

.frule__item[open] {
  border-color: var(--c-accent-300);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .05);
}

/* code | heading | line count | caret */
.frule__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto 10px;
  align-items: center;
  gap: var(--sp-3);
  padding: 13px 16px;
  cursor: pointer;
  list-style: none;
  transition: background .15s ease;
}

/* The default disclosure triangle is replaced by the caret below: list-style + ::marker
   cover every current engine (Safari since 18.4), so no ::-webkit-details-marker rule. */
.frule__head::marker { content: ""; }

.frule__head:hover { background: var(--c-surface-2); }

.frule__item[open] > .frule__head {
  background: var(--c-surface-2);
  border-bottom: 1px solid var(--c-border);
}

.frule__head::after {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--c-muted);
  border-bottom: 2px solid var(--c-muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .18s ease;
}

.frule__item[open] > .frule__head::after { transform: rotate(-135deg); }

/* The ATPCO category ("PE", "MX"). Monospace and fixed-width so the six chips
   form a column the eye can run down, and tinted so the row has an anchor. */
.frule__code {
  min-width: 32px;
  padding: 4px 6px;
  text-align: center;
  border-radius: var(--r-sm);
  background: var(--c-accent-100);
  color: var(--c-accent-700);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
}

.frule__text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.frule__name {
  font-family: var(--font-head);
  font-size: var(--fs-base);
  font-weight: 600;
  line-height: 1.3;
  color: var(--c-ink);
}

/* Amadeus' own heading, kept under the translated one: this block is the
   airline's text and its title is the only part we reworded. */
.frule__orig {
  font-size: var(--fs-xs);
  line-height: 1.3;
  color: var(--c-faint);
  letter-spacing: .02em;
}

.frule__count {
  font-size: var(--fs-xs);
  color: var(--c-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* The rule text itself.

   Was a monospace <pre>. That preserved nothing — the parser had already trimmed
   away Amadeus' indentation — while keeping its ~60-character hard wraps, so every
   line stopped mid-panel and the right half of the block sat empty. The wraps are
   now undone in RuleBlocks() and the indentation is real nesting, so this is
   flowing text in the page's own face: proportional, wrapping to the full width,
   with the hierarchy carried by indent rather than by whitespace nobody could see.

   The CASE is left as filed. These are the conditions being agreed to, and
   lower-casing them would both misrender acronyms (ESTA, MCO, PTA) and put our
   styling between the airline and its own terms. Generous line-height and a
   slightly reduced size do the legibility work instead. */

.frule__body {
  margin: 0;
  padding: 16px 18px 18px;
  max-height: 360px;
  overflow-y: auto;
  background: var(--c-surface);
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--c-ink-2);
}

/* Indent step per nesting level. The cap is 8 because these rules genuinely go
   that deep — measured on PE.PENALTIES: BETWEEN TUNISIA AND FRANCE > ORIGINATING
   TUNISIA > CHANGES > BEFORE DEPARTURE > body > NOTE > body, and a second carrier
   reached eight levels — and flattening the last ones would file a note under the
   wrong clause. Eight steps is 120px of a ~750px column, which the text absorbs.
   Still capped, so a pathological rule cannot indent itself off the panel; the
   media query below tightens the step on narrow screens rather than dropping
   levels, because a level lost is a condition attached to the wrong heading. */
.frule__line,
.frule__sub,
.frule__sep { margin-left: calc(min(var(--d, 0), 8) * var(--frule-step, 15px)); }

.frule__line {
  margin-top: 0;
  margin-bottom: 9px;
  letter-spacing: .01em;
}

.frule__line:last-child { margin-bottom: 0; }

/* Amadeus' own sub-headings ("CANCELLATIONS", "ANY TIME"). Small, spaced and in
   the ink colour so the sections are findable without competing with the
   accordion heading above them. */
.frule__sub {
  margin: 16px 0 7px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--c-ink);
}

.frule__sub:first-child { margin-top: 0; }

/* A run of dashes in the source. Rendered as the rule it stands for rather than
   as a row of hyphens. */
.frule__sep {
  height: 0;
  margin: 13px 0;
  border: 0;
  border-top: 1px dashed var(--c-border);
}

@media (max-width: 640px) {
  .fwin { padding: 11px 13px; }
  .fwin__refresh { flex-basis: 100%; }
  .fwin__refresh .btn { width: 100%; }
  /* The line count is the first thing to go when the heading needs the width. */
  .frule__head { grid-template-columns: auto minmax(0, 1fr) 10px; padding: 11px 13px; }
  .frule__count { display: none; }
  /* Keep every nesting level, but pay less for each one. */
  .frule__body { max-height: 280px; padding: 13px 14px 15px; --frule-step: 8px; }
}

/* =============================================================================
   40. FLIGHTS — « Passagers » (step 4) + « Bon de réservation » (step 5)

   Everything these two pages need beyond what already exists. The form itself
   reuses .panel / .field / .input / .select / .grid-2 / .checkbox, the recap
   reuses .recap / .recap__row / .recap__total / .amount, and the confirmation
   window reuses §39's .fwin wholesale — so what is left is three things: the
   passenger card, the payment-seam note, and the voucher's reference block.
   ============================================================================= */

/* ---- 40.1  Passenger card -------------------------------------------------
   One per name slot. A card rather than a bare fieldset because a two-adult
   booking otherwise renders as sixteen inputs with nothing saying where one
   traveller stops and the next begins. */
.paxcard {
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  padding: var(--sp-3);
  background: var(--c-surface);
}

.paxcard__head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* The slot number, which is also the binder index the inputs carry. */
.paxcard__num {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  font-size: var(--fs-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.paxcard__title {
  font-weight: 700;
  font-size: var(--fs-md);
}

/* The required marker. Kept distinct from .faint so "obligatoire" and
   "optionnel" never read as the same weight of statement. */
.req {
  color: var(--c-danger, #c0392b);
  font-weight: 700;
}

/* ---- 40.2  Payment seam ---------------------------------------------------
   A panel with no controls in it, and that is deliberate: no payment is taken
   at this step and the order lands Reserved. It is marked rather than styled as
   an ordinary panel so the absence reads as a decision instead of an omission.
   Ticket 58728 replaces the body; the shell can stay. */
.paysea {
  border-left: 3px solid var(--c-warning, #d68910);
  background: var(--c-surface-2);
}

/* ---- 40.2b  Payment mode picker (ticket 58728) -----------------------------
   The Traveltodo arrangement — segmented tabs per channel, then a filterable
   list of counters — in our own vocabulary. It ADDS classes only: .segmented
   and .radio are reused untouched, because they are shared with the search
   tabs and every other form on the site.

   The counter list is a radiogroup of stacked rows rather than a <select>: an
   agency is three facts (name, address, phone), and a dropdown can show one. */

/* ---- the mode options -------------------------------------------------
   Full-width selectable cards rather than bare .radio labels. .radio is
   inline-flex and shared with every other form, so two of them land on one
   line and .stack's margin does nothing between them — which is what made
   the modes read as one cramped run. These are block-level, one per line,
   with the label left and the amount right so a column of amounts can be
   compared at a glance. */
.payopt {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  background: var(--c-surface);
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.payopt + .payopt { margin-top: var(--sp-2); }
.payopt:hover { border-color: var(--c-primary-200); background: var(--c-primary-050); }

/* Selected: accent edge and a tint — the same language the counter rows use. */
.payopt:has(input:checked) {
  border-color: var(--c-accent-500);
  background: var(--c-accent-100);
}
.payopt:focus-within { outline: 2px solid var(--c-primary-500); outline-offset: 2px; }

.payopt__main { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }
.payopt__label { font-weight: 600; }
.payopt__note { font-size: var(--fs-xs); color: var(--c-faint); }

/* Right-aligned and non-wrapping: the figure is the thing being compared. */
.payopt__amt {
  flex: 0 0 auto;
  text-align: right;
  font-weight: 700;
  white-space: nowrap;
}

/* ---- the counter finder ------------------------------------------------
   Collapsed by default. The filter stays visible so typing is a way in;
   the list itself only appears once there is a query or the user asks for
   it, which keeps a six-row scroller out of the page until it is wanted. */
/* The opener is the FIELD ITSELF, not a link beside it. A bordered box with a
   caret reads as "this opens" the way a <select> does; the underlined text
   link that lived here was missed because nothing about it looked openable. */
.payfinder__control {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  background: var(--c-surface);
  overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.payfinder__control:hover { border-color: var(--c-primary-200); }
.payfinder__control:focus-within {
  border-color: var(--c-primary-500);
  box-shadow: 0 0 0 3px var(--c-primary-100);
}

/* The input carries no border of its own — the wrapper is the control. */
.payfinder__control .input {
  flex: 1 1 auto;
  border: 0;
  border-radius: 0;
  background: transparent;
  min-width: 0;
}
.payfinder__control .input:focus { outline: none; box-shadow: none; }

.payfinder__caret {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0 var(--sp-3);
  border: 0;
  border-left: 1px solid var(--c-border);
  background: var(--c-surface-3);
  color: var(--c-primary-700);
  font: inherit;
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.payfinder__caret:hover { background: var(--c-primary-100); }
.payfinder__caret::after {
  content: "";
  width: 0; height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid currentColor;
  transition: transform .15s ease;
}
.payfinder__caret[aria-expanded="true"]::after { transform: rotate(180deg); }

/* CHOSEN STATE — the control shows its value the way a <select> does, rather
   than on a separate bar above it. The accent edge is the same selection
   language the option cards and counter rows use, so one glance reads the same
   everywhere on the block. */
.payfinder__control.is-chosen {
  border-color: var(--c-accent-500);
  background: var(--c-accent-100);
  box-shadow: inset 3px 0 0 var(--c-accent-500);
}
.payfinder__control.is-chosen .input { font-weight: 600; color: var(--c-primary-900); }

/* Clearing the choice. The agency is optional and a radio cannot be un-checked
   by clicking it, so without this there is no way back to "no agency". */
.payfinder__clear {
  flex: 0 0 auto;
  width: 1.75rem;
  align-self: center;
  margin-right: var(--sp-1);
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--c-faint);
  font: inherit;
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
}
.payfinder__clear:hover { background: var(--c-surface-3); color: var(--c-primary-700); }
.payfinder__clear[hidden] { display: none; }

/* Bounded height so a long list scrolls inside the panel instead of pushing the
   submit button off the page — the whole point of the filter above it. */
.paylist__rows {
  max-height: 20rem;
  overflow-y: auto;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  background: var(--c-surface);
}

.paylist__row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-bottom: 1px solid var(--c-border);
  cursor: pointer;
}
.paylist__row:last-child { border-bottom: 0; }
.paylist__row:hover { background: var(--c-primary-050); }
.paylist__row[hidden] { display: none; }

/* The selected counter is marked with the accent bar the section titles use, so
   selection reads the same way here as everywhere else on the page. */
.paylist__row:has(input:checked) {
  background: var(--c-primary-100);
  box-shadow: inset 3px 0 0 var(--c-accent-500);
}

.paylist__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.paylist__name { font-weight: 600; }
.paylist__meta { font-size: var(--fs-xs); color: var(--c-faint); }
.paylist__meta--tel { font-variant-numeric: tabular-nums; }

/* ---- 40.3  Voucher reference block ----------------------------------------
   Label / value / hint stacked per reference. A definition list rather than a
   table because these are five unrelated facts, not five rows of one thing —
   and because it has to reflow to one column on a phone and stay printable. */
.vref {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--sp-3);
}

.vref__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--c-border);
  border-radius: var(--r-sm);
  background: var(--c-surface-2);
}

.vref__label {
  font-size: var(--fs-xs);
  color: var(--c-ink-muted);
  text-transform: uppercase;
  letter-spacing: .06em;
}

/* Record locators and order ids are read aloud down a phone and typed back in,
   so they get tabular figures and a touch of tracking. */
.vref__value {
  font-size: var(--fs-md);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
  word-break: break-all;
}

.vref__value--strong { font-size: var(--fs-lg); }

.vref__hint {
  font-size: var(--fs-xs);
  color: var(--c-ink-faint, var(--c-ink-muted));
}

/* ---- 40.4  Wide content ---------------------------------------------------
   The itinerary and passenger tables scroll inside their own box so the PAGE
   never scrolls sideways on a phone. */
.scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* ---- 40.5  Print ----------------------------------------------------------
   The voucher is a document people print. Drop the chrome, keep the content,
   and let the panels sit flat on the page. */
@media print {
  .no-print { display: none !important; }

  .panel,
  .vref__item {
    border-color: #ccc;
    box-shadow: none;
    background: #fff;
    break-inside: avoid;
  }

  .layout-detail { display: block; }
  .recap { margin-top: var(--sp-4); }
}

@media (max-width: 640px) {
  .paxcard { padding: var(--sp-2); }
  .vref { grid-template-columns: 1fr; }
}

/* =============================================================================
   41. HOTEL RESULTS — MOBILE TOOLBAR & FULL-SCREEN PANELS
   @partial: _MobilePanels.cshtml  (markup + controller for /hotel/list)
   -----------------------------------------------------------------------------
   Below the breakpoint the results toolbar (sort <select> + Liste/Carte tabs) and
   the filters sidebar are too wide to sit beside the cards, so they move behind a
   three-button bar — Filtrer · Trier · Carte — each opening a full-screen panel.

   ONE breakpoint: 1080px, the width at which §22 drops .layout-2col to a single
   column. It used to be 760px, the "phone" value, which left a 320px-wide dead
   band: between 761 and 1080 the grid had already collapsed, so the sidebar was no
   longer beside the cards, but the bar that hides it was still inert — the whole
   filters panel sat open above the first result, a full screen of it on an iPad.
   The bar exists BECAUSE the sidebar no longer fits next to the list, so the two
   numbers have to be the same one. Every rule of this section lives in the single
   media block below, and the controller reads the same value from one `matchMedia`
   string.

   ONE DOM, RELOCATED. Nothing here duplicates a control. The <aside class="filters">
   is the very node the desktop renders — the script moves it into the panel body on
   the way down and puts it back on the way up — and the sort rows are a view over
   the existing #sort <select>, which stays the only holder of the sort value. So
   there is no second copy to keep in sync and no duplicated id.
   ========================================================================== */

/* 41.0 — Panel close button colour. Stays on --c-danger for a deployment with no
   client sheet; client-bridge.css points it at the client's own CTA colour. */
:root { --c-msheet-close: var(--c-danger); }

/* 41.1 — Both hooks are display:none by default rather than only inside the media
   block: a stray `hidden` removal (or a script that never runs) can then never leak
   a full-screen panel over the desktop layout. */
.mtoolbar { display: none; }
.msheet   { display: none; }

/* 41.2 — The results map's height, moved off its inline style so the map can be
   restretched inside the full-screen panel without !important. 520px is exactly
   what the list page rendered before. */
.map-embed--results { height: 520px; }

@media (max-width: 1080px) {

  /* ---- 41.3  The bar ------------------------------------------------------
     The desktop cluster (sort + view tabs) is hidden, not moved: its two controls
     are re-presented by the panels below, and hiding is what lets the desktop
     markup stay exactly as it is. */
  .results-toolbar > .cluster { display: none; }

  .mtoolbar {
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    gap: var(--sp-2); margin-top: var(--sp-4);
  }
  .mtoolbar__btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    height: var(--control-h); padding-inline: var(--sp-2);
    border: 1px solid var(--c-border-2); border-radius: var(--r-pill);
    background: var(--c-surface-3); color: var(--c-ink-2);
    font-family: inherit; font-size: var(--fs-md); font-weight: 700;
    white-space: nowrap; cursor: pointer;
  }
  .mtoolbar__btn--accent {
    background: var(--c-accent-500); border-color: var(--c-accent-500); color: #fff;
  }
  .mtoolbar__ico { font-size: 1.05em; line-height: 1; }

  /* ---- 41.4  The panel ----------------------------------------------------
     Higher specificity than the .msheet base rule above, so [hidden] alone drives
     open/close and the base display:none still wins above the breakpoint. */
  .msheet:not([hidden]) { display: flex; }
  .msheet {
    position: fixed; inset: 0; z-index: var(--z-modal);
    flex-direction: column; background: var(--c-surface);
  }
  .msheet__head {
    flex: none; display: flex; align-items: center; justify-content: space-between;
    gap: var(--sp-3); padding: var(--sp-4);
    border-bottom: 1px solid var(--c-border);
  }
  .msheet__title { margin: 0; font-size: var(--fs-xl); }
  .msheet__close {
    flex: none; width: 34px; height: 34px; border: 0; border-radius: 50%;
    background: var(--c-msheet-close); color: #fff; font-size: 15px; line-height: 1; cursor: pointer;
  }
  .msheet__body {
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
    -webkit-overflow-scrolling: touch; padding: var(--sp-4);
  }
  .msheet__foot {
    flex: none; display: flex; justify-content: flex-end;
    padding: var(--sp-3) var(--sp-4);
    border-top: 1px solid var(--c-border); background: var(--c-surface);
  }
  .msheet__apply { text-transform: uppercase; letter-spacing: .04em; }

  /* The relocated sidebar is a card on the desktop grid; inside the panel the panel
     IS the card, so strip the second frame. The groups keep their own dividers. */
  .msheet__body > .filters,
  .msheet__body > .filters > .panel {
    background: none; border: 0; box-shadow: none; padding: 0;
  }
  /* The panel header already says "Filtrer" — do not say it twice. "Réinitialiser"
     is kept and takes the row on its own. */
  .msheet__body > .filters > .panel > .between > h3 { display: none; }
  .msheet__body > .filters > .panel > .between { justify-content: flex-end; margin-bottom: var(--sp-2); }

  /* ---- 41.5  Sort rows ----------------------------------------------------
     One row per <option> of #sort. The radio is the real control (native grouping
     and arrow keys); it is visually replaced by the row itself, so the row carries
     the focus ring. */
  .msort { display: flex; flex-direction: column; }
  .msort__row {
    position: relative; display: flex; align-items: center; gap: var(--sp-3);
    padding: 14px var(--sp-2); border-bottom: 1px solid var(--c-border);
    font-size: var(--fs-md); font-weight: 600; color: var(--c-ink-2); cursor: pointer;
  }
  .msort__row:last-child { border-bottom: 0; }
  .msort__row input { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; }
  .msort__row:focus-within { box-shadow: var(--shadow-focus); border-radius: var(--r-sm); }
  .msort__row.is-active { color: var(--c-accent-600); font-weight: 800; }
  .msort__mark { margin-left: auto; visibility: hidden; }
  .msort__row.is-active .msort__mark { visibility: visible; }

  /* ---- 41.6  Map panel ----------------------------------------------------
     The map fills the body instead of keeping its 16/9 desktop box. */
  .msheet--map .msheet__body { padding: 0; overflow: hidden; }
  .msheet--map .map-host { display: flex; flex-direction: column; height: 100%; }
  .msheet--map .map-host .map-embed {
    flex: 1 1 auto; min-height: 0; height: auto; aspect-ratio: auto;
    border: 0; border-radius: 0;
  }
  .msheet--map .map-host > p { flex: none; margin: 0; padding: var(--sp-3) var(--sp-4); }

  /* ---- 41.7  Body scroll lock --------------------------------------------
     No existing component locks the page, so it is written here. position:fixed
     rather than overflow:hidden because iOS Safari ignores the latter on <body>;
     the script stores the offset in `top` and scrolls back on close. Scoped to the
     media block so the class can never strand a desktop page. */
  body.m-locked { position: fixed; left: 0; right: 0; width: 100%; overflow: hidden; }
}


/* =============================================================================
   42. GALLERY — primary tile, clickable secondary tiles, counter
   @partial: Views\Hotel\Establishment\_Gallery.cshtml   (BOTH routes)
   -----------------------------------------------------------------------------
   42 used to be "IBE STAGE GALLERY": one large photo above a thumbnail strip,
   rendered only on /hotel/ibe/list while /hotel/detail showed the §18 mosaic.
   The mosaic won and both routes now render it, so the stage, the strip and the
   "Reste N photos" overlay are gone. What survived is the part that made the
   stage worth having - the first tile shows every photo in turn - and these are
   the pieces that let it.

   NO ARROWS. Prev/next buttons floated over the primary photo for one revision
   and were removed: the four secondary tiles ARE the control, and each promotes
   itself into the primary slot on click. That also costs nothing in keyboard
   terms - they are <button>s, so Tab and Enter work without a key handler.

   NO LIBRARY, as before. The legacy page ran Galleria 1.4.2 on top of jQuery;
   this ships neither. The markup IS the gallery: with JS off the first photo
   still renders and the mosaic is exactly what §18 draws - degraded, never
   broken.
   ========================================================================== */

/* The primary tile is the only POSITIONED one. .gallery > :first-child already
   spans 2x2 (§18) and .gallery > * already clips overflow; this only makes it the
   containing block for the counter below. */
.gallery__hero { position: relative; }

/* 42.1 - The secondary tiles. They are <button>s so they can be clicked and
   focused, but they must LOOK exactly like the bare <img> tiles they replaced -
   hence the full reset. .gallery img (§18) still sizes the photo inside. */
.gallery__tile {
  display: block; padding: 0; border: 0; background: none;
  width: 100%; height: 100%; cursor: pointer;
  overflow: hidden;
}
/* A quiet hint that the tile does something. Kept to opacity alone: a transform or
   a border would move the photo inside a grid cell that clips it. */
.gallery__tile img { transition: opacity .15s ease; }
.gallery__tile:hover img { opacity: .82; }
.gallery__tile:focus-visible { outline: 2px solid var(--c-accent-500); outline-offset: -2px; }

/* 42.2 - "3 / 9", bottom-right of the primary tile. Its own pill rather than the
   stage gallery's full-width bottom gradient: there is no caption to share a scrim
   with any more, and a gradient across the photo would dim it for one small number. */
.gallery__count {
  position: absolute; right: var(--sp-2); bottom: var(--sp-2);
  padding: 2px 10px; border-radius: 999px;
  background: rgba(0, 0, 0, .55); color: #fff;
  font-size: 12px; font-weight: 700; line-height: 1.6;
}


/* =============================================================================
   43. IBE MAP — Localisation panel card + full-screen map modal
   @partial: _Establishment.cshtml  (IBE route only)
   -----------------------------------------------------------------------------
   The search card and the hotel masthead are FULL WIDTH, as they have always been.
   An earlier revision put the search card and this map into a left rail beside the
   gallery; that was reverted, and the rail rules went with it. The map now rides in
   an ordinary .panel among Description / Conditions d'annulation, and only the card
   and the modal below are still live.
   ========================================================================== */

/* 43.3 - The map card. A real, PANNABLE Leaflet map: drag it, double-click or use
   the +/- control. Only the wheel is held back - see the view for why - so the card
   never swallows a scroll on its way past. The button floats over the pane and keeps
   its own hit area because it is painted above it (z-index below). */
/* `isolation: isolate` is load-bearing, not decoration. Leaflet's own sheet paints its panes at
   z-index 400 and its zoom control at 1000, and `position: relative` alone (z-index auto) does NOT
   open a stacking context — so those values competed in the ROOT context and sailed straight over
   the map modal, whose --z-modal is 80. Isolating the card confines them to it, and the card then
   participates in the page at its natural order like any other block. */
.ibe-maplet { position: relative; isolation: isolate;
  border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--c-border); background: var(--c-surface-2); }
/* 320px, not the 190px the rail version used: at full panel width a short pane reads as a
   letterbox strip rather than a map. Close to the 360px the original Localisation panel set
   inline, less the room the "Voir sur carte" button now floats in. */
.ibe-maplet__canvas { height: 320px; }
.ibe-maplet__canvas .leaflet-container { cursor: grab; }
.ibe-maplet__canvas .leaflet-container:active { cursor: grabbing; }
/* The whole popup is the link to the establishment sheet (_MapKit.cshtml draw()), so it is `block`
   to take the bubble's full width and keeps the body text colour — a blue underlined block would
   read as a stray link rather than as the bubble itself. The underline comes back on hover so the
   pointer still lands on something that announces itself as clickable. */
.leaflet-popup-content .cr-map-popup__link { display: block; color: inherit; text-decoration: none; }
.leaflet-popup-content .cr-map-popup__link:hover,
.leaflet-popup-content .cr-map-popup__link:focus-visible { text-decoration: underline; }
/* Centred with auto margins, NOT translateX(-50%). `transform` is one property, and the button
   already owns it: .btn--primary:hover sets translateY(-1px) (:687) and the two later re-skins
   set `none` (:1276, :1997), while .btn:active sets translateY(1px) (:177). Any of those REPLACES
   a centring transform, so the button jumped half its own width to the right on hover. Pinning
   both edges and letting the margins centre it leaves `transform` free for the lift. */
.ibe-maplet__cta { position: absolute; left: 0; right: 0; bottom: 14px;
  width: max-content; max-width: calc(100% - 2 * var(--sp-3)); margin-inline: auto;
  z-index: 500; white-space: nowrap; }   /* 500: over Leaflet panes (400), under its controls (1000) */

/* 43.4 - Full-screen map modal. Deliberately NOT §41's .msheet: that one is inside
   a max-width:760px block and is the results page's mobile furniture. This is a
   desktop-first dialog and reusing the mobile one would mean unpicking its media
   query for every page that already relies on it. */
/* var(--z-modal) with no fallback, like .modal-backdrop (:469) and §41's sheet (:2952) — the token
   is 80 (:93) and the fallback I had here was dead code that hid which value was really in play. */
.mapmodal { position: fixed; inset: 0; z-index: var(--z-modal);
  display: grid; grid-template-rows: auto 1fr; background: #fff; }
.mapmodal[hidden] { display: none; }
.mapmodal__scrim { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: -1; }
.mapmodal__head { display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--c-border); }
.mapmodal__title { margin: 0; font-size: var(--fs-lg); font-weight: 700; }
.mapmodal__close { border: 0; background: none; font-size: 26px; line-height: 1;
  cursor: pointer; color: var(--c-ink-2); padding: 4px 10px; border-radius: 999px; }
.mapmodal__close:hover { background: var(--c-surface-2); }
.mapmodal__close:focus-visible { outline: 2px solid var(--c-hero-ink); outline-offset: 2px; }
.mapmodal__body { min-height: 0; }

@media (min-width: 900px) {
  .mapmodal { inset: 4vh 4vw; border-radius: var(--r-md); overflow: hidden;
    box-shadow: 0 24px 70px rgba(0,0,0,.35); }
}

/* Same lock §41.7 uses, repeated here because that one is inside a media block. */
body.mapmodal-open { position: fixed; left: 0; right: 0; width: 100%; overflow: hidden; }

/* 43.7 - The back office's "notes tarifaires" in the masthead — .badge--promo, the one chip left
   in the flags row now that the theme pill is gone.
   @partial: _Masthead.cshtml

   IT STANDS AS TALL AS THE BUTTON. It shares the row with "Voir la description", a .btn--sm at
   32px (:186), and at its own 20px the two read as boxes of two different heights, which is the
   one thing the eye catches in a header made of chips. min-height is a literal because .btn--sm
   states one too and the two have to be the same number for the row to line up. A FLOOR, not a
   height: a note that already needs two lines or more is untouched.

   .badge is white-space:nowrap by design (right for "-10%", wrong for a sentence) and
   PromotionTitle is free text: "1er enfant - 6 ans dans la chambre des parents Gratuit" is a real
   one, and "Suite Junior ( 3 Adultes ou 2+1 ( enfant 8ans et plus) =ST double + ST single // …
   //Burkini INTERDIT" is another, at 165 characters. Left nowrap, that second one ran as ONE line
   the width of the masthead, straight under the price column, and pushed "Voir la description"
   onto a row of its own. Same escape as §18's .hotel-card__flags, which was written for this very
   field: let it wrap, cap the measure, and rely on the markup's title= for anything past the
   clamp. Scoped to .badge--promo, so the score link and the ghost button sharing the row are
   untouched.

   A MEASURE, AND NO flex-basis — this is what makes one rule serve both lengths. The note stays an
   ordinary flex item of the cluster, so it takes the width its own text needs up to the cap:
   "Early booking" is still a pill sitting next to the score, while the 165-character note grows to
   the cap and falls into three lines. And because flex breaks LINES before it shrinks items, a
   note that cannot fit beside the chips at its cap moves to a row of its own instead of being
   squeezed into a ribbon — which is also what happens to it as the window narrows.

   56ch holds the longest note this back office has produced in three lines. The clamp is one line
   more than that: a safety net for an operator who pastes a paragraph, not the normal case.

   THE PADDING IS WHAT CENTRES THE TEXT in that floor, and it has to be, because .badge's own
   align-items (:196) does not reach here. The computed display is NOT the -webkit-box declared
   below: Chrome now implements the standard `line-clamp` property, and an element carrying it is
   blockified — getComputedStyle reports `flow-root`. So neither align-items (no flex box) nor
   -webkit-box-pack (no legacy box) touches the text, and a one-line note sat 2px from the top of
   a 32px pill with 14px under it, against the button's balanced 7/8.

   Symmetric padding needs no box model at all. Half of what the floor leaves over one line, top
   and bottom: `1.35em` is this element's own line-height against its own --fs-xs, so the pair
   stays correct if either token is retuned, and a one-line note comes out at exactly the 32px
   min-height asks for. A note of two lines or more simply grows from a centred start. */
.ibe-masthead-flags > .badge--promo {
  max-width: min(100%, 56ch);
  min-height: 32px;
  padding-block: calc((32px - 1.35em) / 2);
  white-space: normal; overflow-wrap: anywhere;
  text-align: left; line-height: 1.35;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; line-clamp: 4;
  overflow: hidden;
}

/* 43.7b - The note and "Voir la description" STACK, one under the other.
   @partial: _Masthead.cshtml (.ibe-masthead-actions, the second flags row)

   Side by side they were a sentence and an action sharing a line, and the sentence decided where
   the action landed: the button sat 300px to the right on one hotel and 500px on the next,
   because the note's own length moved it. Stacked, the button is always in the same place, under
   the note, at the left edge of the column like everything else in this masthead.

   flex-direction rather than a second .stack in the markup, so the row keeps the class §43.7 and
   §43.8 select through. align-items:flex-start is what stops a column flex container stretching
   both children to the full width of the masthead — the note keeps its measure and the button its
   label's width.

   An earlier revision stretched the two to a shared height and a shared bottom edge instead. That
   is what stacking replaces, and both cannot be true at once: in a column the cross axis is the
   horizontal one, so the same `align-self: stretch` would now blow the pair out to full width. */
.ibe-masthead-actions { flex-direction: column; align-items: flex-start; }

/* 43.7c was `.ibe-masthead h1 { text-transform: uppercase }`, the hotel's name forced to capitals
   on the establishment sheet. Dropped: the name is shown in the casing the back office entered,
   here as on every other page. A hotel keyed as "AZIZA THALASSO GOLF" still prints that way,
   because that is what the record says — the difference between the two is the operator's, not
   the stylesheet's to even out. */

/* 43.8 - Masthead buttons on a phone: "Voir la description" and "Choisir ma chambre" both
   span the row.
   @partial: _Masthead.cshtml

   760px is not a new breakpoint, it is THE one: §26.3 (:936) sets `.between { flex-wrap: wrap }`
   at exactly this width, which is what makes the price column drop under the title in the first
   place. Below it the masthead is one column, so a button sized to its own label leaves a ragged
   right edge against the full-width panels underneath; above it the two columns sit side by side
   and stretching either one would tear the header apart. Hence both rules live in here and there
   is nothing to undo at desktop width.

   Two different mechanisms because the two buttons sit in two different boxes, and `width:100%`
   alone fixes neither:
     - the ghost link is a flex item of .ibe-masthead-flags (a .cluster, :156 flex-wrap: wrap), so
       it needs a 100% BASIS to claim a row of its own - otherwise it keeps sharing the line with
       the score link and the theme chip and merely overflows it. Same idiom, and the same reason,
       as the promotion badge in §43.7 above;
     - the accent link is inside .ibe-masthead__price, which is itself a wrapped flex item of
       .between and therefore only as wide as its content: stretching the button inside a column
       that narrow would change nothing, so the COLUMN takes the full row first.

   The price group above it keeps text-align:right and now lands on the true right edge of the
   page rather than the right edge of a content-sized box - the usual phone masthead: figure to
   the right, call to action across the bottom.

   NOT .btn--block (:188) in the markup: that class is unconditional and would stretch both
   buttons at desktop width too, where the masthead is still two columns. The width is a
   responsive concern, so it belongs in a media query rather than on the element.

   EVERY COLOUR BELOW IS A TOKEN, never a literal. The palette is per client: client-bridge.css
   derives the whole --c-accent-* / --c-primary-* ladder from that client's own --x-cta / --x-brand
   with color-mix(), which is why the same masthead is orange on one affiliate and terracotta on
   the next. A hex typed in here would be the one thing on the page that ignores the brand.

   THE PRICE GROUP HAS NO SURFACE, and got there in two steps worth recording so neither is
   reintroduced. It was first the accent tint (--c-accent-100 with --c-accent-700 ink, the
   .badge--promo pairing at :208) so that it and the CTA read as one offer: that put the brand
   colour in the masthead THREE times — stars, plate, button — and the CTA stopped being the thing
   the eye goes to. Dropping to a neutral --c-surface-2 fixed the colour but not the shape: a
   filled box between two full-width buttons made the header read as three stacked cards. So the
   fill went too. The buttons above and below frame the figure well enough on their own, and it
   keeps the --c-ink / --c-muted / --c-faint ramp it wears on every other page (:412-414). */
@media (max-width: 760px) {

  /* ---- Full-width buttons ---------------------------------------------------- */
  .ibe-masthead-flags > .btn { flex: 1 0 100%; }
  .ibe-masthead__price { flex: 1 0 100%; }
  /* Not `.ibe-masthead__price .btn`: that would also catch a button dropped into the price
     group later. This is the CTA, and it is a direct child. */
  .ibe-masthead__price > .btn { width: 100%; }

  /* ---- "Voir la description" ------------------------------------------------
     A tinted plate rather than the bare outline it wears on desktop: stretched across the row
     under a heading and an address, an outline-only button reads as an empty box. --c-primary-050
     / -200 are the brand's own lightest rungs, so it tints with the client rather than with a
     grey, and .btn--ghost's own --c-primary-700 label still sits on it.

     The height override undoes .btn--sm (:186). That size was chosen for a chip sharing a row with
     the score link; owning a full row it has to match the CTA's own presence, so it returns to the
     standard --control-h. .btn--sm is left on the element because it still applies above 760px. */
  .ibe-masthead-flags > .btn--ghost {
    background: var(--c-primary-050);
    border-color: var(--c-primary-200);
    height: var(--control-h);
    font-size: var(--fs-md);
  }
  /* The base hover (:183) is --c-primary-050, which is now the RESTING colour here and would make
     hovering a no-op. Both selectors weigh (0,2,0) and this block is the later one, so the
     affordance has to be restated one rung up rather than inherited. */
  .ibe-masthead-flags > .btn--ghost:hover { background: var(--c-primary-100); }
  /* A caret, because the button scrolls DOWN the page — the same ▾ (U+25BE) the spec panel's own
     toggle uses. ::after rather than a <span> in the markup so it exists only at this width;
     aria-hidden is implicit on generated content, so it stays out of the accessible name. */
  .ibe-masthead-flags > .btn--ghost::after { content: "\25BE"; font-size: .8em; }

  /* ---- The price group ------------------------------------------------------
     NO SURFACE OF ITS OWN — no fill, no radius, no inset padding. It carried a --c-surface-2
     plate for one revision and that is gone: the group already sits between two full-width
     buttons, which frame it on their own, and a third box between them made the masthead read as
     three stacked cards. What is left is pure layout.

     width:max-content centred, NOT the full row: the figure is a short string, so a full-row box
     would put "À partir de" and the rate adrift in whitespace. The cap keeps a four-figure rate
     inside the page. */
  .ibe-masthead__pricebox {
    /* The figure's size, stated once — .price-cur derives from it below, so the two keep their
       proportion whatever this is retuned to. See the sizing note further down. */
    --ibe-price-fs: calc(var(--fs-2xl) + 14px);
    width: max-content; max-width: 100%;
    margin-inline: auto;
  }
  /* The column and the figure both carry .text-right (:611) for the desktop layout. Here the text
     centres instead, over the two buttons it sits between — right-aligned content in a centred
     box reads as a mistake. Two selectors because the utility sits on the wrapper AND on the
     inner figure row. */
  .ibe-masthead__pricebox,
  .ibe-masthead__pricebox .text-right { text-align: center; }

  /* NOTHING recolours .price-now / .price-cur / .price-unit here. They keep the shared
     --c-ink / --c-muted / --c-faint of :412-414, which is the point of the revert above: the
     figure reads the same in this group as it does on a results card or in the booking rail.

     SIZE is another matter. With no surface behind it the figure is the only thing between two
     full-width buttons, so it carries the group on its own and goes up a step rather than down.
     Both rules are (0,2,0) and beat the bare .price-now they override — including the ≤560px
     shrink at :945, which is written for prices sitting inside a card and would otherwise drop
     this one to --fs-xl on exactly the phones this block is for.

     calc() off the token, not a raw 42px, and NOT --fs-3xl: agent density redefines the ladder up
     to --fs-2xl and stops (:104), so --fs-3xl would stay at its full 36px while everything around
     it shrank. Deriving from --fs-2xl keeps the figure in proportion in either mode.

     THE CURRENCY IS A RATIO OF THE FIGURE, not its own rung of the type scale. "755.977" and "DT"
     are one number read aloud as one thing, and the amount has to dominate the unit — the point
     of pairing them this way is that retuning --ibe-price-fs moves both and the hierarchy holds.
     At .58 the unit sits near the figure's cap height, which is what stops "DT" reading as a
     second word. A token would drift: --fs-xl is 22px against 42px here (too meek) but 19px
     against 31px in agent density (too loud). */
  .ibe-masthead__pricebox .price-now { font-size: var(--ibe-price-fs); }
  .ibe-masthead__pricebox .price-cur { font-size: calc(var(--ibe-price-fs) * .58); }

  /* The label sits right on top of the figure: they are one reading ("à partir de 755.977 DT"),
     and .stack's 12px (:154) opened a gap that read as two separate lines. Two classes on the
     same element to clear .stack on specificity rather than on source order. */
  .ibe-masthead__pricebox.stack > * + * { margin-top: var(--sp-1); }

  /* "à partir de" — a capital on the first letter, and no colour of its own: the .muted class in
     the markup still owns that. ::first-letter rather than text-transform:capitalize, which would
     give "À Partir De", and rather than an edit to the price_from string, which _BookingRail and
     the legacy _Establishment share (3 call sites). */
  .ibe-masthead__pricebox > .b2c-only { font-weight: 700; }
  .ibe-masthead__pricebox > .b2c-only::first-letter { text-transform: uppercase; }

  /* The stay line keeps its muted grey and gains the mockup's bed glyph. Same idiom as the 📍 on
     the address line and the 🏨 in the search card — the page already speaks in emoji. */
  .ibe-masthead__pricebox .price-unit::before { content: "\1F6CF\FE0F\00A0"; }

  /* ---- The CTA's chevron ----------------------------------------------------
     .btn is inline-flex with gap:--sp-2 (:171), so the glyph spaces itself and the pair stays
     centred as one unit in the full-width button. */
  .ibe-masthead__price > .btn--accent::after { content: "\203A"; font-size: 1.2em; line-height: 1; }
}

/* 43.6 was ".ibe-pricecta" - the masthead price rendered as one dark-red plate that was
   itself the link to #rooms. Reverted to the Maquettes layout (price group + its own
   .btn--accent button, see _Masthead.cshtml), and the rules went with it: nothing else
   ever used them, and the price group rides on .price-now / .price-cur / .price-unit
   (:412-414, restyled at :754) exactly as it does on every other page. */

/* 43.5 was ".stage-gallery__more" - the "Reste N photos" overlay on the last tile of
   §42's thumbnail strip. The strip went with the stage gallery; the mosaic's own
   "+N photos" tile is .gallery__more (§18) and needs nothing here. */


/* =============================================================================
   44. ROOM CARD PHOTO SLIDESHOW
   @partial: _HotelRoomCards.cshtml
   -----------------------------------------------------------------------------
   A room's section can carry several photos. The card stacks them all and cross-
   fades, rather than swapping one <img>'s src on a timer: a swap shows the card
   background between frames while the next file downloads, and cannot fade.

   .rmc__media img is ALREADY position:absolute; inset:0 (:1585-1586), so the
   stack costs no new layout - only the opacity switch below. Everything here is
   gated on [data-rmc-slideshow], which the view emits only when a card has more
   than one photo, so single-photo cards keep exactly the rendering they had.
   ========================================================================== */

.rmc__media[data-rmc-slideshow] .rmc__photo { opacity: 0; }
.rmc__media[data-rmc-slideshow] .rmc__photo.is-active { opacity: 1; }

/* transform is left to :1586's own transition (the hover zoom); this adds opacity
   to the same property list rather than replacing it. */
.rmc__media[data-rmc-slideshow] .rmc__photo {
  transition: transform .4s ease, opacity .6s ease;
}

/* Dots. Bottom-right so they never sit under .rmc__count (bottom-left, :1591).
   Pointer-events off: they report progress, they are not a control - the card is
   one target and a second clickable thing inside it competes with "Voir la formule". */
.rmc__dots { position: absolute; right: 12px; bottom: 12px; display: flex; gap: 5px;
  pointer-events: none; }
.rmc__dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.55);
  box-shadow: 0 0 0 1px rgba(0,0,0,.18); transition: background .3s ease, width .3s ease; }
.rmc__dot.is-active { background: #fff; width: 14px; border-radius: 3px; }

/* Someone who asked for less motion gets the first photo and nothing moving. The
   script also refuses to start its timer, so this is belt and braces for a card
   already mid-cycle when the preference changes. */
@media (prefers-reduced-motion: reduce) {
  .rmc__media[data-rmc-slideshow] .rmc__photo { transition: none; }
  .rmc__dots { display: none; }
}


/* =============================================================================
   45. REVIEW CRITERIA - per-criterion bars
   @partial: _Establishment.cshtml ("Avis clients")
   -----------------------------------------------------------------------------
   ReviewSatisfactionCriterias is a recap row per criterion, and a hotel can carry
   thirty of them. They used to render as one dot-separated line inside the score
   row's flex child, which could not wrap and ran off the right edge of the panel.

   A three-column grid instead: label, bar, mark. `auto-fit` lets it sit as two or
   three columns on a wide panel and one on a narrow one, without a media query.
   ========================================================================== */

.rev-criteria {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 8px var(--sp-5);
  margin-top: var(--sp-3);
}

/* The row itself: label takes what it needs, the bar takes the rest, the mark is
   a fixed rail so the numbers line up down the column. */
.rev-criteria__row { display: grid; grid-template-columns: minmax(0, 1fr) 72px 34px;
  align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); }

/* One line, ellipsised - the markup carries title="" so the full label stays
   reachable. Criterion names run to "Estimation globale du séjour". */
.rev-criteria__label { color: var(--c-ink-2); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

.rev-criteria__track { height: 6px; border-radius: 999px; background: var(--c-border-2);
  overflow: hidden; }
.rev-criteria__fill { display: block; height: 100%; border-radius: 999px;
  background: var(--c-accent-500); }

.rev-criteria__mark { text-align: right; font-weight: 700; color: var(--c-ink); }

@media (max-width: 760px) {
  /* One criterion per row on a phone; the bar shrinks rather than the label. */
  .rev-criteria { grid-template-columns: 1fr; }
  .rev-criteria__row { grid-template-columns: minmax(0, 1fr) 56px 30px; }
}


/* =============================================================================
   46. MASTHEAD SCORE -> REVIEWS LINK
   @partial: _Establishment.cshtml
   -----------------------------------------------------------------------------
   The score block ("9 / Excellent / 12 avis clients verifies") is an anchor to
   #reviews. It has to look exactly as it did as plain text, so the link chrome is
   undone here rather than the markup being restyled.

   The `:root` prefix is load-bearing, same trap as the price CTA: the global link
   re-skin at :1955 (`:root main a:hover { color: var(--c-brand-orange-700) }`) is
   specificity (0,2,2) and would repaint the whole block gold on hover.
   ========================================================================== */

.score-link { display: inline-flex; align-items: center; gap: var(--sp-3);
  color: inherit; text-decoration: none; border-radius: var(--r-md);
  transition: opacity .15s ease; }
.score-link__text { display: flex; flex-direction: column; }

:root .score-link:hover,
:root .score-link:focus-visible { color: inherit; text-decoration: none; }

/* The affordance: the whole block lifts slightly rather than the text changing
   colour, which would fight the muted grey of the "12 avis" line. */
:root .score-link:hover { opacity: .82; }
.score-link:focus-visible { outline: 2px solid var(--c-hero-ink); outline-offset: 3px; }


/* =============================================================================
   47. PANEL PROSE LABEL
   @partial: Views\Hotel\Establishment\_Description.cshtml
   -----------------------------------------------------------------------------
   47 also held ".panel__subtitle", a second heading level used by the "Les chambres"
   sub-section of the Description panel. That sub-section was removed - it repeated
   what the room cards above already say - and the rules went with it; nothing else
   ever used them.
   ========================================================================== */

/* 47.1 - Little label above a block of prose ("En bref" / "En détail"). Quiet on purpose:
   it names a paragraph group, it does not open a sub-section, so it gets no rule and no
   size bump - only the uppercase tracking the search card's field labels use. */
.panel__label {
  margin: var(--sp-4) 0 var(--sp-2);
  font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--c-muted);
}
.panel__label:first-of-type { margin-top: var(--sp-3); }

/* =============================================================================
   42. POPOVERS — a panel must never stretch the page
   A .popover is position:absolute and pushes nothing, but whatever hangs below
   the fold still extends the document's scroll canvas. On the search page that
   showed up as a growing band of empty white under the hero: every "Ajouter une
   chambre" made #pop-occ taller, and there is no content down there to hide it.

   Two different answers, because the panels are not the same object:

   · The autocomplete lists (destination, aéroports) keep a height cap and scroll
     inside. A scrollbar is what one expects of a long suggestion list, and their
     owners resolve them through the DOM (Flight/Index.cshtml does
     field.querySelector(".popover")), so they must stay where they are.

   · #pop-occ is taken OUT of the flow entirely: app.js placePopover() moves it to
     <body> and switches it to position:fixed, so its height can no longer reach
     the document at all — no scroll canvas, no white band, whatever the number of
     rooms. And rather than scroll inside, it lays the rooms out in COLUMNS: the
     occupancy of a search has to be readable in one glance, so a scrollbar in
     this panel is the wrong answer even when it fits.
   ========================================================================== */
.popover,
.ibe-pop--occ { max-height: min(68vh, 520px); overflow-y: auto; overscroll-behavior: contain; }

/* ---- 42.1  Floating panel ------------------------------------------------
   Positioned against the viewport by app.js; the cap above no longer applies
   because the viewport itself is now the limit. max-width keeps the two-column
   form from running past the screen edge on a narrow window. */
.popover.is-floating,
.ibe-pop.is-floating {
  position: fixed; margin: 0; max-height: none; overflow: visible;
  max-width: calc(100vw - 24px);
}

/* ---- 42.2  Occupancy panel ----------------------------------------------
   "Ajouter une chambre" / "Valider" stay pinned under the room list. Same
   #pop-occ markup on all four consumers (search, résultats, IBE, fiche
   établissement), so this covers the .ibe-pop flavour of the panel too.
   :not([hidden]) on purpose: a bare #pop-occ{display:flex} would outrank
   .popover[hidden]{display:none} on specificity and the panel could never close. */
#pop-occ:not([hidden]) { display: flex; flex-direction: column; }
#pop-occ > #occ-rooms { flex: 0 1 auto; min-height: 0; }
#pop-occ > .cluster,
#pop-occ > .field__hint { flex: 0 0 auto; }
/* Separates the pinned actions from the rows sliding under them. */
#pop-occ > .cluster { border-top: 1px solid var(--c-border); padding-top: var(--sp-3); }

/* Pinned actions. No rule above them: §42.3 now draws one between every pair of
   rooms, and a further line immediately under the last room only added weight. */
#pop-occ > .cluster { padding-top: var(--sp-3); }

/* Columns instead of a scrollbar. app.js adds .occ--cols only when the stacked
   rooms would not fit the viewport, so a 1- or 2-room search keeps the narrow
   panel it has always had; 4 rooms read as a 2x2 grid, half as tall. */
/* column-gap is 0 and the gutter is made of padding instead — the same space, but
   inside the cells rather than between them. With a real gap each cell's border
   stops at its own edge, so the §42.3 row rules came out as two stubs with a hole
   in the middle. Touching cells make one continuous line across the panel, and the
   vertical divider then sits centred in the gutter, sp-5 either side. */
#pop-occ.occ--cols > #occ-rooms {
  display: grid; grid-template-columns: repeat(2, minmax(230px, 1fr));
  column-gap: 0; align-items: start;
}
#pop-occ.occ--cols > #occ-rooms > .occ-room:nth-child(odd) {
  padding-right: var(--sp-5);
}
#pop-occ.occ--cols > #occ-rooms > .occ-room:nth-child(even) {
  padding-left: var(--sp-5); border-left: 1px solid var(--c-border);
}

/* Last resort, and only that: a viewport too short even for the columns (a very
   small laptop with 4 rooms). The list scrolls, the actions stay pinned. */
#pop-occ.is-capped > #occ-rooms {
  flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding-right: 2px;
}

/* ---- 42.3  Room separators ----------------------------------------------
   Rooms are three identical "Adultes / Enfants / Bébés" triplets stacked one on
   the next, and the .occ-room__head label is the only thing marking where one
   ends. Past two or three rooms — exactly when .occ--cols kicks in and the panel
   fills the screen — the counters read as one long list and the head gets lost
   in it. A rule makes the boundary explicit.

   Stacked: every room after the first. In columns: every room that STARTS a grid
   row (3rd, 5th, 7th …), so the two cells of a row are cut off from the row above
   by one line across the panel rather than two unrelated ones. align-items:start
   already tops-aligns the cells of a row, so the two halves stay level even when
   one room has its child-age selects open and the other does not. */
#pop-occ:not(.occ--cols) > #occ-rooms > .occ-room + .occ-room,
#pop-occ.occ--cols > #occ-rooms > .occ-room:nth-child(n+3) {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--c-border);
}

/* =============================================================================
   43. SITE CHROME — real site header + footer
   @partial: Views/Shared/Components/SiteHeader, .../SiteFooter

   Rendered only where cr.resa.Global.HeaderFooterSupported is true, i.e. where
   this funnel IS the site rather than a block embedded in an affiliate page.

   Most of the work was already done: §8 (HEADER / NAV) and §19 (FOOTER) were
   written for exactly this markup and left orphaned when the mockup's chrome was
   dropped — .site-header, .nav-bar, .brand, .main-nav, .header-phone,
   .site-footer and .site-footer__inner all still style it, and §20's print block
   already hides both bands on a voucher. This section only adds what those never
   had: a real logo IMAGE instead of the text lockup, a two-line phone block, and
   the footer payment strip.
   ========================================================================== */

/* ---- 43.1  Not sticky ------------------------------------------------------
   §8 declares .site-header sticky, but every page beneath it was retuned for a
   page with NO header: §24.2 pins .recap / .filters to `top: var(--sp-4)` and
   §24.3 sets a small [id] scroll-margin, both annotated "header removed". A
   sticky band would cover the top of every pinned panel and land on top of every
   anchored jump, on all thirteen pages. Static keeps this ticket additive.
   Making it sticky again is a real change and belongs together with those
   offsets — which is why --header-h in §1 is left alone here. */
.site-header { position: static; }

/* ---- 43.2  Brand lockup ---------------------------------------------------
   The logo is a real asset (images/official_logo.png, 666x189), so it is sized
   by HEIGHT and lets the width follow — the intrinsic width/height attributes on
   the <img> reserve the right box while it loads, so the nav bar does not jump.
   .brand in §8 is built around the text lockup (.brand__mark + .brand__name);
   its gap and font-size are harmless here, its hover rule is the one we want.

   100px IS THE DEFAULT FOR EVERY CLIENT, the same figure the stepper lockup carries
   (§36), so the branding reads identically whether HeaderFooterSupported put this
   header on the page or left the stepper to carry the mark alone.

   THE BAND GROWS WITH IT, on purpose. .nav-bar__inner declares min-height:72px, not
   height, so the logo is simply the tallest item in the row and the bar follows it.
   Nothing here is positioned against a fixed header height — §43.1 already made the
   header static, so --header-h (§1) governs no offset on these pages.

   max-height, NOT height. A client logo is whatever file the client sent: custom\logo.png
   on this deployment is 150x55, and forcing height:100px on it upscaled it 1.8x into a
   staircase-edged blur. max-height gives the same 100px to every asset that HAS the pixels
   and leaves the smaller ones at their native size, crisp — the ceiling is shared, the
   result is not a stretched thumbnail. Raising a client's logo to 100px is therefore an
   ASSET job (ship a taller file), not a CSS one.

   max-width follows the height instead of staying at 220px: the built-in lockup is
   666x189 (3.5:1), so 100px of height wants 350px of width, and the old ceiling would
   have capped it back to ~63px — the height would never have applied. With height:auto
   the pair can only ever shrink the image, so the aspect ratio survives either bound. */
.brand__logo { display: block; height: auto; width: auto; max-height: 100px; max-width: 360px; }
.brand:focus-visible { outline: 2px solid var(--c-primary-600); outline-offset: 3px; border-radius: var(--r-sm); }

/* The stepper carries the same lockup (§36), because without this header it is the
   only branding an embedded deployment gets. With the header there it is the same
   image twice, one band apart. The stepper's copy is the one that goes: the
   header's is a link home, the stepper's is decoration.

   Sibling selector rather than a body class, so it costs the thirteen pages
   nothing: .site-header only exists when the chrome rendered, and .stepper__list
   is already flex:1 and simply takes the freed width (§36 relies on the same
   behaviour when it hides the lockup below 560px). */
.site-header ~ .stepper .brand-logo,
.site-header ~ .stepper .stepper__brand { display: none; }

/* ---- 43.3  Main navigation ------------------------------------------------
   §23.7 already animates the underline in from the left on hover. The current
   section keeps it drawn: it is the only persistent "you are here" the header
   has, and it pairs with aria-current="page" in the markup. */
.main-nav a.is-active { color: var(--c-primary-700); }
.main-nav a.is-active::after { right: 0; }

/* The menu entries repeat the search engine's own tab wording, which carries a
   leading emoji ("🏨 Hôtels en Tunisie"). inline-block keeps the emoji font's
   own metrics from stretching the row. */
.main-nav a { display: inline-block; }

/* More air between the logo and the menu: §8's 16px on top of the bar's 24px gap
   read as one block with the logo. §43.7 resets it to 0 on small screens. */
.main-nav { margin-left: var(--sp-10); }

/* ---- 43.4  Phone ----------------------------------------------------------
   A phone number on a booking site is a call to action, not contact details, so
   it gets the accent icon and the heaviest weight in the bar. The label sits
   above the number rather than beside it: "Réservez par téléphone" is what makes
   a string of digits mean something, and inline it would push the number off a
   narrow bar. §8 already gives .header-phone its `margin-left: auto`. */
.header-phone { display: inline-flex; align-items: center; gap: var(--sp-2); }
.header-phone:hover { text-decoration: none; }
.header-phone__ico { display: inline-grid; place-items: center; }
.header-phone__text { display: grid; line-height: 1.15; }
.header-phone__label {
  font-size: var(--fs-xs); font-weight: 600; color: var(--c-muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.header-phone__number { font-size: var(--fs-lg); font-weight: 800; font-variant-numeric: tabular-nums; }

/* ---- 43.5  Footer credit --------------------------------------------------
   §19 makes .site-footer__inner a wrapping space-between row, so the payment
   strip takes the left and the credit the right with no extra layout here. */
.site-footer__credit { display: grid; gap: 2px; text-align: right; }
.powered-by { margin: 0; }
.powered-by__label { opacity: .75; }
.powered-by__name { font-weight: 800; color: #fff; }
.powered-by__name:hover { text-decoration: underline; }
.site-footer__copy { margin: 0; font-size: var(--fs-xs); color: rgba(255,255,255,.55); }

/* ---- 43.6  Payment marks --------------------------------------------------
   Each scheme is its own name, in its own colour, on a white chip — drawn rather
   than shipped, because the repository has no card artwork and a drawn mark stays
   sharp in print and on a retina screen. White chips on purpose: card marks are
   designed for a light ground and go muddy straight on the dark footer.

   A <ul> because it is a list of what is accepted; the bullets are dropped and
   each item is announced by its own name. */
.paystrip { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3); }
.paystrip__label {
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: rgba(255,255,255,.7);
}
.paymarks { display: flex; flex-wrap: wrap; gap: var(--sp-2); list-style: none; margin: 0; padding: 0; }
.paymark {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 28px; padding: 4px 9px;
  background: #fff; border-radius: var(--r-sm);
  box-shadow: 0 1px 2px rgba(0,0,0,.25);
}
.paymark__art { display: block; flex: none; }
.paymark__name {
  font-size: 11px; font-weight: 800; letter-spacing: .04em;
  text-transform: uppercase; white-space: nowrap; color: var(--c-ink);
}

/* Scheme colours, applied to the name only. Approximations of each brand's own
   ink — enough to be recognised at a glance, and not presented as the official
   logo lockup, which we hold no licensed asset for. */
.paymark--visa       .paymark__name { color: #1A1F71; font-style: italic; }
.paymark--mastercard .paymark__name { color: #231F20; }
.paymark--amex       .paymark__name { color: #006FCF; }
.paymark--edinar     .paymark__name { color: #00843D; }
.paymark--clictopay  .paymark__name { color: #0B4EA2; }
.paymark--paypal     .paymark__name { color: #003087; }

/* ---- 43.7  Small screens --------------------------------------------------
   §26.3 hides .main-nav below 760px, which was right when the only thing in the
   bar was a mockup menu and wrong now that it is the site's only navigation.
   Give it back the full width under the logo and let it scroll sideways rather
   than hiding it behind a toggle nobody has written: at most four entries fit,
   and a burger that opens nothing would be worse than a row that scrolls. */
@media (max-width: 760px) {
  .main-nav {
    display: flex; order: 3; width: 100%; margin-left: 0;
    gap: var(--sp-4); overflow-x: auto; -webkit-overflow-scrolling: touch;
    padding-bottom: 2px; scrollbar-width: none;
  }
  .main-nav::-webkit-scrollbar { display: none; }
  .main-nav a { white-space: nowrap; }

  .brand__logo { max-height: 36px; }

  /* The number stays; the words above it are the first thing to go. */
  .header-phone__label { display: none; }
  .header-phone__number { font-size: var(--fs-md); }

  .site-footer__inner { justify-content: flex-start; }
  .site-footer__credit { text-align: left; }
}

/* ---- 43.8  Phone below 560px ----------------------------------------------
   The number stops fitting beside the logo, and a phone number cannot be wrapped
   or truncated without becoming a different number, so the block becomes an
   icon-only call button instead. The accessible name moves onto the anchor
   (SiteChromeModel.PhoneAriaLabel), so the words are hidden from the eye and not
   from a screen reader, and 40px is a comfortable tap target.

   Same breakpoint at which §36 drops the stepper lockup — the row is down to
   essentials by here. */
@media (max-width: 560px) {
  .header-phone {
    flex: none; width: 40px; height: 40px;
    justify-content: center; border-radius: 50%;
    background: var(--c-primary-050);
  }
  .header-phone__text { display: none; }
}

/* ---- 43.9  Footer body — columns, contact, social, newsletter --------------
   The bottom bar (43.5-43.6) was the whole footer until the references were
   revisited: cte.tn, traveltodo.com and generationbooking.com all carry link
   columns, the agency's coordinates, a social row and a newsletter above it.

   Everything here sits on .site-footer's dark ground (§19), so the palette is
   the on-dark one: white headings, translucent body text, hairline rules at low
   alpha. Nothing in this block restyles a shared class. */

/* Newsletter: one line on a lighter band, so it reads as an invitation rather
   than as another row of footer links. */
.footer-news { background: rgba(255,255,255,.06); border-bottom: 1px solid rgba(255,255,255,.10); }
.footer-news__inner {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-4);
  padding-block: var(--sp-5);
}
.footer-news__title { font-weight: 700; color: #fff; }
.footer-news__form { display: flex; flex: 1 1 320px; max-width: 520px; gap: var(--sp-2); }
.footer-news__input {
  flex: 1 1 auto; min-width: 0; height: var(--control-h);
  padding: 0 var(--sp-3);
  border: 1px solid rgba(255,255,255,.25); border-radius: var(--r-sm);
  background: #fff; color: var(--c-ink); font: inherit;
}
.footer-news__input::placeholder { color: var(--c-faint); }
.footer-news__btn {
  flex: none; height: var(--control-h); padding: 0 var(--sp-5);
  border: 0; border-radius: var(--r-sm); cursor: pointer;
  background: var(--c-brand-orange, #FFA500); color: #1A1315;
  font: inherit; font-weight: 800;
}
.footer-news__btn:hover { filter: brightness(1.08); }

/* TEXT SELECTION ON THE DARK GROUND. §26.5 sets one rule for the whole page —
   ::selection { background: var(--c-primary-200); color: var(--c-primary-900) } — and
   --c-primary-900 is exactly what .site-footer paints its own background with (§19).
   Selecting the e-mail address to copy it therefore drew the text in the colour it was
   sitting on, and it disappeared.

   Fixed values rather than tokens on purpose: this pair has to stay legible whatever a
   client's palette turns the ladder into, and every rung of it is a candidate for the
   footer's background. Both spellings, because Firefox only honours the prefixed one. */
.site-footer ::selection,
.site-footer::selection { background: #FFFFFF; color: #1A1315; }
.site-footer ::-moz-selection,
.site-footer::-moz-selection { background: #FFFFFF; color: #1A1315; }

/* Columns. auto-fit so two columns do not stretch across a desktop width and
   five do not crush — the count is deployment-configured and cannot be assumed. */
.footer-body {
  display: grid; gap: var(--sp-6) var(--sp-7);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  padding-block: var(--sp-7);
}
.footer-col__title {
  margin: 0 0 var(--sp-3); padding-bottom: var(--sp-2);
  font-size: var(--fs-md); font-weight: 800; color: #fff;
  /* The short accent rule under each heading is the one ornament all three
     references share. Kept to the brand orange, on the dark ground only. */
  border-bottom: 2px solid var(--c-brand-orange, #FFA500);
  display: inline-block;
}
.footer-col__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }
.footer-col__list a { color: rgba(255,255,255,.78); font-size: var(--fs-sm); }
.footer-col__list a:hover { color: #fff; text-decoration: underline; }

/* Contact: a definition list read as « Adresse : … » — label and value on ONE
   line, the way all three references print theirs.

   Two grid columns rather than inline elements: `auto` sizes the label column to
   the longest label, so the four values line up in a column instead of starting
   at four different x positions. `1fr` lets the postal address wrap inside its
   own column, under itself and not under the label. min-width:0 on the value is
   what actually allows that wrap — a grid item defaults to min-content width and
   would otherwise push the column wider than the footer.

   The colon lives in the ui.xml label, not here: French sets a space before it
   (« Adresse : ») and English does not ("Address:"), and that is a translation
   decision, not a stylesheet one. */
.footer-contact__list {
  margin: 0; display: grid; grid-template-columns: auto 1fr;
  gap: var(--sp-2) var(--sp-2); font-size: var(--fs-sm);
}
.footer-contact__list dt {
  color: rgba(255,255,255,.55); font-weight: 700; white-space: nowrap;
}
.footer-contact__list dd { margin: 0; min-width: 0; color: rgba(255,255,255,.85); }
.footer-contact__list a { color: rgba(255,255,255,.85); }
.footer-contact__list a:hover { color: #fff; text-decoration: underline; }

/* « À propos » and « Conditions générales de vente », secondary links under the
   coordinates — each present only when the affiliate has written that text. */
.footer-contact__more {
  margin: var(--sp-4) 0 0; padding: 0; list-style: none;
  display: grid; gap: var(--sp-2); font-size: var(--fs-sm);
}
.footer-contact__more a { color: rgba(255,255,255,.78); }
.footer-contact__more a:hover { color: #fff; text-decoration: underline; }

/* Social: round targets, 36px so they are comfortable on a phone. Brand colour
   on hover only — a row of seven saturated logos would out-shout the footer. */
.footer-social {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
  padding-bottom: var(--sp-6);
}
.footer-social__title {
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: rgba(255,255,255,.7);
}
.footer-social__list { display: flex; flex-wrap: wrap; gap: var(--sp-2); list-style: none; margin: 0; padding: 0; }
.footer-social__link {
  display: inline-grid; place-items: center; width: 36px; height: 36px;
  border-radius: 50%; background: rgba(255,255,255,.10); color: #fff;
  transition: background .18s ease, transform .18s ease;
}
.footer-social__link:hover { transform: translateY(-2px); text-decoration: none; }
.footer-social__link--facebook:hover  { background: #1877F2; }
.footer-social__link--instagram:hover { background: #E1306C; }
.footer-social__link--linkedin:hover  { background: #0A66C2; }
.footer-social__link--twitter:hover   { background: #000; }
.footer-social__link--youtube:hover   { background: #FF0000; }
.footer-social__link--whatsapp:hover  { background: #25D366; }
.footer-social__link--tiktok:hover    { background: #000; }

/* Bottom bar: separated by a hairline once anything sits above it. */
.footer-bar { border-top: 1px solid rgba(255,255,255,.12); }

@media (max-width: 760px) {
  .footer-news__inner { flex-direction: column; align-items: stretch; }
  .footer-news__form { max-width: none; }
  .footer-body { gap: var(--sp-5); padding-block: var(--sp-6); }
}

/* ---- 43.10  Contact page ---------------------------------------------------
   @partial: Views/Contact/Index.cshtml

   The page « Nous contacter », modelled on cte.tn / traveltodo.com /
   generationbooking.com: a title band, a row of coordinate cards, then the form
   with the map beside it. The form itself is built from .panel / .field /
   .input / .btn, which already exist — only the four things those cannot
   express are added here. */

/* Title band. The hero's ink, without the hero's photo or its height: this page
   is a destination, not an invitation.
   color is stated on the title, not inherited: §2 gives every h1
   `color: var(--c-ink)`, which beats the white the band sets on its container
   and would leave the title dark on dark. */
.pagehead { background: var(--c-hero-ink, var(--c-primary-900)); color: #fff; }
.pagehead__inner { padding-block: var(--sp-7); }
.pagehead__title { margin: 0; font-size: var(--fs-2xl); font-weight: 800; color: #fff; }
.pagehead__sub { margin: var(--sp-2) 0 0; color: rgba(255,255,255,.78); }

/* Coordinate cards. auto-fit because a deployment with no social profiles shows
   two and one with everything shows three — neither should look under-filled. */
.infocards {
  display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  margin-bottom: var(--sp-6);
}
.infocard {
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--r-lg); padding: var(--sp-5); text-align: center;
  box-shadow: var(--shadow-sm);
}
.infocard__ico {
  display: inline-grid; place-items: center; width: 46px; height: 46px;
  border-radius: 50%; margin-bottom: var(--sp-3);
  background: var(--c-primary-050); color: var(--c-primary-700);
}
.infocard__title { margin: 0 0 var(--sp-2); font-size: var(--fs-md); font-weight: 800; }
.infocard__body { margin: 0; font-size: var(--fs-sm); color: var(--c-muted); line-height: 1.6; }
.infocard__body a { color: var(--c-primary-700); }
.infocard__link { display: inline-block; margin-top: var(--sp-3); font-size: var(--fs-sm); font-weight: 700; }
/* The social row is the footer's, re-centred on a light card: its dark-ground
   background would vanish here, so give it the tint the icons sit on. */
.infocard__social { justify-content: center; }
.infocard__social .footer-social__link { background: var(--c-primary-050); color: var(--c-primary-700); }

/* Form + map. Two columns on a desktop, the form first and wider, because that
   is what the visitor came to do; one column as soon as it stops fitting. */
.contact-layout { display: grid; gap: var(--sp-5); grid-template-columns: 1.35fr 1fr; align-items: start; }
.contact-form__intro { margin: 0 0 var(--sp-4); color: var(--c-muted); }
.contact-form__actions { display: flex; justify-content: flex-end; margin-top: var(--sp-4); }

.contact-map__frame {
  display: block; width: 100%; height: 380px;
  border: 0; border-radius: var(--r-md); background: var(--c-surface-2);
}
.contact-map__link { margin: var(--sp-3) 0 0; font-size: var(--fs-sm); }

/* A field the server rejected. Colour is not the only signal — the message above
   the form names what to fix, and aria-invalid carries it to a screen reader. */
.input.is-invalid { border-color: var(--c-danger); background: var(--c-danger-bg); }
.input.is-invalid:focus { box-shadow: 0 0 0 3px rgba(198,0,28,.20); }

/* The honeypot. NOT display:none: some bots skip hidden inputs, and the point is
   that they fill this one. Taken out of the flow and off-screen instead. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

@media (max-width: 900px) {
  .contact-layout { grid-template-columns: 1fr; }
  .contact-map__frame { height: 300px; }
}


/* =============================================================================
   48. ROOM CARD DESCRIPTION — three lines ending in "… Voir plus"
   @partial: _HotelRoomCards.cshtml   (both establishment routes)
   -----------------------------------------------------------------------------
   The room's description sits in .rmc__body under the occupancy line. It is cut
   to three lines, and the third line ends in "…" followed by a "Voir plus"
   button placed straight after the last word that fits - part of the sentence,
   not detached from it. Pressing it restores the full text and removes the
   button.

   WHERE THE CUT IS MADE. In the script, not here: CSS can clip to three lines,
   but it cannot put a clickable element right after the last visible word. The
   script trims the text word by word until text + "…" + the button fit in three
   lines, and writes the "…" itself onto the end of the trimmed text.
   -webkit-line-clamp is not used because it adds its own, non-clickable
   ellipsis - two "…" on one line.

   The max-height below is therefore only the state BEFORE the script runs: it
   keeps a long description from flashing at full length on first paint. Once the
   text is trimmed it fits inside that height anyway.

   Earlier revisions put the cut mark at the right-hand end of the line over a
   fade, then centred under the text; both read as detached from the sentence,
   and the label was for a while the bare "…" itself, which named no action.
   (§48 was before that a sticky booking rail, since removed.)
   ========================================================================== */

.rmc__desc-text {
  margin: 0; font-size: 14px; line-height: 1.55; color: var(--c-ink-2);
  max-height: calc(1.55em * 3);   /* em = this element's own 14px, so exactly 3 lines */
  overflow: hidden;
}
.rmc__desc.is-open .rmc__desc-text { max-height: none; }

/* 48.1 - The "Voir plus" button, INLINE inside the paragraph, so it flows as the
   last "word" of the third line. Same size and line-height as the text around it
   (the script measures with it in place, so a bigger label would change where the
   cut falls), and the same colour and weight as .rmc__details below so the card's
   two text actions look alike - no fill and no border, for the same reason: the
   accent belongs to the card's real CTA, not to a second reading control.

   nowrap keeps the two words together: they are one label, and a "Voir" alone at
   the end of the third line with "plus" clipped away would read as broken text. */
.rmc__desc-more {
  display: inline; padding: 0 2px;
  border: 0; background: none; cursor: pointer;
  font: inherit; font-weight: 700; white-space: nowrap;
  color: var(--c-primary-600);
}
/* The display:inline above would otherwise beat the browser's own [hidden] rule
   (author styles win over the UA sheet), leaving a "Voir plus" after every short
   description. Same per-component guard as .popover[hidden] (:341). */
.rmc__desc-more[hidden] { display: none; }
.rmc__desc-more:hover { text-decoration: underline; }
.rmc__desc-more:focus-visible { outline: 2px solid var(--c-primary-600); outline-offset: 2px; }


/* =============================================================================
   49. was "QUICK FILTERS IN TWO ROWS" — REMOVED
   @partial: Views\Hotel\Establishment\_QuickFilters.cshtml
   -----------------------------------------------------------------------------
   .ibe-quickfilters--rows turned §33's one-line strip into a two-column grid
   (labels sized to the wider label, chips in the second column) so that a second
   row headed "Pension" could line its chips up under the quick-filter chips in
   any language, with .ibe-quickfilters__row wrapping each row and a 560px rule
   dropping the label column on a phone.

   The Pension row it was built for is gone — "Type de pension", the .formule-chip
   bar on the rooms panel, was the same filter over the same plans one screen
   lower — so the strip is back to the single flex row of §33 and these rules had
   nothing left to select. Nothing else ever used them; §33's .ibe-quickfilters,
   .ibe-quickfilters__label and .qf-chip are untouched and still carry the strip.
   ========================================================================== */


/* =============================================================================
   50. FLIGHT SEARCH BAR ON A PHONE — the card uses the width it is given
   @partial: Views\Flight\List.cshtml · Views\Flight\Index.cshtml · Views\Hotel\Index.cshtml
   -----------------------------------------------------------------------------
   Under 760px §25's bar collapses to a single column (:921) and stops there, so
   every cell became a full-width row carrying one short value — "mar. 15 sept.
   2026" alone on its line, "Rechercher" a small button adrift at the left, and
   the swap circle alone in a band of nothing between the two airports. Two
   thirds of the card was empty space, which is how the panel reads on a phone.

   The grid becomes two columns and each cell states how much of it it takes:
   the airports and the passengers keep a full row (an airport label is long and
   the popover under it is full width), the two dates share one, and the CTA
   spans the width the way the IBE bar's already does (:1410). The swap button
   leaves the flow instead of splitting it: its row has no height, so the circle
   straddles the hairline between the two airports — the border it acts on —
   rather than pushing them apart.

   .search-fields__swap replaces the inline flex the four copies of this markup
   carried, for the single reason that an inline style cannot be overridden from
   here without !important. Its base rule below is that inline style, unchanged.

   A multi-city leg row is the same component with fewer cells: one date, no CTA.
   It carries no --date modifier, so its date keeps the full row it already had.
   ========================================================================== */

.search-fields__swap { display: flex; align-items: center; justify-content: center; }

@media (max-width: 760px) {
  .search-fields--flights { grid-template-columns: 1fr 1fr; }

  /* Full row by default; the exceptions below say so themselves. Listed by cell
     rather than with `> *`, so the calendar and passenger popovers — absolutely
     positioned children of this same grid — keep the container as their
     containing block instead of being re-anchored to a grid area. */
  .search-fields--flights > .search-field,
  .search-fields--flights > .search-fields__swap,
  .search-fields--flights > .search-submit { grid-column: 1 / -1; }

  /* Aller + Retour on one row. In one-way mode the return field keeps its box
     (the views hide it with visibility, not display), so the pair holds and
     nothing below it moves. */
  .search-fields--flights > .search-field--date { grid-column: auto; }

  /* No column gutter, so the two dates' border-top (§627) meet as ONE line
     across the card instead of two stubs with a hole between them. The divider
     they then need between them is the vertical hairline every other pair of
     cells has above 1080px — re-declared !important only because :631 drops it
     with !important for the stacked layout this row no longer is. */
  .search-fields--flights { column-gap: 0; }
  .search-fields--flights > .search-field--date + .search-field--date {
    border-left: 1px solid var(--c-border) !important;
  }

  .search-fields--flights > .search-submit .btn { width: 100%; }

  /* Zero-height row: the button is centred on a 0px line, which puts it on the
     border-top of the destination field. Its own white background and hairline
     border cover that line, so it reads as sitting on the divider. */
  .search-fields--flights > .search-fields__swap {
    height: 0; overflow: visible; justify-content: flex-end;
    padding-right: 10px; position: relative; z-index: 2;
  }
}


/* =============================================================================
   51. ESTABLISHMENT GALLERY — one full-width stage over a scrolling filmstrip
   @partial: Views\Hotel\Establishment\_Gallery.cshtml  (both establishment routes)
   -----------------------------------------------------------------------------
   §18 draws .gallery as a five-tile mosaic and §42 dresses its parts. Both stay
   as they are and this section is a MODIFIER on top, because .gallery img,
   .gallery__hero, .gallery__tile and .gallery__count all still do their job here
   — only the arrangement changes: one photo the width of the page, and every
   other photo in a rail under it.

   Written as `.gallery.gallery--stage` rather than `.gallery--stage` alone: one
   of the rules being overridden is `[data-skin="ibe"] .gallery` (:1696), and a
   single class would lose to it on specificity whatever its position in the file.
   ========================================================================== */

.gallery.gallery--stage {
  /* The rail's height, in one place: §51.3 sizes the thumbnails from it, §51.4
     the arrows, and the padding below reserves exactly that much room. */
  --rail-h: 84px;

  display: block;
  /* The radius moves to the two parts below: kept here it would round the outside
     of the strip as well, and clip the arrows' focus ring. */
  border-radius: 0; overflow: visible;

  /* Full width, and so is everything in it: the stage (§51.1) and the rail
     (§51.2) are both drawn to the column, so they line up by construction
     rather than by being measured against each other.

     The padding is the room the out-of-flow rail no longer claims for itself. */
  position: relative;
  width: 100%;
  padding-bottom: calc(var(--rail-h) + var(--sp-2));
}
.gallery--stage > * { overflow: visible; }

/* ---- 51.1  The stage ------------------------------------------------------
   THE FULL WIDTH OF THE COLUMN, and a height that simply follows the photo's
   own proportions. Nothing is cropped and nothing is stretched: the box is the
   photo's shape, drawn as wide as the page will allow.

   It used to be a fixed-height box whose width fell out of the photo's ratio,
   which left an unused margin either side whenever the photo was squarer than
   the box. A later pass capped the width at the photo's own naturalWidth so
   that a small file would never be enlarged; that cap is gone on purpose. Full
   width was asked for twice, and it is the trade being taken knowingly: a photo
   smaller than the column IS scaled up and will look soft. The images are
   static files with no resizing endpoint, so there is no third option — the
   pixels on disk are all the pixels there are.

   The one guard left is on the IMG, not on this box: a max-height there bounds
   a portrait shot, which would otherwise stand 1800px tall across a 1350px
   column. It letterboxes rather than crops.

   THAT GUARD IS IN PIXELS, AND IT IS DELIBERATELY GENEROUS. It was first
   written as 92dvh, which was wrong twice over: it made the photo's width
   depend on how TALL the window was, and in a short window it caught ordinary
   4:3 photos — `contain` then squeezed them well inside the column, which is
   the opposite of what this section is for. 1200px is clear of every landscape
   shape at any column width the page can offer, so only a true portrait ever
   reaches it.

   NO SURFACE OF ITS OWN: outside that one case the box is exactly the photo's
   shape, so there is nothing left over for a background to show through. */
.gallery--stage .gallery__hero {
  position: relative;
  width: 100%; height: auto;
  border-radius: var(--ibe-radius, var(--r-lg)); overflow: hidden;
  background: transparent;
}

/* Specificity, not order, is what beats `.gallery img` (§23.4, :718) — which
   would otherwise impose height: 100% and crop the photo with `cover`. */
.gallery--stage .gallery__hero img {
  display: block;
  width: 100%; height: auto;
  max-height: 1200px;
  object-fit: contain; object-position: center;
}

/* ---- 51.2  The filmstrip --------------------------------------------------
   A scroll container the full width of the stage, with an arrow laid OVER each
   end. The TRACK is what scrolls, so the two arrows stay put instead of riding
   away with the thumbnails.

   THE RAIL IS AS WIDE AS THE PHOTO. The arrows used to be flex siblings of the
   track, which left the rail the stage's width minus two arrows and two gaps —
   some 84px short — so the thumbnails no longer started and ended where the
   photo above them did. Positioned, the arrows are out of the flow and the
   track spans the whole stage. The strip is the positioning context, and a
   plain block now that it has one in-flow child left. */
.gallery--stage .gallery__strip {
  /* OUT OF FLOW, stretched across the gallery — the same column the stage above
     it fills, so the two line up by construction. Out of flow because a rail of
     twenty-seven thumbnails would otherwise impose a width of its own on the
     gallery. It is its own containing block, which is what the arrows in §51.4
     are positioned against. */
  position: absolute; left: 0; right: 0; bottom: 0;
  height: var(--rail-h);
}
.gallery--stage .gallery__track {
  display: flex; gap: var(--sp-2);
  width: 100%;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-behavior: smooth; scroll-snap-type: x proximity;
  /* The arrows and the swipe ARE the control; a scrollbar under the thumbnails
     only puts a grey band across the photos. */
  scrollbar-width: none; -ms-overflow-style: none;
}
.gallery--stage .gallery__track::-webkit-scrollbar { display: none; }

/* ---- 51.3  Thumbnails -----------------------------------------------------
   A fixed box so the rail reads as one line whatever the photos' aspect ratios
   are; .gallery img (§18) crops them into it. Dimmed until they are hovered or
   on the stage, so the current photo stays findable in a rail of twenty-seven. */
.gallery--stage .gallery__tile {
  flex: 0 0 auto; width: 132px; height: var(--rail-h);
  border-radius: var(--r-sm); overflow: hidden;
  scroll-snap-align: start;
  opacity: .62; transition: opacity .15s ease;
}
.gallery--stage .gallery__tile:hover,
.gallery--stage .gallery__tile.is-current { opacity: 1; }
/* §42.1 dims the PHOTO on hover, which is the opposite of what a filmstrip
   wants: here hovering brings the thumbnail up, not down. */
.gallery--stage .gallery__tile:hover img { opacity: 1; }
.gallery--stage .gallery__tile.is-current {
  outline: 2px solid var(--c-accent-500); outline-offset: -2px;
}

/* ---- 51.4  Arrows ---------------------------------------------------------
   They step the STAGE by one photo and the rail follows, the script scrolling
   the current thumbnail back into view. No disabled state: the stage wraps, the
   same way the timer does. Same height as the thumbnails, so the strip reads as
   one band.

   LAID OVER THE RAIL, not beside it (§51.2): that is what lets the thumbnails
   span the photo's full width. The surface is translucent so the thumbnail
   scrolling underneath stays half-visible — which is the hint that there is
   more rail that way. */
.gallery--stage .gallery__nav {
  position: absolute; top: 0; z-index: 1;
  display: grid; place-items: center;
  width: 34px; height: var(--rail-h); padding: 0;
  border: 1px solid var(--c-border); border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--c-surface) 88%, transparent);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  color: var(--c-ink-2);
  font-size: 22px; line-height: 1; font-weight: 700; cursor: pointer;
  transition: background .15s ease;
}
.gallery--stage .gallery__nav[data-gallery-step="-1"] { left: 0; }
.gallery--stage .gallery__nav[data-gallery-step="1"]  { right: 0; }
.gallery--stage .gallery__nav:hover { background: var(--c-surface-3); }
.gallery--stage .gallery__nav:focus-visible { outline: 2px solid var(--c-accent-500); outline-offset: 2px; }

/* A phone gets smaller thumbnails and narrower arrows — the stage is what it
   came for, and the rail only has to stay swipeable under it. */
@media (max-width: 560px) {
  /* One value: the thumbnails, the arrows and the room reserved below the stage
     all read --rail-h. */
  .gallery.gallery--stage { --rail-h: 62px; }
  .gallery--stage .gallery__tile { width: 92px; }
  .gallery--stage .gallery__nav { width: 28px; font-size: 18px; }
}

/* ---- 51.5  Beside the map -------------------------------------------------
   The gallery takes 2/3 of the row and the map card (.ibe-maplet, §43) the
   last 1/3, stretched to the gallery's full height, filmstrip included.
   Without coordinates the row has no --map modifier and the gallery keeps the
   whole width, exactly as above.

   THE STAGE GETS A FIXED 3:2 BOX HERE, filled with `cover`. With the map beside
   it, a stage whose height followed each photo would resize the map every six
   seconds as the timer advances. 3:2 is the shape of most hotel photography,
   so `cover` trims a sliver off a 4:3 shot rather than letterboxing it.

   AND IT IS WHAT KEEPS THE PHOTO SHARP. At 2/3 of the 1220px container the
   stage is ~800px wide, against ~1190px full width: a photo is enlarged a
   third less, and the usual 1024px hotel file is no longer enlarged at all.
   No resizing endpoint exists (§51.1), so a narrower box is the one lever. */
.gallery-row--map {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--sp-3);
  align-items: stretch;
}
.gallery-row--map .gallery--stage .gallery__hero { aspect-ratio: 3 / 2; }
.gallery-row--map .gallery--stage .gallery__hero img {
  width: 100%; height: 100%; max-height: none;
  object-fit: cover;
}
/* The canvas fills the card absolutely: the card's height comes from the grid
   row, and Leaflet needs a box with a definite height when it is created. */
.ibe-maplet--side { min-height: 0; }
.ibe-maplet--side .ibe-maplet__canvas { position: absolute; inset: 0; height: auto; }

/* Tablet and phone: the map drops under the gallery, at a fixed height. */
@media (max-width: 900px) {
  .gallery-row--map { grid-template-columns: minmax(0, 1fr); }
  .ibe-maplet--side { height: 220px; }
}

/* 52 was a masthead block written for a layout that no longer exists: ".ibe-masthead-cta", a grid
   holding "Choisir ma chambre" and "Voir la description" as a pair under the price, and an
   ".ibe-masthead-note" row for the promotion under the score. Both hooks are gone from
   _Masthead.cshtml — the ghost button moved into .ibe-masthead-flags and the note went back to
   being a chip in that same row — so the rules matched nothing.

   Removed rather than left as dead weight: its @media (max-width: 680px) half also carried
   `.ibe-masthead .text-right { text-align: left }`, which outranked §43.8's own
   `.ibe-masthead__pricebox { text-align: center }` on source order and was quietly left-aligning
   the phone price group. The note's measure now lives with the rest of its rules, in §43.7. */

/* =============================================================================
   51. SOURCE / SUPPLIER FACET - the pill now carries a NAME, not a <Source> id
   It used to read "6522" / "6522-32", so nothing could overflow a 260px sidebar.
   It now reads the supplier's name (Services\Hotel\SupplierDirectory.cs), which
   is arbitrarily long, while .badge is white-space:nowrap.

   Scoped to .filter-list on purpose: this is the only place the badge is boxed
   in. On the cards and the recaps it sits on a .between / .cluster line that can
   simply give it the room, and clipping there would hide the name the change was
   made to show.

   The row is a flex line ending in .filter-count, so the badge has to be allowed
   to shrink - min-width:0 defeats the flex min-content floor - and to clip.
   text-overflow needs a block box, and .badge is inline-flex; overriding that to
   inline-block is safe HERE and only here, because this is the one source badge
   rendered without a .badge__dot (Views\Hotel\_HotelResults.cshtml).
   ========================================================================== */
.filter-list .checkbox { min-width: 0; }
.filter-list .badge--src {
  display: inline-block;
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
}


/* =============================================================================
   52. FLIGHT RESULTS ON A PHONE — the page must not scroll sideways
   @partial: Views\Flight\_FlightCards.cshtml
   -----------------------------------------------------------------------------
   /flight/list measured 624px of document on a 412px phone. Scrolling sideways
   into that empty third is what the results read as: every band — the search
   panel, the title, the toolbar — is the viewport's width and so appears to stop
   two thirds of the way across, while the cards run on past the edge.

   TWO CAUSES, and the first is the one that turns the second into a page-wide
   defect:

   1. `.layout-2col` drops to `grid-template-columns: 1fr` under 1080px (:640).
      A plain `1fr` is `minmax(auto, 1fr)`, and that `auto` floor is the track's
      MIN-CONTENT — so a column whose contents refuse to fold does not overflow
      its own box, it widens the grid, the page and the scroll range with it.
      Here it measured 607.77px inside a 380px container. minmax(0, 1fr) is the
      same idiom §11 already uses for .search-fields (:318) and §31.4 for
      .fdet__r (:3115): the track stays the container's width whatever it holds.

   2. What refused to fold is `.flight-card__buy` (:846). Under 720px it is a
      flex ROW — badge · price · "Aller-retour · 1 voyageur" · taxes · seats left
      · the Devis form — with no flex-wrap, so its min-content is the SUM of six
      blocks, around 600px. Above 720px :851 turns it into a column and the
      question never arises, which is why this only ever showed on a phone.
      Wrapping is declared unconditionally rather than in a media block: over
      720px this is a column, where flex-wrap changes nothing.

   The cards' internal grids were never the problem — .leg__body folds to about
   260px on its own and the legs column was already well inside the viewport.
   ========================================================================== */

.flight-card__buy { flex-wrap: wrap; }

@media (max-width: 1080px) {
  .layout-2col { grid-template-columns: minmax(0, 1fr); }
}


/* =============================================================================
   53. RECENT SEARCHES - cards under the hotel search hero
   @partial: Views\Hotel\Index.cshtml (#recent-searches), filled by its inline
   script from the cr_recent_hotel_searches cookie (app.js).
   -----------------------------------------------------------------------------
   Sits INSIDE .hero__inner, over the photo: the heading is white like the
   hero's own title, the cards stay white. Indented by the search bar's padding
   so the cards line up with the fields above them. Colours are tokens only, so
   a client sheet re-skins the cards with the rest of the page. The list is an
   auto-fill grid with a min(100%, ...) floor: on a phone the cards stack at
   the container's width instead of widening the page.
   ========================================================================== */
.recent-searches { margin-top: var(--sp-6); padding-inline: var(--sp-4); }
.recent-searches__title {
  color: #fff; text-shadow: 0 2px 12px rgba(0,0,0,.35);
  font-size: var(--fs-xl); font-weight: 800; margin-bottom: var(--sp-3);
}
.recent-searches__list {
  display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
}
.recent-card {
  display: flex; align-items: stretch; min-width: 0;
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--r-md); box-shadow: var(--shadow-sm);
  overflow: hidden; color: var(--c-ink); text-decoration: none;
  transition: box-shadow .15s ease, border-color .15s ease;
}
.recent-card:hover, .recent-card:focus-visible {
  box-shadow: var(--shadow-md); border-color: var(--c-primary-500); text-decoration: none;
}
.recent-card__body {
  display: flex; flex-direction: column; gap: 4px; min-width: 0;
  padding: var(--sp-3) var(--sp-4);
}
.recent-card__title {
  font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.recent-card__meta { font-size: var(--fs-sm); color: var(--c-muted); }

/* =============================================================================
   54. HOTEL RESULTS - GALERIE TAB
   @partial: Views\Hotel\_HotelGallery.cshtml (#results-gallery), loaded and
   paged by List.cshtml ("GALERIE TAB").
   -----------------------------------------------------------------------------
   One large card per hotel, top to bottom: name + rating, a stage photo with
   its filmstrip, facilities, highlights (themes) beside a short description,
   then the rates — the list's own .rooms-panel, capped to its first three rows
   until "more options" adds .is-expanded. The list's .hotel-card is untouched.
   ========================================================================== */
/* Block, not flex: in a flex column the stage's aspect-ratio + max-height
   resolved its WIDTH from the clamped height (440 x 16/7 = 1006px), so the
   photo stopped short of the card and that width became the card's min-content. */
.gcard { display: block; min-width: 0; }
.gcard[hidden] { display: none; }
/* The results column is a `1fr` track, i.e. minmax(auto, 1fr): anything wide
   in the gallery widened the whole column past the page. min-width:0 on the
   column (the grid item) lets the track stay at its share. */
.layout-2col > :has(> #results-gallery) { min-width: 0; }
#results-gallery { min-width: 0; max-width: 100%; }

.gcard__head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: var(--sp-3); flex-wrap: wrap; padding: var(--card-pad) var(--card-pad) var(--sp-3);
}
.gcard__head-main { min-width: 0; }
.gcard__title { margin: 0 0 2px; font-size: var(--fs-xl); line-height: 1.25; }
.gcard__title a { color: inherit; text-decoration: none; }
.gcard__title a:hover { color: var(--c-primary-700); text-decoration: underline; }
.gcard__title .stars { margin-left: 6px; font-size: .8em; vertical-align: 2px; }
.gcard__head-end { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* Carousel: the establishment sheet's stage gallery (§51, .gallery--stage),
   used as is — hero, "n / N" counter, filmstrip with ‹ › over its ends. Only
   what a CARD needs differently is stated here, scoped to .gcard. */
.gcard__gallery { padding: 0 var(--card-pad); }
/* §51 reserves the rail's height under the stage; a one-photo hotel has no
   rail, so that room would only be an empty band under its photo. */
.gcard .gallery--stage:not(:has(.gallery__strip)) { padding-bottom: 0; }
/* Same once the browser is left with a single usable photo — the others
   failed to load (List.cshtml, carouselCount): no filmstrip, no arrows, no
   "n / N", and no room kept for them under the stage. */
.gcard .gallery--stage.is-single { padding-bottom: 0; }
.gcard .gallery--stage.is-single .gallery__strip,
.gcard .gallery--stage.is-single .gallery__count { display: none; }
.gcard .gallery--stage.is-single .gallery__hero[tabindex] { cursor: default; }
/* A fixed 3:2 frame, as the sheet uses beside its map (§51.5): every card the
   same height in the list, whatever the shape of its first photo. */
.gcard .gallery--stage .gallery__hero { aspect-ratio: 3 / 2; background: var(--c-surface-3); }
.gcard .gallery--stage .gallery__hero img {
  width: 100%; height: 100%; max-height: none;
  object-fit: cover;
}
.gcard__promo {
  position: absolute; top: 12px; left: 12px; z-index: 1; max-width: calc(100% - 24px);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; box-shadow: var(--shadow-sm);
}
/* A horizontal swipe is the carousel's (touch-action), a vertical one still
   scrolls the page. The photo is not draggable, so a mouse drag is a swipe. */
.gcard .gallery__hero[tabindex] { touch-action: pan-y; user-select: none; cursor: grab; }
.gcard .gallery__hero[tabindex]:active { cursor: grabbing; }
.gcard .gallery__hero:focus-visible { outline: 2px solid var(--c-accent-500); outline-offset: 2px; }
/* A thumbnail whose image failed is no longer a slide (see List.cshtml). */
.gcard .gallery__tile.is-broken { display: none; }

.gcard__body { padding: var(--sp-4) var(--card-pad) 0; display: grid; gap: var(--sp-4); }
.gcard__facilities { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.gcard__facilities-more { cursor: help; font-weight: 700; }
.gcard__cols { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.gcard__cols:empty { display: none; }
.gcard__subtitle {
  margin: 0 0 var(--sp-2); font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; color: var(--c-muted);
}
.gcard__highlights { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; font-size: var(--fs-sm); }
.gcard__highlights li::before { content: "✓"; margin-right: 6px; color: var(--c-success); font-weight: 700; }
/* Clamped: the search's Summary can be a long paragraph; the sheet has the rest. */
.gcard__summary {
  margin: 0 0 var(--sp-2); font-size: var(--fs-sm); color: var(--c-ink-2, var(--c-ink));
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}
.gcard__more-link { font-size: var(--fs-sm); font-weight: 700; }

.gcard__offers { margin-top: var(--sp-4); border-top: 1px solid var(--c-border); }
/* "À partir de <price>" on the left, "n nuits · n adultes" on the right : centred
   vertically between the rule above and the rates panel below — same padding top
   and bottom (a 0 bottom padding left the text sitting on the panel). */
.gcard__offers-head {
  display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap;
  gap: var(--sp-2); padding: var(--sp-3) var(--card-pad);
}
/* The label and the amount share one baseline, whatever their font sizes. */
.gcard__from { display: inline-flex; align-items: baseline; gap: 4px;
    font-size: var(--fs-sm); font-weight: 700; color: var(--c-muted); line-height: 1.2; }
.gcard__from .price-now { font-size: var(--fs-xl); line-height: 1; }
.gcard__offers-head .price-unit { line-height: 1.2; }
/* First three offers only, until "more options". A row the visitor ticked
   always stays visible, so a selection is never hidden from its own total. */
.gcard__offers:not(.is-expanded) .rooms-table > .rate-row:nth-child(n+4):not(:has(.rate-row__check:checked)) { display: none; }
.gcard__more-rates { display: block; margin: 0 auto var(--sp-4); }

.gallery-loading {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-3);
  min-height: 160px; color: var(--c-muted); font-weight: 700;
}
.gallery-loading__spin {
  width: 22px; height: 22px; border-radius: 50%;
  border: 3px solid var(--c-border-2); border-top-color: var(--c-primary-500);
  animation: gallery-spin .8s linear infinite;
}
@keyframes gallery-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .gallery-loading__spin { animation-duration: 2.4s; } }

/* Galerie on a phone: the fourth button of the mobile bar (§41.3), a toggle
   between the list and the gallery. Scoped to the bar that carries it, so the
   flight page's three-button bar is untouched. */
@media (max-width: 760px) {
  #m-toolbar .mtoolbar__btn.is-active {
    background: var(--c-surface); border-color: var(--c-primary-500); color: var(--c-primary-700);
  }
}
/* Four buttons on a 320-400px screen: a little less padding and type so
   "Filtrer · Trier · Carte · Galerie" stay on one line without clipping. */
@media (max-width: 420px) {
  #m-toolbar:has(#m-btn-gallery) { gap: 6px; }
  #m-toolbar:has(#m-btn-gallery) .mtoolbar__btn { padding-inline: 4px; gap: 4px; font-size: var(--fs-sm); }
}
