/* ==========================================================================
   Reset & base
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--color-ink);
  background: var(--color-bg);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul, ol { padding: 0; margin: 0; list-style: none; }
button { font-family: inherit; cursor: pointer; }
h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--color-maroon-800);
  line-height: 1.15;
  margin: 0 0 var(--space-4);
  font-weight: 600;
}
h1 { font-size: var(--fs-h1); line-height: 1.03; letter-spacing: -0.015em; font-weight: 900; }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
p { margin: 0 0 var(--space-4); }
.lead { font-size: var(--fs-lead); color: var(--color-ink-muted); }
.accent { color: var(--color-orange-700); }

/* ==========================================================================
   Layout utilities
   ========================================================================== */
.container {
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-5);
}
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
section { padding-block: var(--space-12); }
section.bg-alt { background: var(--color-bg-alt); }
section.bg-maroon { background: #f1b88833; color: var(--color-ink); }
section.bg-maroon h2, section.bg-maroon h3 { color: #3d0d14; }

/* Admissions — the pre-registration form is the page's one conversion goal,
   so it gets the maroon CTA band (defined above, otherwise unused on the
   site) instead of the flat white every other section on this page uses.
   Extra padding on top of that so it reads as the destination, not another
   row in the sequence. */
#preinscription { padding-block: var(--space-16); }
section.cta-band { background: var(--color-white); border-top: 1px solid var(--color-border); }
.section-head { max-width: 720px; margin-bottom: var(--space-8); }
.section-head.centered { margin-inline: auto; text-align: center; }
/* Un-scoped from .section-head: the same label is used standalone above
   sub-section h3s (À propos' Vision/Mission/Valeurs, Mot de la Fondatrice),
   where it was previously rendering as unstyled inline text. */
.eyebrow {
  display: inline-block;
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-orange-700);
  margin-bottom: var(--space-2);
}
/* Section glow — a soft light wash spanning two adjacent sections: fades in
   from the outer edge of each (top of the first, bottom of the second) and
   fades to nothing at the seam where they meet, so the pair reads as one
   continuous block rather than two flat-white sections stacked. */
.section-glow-top,
.section-glow-bottom { position: relative; }
.section-glow-top > .container,
.section-glow-bottom > .container { position: relative; z-index: 1; }
.section-glow-top::before,
.section-glow-bottom::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.section-glow-top::before { background: linear-gradient(to bottom, var(--color-bg-alt), transparent); }
.section-glow-bottom::before { background: linear-gradient(to top, var(--color-bg-alt), transparent); }

.grid { display: grid; gap: var(--space-6); }
/* Grid items default to min-width:auto, so a child's intrinsic content
   (a <select>'s widest <option>, an unbreakable string) can force its track
   wider than the container regardless of an explicit width:100% — the
   classic grid "blowout" overflow. Contact's form card hit this at mobile
   widths; zeroing it out is the standard fix and has no effect when content
   already fits. */
.grid > * { min-width: 0; }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  section { padding-block: var(--space-8); }
  /* Alternating text/media rows (À propos' Vision/Mission/Valeurs, the
     "Mot de la direction" close) put the media-placeholder first in the DOM
     for rhythm on desktop. Collapsed to one column on mobile, that put a
     decorative placeholder ahead of the actual heading/content in reading
     order. Force it back below its sibling instead of reflowing the desktop
     rhythm. */
  .row-media-first .media-placeholder, .row-media-first .section-photo { order: 1; }
}

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.85rem 1.6rem;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 0.95rem;
  border: 1px solid transparent;
  white-space: nowrap;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.btn-primary { background: var(--color-orange-700); color: var(--color-white); }
.btn-primary:hover { background: var(--color-orange-800); transform: translateY(-1px); }
.btn-primary:focus-visible { outline: 2px solid var(--color-maroon-800); outline-offset: 2px; }
.btn-ghost {
  background: rgba(255,255,255,0.08);
  border-color: rgba(255,255,255,0.35);
  color: inherit;
}
.btn-ghost:hover { background: rgba(255,255,255,0.16); }
.btn-outline-maroon {
  background: transparent;
  border-color: var(--color-maroon-800);
  color: var(--color-maroon-800);
}
.btn-outline-maroon:hover { background: var(--color-maroon-800); color: var(--color-white); }
.btn-light {
  background: var(--color-bg-alt);
  color: var(--color-ink);
  border-color: var(--color-border);
}
.btn-light:hover { background: var(--color-border); }

/* ==========================================================================
   Cards
   ========================================================================== */
.card {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  box-shadow: var(--shadow-card);
}
.card h3 { margin-bottom: var(--space-2); }
.icon-square {
  width: 52px; height: 52px;
  border-radius: var(--radius-sm);
  background: var(--color-maroon-800);
  color: var(--color-orange-500);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: var(--space-4);
}
.icon-square.light { background: var(--color-orange-100); color: var(--color-orange-700); }
.icon-square svg { display: block; width: 26px; height: 26px; }

/* ==========================================================================
   Pillar scroll — "Pourquoi choisir Excel ?" as a scrollytelling strip.
   The illustration pins in a sticky frame while the three text steps scroll
   past beside it; components.js swaps the active illustration/step via
   IntersectionObserver as each step crosses the viewport's center band.
   Deliberately not a 3-up icon card grid: a numbered outline mark stands in
   for the icon-square so this section reads differently from the Card
   pattern used elsewhere on the page.
   ========================================================================== */
.pillars-heading h2 { font-size: clamp(2.1rem, 1.7rem + 1.6vw, 3rem); }
.pillars-scroll-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-10); align-items: start; }
.pillars-visual-sticky {
  position: sticky;
  /* top MUST stay small/near-zero, not viewport-centered — .pillars-heading
     sits in normal flow directly above this grid, in the same scrolling
     column. A small top offset means a large amount of scrolling is needed
     before this element's natural position crosses the threshold, which is
     exactly what gives the heading time to scroll fully out of view first.
     A tried-and-reverted top:50vh crossed that threshold far earlier in the
     scroll — while the heading was still on screen — and the two visibly
     collided. Keep this close to the header-clearance value only. */
  top: var(--header-h, 116px);
  /* No height of its own previously — the frame pinned near the top of the
     ~1008px-tall text column and a large empty block sat below it for
     virtually the whole scroll (the original complaint). A near-full-
     viewport height was tried and reverted too: it ate so much of the row's
     height budget that the sticky range (row height minus wrapper height)
     shrank to a sliver and the photo scrolled off almost immediately. This
     height is deliberately modest — enough to flex-center the frame with a
     comfortable, even margin, while still leaving most of the row's height
     as scroll room for the pin to hold through steps 1 and 2. */
  height: min(56vh, 560px);
  display: flex;
  align-items: center;
  justify-content: center;
}
.pillars-visual-frame { position: relative; width: 100%; max-width: 560px; border-radius: var(--radius-md); overflow: hidden; aspect-ratio: 480 / 320; box-shadow: var(--shadow-card); }
.pillar-visual-media {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  object-fit: cover;
  opacity: 0; transform: scale(0.98);
  transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.pillar-visual-media.active { opacity: 1; transform: scale(1); }
/* Mobile/tablet-only per-step illustration (see below 900px block): a plain,
   always-visible image directly inside each step, since the sticky-pinned
   shared illustration above has no "beside" column to pin against once the
   layout stacks, and position:sticky inside a single-item grid row behaves
   inconsistently across browsers, overlapping the text under it. Hidden on
   desktop, where the scrollytelling illustration is still used instead. */
.pillar-step-visual { display: none; margin-bottom: var(--space-5); }
.pillar-step-visual img { display: block; width: 100%; height: 100%; object-fit: cover; }

.pillars-text-track { display: flex; flex-direction: column; }
.pillar-step { min-height: 42vh; display: flex; flex-direction: column; justify-content: center; opacity: 0.35; transition: opacity 0.4s ease; }
.pillar-step.is-active { opacity: 1; }
.pillar-index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  border: 1.5px solid var(--color-maroon-800);
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--color-maroon-800);
  margin-bottom: var(--space-4);
}
@media (max-width: 900px) {
  .pillars-scroll-grid { grid-template-columns: 1fr; gap: var(--space-2); }
  /* Swap the scrollytelling widget for a plain stacked layout: drop the
     sticky shared illustration and the scroll-linked dimming/pacing that
     existed only to synchronize with it, show each step's own illustration
     instead (see .pillar-step-visual above), and let steps size to their
     content rather than reserving a viewport-height's worth of scroll room. */
  .pillars-visual-sticky { display: none; }
  .pillar-step-visual { display: block; }
  .pillar-step {
    min-height: 0;
    opacity: 1;
    padding-block: var(--space-8);
    border-top: 1px solid var(--color-border);
  }
  .pillar-step:first-child { padding-block-start: 0; border-top: none; }
  .pillar-step:last-child { padding-block-end: 0; min-height: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .pillar-visual-media { transition: opacity 0.15s ease; transform: none; }
  .pillar-step { transition: none; }
}

/* ==========================================================================
   Step tabs — click-driven numbered list (left) + fading detail panel
   (right), used for the Admissions procedure. A shorter, linear sibling of
   the pillars-scroll pattern above: same numbered-circle-plus-connector idea
   and the same panel cross-fade technique, but switched by click instead of
   scroll since this list sits lower on the page and describes a process the
   visitor steps through deliberately rather than scrolls past.

   Desktop (≥768px, d-md-block): Bootstrap Tabs (nav-pills + tab-content),
   .step-tab/.step-panel keep their original look, cross-fade reproduced by
   overriding Bootstrap's tab-pane display toggling below (scoped to
   .steps-detail so it doesn't affect the separate mobile accordion markup).
   Mobile (<768px, d-md-none): a separate Bootstrap Accordion tree (own
   duplicated content, see admissions.html) replaces the old DOM-relocation
   hack — Bootstrap's Tab component assumes one static DOM location per pane,
   which the old relocate-on-resize trick fought against. */
.steps-interactive { display: grid; grid-template-columns: minmax(260px, 340px) 1fr; gap: var(--space-8); align-items: start; }
.steps-tabs { position: relative; }
.steps-tabs::before {
  content: "";
  position: absolute;
  left: 21px;
  top: 26px;
  bottom: 26px;
  width: 2px;
  background: var(--color-border);
}
.step-tab {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  text-align: left;
  padding: var(--space-3);
  margin-inline: calc(-1 * var(--space-3));
  width: calc(100% + (var(--space-3) * 2));
  cursor: pointer;
  transition: background 0.15s ease;
}
.step-tab:hover { background: var(--color-bg-alt); }
.step-tab-num {
  flex-shrink: 0;
  width: 44px; height: 44px;
  border-radius: 50%;
  border: 1.5px solid var(--color-maroon-800);
  color: var(--color-maroon-800);
  background: var(--color-bg);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-weight: 600;
  transition: background 0.2s ease, color 0.2s ease;
}
.step-tab-label { display: flex; flex-direction: column; gap: 4px; padding-top: var(--space-2); }
.step-tab-label strong { font-size: 1.05rem; color: var(--color-ink-muted); font-weight: 600; transition: color 0.2s ease; }
.step-tab-label span { font-size: 0.92rem; color: var(--color-ink-muted); }
.step-tab.active .step-tab-num { background: var(--color-maroon-800); color: var(--color-white); }
.step-tab.active .step-tab-label strong { color: var(--color-maroon-800); }
.step-tab:hover .step-tab-num { border-color: var(--color-orange-600); }

/* Chevron — the affordance cue that these rows are clickable, not a static
   list. Hidden until hover (mouse) or always shown on the active step (a
   touch-friendly signal, since hover doesn't exist on phones). */
.step-tab-chevron {
  flex-shrink: 0;
  align-self: center;
  margin-left: auto;
  color: var(--color-maroon-800);
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.step-tab-chevron svg { display: block; width: 18px; height: 18px; }
.step-tab:hover .step-tab-chevron,
.step-tab.active .step-tab-chevron { opacity: 1; transform: none; }

.steps-detail {
  position: relative;
  /* Raised from 350px so the step photo can be meaningfully larger without
     the panel feeling packed — the media no longer stretches edge to edge,
     it sits at a fixed size with clear space above and below it. */
  min-height: 400px;
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-8);
}
/* Scoped to .steps-detail (the desktop tab-content box) rather than the bare
   .step-panel class, since that class also appears — with a totally
   different, non-absolute layout — inside the mobile accordion's
   .accordion-body. This overrides Bootstrap's own .tab-pane display:none/
   block toggling (same 0-2-0 specificity as Bootstrap's ".tab-content >
   .tab-pane", so base.css loading after bootstrap.min.css wins) so every
   pane stays absolutely stacked and cross-fades via opacity, exactly like
   the old (pre-Bootstrap) implementation — only the .active class (now
   toggled by Bootstrap's Tab JS instead of custom code) drives the swap. */
.steps-detail .step-panel {
  position: absolute;
  inset: var(--space-8);
  display: flex;
  flex-direction: column;
  justify-content: center;
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
  transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1), transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.steps-detail .step-panel.active { opacity: 1; transform: none; pointer-events: auto; }
.step-panel-inner { position: relative; display: flex; align-items: center; height: 100%; }
.step-panel-text { flex: 1; min-width: 0; padding-right: calc(240px + var(--space-5) + var(--space-6)); }
/* Reverted 2026-08-23 (Joseph's call) from the fixed 320x240 landscape 4:3
   block back to a taller portrait frame — accepted trade-off: the 4:3
   landscape source photos get cropped tighter than the wide-block version.
   Fixed height (not a top/bottom stretch) and centred via transform so the
   gap above and below is always equal regardless of panel height. */
.step-panel-media {
  position: absolute;
  top: 50%;
  right: var(--space-5);
  transform: translateY(-50%);
  width: 240px;
  height: 320px;
}
/* Real step photos. The parent is a fixed 4:3 block, so the image takes its
   frame from the panel rather than from its own aspect ratio — cover, not
   contain, or portrait and 16/9 sources would size the block differently from
   one tab to the next. */
.step-panel-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-md);
}
.step-panel-num {
  display: block;
  font-family: var(--font-display);
  font-size: 2.75rem;
  font-weight: 600;
  color: var(--color-orange-100);
  line-height: 1;
  margin-bottom: var(--space-3);
}
.step-panel h3 { margin-bottom: var(--space-3); }
.step-panel p { margin: 0; color: var(--color-ink-muted); max-width: 42ch; }
@media (prefers-reduced-motion: reduce) {
  .steps-detail .step-panel { transition: none; transform: none; }
  .step-tab-chevron { transition: opacity 0.15s ease; transform: none; }
}

/* ==========================================================================
   Step accordion — mobile-only (<768px) sibling of the step-tabs above.
   Its own duplicated markup (see admissions.html) rather than the old
   relocate-the-DOM-node trick, since Bootstrap's Accordion (like its Tabs)
   expects a fixed structure. Reuses .step-tab-num/.step-panel-photo for a
   consistent look; the rest is themed through the --bs-accordion-* remap
   in bootstrap-theme.css plus these few layout-only overrides.
   ========================================================================== */
#steps-accordion .accordion-item { border-color: var(--color-border); }
#steps-accordion .accordion-button {
  font-family: var(--font-display);
  font-weight: 600;
  gap: var(--space-4);
}
#steps-accordion .accordion-button .step-tab-num { margin: 0; }
#steps-accordion .accordion-button:not(.collapsed) .step-tab-num {
  background: var(--color-maroon-800);
  color: var(--color-white);
}
#steps-accordion .accordion-body { color: var(--color-ink-muted); }
#steps-accordion .accordion-body .step-panel-num { display: block; margin-bottom: var(--space-2); }
#steps-accordion .accordion-body .step-panel-photo { width: 100%; max-width: 240px; height: 180px; margin-top: var(--space-4); }

/* ==========================================================================
   Badge pill (eyebrow-as-pill, used in heroes)
   ========================================================================== */
.badge-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--color-orange-100);
  color: var(--color-orange-700);
  font-size: var(--fs-small);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: var(--space-2) var(--space-4);
  border-radius: 999px;
  margin-bottom: var(--space-5);
}
/* Hero eyebrow, on dark hero imagery: the chip shape is dropped in favor of
   plain text with a thin underline, per live devtools preview on a-propos. */
.badge-pill.on-dark {
  background: rgba(255, 255, 255, 0);
  color: rgba(255, 255, 255, 0.88);
  padding: 0 0 var(--space-2);
  border-radius: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.26);
}

/* ==========================================================================
   Placeholder media block (stand-in for real photos until assets arrive)
   ========================================================================== */
.media-placeholder {
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  text-align: center;
  color: var(--color-ink-muted);
  font-size: var(--fs-small);
  min-height: 220px;
  padding: var(--space-6);
}
.media-placeholder .placeholder-icon {
  width: 56px; height: 56px;
  border-radius: 50%;
  background: var(--color-maroon-800);
  color: var(--color-orange-300);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.media-placeholder .placeholder-icon svg { display: block; width: 26px; height: 26px; }
.media-frame { position: relative; }
.floating-card {
  position: absolute;
  left: var(--space-5);
  bottom: calc(-1 * var(--space-5));
  background: var(--color-maroon-800);
  color: var(--color-white);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-raised);
  font-size: 0.9rem;
  line-height: 1.4;
}
.floating-card strong { display: block; font-family: var(--font-display); font-size: 1.05rem; }
.floating-card .tag { color: var(--color-orange-500); font-weight: 700; font-size: 0.75rem; letter-spacing: 0.05em; text-transform: uppercase; }

/* ==========================================================================
   Story visual — overlapping arch + circle photo composition with a dashed
   accent ring, used once for "Notre histoire" so that section reads as a
   distinct visual moment rather than repeating the single flat
   .media-placeholder pattern used elsewhere on this page.
   ========================================================================== */
.story-visual-wrap { max-width: 380px; margin-inline: auto; }
.story-visual { position: relative; padding: var(--space-3) var(--space-8) var(--space-10) 0; }
.story-visual-ring {
  position: absolute;
  inset: 0 calc(var(--space-8) - var(--space-3)) var(--space-10) 0;
  border: 2px dashed var(--color-orange-300);
  border-radius: 50% 50% var(--radius-lg) var(--radius-lg) / 34% 34% 10% 10%;
}
.story-visual-main {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  min-height: 0;
  object-fit: cover;
  border-radius: 50% 50% var(--radius-md) var(--radius-md) / 34% 34% 8% 8%;
}
.story-visual-accent {
  position: absolute;
  z-index: 2;
  right: 0;
  bottom: var(--space-6);
  width: 46%;
  aspect-ratio: 1 / 1;
  min-height: 0;
  object-fit: cover;
  border-radius: 50%;
  border: 5px solid var(--color-bg);
  box-shadow: var(--shadow-raised);
}
.story-visual-badge { left: auto; right: calc(-1 * var(--space-3)); bottom: var(--space-3); z-index: 3; }
.story-visual-wrap .illustration-caption { margin-top: var(--space-6); }
/* Real photo slots reused 3x below for Vision/Mission/Valeurs, replacing the
   flat .media-placeholder pattern once real photography exists for a slot. */
.section-photo {
  display: block;
  height: 100%;
  min-height: 260px;
  object-fit: cover;
  border-radius: var(--radius-md);
}
@media (max-width: 640px) {
  .story-visual-wrap { max-width: 300px; }
  .story-visual-badge { right: 0; }
}

/* ==========================================================================
   Site header / nav
   ========================================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
}

/* Topbar — thin utility strip above the main nav */
.topbar {
  background: var(--color-white);
  color: var(--color-ink);
  font-size: 0.82rem;
  border-bottom: 1px solid var(--color-border);
  transition: background-color 0.35s ease, border-color 0.35s ease, color 0.35s ease;
}
.topbar .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-2);
}
.topbar-left { display: flex; align-items: center; gap: var(--space-3); white-space: nowrap; }
.topbar-left .sep { opacity: 0.4; }
.topbar-right { display: flex; align-items: center; gap: var(--space-5); }
.topbar-link { color: var(--color-ink); font-weight: 500; white-space: nowrap; transition: color 0.35s ease; }
.topbar-link:hover, .topbar-link.active { color: var(--color-orange-600); }

.lang-switch { position: relative; }
.lang-toggle {
  background: none; border: none; color: var(--color-ink); font-size: 0.82rem;
  display: flex; align-items: center; gap: var(--space-2); padding: 0;
  transition: color 0.35s ease;
}
.lang-toggle:hover { color: var(--color-orange-600); }
.lang-toggle .chevron { display: block; width: 10px; height: 10px; transition: transform 0.15s ease; }
.lang-toggle[aria-expanded="true"] .chevron { transform: rotate(180deg); }
.topbar-left .pin-icon, .topbar-link .pin-icon { display: inline-block; width: 13px; height: 13px; vertical-align: -1px; margin-right: 3px; }
.lang-menu {
  position: absolute; right: 0; top: calc(100% + 10px);
  background: var(--color-white); border: 1px solid var(--color-border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-card);
  min-width: 150px; z-index: 80; overflow: hidden;
}
.lang-menu[hidden] { display: none; }
.lang-menu button {
  width: 100%; text-align: left; padding: var(--space-3) var(--space-4);
  background: none; border: none; color: var(--color-ink); font-size: 0.88rem;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
}
.lang-menu button:hover { background: var(--color-bg-alt); }
.lang-menu button:disabled {
  color: var(--color-ink-muted);
  cursor: not-allowed;
}
.lang-menu button:disabled:hover { background: none; }
.lang-menu .soon-tag {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-orange-700);
  background: var(--color-orange-100);
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
}

/* Navbar — main logo + navigation, dark maroon per brand update */
.navbar { background: var(--color-maroon-900); transition: background-color 0.35s ease; }
.navbar .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding-block: var(--space-3);
}
.logo-lockup {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.15rem;
  color: var(--color-white);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  white-space: nowrap;
  flex-shrink: 0;
}
/* Main navbar only (.logo-lockup is reused, unscoped, in the footer and in
   admin.html's login card / dashboard topbar — those keep the plain rule
   above). Logo and nav-cta become mirrored flex items here (equal
   flex-basis:0, equal flex-grow) so they always end up the SAME final width
   regardless of their own (very different) content widths — that's what
   pins nav-links exactly on the bar's true center instead of just the
   midpoint between logo and cta. Each still packs its own content to its
   outer edge (logo left, cta right), so the logo's on-screen position never
   shifts. */
.navbar .logo-lockup { flex: 1 1 0%; justify-content: flex-start; }
.logo-lockup .mark {
  width: 36px; height: 36px;
  object-fit: contain;
  flex-shrink: 0;
  /* Bootstrap ships a `.mark` utility class (text-highlight helper: padding +
     background-color + color, same idea as the <mark> element) that this
     image's own unrelated `.mark` class name collides with — reset the three
     properties Bootstrap adds so the logo doesn't get a highlight box/padding. */
  padding: 0;
  background-color: transparent;
  color: inherit;
}
/* Header uses the full validated lockup (icon + wordmark + tagline, on a
   transparent ground designed for the maroon navbar) in place of the old
   icon-only mark + separate text span — sized taller to keep the tagline
   legible instead of shrinking the whole lockup to icon-mark height. */
.navbar .logo-lockup .mark { height: 56px; width: auto; }
/* .nav-links is now also the Bootstrap collapse target (id="nav-links",
   classes .collapse.navbar-collapse.nav-links all on one element — see
   components.js renderHeader()) instead of a separate node wrapped by one.
   Bootstrap's Tab/Collapse JS toggles .show (not the old .open) and, below
   the navbar-expand-lg breakpoint, would normally set display:none when
   closed — overridden below so the original transform-driven slide (not
   Bootstrap's height-collapse animation) still drives the open/close. */
.nav-links { display: flex; flex: 0 0 auto; gap: var(--space-5); align-items: center; flex-wrap: nowrap; }
/* Tighten link spacing only in the row layout (desktop) to keep .logo-lockup
   and .nav-cta's mirrored flex-basis:0 sizing comfortably clear of each
   other at the narrower end of the lg range — the off-canvas mobile list
   below keeps the roomier var(--space-5) gap since it isn't fighting for
   horizontal room. clamp() instead of one fixed value: the safety margin is
   tightest right at 992px (where .navbar .logo-lockup/.nav-cta's mirrored
   flex-basis:0 sizing has the least slack), so the gap scales down smoothly
   to 1rem there, then eases back up to the original 1.5rem by 1200px, where
   Bootstrap's own container steps from 960px to 1140px and the extra ~180px
   of room makes the tighter gap unnecessary. */
@media (min-width: 992px) {
  .nav-links { gap: clamp(1rem, -1.39rem + 3.85vw, 1.5rem); }
}
.nav-links a { font-size: 0.92rem; font-weight: 500; color: rgba(255,255,255,0.85); white-space: nowrap; }
.nav-links a:hover, .nav-links a.active { color: var(--color-orange-300); }
.nav-cta { display: flex; flex: 1 1 0%; justify-content: flex-end; gap: var(--space-4); align-items: center; }
/* Below 640px the full wordmark + Préinscription button + hamburger don't
   fit on one row (both .logo-lockup and .nav-cta refuse to shrink below
   their own content's min-content width — the default flex min-width:auto
   floor applies regardless of flex-shrink, since neither sets overflow —
   so the overflow used to spill off-screen and take the menu button with
   it, a real bug, not the documented off-canvas-panel non-issue). Shrink
   the lockup image itself, the standard responsive-logo pattern, instead
   of shrinking the CTA or hamburger. */
@media (max-width: 640px) {
  .navbar .logo-lockup .mark { height: 42px; }
  /* The CTA's full padding/font-size (sized for body copy) makes the navbar
     row noticeably thicker than the icon-only logo and hamburger next to it
     on mobile — shrink just this instance rather than every .btn site-wide. */
  .nav-cta .btn { padding: 0.55rem 1.1rem; font-size: 0.85rem; }
}
/* 44x44 hit target (mobile touch-target minimum) around a visually
   unchanged 24px icon, via padding rather than enlarging the glyph.
   display:flex is unconditional (not media-scoped) since Bootstrap's own
   .navbar-expand-lg .navbar-toggler{display:none} rule — more specific,
   media-scoped to >=992px — already hides it above the breakpoint; below
   that nothing overrides this flex, so the button shows exactly when
   Bootstrap's own breakpoint says it should. */
.nav-toggle { display: flex; align-items: center; justify-content: center; background: none; border: none; color: var(--color-white); padding: 10px; margin: -10px; }
.nav-toggle svg { display: block; width: 24px; height: 24px; }

/* 991.98px (not 980px) deliberately matches Bootstrap's own navbar-expand-lg
   cutoff (992px) exactly, so there's no dead zone where the toggler and the
   off-canvas panel disagree about which breakpoint state they're in. */
@media (max-width: 991.98px) {
  .topbar-left .sep + span { display: none; }
  #nav-links.collapse:not(.show) {
    /* Stay in flow (not Bootstrap's display:none) so the transform below can
       animate — matches the pre-Bootstrap behavior exactly. */
    display: flex;
  }
  #nav-links.collapsing {
    /* Bootstrap's Collapse JS animates height via inline style by default;
       neutralized so the transform/visibility transition below is the only
       thing that animates (the site's original slide, not a height-collapse). */
    height: auto !important;
    overflow: visible !important;
    transition: none !important;
  }
  .nav-links {
    /* .site-header is `position: sticky`, which makes it the containing block for
       fixed-position descendants — so `bottom: 0` here would resolve against the
       header's own (short) box, not the viewport. Use explicit vh-based height instead.
       --header-h is kept in sync with the header's real height by components.js,
       so this doesn't drift out of sync (and leave a gap) if header content changes. */
    position: fixed; top: var(--header-h, 116px); left: 0; right: 0;
    height: calc(100vh - var(--header-h, 116px));
    background: var(--color-white);
    flex-direction: column; align-items: flex-start; padding: var(--space-6);
    gap: var(--space-5); transform: translateY(-110%); visibility: hidden;
    transition: transform 0.25s ease, visibility 0s linear 0.25s;
    overflow-y: auto; border-top: 1px solid var(--color-border);
  }
  /* The off-canvas panel is its own white card, independent of the navbar
     strip's dark background above it — reset link color back to dark so
     it stays readable against the panel's white surface. */
  .nav-links a { color: var(--color-ink); }
  .nav-links a:hover, .nav-links a.active { color: var(--color-orange-600); }
  /* -110% is relative to the panel's OWN (viewport-dependent) height, not the
     fixed 116px top offset, so on some viewport heights the translated box's
     bottom edge doesn't clear y:0 — a sliver stays in the layout box and
     paints over the topbar/navbar even while "closed". visibility:hidden
     (delayed so the slide-up transition still plays) removes it from paint
     regardless of what the transform math works out to. Bootstrap's Collapse
     JS toggles .show (was .open pre-Bootstrap). */
  .nav-links.show { transform: translateY(0); visibility: visible; transition: transform 0.25s ease; }
}
@media (max-width: 640px) {
  .topbar-right .topbar-link { display: none; }
}

/* ==========================================================================
   Header — overlay variant (hero pages)
   Pages that open on a dark .page-hero (body.has-hero-header) float the
   header over that hero with no fill so the hero image/color reads edge to
   edge, then swap in the normal topbar/navbar colors once the hero has
   scrolled past — components.js toggles .is-scrolled on #site-header past a
   small scroll threshold. Pages without a hero (accueil, espace parents,
   admin) keep the plain sticky/opaque header untouched below.
   ========================================================================== */
body.has-hero-header .site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  width: 100%;
}
body.has-hero-header .site-header .topbar,
body.has-hero-header .site-header .navbar {
  background: transparent;
}
body.has-hero-header .site-header .topbar { border-bottom-color: transparent; }
body.has-hero-header .site-header .topbar,
body.has-hero-header .site-header .topbar-link,
body.has-hero-header .site-header .lang-toggle {
  color: rgba(255, 255, 255, 0.92);
}
body.has-hero-header .site-header .topbar-link:hover,
body.has-hero-header .site-header .topbar-link.active {
  color: var(--color-orange-300);
}
/* Scrolled: fill the header back in with its normal opaque colors. */
body.has-hero-header .site-header.is-scrolled .topbar {
  background: var(--color-white);
  border-bottom-color: var(--color-border);
  color: var(--color-ink);
}
body.has-hero-header .site-header.is-scrolled .topbar-link,
body.has-hero-header .site-header.is-scrolled .lang-toggle {
  color: var(--color-ink);
}
body.has-hero-header .site-header.is-scrolled .topbar-link:hover,
body.has-hero-header .site-header.is-scrolled .topbar-link.active {
  color: var(--color-orange-600);
}
body.has-hero-header .site-header.is-scrolled .navbar {
  background: var(--color-maroon-900);
}
/* The header is out of flow (fixed) on these pages, so the hero — the only
   section that sits behind it — carries its own top offset instead of a
   body-wide padding-top; every section after it just follows in normal flow
   underneath the now-opaque header. */
body.has-hero-header main > .page-hero:first-child {
  padding-top: calc(var(--header-h, 116px) + var(--space-12));
}

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer { background: #141414; color: rgba(255,255,255,0.82); }
.site-footer a { color: rgba(255,255,255,0.82); }
.site-footer a:hover { color: var(--color-orange-500); }
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: var(--space-8); padding-block: var(--space-10); }
.footer-grid > * { min-width: 0; }
.footer-grid h4 { color: var(--color-white); font-family: var(--font-body); font-size: 0.95rem; letter-spacing: 0.04em; text-transform: uppercase; margin-bottom: var(--space-3); }
.footer-grid ul li { margin-bottom: var(--space-2); font-size: 0.92rem; }
.footer-bottom { border-top: 1px solid rgba(255,255,255,0.12); padding-block: var(--space-4); display: flex; justify-content: space-between; font-size: var(--fs-small); color: rgba(255,255,255,0.55); }
@media (max-width: 880px) {
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-bottom { flex-direction: column; gap: var(--space-2); text-align: center; }
}
@media (max-width: 480px) {
  .footer-grid { grid-template-columns: 1fr; gap: var(--space-6); }
}

/* ==========================================================================
   WhatsApp floating button
   ========================================================================== */
.whatsapp-fab {
  position: fixed;
  bottom: var(--space-5);
  right: var(--space-5);
  width: 58px; height: 58px;
  border-radius: 50%;
  background: #25D366;
  color: var(--color-white);
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-raised);
  z-index: 60;
  transition: transform 0.15s ease, bottom 0.2s ease;
}
.whatsapp-fab svg { display: block; width: 28px; height: 28px; }
.whatsapp-fab:hover { transform: scale(1.06); }
/* Cookie banner is also fixed-bottom on mobile; when both are visible on a short
   viewport they can overlap, so the FAB steps up while the banner is shown. */
.whatsapp-fab.banner-visible { bottom: calc(var(--space-5) + 96px); }
@media (min-width: 641px) {
  .whatsapp-fab.banner-visible { bottom: var(--space-5); }
}

/* ==========================================================================
   Forms
   ========================================================================== */
.form-field { margin-bottom: var(--space-4); }
.form-field label { display: block; font-size: 0.9rem; font-weight: 600; margin-bottom: var(--space-2); color: var(--color-maroon-800); }
.form-field input, .form-field select, .form-field textarea,
.form-control, .form-select {
  width: 100%;
  padding: 0.75rem 0.9rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  font-family: inherit;
  font-size: 0.95rem;
  background-color: var(--color-white);
}
.form-field input:focus, .form-field select:focus, .form-field textarea:focus,
.form-control:focus, .form-select:focus {
  outline: 2px solid var(--color-orange-700);
  outline-offset: 1px;
  border-color: var(--color-border);
  box-shadow: none;
}
.form-note { font-size: var(--fs-small); color: var(--color-ink-muted); margin-top: var(--space-2); }

.contact-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex-shrink: 0;
  color: var(--color-orange-700);
  vertical-align: -5px;
  margin-right: var(--space-2);
}
.contact-icon svg { display: block; width: 16px; height: 16px; }

/* ==========================================================================
   Cookie / GDPR banner
   ========================================================================== */
.cookie-banner {
  position: fixed;
  left: var(--space-4); right: var(--space-4); bottom: var(--space-4);
  max-width: 640px;
  margin-inline: auto;
  background: var(--color-maroon-900);
  color: var(--color-white);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  box-shadow: var(--shadow-raised);
  z-index: 70;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: center;
  justify-content: space-between;
}
.cookie-banner p { margin: 0; font-size: 0.9rem; color: rgba(255,255,255,0.85); }
.cookie-banner[hidden] { display: none; }

/* ==========================================================================
   Prototype / mockup badge
   ========================================================================== */
.proto-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--color-orange-100);
  color: var(--color-orange-700);
  border: 1px solid var(--color-orange-600);
  font-size: var(--fs-small);
  font-weight: 600;
  padding: var(--space-2) var(--space-4);
  border-radius: 999px;
  margin-bottom: var(--space-4);
}

/* ==========================================================================
   Hero — light variant (Accueil)
   ========================================================================== */
.hero {
  background: var(--color-bg-alt);
  padding-block: var(--space-12) var(--space-16);
  overflow: clip;
}
/* Was CSS Grid (.grid-2, 1fr 1.08fr) with a bespoke mobile-collapse rule;
   now Bootstrap's .row/.col-12.col-md-6 handles both the split and the
   stacking natively, so only the vertical-centering + gutter-width tweaks
   need to survive the conversion. */
.hero .row { align-items: center; --bs-gutter-x: 3rem; }
.hero-copy { margin-top: calc(-1 * var(--space-4)); }
.hero-actions { display: flex; gap: var(--space-4); margin-top: var(--space-4); flex-wrap: wrap; }
.hero h1 { color: var(--color-ink); }
.hero h1 .accent { color: var(--color-orange-700); }

/* Media visual — the illustration + floating card, with a color panel
   offset behind it for layered depth (the composition device that stands
   in for a photo-frame treatment until real photography exists). */
.media-visual { position: relative; z-index: 0; aspect-ratio: 4 / 3; }
.media-visual::before {
  content: "";
  position: absolute;
  top: 22px; left: 22px; right: -22px; bottom: -22px;
  background: var(--color-maroon-800);
  border-radius: var(--radius-lg);
  z-index: -1;
}
@media (max-width: 640px) {
  .media-visual::before { top: 14px; left: 14px; right: -14px; bottom: -14px; }
}

/* Hero slideshow — now Bootstrap's Carousel (.carousel-fade), which owns the
   absolute-stack + opacity crossfade at the .carousel-item level (themed in
   bootstrap-theme.css) instead of on the <img> itself; this just makes the
   carousel and its images fill the aspect-ratio box set on .media-visual. */
#hero-carousel, #hero-carousel .carousel-inner, #hero-carousel .carousel-item { height: 100%; }
img.hero-slide { display: block; width: 100%; height: 100%; object-fit: cover; }

.media-frame { text-align: center; }
.media-frame .badge-pill { margin: var(--space-4) 0 0; }

/* Hero entrance — a single deliberate reveal on load, not a scroll-triggered
   gate. Content is visible by default (animation only shifts easing/position),
   so it never depends on IntersectionObserver firing or a tab being foregrounded. */
.hero-copy > * { animation: hero-rise 0.6s cubic-bezier(0.16, 1, 0.3, 1) both; }
.hero-copy > *:nth-child(1) { animation-delay: 0s; }
.hero-copy > *:nth-child(2) { animation-delay: 0.06s; }
.hero-copy > *:nth-child(3) { animation-delay: 0.12s; }
.hero-copy > *:nth-child(4) { animation-delay: 0.18s; }
.hero .media-frame { animation: hero-fade 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.1s both; }
@keyframes hero-rise {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes hero-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-copy > *, .hero .media-frame { animation: none; }
}

/* ==========================================================================
   Scroll reveal — sections below the hero rise in as they enter view,
   staggered via the --reveal-delay custom property set per element.
   ========================================================================== */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1) var(--reveal-delay, 0s),
              transform 0.6s cubic-bezier(0.16, 1, 0.3, 1) var(--reveal-delay, 0s);
}
.reveal.in-view { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* ==========================================================================
   Brand illustration — real photography for hero/welcome slots (previously
   interim SVG motifs, replaced 2026-07-10). Aspect ratio + object-position
   are set per-instance inline since each photo's native framing differs.
   ========================================================================== */
.brand-illustration {
  border-radius: var(--radius-md);
  overflow: hidden;
  display: block;
  width: 100%;
  height: auto;
}
.illustration-caption {
  margin-top: var(--space-3);
  font-size: var(--fs-small);
  color: var(--color-ink-muted);
  text-align: center;
}
/* The hero's floating-card hangs below the image edge by design (see the
   Elevation section in DESIGN.md); its box-shadow blur reaches further still,
   so the caption underneath needs extra clearance or the card's shadow
   overlaps its text. */
.hero .illustration-caption { margin-top: var(--space-10); }

/* ==========================================================================
   Trust strip — compact secondary reassurance points, deliberately lighter
   weight than the primary pillar cards above so the two sections read as a
   hierarchy rather than two competing "why choose us" pitches.
   ========================================================================== */
.trust-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  padding-block: var(--space-6);
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
}
.trust-strip-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  flex: 1 1 220px;
}
.trust-strip-item .glyph {
  flex-shrink: 0;
  width: 36px; height: 36px;
  border-radius: var(--radius-sm);
  background: var(--color-orange-100);
  color: var(--color-orange-700);
  display: flex; align-items: center; justify-content: center;
}
.trust-strip-item .glyph svg { display: block; width: 19px; height: 19px; }
.trust-strip-item h4 {
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-ink);
  margin: 0 0 0.2rem;
}
.trust-strip-item p {
  margin: 0;
  font-size: 0.88rem;
  color: var(--color-ink-muted);
}
@media (max-width: 640px) {
  .trust-strip { flex-direction: column; gap: var(--space-4); }
  /* flex: 1 1 220px's basis is main-axis; once flex-direction flips to
     column above, that 220px becomes a min HEIGHT per item (plus grow
     stretching it further), leaving a big empty gap under each item's
     one line of text. Reset to content-sized now that main axis is vertical. */
  .trust-strip-item { flex: 0 1 auto; }
}

/* ==========================================================================
   Fees table — per-level pricing table with a filled maroon header band
   (adapted from a reference Joseph shared), a compact stat row for the
   secondary one-off fees, and a callout card for the CTA. Values are
   deliberately "Sur demande" rather than invented FCFA amounts: the brief
   never supplied real tuition figures, so this keeps the structured,
   professional look of the reference without fabricating pricing.
   ========================================================================== */
.fees-table-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow-x: auto;
  overflow-y: hidden;
  box-shadow: var(--shadow-card);
}
.fees-table { width: 100%; border-collapse: collapse; }
.fees-table thead th, .fees-table tbody td { white-space: nowrap; }
.fees-table thead th {
  background: var(--color-maroon-800);
  color: var(--color-white);
  text-align: left;
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: var(--space-4) var(--space-6);
}
.fees-table tbody td {
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--color-border);
  font-size: 0.95rem;
}
.fees-table tbody tr:last-child td { border-bottom: none; }
.fees-table tbody tr:nth-child(even) { background: var(--color-bg-alt); }
.fees-table tbody td:first-child { font-weight: 600; color: var(--color-maroon-800); }
.fees-table tbody td:last-child { color: var(--color-ink-muted); }

.fees-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  margin-top: var(--space-6);
}
.fees-stat { display: flex; align-items: center; gap: var(--space-3); flex: 1 1 240px; }
.fees-stat-dot { flex-shrink: 0; width: 10px; height: 10px; border-radius: 50%; }
.fees-stat strong { display: block; font-size: 0.92rem; }
.fees-stat span { display: block; font-size: 0.85rem; color: var(--color-ink-muted); }

.fees-callout {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-top: var(--space-8);
  flex-wrap: wrap;
}
.fees-callout .icon-square { margin-bottom: 0; }
.fees-callout-copy { flex: 1 1 320px; }
.fees-callout-copy h4 { margin: 0 0 0.2rem; }
.fees-callout-copy p { margin: 0; color: var(--color-ink-muted); font-size: 0.92rem; }

.fees-callout-payments h4 { margin: 0 0 0.3rem; }
.fees-callout-payments > p { margin: 0 0 var(--space-5); color: var(--color-ink-muted); font-size: 0.92rem; }
.payment-methods-row { display: flex; flex-wrap: wrap; gap: var(--space-6); align-items: center; }
.payment-method { display: flex; align-items: center; gap: var(--space-3); }
.payment-logo { height: 40px; width: auto; max-width: 130px; object-fit: contain; }
.payment-method span { font-weight: 600; color: var(--color-maroon-800); font-size: 0.9rem; white-space: nowrap; }
@media (max-width: 640px) {
  .payment-methods-row { gap: var(--space-4); }
  .payment-logo { height: 32px; }
}
@media (max-width: 640px) {
  .fees-table thead th, .fees-table tbody td { padding: var(--space-3); }
  /* Level names ("Primaire — CM2") were wrapping to 2 lines at narrow widths
     while "Sur demande" stayed on 1, leaving the row's price cell stranded
     with awkward empty space below it and rows of uneven height. Tightening
     the padding above closes most of the gap; nowrap on the level column
     finishes it now that both columns' content fits the available width. */
  .fees-table tbody td:first-child { white-space: nowrap; }
  .fees-callout { flex-direction: column; align-items: flex-start; }
}

/* ==========================================================================
   Check grid — 2x2 confidence checklist paired with the value-intro
   illustration. A filled circular check badge, deliberately round (not the
   icon-square) so it reads as its own component rather than a card repeat.
   ========================================================================== */
.check-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4) var(--space-6);
  margin-top: var(--space-6);
}
/* Single-column variant: a 2x2 grid would leave an odd item dangling for
   the 3-item Vision/Mission checklists on À propos. Row gap opened up past
   the 2-column default so the checkmarks read as distinct claims, not a
   packed list. */
.check-grid--stacked { grid-template-columns: 1fr; gap: var(--space-5); }
/* Shared row rhythm for À propos' alternating text/image rows (Vision,
   Mission, Valeurs): centers the two columns and spaces peer rows apart
   without every row repeating the same inline style. */
.guide-row { align-items: stretch; }
.guide-row + .guide-row { margin-top: var(--space-10); }
/* Paragraphs have margin-bottom only (see `p` rule above), so a paragraph
   sitting directly after the checklist (Vision/Valeurs on À propos) needs
   its own top spacing — otherwise it sits flush against the last check-item. */
.check-grid + p { margin-top: var(--space-6); }
.copy-right .value-summary { margin-left: auto; }
.check-item { display: flex; align-items: center; gap: var(--space-3); font-weight: 600; color: var(--color-ink); font-size: 0.95rem; }
.check-glyph {
  flex-shrink: 0;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: var(--color-orange-600);
  color: var(--color-white);
  display: flex; align-items: center; justify-content: center;
}
.check-glyph svg { display: block; width: 13px; height: 13px; }
/* Larger badge for check-items that carry a distinct pictogram per item
   (À propos' Vision/Mission) instead of a repeated checkmark — the extra
   detail needs more room than the 13px checkmark did. */
.check-glyph--icon { width: 28px; height: 28px; }
.check-glyph--icon svg { width: 16px; height: 16px; }
/* Right-aligned text column variant — used on À propos' Vision and Valeurs
   rows so the copy reads flush against the edge nearest the paired photo,
   instead of hugging the far page edge with a wide gap before the image. */
.copy-right { text-align: right; }
/* row-reverse so the checkmark glyph itself sits nearest the photo (main-start
   in a reversed row is the right edge), with its label trailing away from it —
   not just the whole icon+label unit pushed right with the icon still leftmost. */
.copy-right .check-item { flex-direction: row-reverse; justify-content: flex-start; text-align: right; }
.value-intro-media { margin-top: var(--space-10); }
@media (max-width: 640px) {
  .check-grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Schedule card — dark maroon card pairing an illustration with a day's
   routine; reuses the CTA/page-hero dark-band language (see the Maroon
   Shadow / One Warm Note rules in DESIGN.md) instead of inventing a new
   surface treatment for this one section.
   ========================================================================== */
.schedule-card { display: flex; flex-direction: column; justify-content: center; background: var(--color-maroon-800); color: var(--color-white); border-radius: var(--radius-md); padding: var(--space-8); box-shadow: var(--shadow-raised); }
.schedule-card h3 { color: var(--color-white); margin-bottom: var(--space-2); }
.schedule-card > p { color: rgba(255, 255, 255, 0.75); margin-bottom: var(--space-6); }
.schedule-row { display: flex; align-items: center; justify-content: space-between; padding-block: var(--space-3); border-bottom: 1px solid rgba(255, 255, 255, 0.14); font-size: 0.95rem; }
.schedule-row:last-child { border-bottom: none; }
.schedule-row .time { color: var(--color-orange-300); font-weight: 600; }
@media (max-width: 640px) {
  .schedule-card { padding: var(--space-6); }
}

/* ==========================================================================
   Hero — dark variant (interior page headers)
   ========================================================================== */
.page-hero {
  background: var(--color-maroon-800);
  color: var(--color-white);
  padding-block: var(--space-12) var(--space-10);
}
.page-hero h1 { color: var(--color-white); margin-bottom: var(--space-2); }
.page-hero h1 .accent { color: #EAA813; }
.page-hero p { color: rgba(255,255,255,0.75); margin: 0; max-width: 56ch; }

/* Hero — image variant: brand illustration backdrop, flat dark scrim keeps
   text contrast holds over the photo */
.page-hero--image { position: relative; overflow: hidden; }
.page-hero-bg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; object-fit: cover; object-position: center 60%; }
.page-hero-overlay {
  position: absolute;
  inset: 0;
  background: #000000b0;
}
.page-hero--image .container { position: relative; z-index: 2; }

/* ==========================================================================
   Tag row — inline pill list for a centered statement block (Méthodes
   philosophy intro). A lighter alternative to check-grid, which is already
   used on Accueil and À propos — this section is a single manifesto
   statement, not a two-column text+visual block, so it gets its own shape.
   ========================================================================== */
.tag-row { display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: center; margin-top: var(--space-6); }
.tag-row span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border);
  color: var(--color-maroon-800);
  font-weight: 600;
  font-size: 0.88rem;
  padding: var(--space-2) var(--space-4);
  border-radius: 999px;
}
.tag-row svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--color-orange-700); }

/* ==========================================================================
   Compare table — Méthodes methodology section. Argues the pedagogy case
   directly (classic approach vs. Excel's, row by row) instead of another
   icon-card grid like the ones already used elsewhere on the site.
   ========================================================================== */
.compare-table-wrap { position: relative; overflow-x: auto; border-radius: var(--radius-md); box-shadow: var(--shadow-card); }
.compare-table { width: 100%; border-collapse: collapse; min-width: 560px; background: var(--color-white); }
.compare-table th, .compare-table td { padding: var(--space-4) var(--space-5); text-align: left; font-size: 0.95rem; border-bottom: 1px solid var(--color-border); vertical-align: top; }
.compare-table thead th { font-family: var(--font-body); font-size: var(--fs-small); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-ink-muted); }
.compare-table thead th.highlight { color: var(--color-white); background: var(--color-maroon-800); }
.compare-table tbody th { font-weight: 600; color: var(--color-maroon-800); white-space: nowrap; }
.compare-table tbody td { color: var(--color-ink-muted); }
.compare-table tbody td.highlight { color: var(--color-ink); font-weight: 600; background: var(--color-orange-100); }
.compare-table tbody tr:last-child th, .compare-table tbody tr:last-child td { border-bottom: none; }
/* Row-tracking tint on hover — not implying the row is clickable, just
   helping the eye hold a line across two data columns while comparing. */
.compare-table tbody tr:hover th, .compare-table tbody tr:hover td { background-color: var(--color-bg-alt); }
.compare-table tbody tr:hover td.highlight { background-color: #F6D9B8; }
.compare-check { display: inline-flex; vertical-align: -3px; margin-right: var(--space-2); width: 15px; height: 15px; color: var(--color-orange-700); }
/* Below 640px the table becomes a per-criterion card stack instead of a
   horizontally-scrolling table. The scroll approach buried "Notre approche à
   Excel" — the actual pitch — off-screen behind a fade hint that most
   visitors on a phone would never act on: the neutral "classique" column was
   the default view, and the highlighted answer only appeared after a
   horizontal swipe most people don't think to make. Stacking removes the
   scroll entirely and reorders each card so the highlighted Excel answer
   renders immediately under its criterion, ahead of the classic-approach
   line rather than hidden past it. */
@media (max-width: 640px) {
  .compare-table-wrap { overflow-x: visible; box-shadow: none; border-radius: 0; }
  .compare-table-wrap::after { display: none; }
  .compare-table { display: block; min-width: 0; border-collapse: separate; border-spacing: 0; }
  .compare-table thead {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }
  .compare-table tbody { display: block; }
  .compare-table tr {
    display: flex;
    flex-direction: column;
    background: var(--color-white);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    margin-bottom: var(--space-4);
  }
  .compare-table tr:last-child { margin-bottom: 0; }
  .compare-table th, .compare-table td { padding: 0; border-bottom: none; }
  .compare-table tbody th {
    order: -2;
    white-space: normal;
    font-family: var(--font-display);
    font-size: 1.05rem;
    margin-bottom: var(--space-3);
  }
  .compare-table td::before {
    display: block;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    margin-bottom: 4px;
  }
  .compare-table td:not(.highlight) { margin-top: var(--space-3); }
  .compare-table td:not(.highlight)::before { content: "Approche classique"; color: var(--color-ink-muted); opacity: 0.75; }
  .compare-table td.highlight {
    order: -1;
    border-radius: var(--radius-sm);
    padding: var(--space-3);
  }
  .compare-table td.highlight::before { content: "Notre approche à Excel"; color: var(--color-orange-700); }
}

/* ==========================================================================
   Tool row list — Méthodes digital tools section. A divided list (icon-chip
   + heading + description per row) instead of a card grid, paired with a
   device illustration, so this section reads as one showcase rather than a
   repeat of the icon-square card pattern.
   ========================================================================== */
/* Tools grid — the digital-tools section gives its text column more width
   than the illustration (col-md-5/col-md-7): the tool-list is the section's
   real content, the illustration is decorative support, so the ratio
   reflects that instead of treating them as equal partners. Column widths
   now come from the col-md-5/col-md-7 classes in markup (§4 grid recipe);
   this rule just keeps the gutter/vertical-alignment tweaks. */
.tools-grid { --bs-gutter-x: 4rem; align-items: stretch; }
.tools-photo { width: 100%; height: 100%; min-height: 320px; object-fit: cover; border-radius: var(--radius-md); box-shadow: var(--shadow-card); }
@media (max-width: 640px) {
  .tools-photo { min-height: 260px; }
}

.tool-list { display: flex; flex-direction: column; margin-top: var(--space-6); }
.tool-row { display: flex; align-items: flex-start; gap: var(--space-4); padding-block: var(--space-5); border-bottom: 1px solid var(--color-border); }
.tool-row:first-child { padding-top: 0; }
.tool-row:last-child { border-bottom: none; padding-bottom: 0; }
.tool-row .icon-square { margin-bottom: 0; flex-shrink: 0; }
.tool-row h4 { margin: 0 0 0.2rem; font-family: var(--font-body); font-size: 1rem; color: var(--color-ink); }
.tool-row p { margin: 0; font-size: 0.9rem; color: var(--color-ink-muted); }

/* ==========================================================================
   Activities carousel — Méthodes practical-activities section. A horizontal
   scroll-snap strip, the site's first sideways-scrolling section: this
   content is a flat list of six short activity types, better scanned in a
   row than forced into a column grid like every other section on the site.
   Bold flat-color chips (no icon, no border/card-shadow) rather than the
   white icon-square cards used for "digital tools" above — a reference
   Joseph shared used oversized colorful label chips for a specialty-program
   list, so this section borrows that energy: the color and type carry the
   section instead of an icon set.
   ========================================================================== */
.activities-head { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-8); align-items: end; margin-bottom: var(--space-8); }
.activities-head h2 { margin-bottom: 0; }
.activities-head .lead { margin: 0; }
@media (max-width: 760px) {
  .activities-head { grid-template-columns: 1fr; align-items: start; gap: var(--space-4); }
}

.activities-scroll {
  display: flex;
  gap: var(--space-5);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-3);
  scrollbar-width: thin;
  scrollbar-color: var(--color-orange-300) transparent;
}
.activity-card {
  scroll-snap-align: start;
  flex: 0 0 210px;
  min-height: 170px;
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5);
  display: flex;
  flex-direction: column;
  /* Bottom-aligned, not centered: headings run 1-2 lines depending on label
     length, and centering let each card's content float to a different
     vertical offset, breaking the row's shared baseline (the reported bug).
     Anchoring to the bottom keeps that shared baseline AND matches the photo
     variant below, where the label has to sit on the scrim — so a row that
     mixes sourced and not-yet-sourced cards still lines up. */
  justify-content: flex-end;
  gap: var(--space-2);
}
.activity-card h4 { margin: 0; font-family: var(--font-display); font-size: 1.2rem; color: var(--color-maroon-800); }
.activity-card p { margin: 0; font-size: 0.85rem; color: var(--color-ink-muted); }
.activity-card.tint-a { background: var(--color-orange-100); }
.activity-card.tint-b { background: var(--color-maroon-100); }
.activity-card.tint-c { background: var(--color-gold-100); }

/* Photo variant. Deliberately a MODIFIER rather than a redefinition of
   .activity-card: only one of the six activities has a photograph so far, and
   the cards still waiting on one keep the flat-chip look instead of sitting
   empty — so the section can ship part-sourced without looking broken. The
   tint-* fill stays underneath as the loading/failure state. */
.activity-card.has-photo {
  position: relative;
  overflow: hidden;
  flex-basis: 240px;
  min-height: 300px;
  padding: var(--space-5);
}
.activity-card-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
/* Scrim. Weighted opaque enough at the bottom that white type clears AA over
   ANY photograph: the worst case is a blown-out white region sitting directly
   under the label, and even that measures 13.5:1 for the h4 and 10.9:1 for the
   description. New photos can be dropped in without per-image contrast tuning. */
.activity-card.has-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(
    to top,
    rgb(38 38 38 / 87%) 0%,
    rgb(0 0 0 / 74%) 32%,
    rgba(61, 13, 20, 0.24) 62%,
    rgba(61, 13, 20, 0.04) 100%
  );
}
.activity-card.has-photo h4,
.activity-card.has-photo p { position: relative; z-index: 2; }
.activity-card.has-photo h4 { color: var(--color-white); }
.activity-card.has-photo p { color: rgba(255, 255, 255, 0.86); }
.activity-card.has-photo:hover .activity-card-media { transform: scale(1.05); }
@media (prefers-reduced-motion: reduce) {
  .activity-card-media,
  .activity-card.has-photo:hover .activity-card-media { transition: none; transform: none; }
}
@media (max-width: 640px) {
  .activity-card { flex-basis: 190px; min-height: 160px; }
  .activity-card.has-photo { flex-basis: 210px; min-height: 260px; }
}

/* ==========================================================================
   Support card — dark inset panel for the Méthodes differentiated-pedagogy
   section, reusing schedule-card's dark-surface language for a plain list
   of commitments. Deliberately NOT a third numbered-step component — that
   shape is already used for Accueil's pillars-scroll and Admissions' steps.
   ========================================================================== */
.support-card { background: var(--color-maroon-800); color: var(--color-white); border-radius: var(--radius-md); padding: var(--space-8); box-shadow: var(--shadow-raised); }
.support-card h3 { color: var(--color-white); }
.support-card > p { color: rgba(255,255,255,0.75); }
.support-list { display: flex; flex-direction: column; gap: var(--space-4); margin-top: var(--space-6); }
.support-list-item { display: flex; align-items: flex-start; gap: var(--space-3); }
.support-list-item .dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: 50%; background: var(--color-orange-300); margin-top: 8px; }
.support-list-item span { font-size: 0.92rem; color: rgba(255,255,255,0.9); }
@media (max-width: 640px) {
  .support-card { padding: var(--space-6); }
}

/* ==========================================================================
   Dual CTA — Méthodes closing section. Two side-by-side choice cards instead
   of the single full-bleed maroon band used for Admissions' pre-registration
   form: this page has two equally-weighted next steps, not one conversion
   goal, so it shouldn't borrow that page's "destination" signal.
   ========================================================================== */
.dual-cta { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-6); }
/* Own grid, not the shared .grid class, so it needs its own min-width:0 —
   without it the longer CTA label ("Découvrir l'équipe →", nowrap by
   default on .btn) forces the track wider than its container at mobile
   widths, pushing the card past the viewport edge. */
.dual-cta > * { min-width: 0; }
.dual-cta-card { background: var(--color-white); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-8); box-shadow: var(--shadow-card); text-align: center; }
.dual-cta-card .icon-square { margin-inline: auto; }
.dual-cta-card h3 { margin-bottom: var(--space-2); }
.dual-cta-card p { color: var(--color-ink-muted); margin-bottom: var(--space-6); }
@media (max-width: 640px) {
  .dual-cta { grid-template-columns: 1fr; }
  /* Sizing the track correctly (above) isn't enough on its own: the button
     text still can't wrap by default, so it would overflow the now
     correctly-sized card instead. Let it wrap here specifically rather than
     lifting .btn's nowrap sitewide, which exists to keep short labels intact
     everywhere else. */
  .dual-cta-card .btn { white-space: normal; text-align: center; }
}

/* ==========================================================================
   Role card — Équipe page. The school hasn't opened yet, so real staff
   names/photos/bios don't exist (same reasoning as the anonymous "Mot de la
   direction" placeholder on À propos: honest placeholder, not an invented
   identity). Instead of fabricating team members, each card presents a
   recruited-for role with its real qualification bar — still delivers the
   brief's "diplomas / experience / specialties" content, without pretending
   a specific hired person exists yet.
   ========================================================================== */
.transparency-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  max-width: 720px;
  margin: 0 auto var(--space-10);
  padding: var(--space-5) var(--space-6);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.transparency-note .icon-square { margin-bottom: 0; flex-shrink: 0; }
.transparency-note p { margin: 0; color: var(--color-ink-muted); font-size: 0.92rem; }
@media (max-width: 640px) { .transparency-note { flex-direction: column; align-items: flex-start; } }

.role-card { background: var(--color-white); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-6); box-shadow: var(--shadow-card); text-align: center; }
.role-avatar { width: 72px; height: 72px; margin: 0 auto var(--space-4); }
.role-avatar svg { width: 30px; height: 30px; }
.role-card .badge-pill { margin-bottom: var(--space-3); }
.role-card h3 { margin-bottom: var(--space-2); }
.role-card > p { color: var(--color-ink-muted); font-size: 0.92rem; margin-bottom: var(--space-5); }
.role-meta { display: flex; flex-direction: column; gap: var(--space-3); text-align: left; border-top: 1px solid var(--color-border); padding-top: var(--space-5); margin: 0; }
.role-meta div { display: flex; flex-direction: column; gap: 2px; }
.role-meta dt { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-orange-700); margin: 0; }

/* ==========================================================================
   Team card — photo-topped role card (équipe): tag pill over the photo,
   title/description body, qualification bar pinned to the bottom so cards
   of uneven copy length still line up. Photos are the same tasteful stock
   stand-ins disclosed in .transparency-note above the grid, not invented
   staff portraits.
   ========================================================================== */
.team-card { background: var(--color-white); border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; box-shadow: var(--shadow-card); display: flex; flex-direction: column; transition: transform 0.2s ease, box-shadow 0.2s ease; }
.team-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-raised); }
.team-card-media { position: relative; aspect-ratio: 4 / 3; overflow: hidden; background: var(--color-bg-alt); }
.team-card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.team-card-avatar {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  background:
    radial-gradient(circle at 30% 22%, rgba(140, 46, 66, 0.08), transparent 55%),
    var(--color-bg-alt);
}
.team-card-avatar-badge {
  width: 30%; aspect-ratio: 1 / 1;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(145deg, var(--color-maroon-700), var(--color-orange-600));
  box-shadow: var(--shadow-card);
}
.team-card-avatar-badge svg { width: 52%; height: 52%; color: var(--color-white); }
.team-card-tag {
  position: absolute; top: var(--space-4); left: var(--space-4);
  background: var(--color-orange-700); color: var(--color-white);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  padding: var(--space-2) var(--space-4); border-radius: 999px;
}
.team-card-body { padding: var(--space-5) var(--space-6) var(--space-6); display: flex; flex-direction: column; flex: 1; text-align: left; }
.team-card-body .badge-pill { margin-bottom: var(--space-3); }
.team-card-body h3 { margin: 0 0 var(--space-3); padding-bottom: var(--space-3); border-bottom: 1px solid var(--color-border); font-size: 1.15rem; }
.team-card-body > p { color: var(--color-ink-muted); font-size: 0.92rem; margin: 0 0 var(--space-4); }
.team-card-body .role-meta { margin-top: auto; padding-top: var(--space-4); }
.role-meta dd { font-size: 0.9rem; color: var(--color-ink); margin: 0; }

/* ==========================================================================
   Activity showcase — Vie scolaire extracurricular section. A centered,
   wrapping row of fixed-width cards (3 per row, the 5th wraps to a centered
   row of its own), each card built from a photo on top and a text block
   pinned to the bottom, with one card
   highlighted in solid terracotta the way the references highlight one
   featured offering. Replaces an earlier day-by-day timetable-style grid —
   dropping explicit days/times removes the main thing that made it readable
   as the school's real (not-yet-existing) schedule, and the .proto-badge +
   dashed footnote below still say so explicitly regardless.
   ========================================================================== */
.activity-showcase-wrap { margin-top: var(--space-6); }
/* Wrapping flex row, not a fixed-column grid — a fixed card width plus
   justify-content:center means 3 cards fill row one, the leftover 2 wrap to
   row two and land centered (a 3-column grid would leave them stuck at the
   left instead). The shared width + aspect-ratio on the media also keeps
   every card's photo the exact same rendered size. Image always on top,
   copy pinned to the bottom (see the media-then-body DOM order on every
   .activity-feature below). */
.activity-showcase { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-6); margin-top: var(--space-5); }
.activity-feature { flex: 0 1 260px; max-width: 260px; display: flex; flex-direction: column; background: var(--color-bg-alt); border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; box-shadow: var(--shadow-card); }
.activity-feature-media { aspect-ratio: 4 / 3; }
.activity-feature-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.activity-feature-body { flex: 0 0 auto; padding: var(--space-6); display: flex; flex-direction: column; justify-content: center; }
.activity-feature-body h4 { margin: 0 0 var(--space-2); font-family: var(--font-display); font-size: 1.15rem; color: var(--color-maroon-800); }
.activity-feature-body p { margin: 0; font-size: 0.92rem; line-height: 1.5; color: var(--color-ink-muted); }
.activity-feature--highlight { background: var(--color-orange-700); border-color: var(--color-orange-700); }
.activity-feature--highlight .activity-feature-body h4 { color: var(--color-white); }
.activity-feature--highlight .activity-feature-body p { color: rgba(255,255,255,0.85); }
.activity-feature--highlight .activity-feature-tag { margin-bottom: var(--space-3); }
.activity-feature-tag { display: inline-block; width: fit-content; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--color-white); background: rgba(255,255,255,0.18); padding: 3px 10px; border-radius: 999px; margin-bottom: var(--space-2); }
.activity-showcase-wrap .proto-badge { margin-bottom: 0; }
.activity-footnote { display: flex; align-items: flex-start; gap: var(--space-3); margin-top: var(--space-6); padding: var(--space-4) var(--space-5); background: var(--color-bg-alt); border: 1px dashed var(--color-orange-600); border-radius: var(--radius-md); font-size: 0.88rem; color: var(--color-ink-muted); }
.activity-footnote svg { flex-shrink: 0; width: 18px; height: 18px; margin-top: 2px; color: var(--color-orange-700); }

/* ==========================================================================
   Timeline scroll — Vie scolaire "rythme de l'année" (events + sorties
   combined). A horizontal connecting-line timeline with alternating cards,
   the site's first line-based timeline: Admissions' calendar is a vertical
   list of rows, this is a different shape because it's telling a year-long
   story a parent scrolls through, not a lookup table of dates.
   ========================================================================== */
/* .timeline-scroll (not .timeline-track) is the actual scroll container.
   .timeline-track is a flex row sized to `max-content`, so its own box is
   exactly as wide as its children — if the overflow-x lived on .timeline-track
   instead, that element's box would only ever be as wide as the visible
   viewport (a block-level box fills its container by default), so the
   ::before line's `right` offset (measured against that box) fell short of
   the true scrollable width and never reached the last dot once scrolled. */
.timeline-scroll {
  position: relative;
  margin-top: var(--space-4);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--color-orange-300) transparent;
}
/* Nodes use an explicit 3-row grid (fixed side, dot, fixed side) instead of
   flex + column-reverse: with column-reverse, each dot's vertical offset
   depends on its own card's text length, so dots land at different heights
   node to node and never actually sit on the connecting line. Fixed grid
   rows make the dot row's position identical across every node regardless
   of content, which is what the connecting line needs to align against.
   Row heights (156px) are sized for worst-case wrapped copy (2-line heading
   + up to 2-line description) — the earlier 132px was sized against a
   headless-preview render that wrapped less aggressively than real Chrome,
   and since overflow-y is hidden on the scroll container above, any content
   taller than its fixed row was being silently clipped instead of shown. */
.timeline-track { position: relative; display: flex; gap: var(--space-6); width: max-content; min-width: 100%; padding: var(--space-4) var(--space-2); scroll-snap-type: x proximity; }
/* top = track's own top padding (--space-4, 16px) + row1 height (156px) +
   half of the dot row (16px) = 188px, so the line meets the grid's fixed
   dot row exactly instead of the row-relative coordinate the pseudo-element's
   own "top" offset (measured from the padding box, i.e. before that padding
   is applied) would otherwise miss by that same 16px. */
.timeline-track::before { content: ""; position: absolute; left: var(--space-2); right: var(--space-2); top: 187px; height: 2px; background: var(--color-border); z-index: 0; }
.timeline-node { position: relative; z-index: 1; flex: 0 0 220px; display: grid; grid-template-rows: 156px 32px 156px; justify-items: center; scroll-snap-align: start; border-radius: var(--radius-md); }
.timeline-dot { grid-row: 2; align-self: center; width: 16px; height: 16px; border-radius: 50%; background: var(--color-maroon-800); border: 3px solid var(--color-bg); box-shadow: 0 0 0 2px var(--color-maroon-800); flex-shrink: 0; transition: background 0.2s ease, box-shadow 0.2s ease; }
.timeline-node.is-key .timeline-dot { background: var(--color-orange-700); box-shadow: 0 0 0 2px var(--color-orange-700); }
.timeline-card { grid-row: 3; align-self: start; background: var(--color-white); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-4) var(--space-5); box-shadow: var(--shadow-card); text-align: center; width: 100%; transition: border-color 0.2s ease, transform 0.2s ease; }
.timeline-node:nth-child(even) .timeline-card { grid-row: 1; align-self: end; }
.timeline-node.is-key .timeline-card { border-color: var(--color-orange-600); }
.timeline-month { display: block; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--color-orange-700); margin-bottom: 4px; }
.timeline-card h4 { margin: 0 0 4px; font-family: var(--font-body); font-size: 0.95rem; color: var(--color-maroon-800); }
.timeline-card p { margin: 0; font-size: 0.82rem; color: var(--color-ink-muted); }
/* Interactive on hover/focus (keyboard reachable via tabindex="0" on the
   node) — every node borrows the same tint/border treatment .is-key wears
   permanently, so Rentrée stays the one always-emphasized date while the
   rest light up as you explore them. */
.timeline-node:hover .timeline-dot, .timeline-node:focus-visible .timeline-dot { background: var(--color-orange-700); box-shadow: 0 0 0 2px var(--color-orange-700); }
.timeline-node:hover .timeline-card, .timeline-node:focus-visible .timeline-card { border-color: var(--color-orange-600); transform: translateY(-3px); }
.timeline-node:focus-visible { outline: 2px solid var(--color-orange-700); outline-offset: 4px; }
@media (max-width: 640px) {
  .timeline-node { grid-template-rows: 28px auto; }
  .timeline-dot { grid-row: 1; }
  .timeline-card, .timeline-node:nth-child(even) .timeline-card { grid-row: 2; align-self: start; }
  .timeline-track::before { top: 29px; }
}
@media (prefers-reduced-motion: reduce) {
  .timeline-node:hover .timeline-card, .timeline-node:focus-visible .timeline-card { transform: none; }
}

/* ==========================================================================
   Gallery bento — Vie scolaire photo gallery. A hand-placed asymmetric grid
   (one tall featured tile per outer column, two stacked in the middle) with
   a click-to-enlarge lightbox, rather than a uniform card grid: this section
   is meant to read as a handful of real glimpses, not a repeating pattern.
   Photos are tasteful stock stand-ins, disclosed via .transparency-note
   above the grid using the same convention as Équipe's role cards.
   ========================================================================== */
.gallery-grid { display: grid; grid-template-columns: repeat(5, 1fr); grid-auto-rows: 170px; gap: var(--space-5); margin-top: var(--space-8); }
.gallery-tile { position: relative; display: block; width: 100%; height: 100%; padding: 0; margin: 0; border: none; border-radius: var(--radius-md); overflow: hidden; cursor: zoom-in; box-shadow: var(--shadow-card); }
.gallery-tile img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1); }
.gallery-tile:hover img { transform: scale(1.05); }
.gallery-tile-icon { position: absolute; right: var(--space-3); bottom: var(--space-3); width: 34px; height: 34px; border-radius: 50%; background: rgba(61,13,20,0.55); color: var(--color-white); display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.2s ease; }
.gallery-tile-icon svg { width: 16px; height: 16px; }
.gallery-tile:hover .gallery-tile-icon, .gallery-tile:focus-visible .gallery-tile-icon { opacity: 1; }
@media (max-width: 900px) {
  .gallery-grid { grid-template-columns: repeat(3, 1fr); grid-auto-rows: 170px; }
}
@media (max-width: 640px) {
  .gallery-grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 150px; }
}

/* Now a Bootstrap Modal (.modal#gallery-lightbox > .modal-dialog >
   .modal-content) — Bootstrap's own .modal/.modal-backdrop supply the
   fixed overlay and dimming (themed via --bs-backdrop-* in
   bootstrap-theme.css), so this only styles the frame: a transparent,
   borderless .modal-content just showing the photo + close button. */
.gallery-lightbox .modal-content { background: transparent; border: none; }
.gallery-lightbox img { max-width: min(90vw, 1100px); max-height: 85vh; margin-inline: auto; border-radius: var(--radius-sm); box-shadow: var(--shadow-raised); }
.gallery-lightbox-close { position: absolute; top: var(--space-5); right: var(--space-5); width: 44px; height: 44px; border-radius: 50%; background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.35); color: var(--color-white); font-size: 1.5rem; line-height: 1; display: flex; align-items: center; justify-content: center; z-index: 1; }
.gallery-lightbox-close:hover { background: rgba(255,255,255,0.22); }
.gallery-lightbox-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 48px; height: 48px; border-radius: 50%; background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.35); color: var(--color-white); display: flex; align-items: center; justify-content: center; z-index: 1; }
.gallery-lightbox-nav:hover { background: rgba(255,255,255,0.22); }
.gallery-lightbox-nav svg { width: 22px; height: 22px; }
.gallery-lightbox-prev { left: var(--space-5); }
.gallery-lightbox-next { right: var(--space-5); }
@media (max-width: 640px) {
  .gallery-lightbox-prev { left: var(--space-2); }
  .gallery-lightbox-next { right: var(--space-2); }
}


/* ==========================================================================
   Hire steps — Équipe recruitment process. Deliberately NOT a third instance
   of .steps-interactive (already used for Admissions and Méthodes): this is
   a short, non-interactive sequence meant to be scanned at a glance, so a
   plain numbered row (same idiom as .grid-4 + .icon-square, swapping the
   icon for a step number) fits better than another click/detail-panel widget.
   ========================================================================== */
.hire-steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-6); }
.hire-step-num {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px;
  border-radius: 50%;
  background: var(--color-maroon-800);
  color: var(--color-white);
  font-family: var(--font-display);
  font-weight: 600;
  margin-bottom: var(--space-3);
}
.hire-step h4 { margin: 0 0 4px; font-family: var(--font-body); font-size: 1rem; color: var(--color-maroon-800); }
.hire-step p { margin: 0; font-size: 0.88rem; color: var(--color-ink-muted); }
@media (max-width: 900px) { .hire-steps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .hire-steps { grid-template-columns: 1fr; } }
