/* ékla — Éclat. Corail & Soleil.
   Values transcribed from the approved mockups; see plan Global Constraints.

   SOURCES OF TRUTH
     .superpowers/brainstorm/410-1785146319/content/eclat-couleurs.html      (option A)
     .superpowers/brainstorm/410-1785146319/content/homepage-directions.html (Éclat column)
     .superpowers/brainstorm/410-1785146319/content/agenda-filtres.html      (Éclat column)

   WHY THIS FILE EXISTS
     Until Plan 5a the four "identities" differed only in theme.json tokens and shared
     one 10-line header and one 10-line footer. The client's verdict was "raw html with
     text and photos", and it was fair: the whole project contained 2.3 KB of CSS and
     loaded no theme stylesheet at all. This is Éclat's own voice — a sticky glass
     header with a gradient wordmark, coral pill filters, and a dark four-column footer.

   CASCADE
     Enqueued as `eclat-identity` with `ekla-foundation` as a DECLARED dependency, so it
     always lands after WordPress global styles and after foundation.css. Verified in the
     rendered head: global-styles → ekla-foundation → eclat-identity. Consequences:
       · theme.json emits `h1,…,h6 {…}` and `a:where(:not(.wp-element-button)) {…}` at
         element specificity, so a plain element selector here wins on source order;
       · it emits `:root :where(.wp-element-button, …)` and `:root :where(.is-layout-flex)`
         at (0,1,0), so a single class here ties and wins on source order;
       · preset classes (`.has-small-font-size`) are emitted `!important` and are NOT
         fought here — where a block declares a preset size, that size stands.
     [6C 2026-08-24: "No `!important` is used below" was false by one and is corrected here
     rather than silently fixed -- §13's `.eclat-tile__img a:focus-visible` box-shadow carries
     the file's one sanctioned `!important`, matching foundation.css's own halo at equal
     specificity (comment-stripped count pinned at 1 by verify-5e.php C8 / verify-6b6.php G1h).
     No OTHER `!important` is used below.]

   DIVISION OF LABOUR
     foundation.css owns STRUCTURE for .ekla-wordmark,
     .ekla-filters*, .ekla-sessions, .ekla-daterange and it already sets
     `text-wrap: balance` on h1–h6. Nothing structural is repeated here; this file
     changes appearance only, plus the few structural facts that ARE identity choices
     (a segmented période toggle, a sticky filter card, a full-bleed header).

   FONTS
     Bricolage Grotesque (display) and Inter (body) are self-hosted and declared in
     ekla-vivant/theme.json `fontFamilies`, reachable as
     var(--wp--preset--font-family--display | --body). Nothing here pulls a font in at
     runtime — no at-rule import, no <link>, no CDN of any kind: GDPR forbids a runtime
     call to Google Fonts, and the fonts are served from this theme's own assets/fonts/.
     (Phrased without the literal at-rule name on purpose: a `grep -r` GDPR audit for it
     across the project should return nothing, and used to match this sentence.)
*/

/* --- 1. tokens ----------------------------------------------------------- */
/* Palette hexes live in theme.json (base/contrast/primary/secondary/tertiary/accent).
   What lives here is everything the palette cannot express: gradients, hairlines, the
   two muted browns the mockups use for secondary text, and the shadow recipes. */
:root {
  /* The brand gradient, transcribed as the plan states it (#FF4D3D -> #FF9E1B) — but
     since Plan 6b Task 9 ("Couleurs de saison") wired onto the same primary/secondary
     preset vars theme.json exposes under the `soleil` slug, so a client-edited season
     palette (mu-plugins/ekla-core/palette-saison.php) reaches this gradient too. The
     render is identical by construction: primary/secondary ARE #FF4D3D/#FF9E1B today. */
  --eclat-soleil: linear-gradient(115deg, var(--wp--preset--color--primary) 0%, var(--wp--preset--color--secondary) 100%);

  /* Two deliberate variants, both transcribed rather than invented, now on the same
     preset vars (Plan 6b Task 9):
       chip — agenda-filtres.html `.ec-chip.on` stops at #FF7A2B. Across a ~90px pill
              the full soleil ramp arrives at near-pure orange and the coral is lost,
              so the mockup shortens it -- expressed here as the 50/50 color-mix of
              primary/secondary (#FF762C), a <=4/255-per-channel delta from the
              transcribed #FF7A2B. Used for every small gradient surface.
       fill — agenda-filtres.html `.ec-noimg` runs at 140deg, not 115deg. On a tall
              4:3 poster a near-diagonal ramp reads as light falling down the tile;
              115deg would band across it. Used for large gradient surfaces. */
  --eclat-soleil-chip: linear-gradient(115deg, var(--wp--preset--color--primary) 0%, color-mix(in srgb, var(--wp--preset--color--primary) 50%, var(--wp--preset--color--secondary)) 100%);
  --eclat-soleil-fill: linear-gradient(140deg, var(--wp--preset--color--primary) 0%, var(--wp--preset--color--secondary) 100%);

  /* Hairlines. `--hair` is the header/footer rule; the filter card and the chips each
     carry their own slightly warmer line in the mockup. */
  --eclat-hair: #FFE6D8;        /* eclat-couleurs.html  --hair                */
  --eclat-hair-warm: #FFE0CE;   /* agenda-filtres.html  .ec-fbar border       */
  /* The mockup's `.ec-chip` border is #FFDCC8, which measures 1.21:1 against the cream
     card. WCAG 1.4.11 needs 3:1 for the boundary of a control, and this boundary is the
     chip's ONLY affordance — foundation.css:66 removes the underline. So the token is
     the mockup's hue, darkened until it complies: `accent` #D92D1C tinted 26% toward the
     cream ground, i.e. the lightest step on the palette's own coral ramp that clears
     3.2:1. Measured 3.23:1 against the cream card and 3.42:1 against the white chip
     fill — both adjacent surfaces pass. Human ruling, Task 5 review item 6. */
  --eclat-chip-line: #E36253;   /* was #FFDCC8 (1.21:1) — see above          */

  /* Text. `ink` is navigation and chip labels, `meta` is secondary prose, `faint` is
     the smallest uppercase labels. All three are from the mockups. */
  --eclat-ink: #5A3324;         /* eclat-couleurs.html  --navink              */
  --eclat-meta: #8A5A44;        /* eclat-couleurs.html  --swink / .cnt        */
  /* TRANSCRIBED BUT UNUSABLE FOR TEXT, and kept declared for exactly that reason.
     #A0705C measures 4.23:1 on white, 3.99:1 on the cream card and 3.80:1 on the
     tertiary track — all below the 4.5:1 AA floor at the small sizes the mockup draws it
     at. The one place that reached for it was measured and sent elsewhere: §5's filter
     label (→ --eclat-meta, 5.47:1 on cream). It stays as the mockup's own value so the
     next author finds this note instead of re-transcribing the hex and shipping it.
     Not a text colour. */
  --eclat-faint: #A0705C;       /* eclat-couleurs.html  --badgetx / .ec-flab  */

  /* Warm grounds. `cream` is the filter card; `tertiary` (#FFF0E6) covers the rest and
     already exists as a palette slug, so it is not duplicated here. */
  --eclat-cream: #FFF7F1;       /* agenda-filtres.html  .ec-fbar background   */

  /* On the dark footer the small caps needed a warm tint, not plain white, or the whole
     block went grey. The violet-era mockup used a tint of its own brand; the plan
     states Éclat's equivalent explicitly.
     [6B5 2026-08-23: DORMANT — zero consumers since client item 3 moved the footer onto the
     coral and its headings onto `contrast`. Kept declared, on the `--eclat-faint` precedent
     above, so the next author finds this note instead of re-transcribing the hex and shipping
     a 2.39:1 heading. Not a colour for this footer any more.] */
  --eclat-tint: #FFB59B;

  /* [6B5 2026-08-23: was `rgba(255, 77, 61, .35)` -- the DEFAULT coral as a frozen literal,
     so every surface wearing this shadow kept the factory colour after the client edited
     "Couleur principale". Restated as the same alpha over the palette var, which is the
     same principle client item 3 asks for on the footer. Three consumers: the section 4
     button rule (`.wp-element-button, .eclat-btn`), the `--ekla-form-btn-shadow` token, and
     `.eclat-door`.] */
  --eclat-shadow: 0 4px 14px color-mix(in srgb, var(--wp--preset--color--primary) 35%, transparent);
  /* [6B5 2026-08-23: `--eclat-shadow` above was migrated to the palette var by an earlier
     6b5 fix, but its two siblings below were left as frozen `rgba(255, 77, 61, …)` literals —
     the DEFAULT coral, not the client's edited "Couleur principale". That half-migration made
     every button's shadow shift hue between rest (`--eclat-shadow`, palette-driven) and
     hover/active (`--eclat-shadow-chip`, still factory coral): change the palette and buttons
     would visibly shift colour on hover instead of staying consistent. These are a matched
     set and must move together, so `--eclat-shadow-chip` below and its sibling literals at
     the button-hover rule and `--ekla-form-btn-shadow-hover` are now restated the same way.] */
  --eclat-shadow-chip: 0 3px 10px color-mix(in srgb, var(--wp--preset--color--primary) 32%, transparent);   /* .ec-chip.on */
  --eclat-shadow-pop: 0 12px 30px rgba(43, 20, 9, .14);     /* menus, raised cards */

  --eclat-radius: 16px;         /* .ec-fbar, large cards        */
  --eclat-radius-sm: 14px;      /* .ec-card .ph, .ec-noimg      */
  --eclat-pill: 999px;

  /* One duration for every hover in the identity, so nothing feels out of step.
     foundation.css already collapses it to .01ms under prefers-reduced-motion. */
  --eclat-ease: 140ms ease;

  /* One measured value, not a guess: 1rem padding top + 1rem bottom + a 1.9rem wordmark
     set at line-height 1. Anything that must clear the sticky header reads it from here
     instead of hard-coding a second copy.
     [6C 2026-08-24: that arithmetic sums to 3.9rem, not 4rem -- the wordmark size this was
     written against has since drifted (§2/§3 changes). The rounder 4rem is not a stale
     leftover of the old sum, though: §3's own live DOM measurement (line ~240 below) puts
     the real header at 63.40625px = 3.9628rem, which 4rem still safely clears. Ledgered
     here rather than re-deriving a new "exact" sum that would only drift again.]
     [6C FIX WAVE 2026-08-24: 4rem is now genuinely stale, not just re-derived differently --
     the header has grown since the note above (6a/6b/6c nav additions), and this was the
     root cause of ruling R8's anchor-landing defect (targets scrolled to ~19px UNDER the
     header on /videotheque/, /come-and-dance/, /art-a-lecole/). Re-measured live via an
     iframe harness across the full range this token must cover: header height holds at
     89.390625px from 1024px up to 1278px wide (nav wraps to 2 lines here), then jumps to a
     flat 127px from 1280px through 1920px tested (nav fits on 1 line) -- an organic
     content-wrap threshold, not an authored breakpoint (grepped: no @media near 1280 in
     this file or foundation.css). Worst case across the whole >=1024 range is 127px =
     7.9375rem. Rounded up to 8.5rem (136px) -- the same "+.5rem" breathing idiom this file
     already uses (see the retired .ekla-filters note above, `calc(var(--eclat-header-h) +
     .5rem)`) -- for safety against subpixel/font-rendering drift. This is the ONLY var()
     consumer of --eclat-header-h in the codebase (grepped both identity.css and
     foundation.css; the other two mentions are prose in comments, not consumers), so
     correcting it here cannot affect anything else. Re-measured clearance after this fix:
     see the scroll-padding-top rule below.] */
  --eclat-header-h: 8.5rem;
}

/* The sticky header would otherwise swallow every in-page anchor target. foundation.css
   sets scroll-margin-top to spacing--40 for skin-neutral breathing room; Éclat has to
   add its own header height on top, which only Éclat knows. */
html { scroll-padding-top: calc(var(--eclat-header-h) + var(--wp--preset--spacing--30)); }

/* --- 2. typography ------------------------------------------------------- */
/* Bricolage Grotesque is the display face; tighten it hard at large sizes.
   theme.json already routes headings to it at weight 700 / tracking -0.02em, which is
   the right default for a generic block theme and much too loose for Éclat: every
   mockup heading is weight 800 at -.035em or tighter. Sizes come from the parent's
   fluid scale (Task 2) rather than the mockups' fixed pixels, because the mockups are
   drawn at a single 1280px width and the real site is fluid — the clamp end-points were
   chosen in Task 2 to land on the mockup values at that width. */
body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, .ekla-wordmark { letter-spacing: -.035em; }

h1 {
  font-size: var(--wp--preset--font-size--xx-large);
  font-weight: 800;
  letter-spacing: -.04em;      /* .ec-hero h1 / .ec-top h1 */
  line-height: .98;            /* .ec-hero h1 is .97 at 70–76px */
}
h2 {
  font-size: var(--wp--preset--font-size--x-large);
  font-weight: 800;
  line-height: 1.06;           /* .ec-sech h2 sits on a single line at 38–40px */
}
h3 {
  font-size: var(--wp--preset--font-size--large);
  font-weight: 700;            /* .ec-card h3 steps back to 700 */
  letter-spacing: -.02em;
  line-height: 1.2;
}
h4, h5, h6 { font-weight: 700; letter-spacing: -.015em; }

/* Prose links. theme.json points them at `primary` (#FF4D3D), which measures 3.29:1 on
   white and fails WCAG AA for body text. Every mockup link is `--link: #D92D1C`
   (= the `accent` slug), which measures 4.83:1 and passes. The mockup is both the
   approved value and the accessible one. */
a:where(:not(.wp-element-button)) {
  color: var(--wp--preset--color--accent);
  text-decoration-thickness: .07em;
  text-underline-offset: .18em;
}
a:where(:not(.wp-element-button)):hover { text-decoration-thickness: .12em; }

/* A measure for prose that patterns and templates set inside their own containers —
   Task 2 widened the content column to 1280px, and a paragraph allowed to fill it runs
   to about 150 characters a line.
   SCOPE, measured rather than assumed: a paragraph that is a DIRECT child of a
   constrained container is given the full content width by theme.json's layout rule at
   (0,1,0), which outranks this element selector, so migrated page and actualité prose
   is deliberately NOT capped here. Overriding that was tried and reverted: the migrated
   content carries `text-align:center` as an INLINE style rather than a preset class, so
   capping those paragraphs left-anchored a 772px box inside a 1280px column and pushed
   the centred text visibly off-centre. Giving those 54 pages a proper measure is a
   content-layout decision for a task that owns page.html, not a side effect here.
   Aligned paragraphs opt out below by class or by inline style, for the same reason. */
p { max-width: 72ch; }
.eclat-footer p,
.ekla-filters p,
p.has-text-align-center,
p.has-text-align-right,
p[style*="text-align"],
figcaption { max-width: none; }

/* --- 3. header ----------------------------------------------------------- */
/* homepage-directions.html `.ec-hd`: translucent white at .92, blur(8px), one hairline,
   z-index above the hero. The blur is what keeps a full-bleed photograph legible as it
   scrolls under the wordmark. The plain `background` below is a real fallback, not a
   claimed one: a UA that cannot parse color-mix() drops that declaration at parse time
   and keeps this one, so the header degrades to an OPAQUE base-coloured bar — the
   behaviour plan §risk 1 promises. Without it the whole declaration would be dropped
   and a sticky bar would render fully transparent with page text scrolling through it,
   which is worse than either. */
.eclat-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--wp--preset--color--base);
  background: color-mix(in srgb, var(--wp--preset--color--base) 92%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--eclat-hair);
}

/* …AND IT HAS NEVER ACTUALLY STUCK (Task 10, human ruling). The declaration above is
   correct and inert: core/template-part renders this part inside a wrapper of its own,
   `<header class="wp-block-template-part">`, read out of the live DOM rather than
   assumed. That wrapper is the sticky element's containing block, and it is exactly as
   tall as the header inside it — outer == inner == 63.40625px, measured on the home,
   the show page and the agenda — so sticky has zero slack to travel in and the bar
   simply scrolls away: at scrollY 800 the header's bottom measured -704.6.
   The fix is to stick the WRAPPER instead, whose own containing block is
   `.wp-site-blocks`, i.e. the whole page. Selected by TAG and not by class alone: core
   gives the footer part the identical `.wp-block-template-part` on a <footer>, and a
   sticky footer is not wanted.
   `.eclat-header`'s own `position: sticky` is kept rather than deleted, because it is
   what holds the bar in the site editor's canvas, where the part's markup is rendered
   WITHOUT this wrapper. Nested inside a sticky wrapper it stays inert — its containing
   block is that wrapper, so its offset clamps to 0 and nothing moves twice.
   KNOCK-ON — CLOSED IN PLAN 5b TASK 2, and this paragraph is kept as the record of how,
   because it was written in the present tense and stayed that way for a whole plan while
   describing a bug that no longer existed. (Comment only; corrected at the final review of
   Plan 5b. Éclat is gate-locked at verify-5a 47/0 and no declaration here changes.)
   WHAT WAS TRUE WHEN THIS WAS WRITTEN: `.ekla-filters` carried the sticky at
   `calc(var(--eclat-header-h) + .5rem)`, an offset written for a header that stays, and it
   had NEVER stuck — measured on /spectacles/ at scrollY 150 / 167 / 180 / 210 / 300, travel
   0.00px at every one; not a small gap, the card simply scrolled away. Same bug class as the
   header's, one level down: its containing block was `.eclat-agenda__util`, only as tall as
   the query title plus the card itself. Injecting `display: contents` on that wrapper pinned
   the card at exactly 72px at scrollY 200/400/800 — but the same injection dropped the
   wrapper's `padding: 48px 28px 28px`, 76px of vertical air and the page's only gutters below
   `content-size`, so that one-liner was never shipped.
   WHAT ACTUALLY FIXED IT: the restructure this paragraph called "a layout decision this task
   does not own" — Task 2 owned it and did it. archive-spectacle.html now puts the query title
   in its own `.eclat-agenda__head`, leaving `.eclat-agenda__util` holding the filter card
   alone; both are direct children of `main.eclat-agenda`, so the wrapper's containing block is
   the whole poster wall. The sticky moved OFF `.ekla-filters` and onto `.eclat-agenda__util`
   (§13, at `min-width: 64rem`), where it has real travel. Nothing in §5 is pending.
   UPDATE (Task 6, D12, 2026-08-19): the sticky itself is gone -- client decision, the pinned
   card covered the poster wall on scroll, and the client's belief that Task 6's new édito
   content above it would fix that on its own was wrong. The structural split (head/util as
   siblings) STAYS; only position:sticky/top/z-index on .eclat-agenda__util were removed, at
   §13. The panel is now static and its opaque ground is kept deliberately. */
header.wp-block-template-part {
  position: sticky;
  top: 0;
  z-index: 50;
}

/* The header group is `layout: default`, so it never receives .has-global-padding and
   spans the full viewport; the inner row carries the measure and the gutters. 1600px is
   wideSize (Task 2) — the header tracks the widest thing on the page, not the 1280px
   text column, which is what stops it reading as a floating ribbon. */
.eclat-header__inner {
  max-width: var(--wp--style--global--wide-size, 1600px);
  margin-inline: auto;
  padding: 1rem var(--wp--preset--spacing--40);
  gap: var(--wp--preset--spacing--40);
}

/* The wordmark. ékla has no logo file; the mockups draw it as the display face with the
   soleil gradient clipped to the glyphs (`--logo: transparent`, `--logobg: <gradient>`).
   Weight, tracking and line-height come from foundation's .ekla-wordmark.
   IT IS 2.07:1 AT ITS LIGHTEST STOP ON WHITE (3.29:1 at the coral end), and it ships
   that way deliberately: WCAG 1.4.3 exempts logotypes — "text that is part of a logo or
   brand name has no contrast requirement" — and this mark IS the logo, standing in for
   the image file ékla does not have. The exemption covers the mark and nothing else;
   every other place the ramp meets text in this file takes `contrast` ink instead. The
   site name is also in the <title> and in the footer prose at full contrast, so nothing
   here is the only route to the information. */
/* [6C 2026-08-24: this block and its two siblings below (§3 media query included) target
   `.eclat-wordmark`, but markup renders `.ekla-wordmark` (foundation.css:75, the ekla- prefix,
   not eclat-) -- grep confirms no template/pattern emits class="eclat-wordmark" anywhere in
   themes/. Dead selectors: the WCAG/gradient reasoning above and the ratios below describe a
   design decision that no longer reaches the DOM. Left in place rather than deleted --
   comment-hygiene work does not touch executable lines -- flagged for a future cleanup task.] */
.eclat-wordmark { font-size: 1.9rem; }
.eclat-wordmark,
.eclat-wordmark a {
  background: var(--eclat-soleil);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* The site-title block wraps its text in an <a>, and theme.json colours and underlines
   every anchor. Without this the gradient is clipped on the <p> while the visible glyphs
   are painted solid primary by the link rule — the gradient would simply not appear. */
.eclat-wordmark a { text-decoration: none; }
.eclat-wordmark a:hover { filter: saturate(1.15) brightness(1.04); }

/* Navigation. .ec-nav: 14.5px / weight 500 / gap 25–26px / colour --navink.
   The real menu carries nine top-level items against the mockup's four, so the gap is
   taken at the tight end of the range and tightened again below 1400px. */
.eclat-nav {
  font-size: .90625rem;
  font-weight: 500;
  color: var(--eclat-ink);
}
.eclat-nav,
.eclat-nav .wp-block-navigation__container { gap: .5rem 1.5625rem; }

.eclat-nav a {
  text-decoration: none;
  color: inherit;
  transition: color var(--eclat-ease);
}
/* Hover and current state — CARRY-FORWARD FIX (Task 5 report §5), made real by Task 6.
   `accent` #D92D1C reads 4.84:1 on white, but this header is
   `color-mix(base 92%, transparent)`: 8% of whatever scrolls beneath it composites
   through. Over near-black content the ground becomes #EBEBEB and accent falls to
   4.06:1, below the 4.5:1 floor for 14.5px text. That was hypothetical until the home
   put a full-bleed photograph under this bar; measured against that photograph it is
   4.40:1 at the BRIGHTEST 8x8 block in the strip that passes under the header, and
   worse everywhere else. It fails in practice, not only in theory.
   Of the three costed options, recolouring the state is the one that changes nothing
   anyone approved: `.ec-nav` in homepage-directions.html draws a single colour and no
   hover or current state at all, so these two rules were an implementer's choice in
   Task 5, not a transcription. Raising the header to 96% still measures only 4.44:1
   over black AND edits a mockup value; scrimming the header over the hero darkens the
   backdrop, which makes this worse, not better. Recolouring also covers the dark
   footer, the temps-fort panel and Task 7's dark agenda, which a hero-only fix
   would not.
   The brand cue survives as the underline rather than as the text colour: `accent` on
   the worst composite is 4.06:1, above the 3:1 WCAG 1.4.11 asks of a graphical state
   indicator. Task 5 avoided an underline here for fear of the submenu chevrons; the
   live markup puts each chevron in a <button> BESIDE the <a>, not inside it, so an
   underline on the link cannot reach it. */
.eclat-nav a:hover,
.eclat-nav .current-menu-item > a,
.eclat-nav [aria-current="page"] {
  text-decoration: underline;
  text-decoration-color: var(--wp--preset--color--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: .45em;
}

/* While a submenu is open, drop the parent's underline: the stroke hangs 2.4px
   below the link box and the dropdown's 12px rounded top-left corner leaves a
   4-7px red tick of it exposed. Specificity (0,4,1) beats all three underline
   selectors above. (6b3 item 8)
   [CORRECTED 2026-08-21 (6b3 final review): actual specificity is (0,4,0) —
   no element selector in the chain; still outranks all three underline
   selectors (max (0,2,1))] */
.eclat-nav .has-child:is(:hover, :focus-within) > .wp-block-navigation-item__content {
  text-decoration: none;
}

.eclat-nav .current-menu-item > a,
.eclat-nav [aria-current="page"] { font-weight: 600; }

/* The colour has to be set on the LIST ITEM, not on the link. Core ships
     .wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content
       { color: inherit }
   with the class repeated on purpose, i.e. (0,3,0) — read out of core's shipped
   stylesheet, not assumed. Consequence, from that arithmetic: Task 5's
   `.eclat-nav a:hover { color: accent }` is (0,2,1) and loses to it, so the state this
   fix replaces was never painting its colour to begin with. Colouring the <li> is what
   core's `inherit` is built to read, and it costs (0,2,0) instead of an escalating
   selector or an !important. Confirmed by hovering a real nav item in the browser and
   reading the rendered pixels. */
.eclat-nav .wp-block-navigation-item:hover,
.eclat-nav .current-menu-item,
.eclat-nav .wp-block-navigation-item:has(> [aria-current="page"]) {
  color: var(--wp--preset--color--contrast);
}

/* Submenus. Core ships a 1px rgba(0,0,0,.15) box with square corners and no shadow,
   which is the single most "stock Gutenberg" surface on the page.

   THE SELECTOR COMPOUNDS `.wp-block-navigation`, AND IT HAS TO. Core ships
     .wp-block-navigation:not(.has-background) .wp-block-navigation__submenu-container
       { background-color: <opaque white>; border: 1px solid rgba(0,0,0,.15); }
   `:not()` takes the specificity of its argument, so that selector is CLASS + CLASS +
   CLASS = (0,3,0). A plain `.eclat-nav .wp-block-navigation__submenu-container` is
   (0,2,0) and LOSES on specificity — not on source order, so no enqueue ordering could
   ever have fixed it. Measured in a browser before this compound was added:
   `border-top-color: rgba(0, 0, 0, 0.15)`, i.e. core's line, not this file's. The
   `background-color` and `border` below were in the file, were reviewed, and were not
   being painted. Human ruling, 2026-07-28; the identical gap was found and fixed across
   all sibling identities in the same round (their theme dirs are gone as of Plan 6b Task 3).
   `.eclat-nav` sits ON the <nav>, which itself carries `wp-block-navigation`, so the two
   classes compound on ONE element rather than describing an ancestor chain. (Core also
   copies the className onto the inner container <ul>, which carries `wp-block-navigation`
   too — so a descendant-combinator form would also have matched, but only by relying on
   that duplication. The compound does not.)

   THE BORDER IS `--eclat-chip-line`, NOT `--eclat-hair`, AND THAT IS THE POINT OF THE FIX.
   Making the declared rule win is not the same as making it correct: this panel's ground
   is `base`, the page behind it is also `base`, so the panel separates from the page by
   1.00:1 and the border is the ONLY thing that identifies the open submenu as a distinct
   surface — which is what WCAG 1.4.11 asks 3:1 of. Measured on the live page:
     · core's line, composited        #D9D9D9  = 1.41:1 against both  — fails
     · --eclat-hair       #FFE6D8              = 1.20:1 against both  — fails, and is
       WORSE than the core line it would have replaced, so the selector fix alone would
       have made this boundary measurably worse while looking like a repair
     · --eclat-chip-line  #E36253              = 3.42:1 against both  — passes
   --eclat-chip-line is the token §1 already keeps for exactly this job ("the mockup's hue,
   darkened until it complies"), so this is a reuse of a measured decision rather than a
   new one. --eclat-hair keeps its own role: the header and footer rules, where it is a
   decorative division inside a surface and 1.4.11 does not reach it. */
.eclat-nav.wp-block-navigation .wp-block-navigation__submenu-container {
  background-color: var(--wp--preset--color--base);
  border: 1px solid var(--eclat-chip-line);   /* 3.42:1 — the boundary, not a hairline */
  border-radius: 12px;
  box-shadow: var(--eclat-shadow-pop);
  padding: .4rem;
  /* 12rem, NOT the 15rem this shipped with, and the 3rem is a horizontal-overflow fix rather
     than a taste change. The right-most submenu-bearing item is a fixed distance from the
     nav's right edge, so the room its panel has is CONSTANT at 220px from 1024px to 1366px
     of viewport (measured: 1024/1100/1200/1280/1366 all report 220). A 15rem panel renders
     255px wide and overflowed the document by exactly 255-220 = 35px at every one of those
     widths, and by 1px at 1440/1600 where the room is 253px.
     It only became reachable when the skin switcher left the header (foundation.css, same
     pass): the nav then spread to the full row and pushed this panel against the edge. So
     this is the second-order cost of that fix, found by re-sweeping afterwards rather than
     by assuming the first measurement still held.
     WHY min-width AND NOT RIGHT-ALIGNMENT: right-alignment was measured and also works, but
     it depends on where the nav items sit, so a future menu edit could push a left-hand panel
     off the other edge with nothing going red. A narrower panel cannot do that. 12rem renders
     207px, clearing the tightest room by 13px; 13rem still left 3px of overflow. Verified 0
     at 1024/1100/1200/1280/1366/1440/1600/1920.
     The (now-retired) sibling identity carried the identical declaration and the
     identical reasoning -- one mechanism, not a band hack in one and a width in
     the other. */
  min-width: 12rem;
}
/* [6D 2026-09-04, client item 4] The LAST item is now a submenu (Contact -> Contact + Contact
   artiste). The 12rem rule above sized the panel for the 220px the Pôle item has to its right;
   a last item has ~60px, so no width can fit and the closed panel (absolute, left-anchored,
   207px) overflowed the DOCUMENT by 127px at 1024-1400px -- horizontal scroll on every page,
   panel opening off-screen. The right-alignment the note above declined for the general case
   is exactly right for :last-child: its panel's right edge is the item's own right edge, which
   sits inside the container by construction, so no future menu edit can push it off either
   side (core uses the same left:auto/right:0 for .items-justified-right). Scoped to the
   non-overlay state so the mobile menu keeps core's positioning. Measured after: 0 overflow
   at 1024/1100/1280/1400/1440, panel right edge = item right edge (994/993 at 1024). */
.eclat-nav .wp-block-navigation__responsive-container:not(.is-menu-open)
  .wp-block-navigation__container > .has-child:last-child > .wp-block-navigation__submenu-container {
  left: auto;
  right: -1px;
}
.eclat-nav .wp-block-navigation__submenu-container a {
  border-radius: 8px;
  padding: .5rem .75rem;
  font-size: .875rem;
}
.eclat-nav .wp-block-navigation__submenu-container a:hover {
  background: var(--wp--preset--color--tertiary);
}
.eclat-nav .wp-block-navigation__submenu-icon svg { width: .7em; height: .7em; }

/* The mobile overlay. Core's responsive container inherits its background, which on a
   transparent header resolves to nothing at all and stacks the menu over the page. */
.eclat-nav .wp-block-navigation__responsive-container.is-menu-open {
  background-color: var(--wp--preset--color--base);
  color: var(--wp--preset--color--contrast);
  padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--40);
}
.eclat-nav .wp-block-navigation__responsive-container.is-menu-open a { font-size: 1.25rem; }
.eclat-nav .wp-block-navigation__responsive-container.is-menu-open
  .wp-block-navigation__submenu-container {
  border: 0;
  box-shadow: none;
  padding-left: 0;
}
.eclat-nav .wp-block-navigation__responsive-container-open,
.eclat-nav .wp-block-navigation__responsive-container-close {
  color: var(--eclat-ink);
}

/* [6C 2026-08-24] Overlay band 600-1023.98px. Core un-overlays its nav at 600px
   (wp-includes/blocks/navigation/style.css:590-604 container, :647-651 hamburger);
   nine items measured 3 rows @640 / 2 rows @768 (browser pass 2026-08-19). This
   band restores core's own overlay state; >=1024 stays inline (the 1-row floor,
   zero slack: media queries evaluate against innerWidth). Selectors restate
   core's :not() chains to clear its (0,3,0) specificity. Core's view.js and the
   theme focus trap key on classes, not width - both measured, neither changes. */
@media (min-width: 600px) and (max-width: 1023.98px) {
  .eclat-nav .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
  }
  .eclat-nav .wp-block-navigation__responsive-container-open:not(.always-shown) {
    display: flex;
  }
}

/* Nine menu items plus a wordmark plus the demo switcher is a lot of header. Rather
   than let it wrap into two ragged rows, the type and the gaps step down once before
   core's own 600px overlay breakpoint takes over. [6C 2026-08-24: the band above now
   overlays 600-1023.98px; this step-down's own relevance narrows to >=1024, where the
   nav is inline.]
   PLAN 5C FINAL FIX WAVE (2026-07-31), C1: Task 7 relabelled Agenda -> Spectacles (spec
   6.1, +4 characters). Measured live with a same-origin iframe harness: at 1024px this
   nav wrapped to two rows, 8.6px short of the room it had with the old label. The column
   gap is the lever (same one this rule already used to fit nine items) -- 1.1rem -> .75rem
   frees 8 gaps x 5.6px = 44.8px, clearing the deficit with 36.2px to spare. Re-measured
   clean afterwards: 1 row at 1024-1400px, no new overflow, submenus still fully on-screen.
   The sibling identities (now retired) got the identical .75rem at this same breakpoint
   -- one lever, not a per-identity hack.
   [6D 2026-09-04, client item 4] Same lever, third time. The Contact leaf became a submenu
   (Contact + Contact artiste) and its chevron adds 8px + .25em margin = 11.4px to the row:
   at 1024px the nine items measured 866px against 859px of room and wrapped (2 rows from
   1024 to 1031px, 1 row from 1032; DOM A/B with the chevron removed: 1 row at 1024).
   .75rem -> .65rem frees 8 gaps x 1.6px = 12.8px; .7rem (6.4px) was measured NOT enough.
   Re-measured after: 1 row at 1024-1400px, no overflow, submenus on-screen (see the 6d ledger). */
@media (max-width: 1400px) {
  .eclat-nav { font-size: .84375rem; }
  .eclat-nav,
  .eclat-nav .wp-block-navigation__container { gap: .4rem .65rem; }
  .eclat-header__inner { padding-inline: var(--wp--preset--spacing--30); }
}
@media (max-width: 900px) {
  .eclat-wordmark { font-size: 1.6rem; }   /* [6C 2026-08-24: dead selector, see §3's note above.] */
  .eclat-header__inner { padding-block: .75rem; }
}

/* --- 4. buttons ---------------------------------------------------------- */
/* .ec-cta: 14px/28px, weight 700, radius 99px, filled with the brand. theme.json's
   button element style (flat `primary`, 4px radius) is the stock look this replaces. */
.wp-element-button,
.eclat-btn {
  display: inline-block;
  border-radius: var(--eclat-pill);
  font-weight: 700;
  font-size: .9375rem;                  /* .ec-cta 15px */
  line-height: 1.2;
  padding: .9rem 1.75rem;
  /* [6B4 2026-08-22, client ruling: gradients OFF buttons.] Flat secondary replaces the
     soleil ramp. Encre on secondary measures 14.55:1 (6b3 browser pass, Pair 3) — this
     also RESOLVES the button half of the accepted D6b3-2/D9 sub-AA residual (encre on
     the ramp's coral stop was 4.28:1). The old pair `background: var(--eclat-soleil)` +
     `background-color: transparent` is gone: with a flat color the transparent companion
     would NULL it (it existed only to kill theme.json's flat primary under the gradient
     IMAGE; setting background-color ourselves supersedes that job). */
  background-color: var(--wp--preset--color--secondary);
  color: var(--wp--preset--color--contrast);
  border: 0;
  box-shadow: var(--eclat-shadow);
  text-decoration: none;
  cursor: pointer;
  transition: box-shadow var(--eclat-ease), transform var(--eclat-ease);
}
.wp-element-button:hover,
.eclat-btn:hover {
  box-shadow: 0 6px 20px color-mix(in srgb, var(--wp--preset--color--primary) 45%, transparent);
  transform: translateY(-1px);
}
.wp-element-button:active,
.eclat-btn:active { transform: translateY(0); box-shadow: var(--eclat-shadow-chip); }

/* Éclat option A has no outlined CTA of its own — both of its buttons are filled
   (`--cta: #fff` on the hero, `--cta2: #2B1409` beside it). The ghost is derived from
   the one outlined treatment in the mockup set (Encre & Signal's `.ec-cta.o`,
   `border: 2px solid rgba(255,255,255,.55)`), rewritten in currentColor so the single
   rule works on the white page and on the dark footer/agenda without a second copy.
   Task 6 gives it its first consumer: the hero's second CTA. core/button puts a block
   `className` on the WRAPPER, never on the <a>, so `.wp-block-button.is-ghost > …`
   is how the markup reaches this rule; the bare class still works on any element. */
.eclat-btn--ghost,
.wp-block-button.is-ghost > .wp-element-button {
  background: none;
  background-color: transparent;
  color: currentColor;
  /* 55%, not 45%: the border is this button's only boundary, so WCAG 1.4.11 applies.
     Measured composited over each ground — 45% gave 2.92:1 on the white page (fail);
     55% gives 3.94:1 on white and 5.96:1 on the dark footer ground.
     Two declarations, the same idiom §3 uses on the header background and for the same
     reason: a UA that cannot parse color-mix() drops that declaration at PARSE time, and
     with only one declaration the whole `border` goes with it — the home hero's second
     CTA would become bare white text over a photograph with no boundary at all. The
     fallback is full-strength currentColor, i.e. this button's own hover appearance:
     heavier than intended, never absent.
     [6B5 2026-08-23: the dark-footer figure is stale -- that ground is coral since client
     item 3. Not recomputed on purpose: `.eclat-btn--ghost` has ZERO consumers (grep of
     templates, parts and patterns finds none outside this rule), so the number would
     describe a pairing nothing renders. If this button is ever revived, measure it then.] */
  border: 2px solid currentColor;
  border: 2px solid color-mix(in srgb, currentColor 55%, transparent);
  box-shadow: none;
  padding: calc(.9rem - 2px) calc(1.75rem - 2px);
}
.eclat-btn--ghost:hover,
.wp-block-button.is-ghost > .wp-element-button:hover {
  border-color: currentColor;
  box-shadow: none;
  transform: none;
  background: color-mix(in srgb, currentColor 8%, transparent);
}

/* --- 5. agenda filters (classes from ekla_render_agenda_filters) ---------- */
/* The live callback emits, in this order: a .ekla-filters__quand role="group" toggle,
   three .ekla-filters__group rows (Public / Âge / Lieu), a .ekla-filters__count
   paragraph, and — only when something is filtered — a .ekla-filter.ekla-filter--clear
   link. `.ekla-filter.is-active` is a COMPOUND: the markup is
   `class="ekla-filter is-active"` paired with `aria-current="true"`.
   Verified against $THEMES/ekla/functions.php lines 132–184, not against the brief's
   class list, which omitted both __quand and the compound.

   agenda-filtres.html `.ec-fbar`: cream card, 1px warm border, radius 16, 16/18 padding.
   foundation.css already makes .ekla-filters a column flex; only the skin is set here,
   plus the row rhythm, which the mockup pins at 11px against foundation's 1rem.
   [6C2 2026-08-25, client item 1: the card's colours are PALETTE SLOTS now, not the
   mockup's fixed Éclat literals, so the card follows `ekla_saison_colors` ("the brochure
   palette") like the buttons and the band do. Card = `tertiary` ("Fond chaud"), ink =
   `contrast`, outlines = `accent`, hover/active chip = the soleil ramp with `contrast` ink.
   `accent` rather than `primary` for the outlines because it is the edge that has to
   clear 3:1 (WCAG 1.4.11, the Task 5 ruling): under today's palette primary #E7411C
   measures ~2.6:1 on the lavender card, accent #992910 ~4.9:1. The two text pairs this
   card now relies on — contrast-on-tertiary and contrast-on-primary — are the palette
   engine's own badges ("Texte sur fond chaud", "Encre des puces de filtre"), re-measured
   on every save.] */
.ekla-filters {
  background: var(--wp--preset--color--tertiary);
  border: 1px solid color-mix(in srgb, var(--wp--preset--color--accent) 35%, transparent);
  border-radius: var(--eclat-radius);
  padding: 1.1rem 1.15rem;
  gap: .7rem;                    /* .ec-frow margin-bottom: 11px */
  /* This <nav> comes from a render callback, so it carries no wp-block-* class and
     core's border-box reset never reaches it. Measured without this: the constrained
     layout caps it at 1280px of CONTENT and it renders 1319px, overhanging the page
     title by 19px a side. */
  box-sizing: border-box;
}

/* The période toggle. Éclat's only segmented control in the mockups is the identity
   switcher `.ec-sw` — a tinted track at radius 99px, 4px padding, 4px gap, with the
   active segment filled by the brand gradient. The same object, reused. */
.ekla-filters__quand {
  display: inline-flex;
  align-self: flex-start;
  gap: .25rem;
  padding: .25rem;
  border-radius: var(--eclat-pill);
  /* [6C2: `base`, not `tertiary` — the card itself is tertiary now, so a tertiary track
     would vanish into it.] */
  background: var(--wp--preset--color--base);
}
.ekla-filters__quand .ekla-filter {
  background: none;
  border-color: transparent;
  box-shadow: none;
  color: var(--wp--preset--color--contrast);
  padding: .35rem .8rem;         /* .ec-sw i: 5px 10–11px */
  font-size: .71875rem;
}
.ekla-filters__quand .ekla-filter:hover {
  background: var(--wp--preset--color--tertiary);
  border-color: transparent;
  color: var(--wp--preset--color--contrast);
}
.ekla-filters__quand .ekla-filter.is-active {
  background: var(--eclat-soleil-chip);
  color: var(--wp--preset--color--contrast);   /* white measured 2.60:1 at the #FF7A2B stop
     [6C 2026-08-24: stale -- `contrast` defaults to #2B1409 "Encre brûlée", not white; this
     ratio described an earlier design. The live pair is now owned by palette-saison.php's
     own badge ("Encre des puces de filtre — extrémité corail", floor 4.5), re-measured on
     every save, which is the authoritative check, not this frozen figure. */
  box-shadow: var(--eclat-shadow-chip);
}

/* Rows. foundation aligns the label to the baseline, which is right for underlined text
   links and wrong for pills — against a 30px pill a baseline-aligned label sits low. */
.ekla-filters__group {
  align-items: center;
  gap: .5rem .58rem;             /* .ec-frow gap: 9px */
}

/* (`.ekla-filters__group ul { flex: 1 }` used to live here. It is structural and every
   identity meets it, so it moved to foundation.css, which now carries the measurement
   note with it. Nothing identity-specific was lost: it was that one declaration.) */

/* .ec-flab: 11px / uppercase / .12em / weight 800 / #A0705C / width 62px. */
.ekla-filters__label {
  font-size: .6875rem;
  text-transform: uppercase;
  letter-spacing: .12em;
  font-weight: 800;
  /* The mockup's `.ec-flab` is #A0705C, which measures 3.99:1 against the cream card it
     actually sits on — not the white I first measured it against. --eclat-meta is the
     same warm brown family one step down and measures 5.47:1. Human ruling, item 5.
     [6C2: `contrast` — the card is a palette surface now; badge "Texte sur fond chaud".] */
  color: var(--wp--preset--color--contrast);
  min-width: 4rem;
}

/* .ec-chip: 13px / weight 600 / 7px 15px / radius 99 / white on a 1.5px warm line. */
.ekla-filter {
  font-size: .8125rem;
  font-weight: 600;
  padding: .45rem .95rem;
  border-radius: var(--eclat-pill);
  background: var(--wp--preset--color--base);
  border: 1.5px solid var(--wp--preset--color--accent);
  color: var(--wp--preset--color--contrast);
  line-height: 1.25;
  transition: border-color var(--eclat-ease), color var(--eclat-ease), background var(--eclat-ease);
}
/* Hover moves the boundary to `accent`, not `secondary`. Same 1.4.11 reasoning as the
   resting border: secondary #FF9E1B measures 1.95:1 on the cream card, so hovering a
   chip made its outline LESS visible than at rest — fixing the resting state alone would
   have left that. Accent measures 4.57:1 and matches the hover label colour.
   [6C2: the resting outline IS accent now, so hover previews the active state instead —
   the soleil fill with `contrast` ink, the exact pair `.ekla-filter.is-active` below uses
   and the palette badge "Encre des puces de filtre" guards.] */
.ekla-filter:hover {
  background: var(--eclat-soleil-chip);
  border-color: transparent;
  color: var(--wp--preset--color--contrast);
}

/* .ec-chip.on. The `text-decoration: none` is not decoration: foundation gives the
   active filter an underline, which is the right skin-free affordance for a text link
   and reads as a rendering fault once the chip is a filled pill. */
.ekla-filter.is-active {
  background: var(--eclat-soleil-chip);
  border-color: transparent;
  color: var(--wp--preset--color--contrast);   /* white measured 2.60:1 at the #FF7A2B stop
     [6C 2026-08-24: stale -- `contrast` defaults to #2B1409 "Encre brûlée", not white; this
     ratio described an earlier design. The live pair is now owned by palette-saison.php's
     own badge ("Encre des puces de filtre — extrémité corail", floor 4.5), re-measured on
     every save, which is the authoritative check, not this frozen figure. */
  box-shadow: var(--eclat-shadow-chip);
  text-decoration: none;
}
.ekla-filter.is-active:hover {
  color: var(--wp--preset--color--contrast);
  border-color: transparent;
}

/* "Effacer les filtres" carries .ekla-filter too, so the pill has to be undone. The
   horizontal padding goes with it so the link aligns flush under the row labels.
   .ec-clear: 12.5px / weight 600 / accent / underlined. */
.ekla-filter--clear {
  background: none;
  border: 0;
  padding-inline: 0;
  font-size: .78125rem;
  text-decoration: underline;
  color: var(--wp--preset--color--accent);
}
.ekla-filter--clear:hover { color: var(--wp--preset--color--contrast); }

/* The result count. In the mockup it sits above the card ("226 spectacles — filtrez
   par âge, public ou lieu"); the render callback emits it as the second-to-last child
   INSIDE the nav, so it is treated here as the card's summary line and given a hairline
   to separate it from the last row of chips. Restructuring the callback would mean
   touching shared parent PHP, which is out of this task's scope. */
.ekla-filters__count {
  font-size: .9375rem;
  color: var(--wp--preset--color--contrast);
  border-top: 1px solid color-mix(in srgb, var(--wp--preset--color--accent) 35%, transparent);
  padding-top: .8rem;
  margin-top: .15rem;
}
.ekla-filters__count b,
.ekla-filters__count strong { color: var(--wp--preset--color--accent); }

/* A 4rem label beside wrapping chips leaves about six characters of usable row on a
   360px phone. Below the tablet breakpoint the label takes its own line instead. */
@media (max-width: 600px) {
  .ekla-filters { padding: .9rem; border-radius: var(--eclat-radius-sm); }
  .ekla-filters__group { flex-direction: column; align-items: flex-start; gap: .4rem; }
  .ekla-filters__label { min-width: 0; }
}

/* --- 6. dates ------------------------------------------------------------ */
/* ekla_render_show_dates() emits either a <p class="ekla-daterange"> range or a
   <ul class="ekla-sessions"> of individual performances. foundation strips the list
   chrome; the rhythm and the colour are Éclat's. */
.ekla-daterange {
  color: var(--eclat-meta);
  font-size: .9375rem;
  font-variant-numeric: tabular-nums;
}

/* A show's session list is a table of dates in all but name — one line per performance,
   read by scanning down the day numbers. Tabular figures keep that column true. */
.ekla-sessions {
  font-variant-numeric: tabular-nums;
  font-size: .9375rem;
  color: var(--eclat-ink);
  max-width: 34rem;
}
.ekla-sessions li {
  padding: .5rem 0;
  border-bottom: 1px solid var(--eclat-hair);
}
.ekla-sessions li:first-child { border-top: 1px solid var(--eclat-hair); }
.ekla-sessions li::first-letter { text-transform: uppercase; }

/* --- 7. no-image fallback ------------------------------------------------- */
/* 26 of 232 shows have no photograph (re-derived at the final review of Plan 5b; this said
   31, which nobody could reproduce -- 206 of the 232 carry a thumbnail. Comment only: Eclat is
   gate-locked at verify-5a 47/0 and no declaration in this file changes).
   The mechanism is `:not(:has(img))` -- there is no
   element to hang a class on, because wp:post-featured-image renders nothing at all when
   the thumbnail is missing. (The shared `.ekla-noimg` primitive this section used to style
   was retired in Plan 5b Task 1 for exactly that reason: styled in two files, emitted by
   none -- 0 occurrences on every page.)
   Nothing is declared in this section any more. §13's `.eclat-tile:not(:has(img))` is
   already this identity's no-image fallback for the poster wall AND the home rail (the
   same pattern renders both, by slug): same `--eclat-soleil-fill` gradient, same `contrast`
   ink this section used, plus the scrim/pill/date-range inversions a bare flex box never
   needed. Shipping a second ruleset here would compete with §13 rather than complete it --
   merged by deleting, not by adding. */

/* --- 8. footer ----------------------------------------------------------- */
/* homepage-directions.html `.ec-ft`: dark ground, four columns at 1.4fr 1fr 1fr 1fr,
   32px gap, 13px type, uppercase tinted column headings, prose at reduced weight.
   Éclat's dark ground is LOCAL — `base` stays white everywhere else (plan constraint);
   the footer gets it from the block's own contrast/base colour attributes, not from a
   root override.
   [6B5 2026-08-23, client item 3: the ground described above is no longer what ships. The
   footer now takes the palette's `primary` -- the client's "Couleur principale (corail)" --
   with `contrast` ink on it. Still from the block's own colour attributes and still not from
   a root override, so that half holds. What is dead is "dark ground" and the contrast/base
   pairing, and with them the tinted column headings: the tint token is dormant. Ink on the
   unaltered coral is 4.28:1, a knowingly shipped sub-AA residual (ruling R2).]
   [6B6 2026-08-23, item 1: "still from the block's own colour attributes" no longer holds for
   INK -- script 46 took textColor off the block, so ink now comes from this rule's own `color`
   declaration below (var(--ekla-ink-footer, ...)), not from a block attribute. The ground half
   is unchanged: `backgroundColor:"primary"` still lives on the block, for the !important
   reason the footer part's own comment gives.] */
.eclat-footer {
  padding-block: var(--wp--preset--spacing--60);
  /* Core gives every .wp-site-blocks child a spacing--40 top margin. A full-bleed coloured
     band wants more air than 1.75rem above it or it looks like a stray section. [6B5
     2026-08-23: read "a dark band" until client item 3 made this ground coral. The reasoning
     was never about darkness -- only about a band of colour meeting the white page above
     it -- so the rule is unchanged and only the wording needed correcting.] */
  margin-block-start: var(--wp--preset--spacing--60);
  font-size: .875rem;
  line-height: 1.75;
  /* 6b6 item 1: the client sets this surface's ink independently of the global `contrast`.
     The colour is NOT on the block's textColor attribute any more, because WordPress emits
     `.has-contrast-color { color: … !important }` and this file may not use !important (a
     tested invariant -- verify-5e C8) [6C 2026-08-24, fix round 1: false by one, same slip as
     §1's old header claim -- the comment-stripped count verify-5e C8 / verify-6b6 G1h actually
     pin is exactly 1, not 0 (§13's `.eclat-tile__img a:focus-visible` box-shadow, the file's one
     sanctioned exception). The reasoning here is unaffected: this rule still has to win as a
     PLAIN rule against `.has-contrast-color`'s `!important`, which a second `!important` here
     could not reliably do either]. With the attribute gone, a plain rule wins, and the
     fallback means an unset slot is indistinguishable from the build before this feature
     existed. The GROUND is untouched and still comes from the block's own backgroundColor
     attribute. */
  color: var(--ekla-ink-footer, var(--wp--preset--color--contrast));
}

/* The mockup's 1.4fr 1fr 1fr 1fr, expressed through the columns block: the first column
   is declared 34% in the markup and the remaining three share what is left. Core stacks
   them below 782px on its own, which is the right breakpoint for four columns. */
.eclat-footer__cols { gap: 2rem; }

/* CORRECTED 2026-08-21 (6b3 item 2): the wordmark's exclusion-by-name is gone because the
   class it named (`.eclat-footer__mark`) no longer exists — the footer now renders the real
   logo (see `.eclat-footer__logo` below), which needs no such carve-out. */
/* [6B5 2026-08-23, client item 3: the 80% dim is GONE, not retuned. It existed to soften white
   on a dark ground; on the coral ground the text is ink, and dimmed ink measures 3.38:1 against
   4.28:1 undimmed. At this contrast there is nothing left to spend on softening, so the rule
   now only cancels the width cap it always did.] */
.eclat-footer__col p {
  color: inherit;
}

/* Core gives every flow child a spacing--40 (1.75rem) top margin, which is a paragraph
   break, not a line break — inside a footer column the address and the link lists want
   a tight rhythm, and the gap under a heading should be the heading's own margin. */
.eclat-footer__col > * + * { margin-block-start: .45rem; }
/* CORRECTED 2026-08-21 (6b3 item 2): renamed from `.eclat-footer__mark + p` — the wordmark
   paragraph it named is gone, replaced by `.eclat-footer__logo` (a sibling div, same
   position), and the paragraph right after it still wants a flush top (the logo's own
   margin-block-end below already sets the gap, see `.eclat-footer__logo img`). */
.eclat-footer__logo + p,
.eclat-footer__h + p { margin-block-start: 0; }

/* 6b3 item 2: real logo in the footer (same attachment + "Remplacer" path as the
   header, D10). The file is near-black ink on transparent and the footer ground
   was dark, so it was forced white here.
   [6B5 2026-08-23, client item 3: the filter is DELETED. The ground is now the coral and the
   text around it is ink, so the file's own near-black is exactly the colour this footer wants;
   inverting it would leave a white mark floating on coral, matching nothing beside it.] */
.eclat-footer__logo img {
  display: block;
  width: 120px;
  height: auto;
  margin-block-end: .75rem;
}

/* .ec-ft h4: uppercase, .14em, tinted. The block markup declares `small` as a preset
   font size, which WordPress emits with !important, so the size is left to the preset
   rather than fought — the tracking and the tint are what make it read as a column
   heading. #FFB59B on #2B1409 measures 10.22:1. (Written as 10.3 until the 5b re-review;
   re-derived to 10.2218. Nothing asserts this figure, so it was unchecked prose — and the final fix
   wave made three comment-only edits to this very file without catching it. Called out rather than
   filed as a rounding nit because in a file this heavily annotated an unasserted number is only as
   good as the last reader who bothered to divide.)
   [6B5 2026-08-23, client item 3: that 10.22:1 is now STALE — it measured #FFB59B against the
   ink ground this footer no longer has. On the coral ground the same tint measures 2.39:1, i.e.
   a heading you cannot read, so the colour is now `contrast` like the rest of the footer. What
   still separates a column heading from the links under it is the uppercase, the .14em tracking
   and the 700 weight — the tint was never carrying that work alone.] */
.eclat-footer__h {
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--wp--preset--color--contrast);
  font-weight: 700;
  margin-block-end: .9rem;
}

/* Link lists. Full-strength white at .82 opacity measured 12:1 on the dark ground, and
   the hover restored it to 1 rather than changing hue, so a column of eight links had
   one moving part instead of eight.
   [6B5 2026-08-23, client item 3: both halves of that are gone. On the coral ground the ink is
   at 4.28:1 undimmed and 3.48:1 at .82, so there is no opacity left to spend and the opacity
   IDIOM itself had to go — which removed the hover affordance with it, since the hover WAS the
   opacity restore. The underline, already in the rule below, is now the whole hover. The colour
   is inherited from the block's textColor rather than named, so the links and the prose move
   together the next time this ground changes.] */
.eclat-footer a {
  color: inherit;
  text-decoration: none;
}
.eclat-footer a:hover { text-decoration: underline; }

@media (max-width: 781px) {
  .eclat-footer { padding-block: var(--wp--preset--spacing--50); }
  .eclat-footer__cols { gap: 2.25rem; }
}

/* --- 9. home: hero ------------------------------------------- (Task 6) --- */
/* homepage-directions.html `.ec-hero`: a photograph at full bleed, 560px tall at the
   mockup's fixed 1280px, with the text set bottom-left over it and two CTAs. The plan
   states the live height as 78vh, so that is what the template declares.

   THE OVERLAY IS TWO LAYERS, and both are encre — never coral. A coral tint stains the
   photograph; the coral belongs to the wordmark, the buttons and the badges.
     · core's uniform dim (`dimRatio: 50`, `overlayColor: contrast`, in the template) is
       the readability FLOOR, not decoration. Every ratio quoted here and in §14 is the
       PHOTOGRAPH-INDEPENDENT BOUND — white text over the overlay stack above a
       hypothetical pure-white picture. It needs no image file, so it reproduces from
       arithmetic, and it holds for any photograph the client later drops in. With the
       scrim alone and no dim, the lede's 4.5:1 floor is already lost above roughly 23%
       of the hero's height (4.33:1 at 25%, 2.30:1 at 40%) — which is most of where a
       headline this size sits. With the dim underneath, every measured text position
       clears its threshold at every viewport, worst case 4.03:1 against a 3.0 floor.
       METHOD, since the table itself is not worth carrying here: composite the scrim's
       own alpha at the text's y-position over core's dim over pure white, take WCAG
       relative luminance of the result, and sweep the text positions the hero produces
       at each viewport height. Nothing is sampled from a photograph, so the figures
       reproduce from arithmetic and hold for any picture the client later drops in.
     · the scrim below is the SHAPE: bottom-weighted, so the top of the frame stays a
       photograph and the text block lands on something solid.
   Together they read as a warm cinematic grade. The mockup's own veil runs alpha
   .55–.86 in the brand hue across the WHOLE frame, so this is lighter than what was
   approved, and hue-neutral.

   STACKING — the scrim is a ::before, and that is load-bearing rather than arbitrary.
   WP 7.0.2's cover stylesheet ends with
     body:not(.editor-styles-wrapper) .wp-block-cover:not(…:has(…)) .wp-block-cover__
     inner-container { z-index: 1 }
   which is roughly (0,6,1) — measured, not guessed: an ::after at z-index 1 is
   generated last among the cover's z-index-1 boxes and therefore paints OVER the text,
   and lifting the inner container to 2 loses to that rule without an !important this
   file did not use at the time this was written [6C 2026-08-24, fix round 1: same slip
   as §1/§9's corrected claims -- the file now carries one sanctioned exception (§13's
   `.eclat-tile__img a:focus-visible` halo). The point stands unchanged: THIS rule still
   has no !important available to it, sanctioned or otherwise, so the ::before approach
   is still the one that works here]. A ::before is generated FIRST instead, so the paint order becomes
   photograph (z-index 0) → scrim → core's dim span → text. Stacking two translucent
   layers of the SAME colour is commutative — the composite is
   c(a1+a2-a1a2) + (1-a1)(1-a2)·photo either way — so the dim landing above the scrim
   changes nothing. Core's own `:before` rules are all gated on `.has-background-dim`
   being on the COVER, and in this markup it is on the span, so nothing is displaced.
   CORRECTED 2026-08-21 (6b3): the premise above is false on WP 7.1 — core's z-index
   rules are :has()-gated and do not apply to this markup; the lift is now done
   explicitly by the rule below. */
.eclat-hero {
  align-items: flex-end;                 /* core centres cover content; .ec-hero .txt is at the bottom */
  padding-block: var(--wp--preset--spacing--70) var(--wp--preset--spacing--50);
  /* The hero carries the gutter itself (see the measure note below), so core's 1em is
     replaced rather than added to. */
  padding-inline: var(--wp--preset--spacing--30);
}
.eclat-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(0deg,
    rgba(43, 20, 9, .88) 4%,
    rgba(43, 20, 9, .25) 48%,
    rgba(43, 20, 9, 0) 76%);
}

/* WP 7.1 gates its cover z-index rules behind
   :not(:has(.wp-block-cover__background + .wp-block-cover__inner-container));
   this markup fails the guard, so the container computes z-index:auto and the
   ::before scrim (z-index:1) paints OVER the content. Lift it ourselves:
   photo (auto) -> dim span (auto) -> scrim (1) -> content (2). (6b3 item 1) */
.eclat-hero .wp-block-cover__inner-container { z-index: 2; }

/* `.ec-hero .txt { left: 56px }` and `.ec-sec { padding: 0 56px }` share a left edge in
   the mockup, so the headline has to start exactly where "À venir" does — and those
   section headers are `alignwide`, i.e. 1600px.
   The cover's inner container is therefore declared `layout: default` (flow) in the
   template rather than `constrained`, and takes the measure here. That is not a style
   preference: WordPress emits the constrained layout as
     :root :where(.is-layout-constrained) > :where(…){ margin-left: auto !important }
   so a constrained container centres EVERY child, and capping the headline at 15ch
   produced an 884px box centred in the viewport instead of a headline set at the
   column's left edge. `margin-inline: 0` cannot beat that !important, and this file
   used none at the time this was written [6C 2026-08-24: it now carries one sanctioned
   exception, §13's `.eclat-tile__img a:focus-visible` halo -- see §1's corrected header note].
   Under flow layout the children are simply left-aligned and `max-width`
   means what it says. The gutter lives on `.eclat-hero` above, so the content edge
   lands on (100vw − 1600px) / 2 at any width, exactly like `.eclat-sech`. */
.eclat-hero .wp-block-cover__inner-container {
  max-width: var(--wp--style--global--wide-size);
  margin-inline: auto;
}

/* .ec-hero h1 { max-width: 15ch }, .ec-hero p { max-width: 46ch; line-height: 1.5 }.
   The lede's own class is needed because §2 caps every paragraph at 72ch. The mockup's
   `opacity: .94` on the lede is deliberately NOT carried over: it buys nothing visible
   and only spends contrast on a surface that has none to spare. */
.eclat-hero__title { max-width: 15ch; }
.eclat-hero__lede { max-width: 46ch; line-height: 1.5; }
.eclat-hero__actions { gap: .625rem; }   /* .ec-cta.o { margin-left: 10px } */

@media (max-width: 781px) {
  .eclat-hero { padding-block: var(--wp--preset--spacing--60) var(--wp--preset--spacing--40); }
  .eclat-hero__title { max-width: none; }
  /* The scrim flattens on a phone, and that is an accessibility measurement rather than
     a taste. Measured in a 414x760 viewport: the headline wraps to four lines and its top
     edge lands at 79.6% of a 593px hero, where the bottom-weighted scrim above has already
     decayed to zero — so only the flat 50% dim protects it. On the same bound as §9 that
     pair is 3.38:1. It passes, because 48.7px at weight 800 is large text and the floor is
     3:1, but on a 13% margin with nothing held in reserve if the picture is ever swapped.
     Flattened, the same pair is 7.29:1. The desktop scrim above is the plan's value,
     untouched; this overrides it only below 781px. */
  .eclat-hero::before {
    background: linear-gradient(0deg,
      rgba(43, 20, 9, .88) 4%,
      rgba(43, 20, 9, .62) 60%,
      rgba(43, 20, 9, .30) 100%);
  }
}

/* --- 10. home: upcoming filmstrip + poster cards ------------- (Task 6) --- */
/* `.ec-sec` / `.ec-sech`: the section furniture shared by "À venir" and "Actualités" —
   64px of air, then a baseline-aligned row of the h2 and a "tout …→" link. Both rows
   are `alignwide`, i.e. 1600px, which is also `.eclat-header__inner`'s width, so the
   home's sections line up with the header rather than floating inside it. */
.eclat-sec { margin-block-start: var(--wp--preset--spacing--60); }
.eclat-sech {
  align-items: flex-end;
  gap: var(--wp--preset--spacing--30);
  margin-block-end: var(--wp--preset--spacing--40);   /* .ec-sech margin-bottom: 26px */
}
.eclat-sech > * { margin-block: 0; }
.eclat-sech__more { font-size: .875rem; font-weight: 600; }   /* .ec-sech a: 14px / 600 */
.eclat-sech__more a { text-decoration: none; }
.eclat-sech__more a:hover { text-decoration: underline; }

/* `.ec-strip`: `repeat(3, 1fr) 150px` — three cards and a fourth cropped at the edge.
   The mockup draws that fourth card image-only, with no title and no pills, which is
   what makes it a filmstrip rather than a four-column grid: it is cut off, and the row
   continues past the edge. The parent's `ekla/show-grid` returns six posts in a
   `grid / columnCount 3` post-template and was shared with Papier and Jardin [6C 2026-08-24:
   both retired since Plan 6b Task 3; the pattern is unchanged, still parent-owned, and this
   is still the reason the columns are re-laid here rather than in it], so the
   columns are re-laid here instead of in the pattern.
   FLEX, not grid, and that is measured rather than stylistic. Two grid attempts failed:
   `grid-auto-columns: minmax(15rem, calc(…))` treats the calc as a CEILING, so all six
   tracks settled at 15rem, fitted inside the container and produced no rail at all; and
   core ships
     @media (max-width:600px){ .wp-block-post-template-is-layout-grid ×4 {
       grid-template-columns: 1fr } }
   at (0,4,0), which outranks any reasonable `grid-template-columns` reset and rendered
   65px cards on a 414px phone. A flex row makes `grid-template-columns` inert, so that
   rule cannot reach it, and it is the same shape foundation.css's `.ekla-rail` already
   uses for this exact job — the post-template cannot be given that class, so the rules
   are restated here rather than shared.
   The card width is a `max()`: below about 21rem of container it floors at 15rem and the
   rail simply scrolls further, rather than shrinking six cards into slivers. Keyboard
   users reach every card through the links inside it, and the browser scrolls the
   container to the focused one. */
.eclat-strip .wp-block-post-template {
  display: flex;
  gap: var(--wp--preset--spacing--40);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  padding-block-end: var(--wp--preset--spacing--30);
  scrollbar-width: thin;
  scrollbar-color: var(--eclat-chip-line) var(--wp--preset--color--tertiary);
}
.eclat-strip .wp-block-post-template > li {
  flex: none;
  width: max(15rem, calc((100% - 150px - 3 * var(--wp--preset--spacing--40)) / 3));
  scroll-snap-align: start;
}

/* `.ec-card .ph`: radius 16, 14px of air under it. The featured image renders as a
   <figure style="aspect-ratio:4/3">, so the radius needs the clip on the figure. */
.eclat-sec .wp-block-post-featured-image {
  border-radius: var(--eclat-radius);
  overflow: hidden;
  margin-block-end: .875rem;
  background: var(--wp--preset--color--tertiary);
}
.eclat-sec .wp-block-post-title { margin-block: 0 .5rem; }   /* .ec-card h3 margin-bottom: 8px */
.eclat-sec .wp-block-post-title a { text-decoration: none; color: var(--wp--preset--color--contrast); }
.eclat-sec .wp-block-post-title a:hover { color: var(--wp--preset--color--accent); }
.eclat-sec .ekla-daterange { margin-block-end: .5rem; }

/* `.ec-pills`: 11.5px / 600 pills, the age one carrying the brand. core/post-terms
   emits its terms as anchors with a `__separator` span between them, so the separator
   is hidden and the container becomes the flex row. The age pill's encre-on-soleil is
   transcribed, not invented: eclat-couleurs.html option A pairs `--pillA: #FF9E1B`
   with `--pillAink: #2B1409` (8.42:1). */
.eclat-sec .wp-block-post-terms { display: flex; flex-wrap: wrap; gap: .375rem; }
.eclat-sec .wp-block-post-terms__separator { display: none; }
/* THE HAIRLINE IS A HUMAN RULING (Task 10), and it overrides the mockup, which draws
   these pills borderless. Measured on THIS section's ground — the white card:
   the tertiary fill separates the pill from the card by 1.11:1 and the age pill's
   secondary fill by 2.07:1, where WCAG 1.4.11 asks 3:1 of an interactive component's
   boundary — and foundation.css:66 has already removed the underline, so the fill was
   the pill's only affordance. --eclat-chip-line is the token §1 keeps for exactly this
   job; it measures 3.42:1 against the white card here (and 3.07:1 against the tertiary
   fill it encloses). One value covers the age pill too, because the age pill's GROUND
   is the same white card; only its fill differs.
   The padding is reduced by the border's own 1px on each side so the rendered pill is
   the size the mockup drew, to the pixel — the same compensation §4's ghost button
   makes. Fill, type and shape are untouched.

   [6B4 2026-08-22: RULING REVERSED by the client — pills are borderless again, as the
   mockup drew them. See §14's note; measurements above kept as history.] */
.eclat-sec .wp-block-post-terms a {
  text-decoration: none;
  font-size: .71875rem;
  font-weight: 600;
  padding: .25rem .625rem;
  border-radius: var(--eclat-pill);
  background: var(--wp--preset--color--tertiary);
  color: var(--eclat-ink);
  transition: background-color var(--eclat-ease);
}
.eclat-sec .wp-block-post-terms a:hover { background: var(--eclat-hair); }
.eclat-sec .taxonomy-age a {
  background: var(--wp--preset--color--secondary);
  color: var(--wp--preset--color--contrast);
}
.eclat-sec .taxonomy-age a:hover { background: var(--wp--preset--color--primary); }

/* Task 6's temporary no-photograph poster stood here and is GONE (Task 7). It existed
   because the parent's `ekla/show-card` rendered core/post-featured-image, which emits
   nothing when there is no thumbnail, so the home's rail was five cards of bare text.
   `ekla-vivant/patterns/show-card.php` now emits `<article class="… eclat-tile">` for
   every show-card on the site, home included, and §13's `.eclat-tile:not(:has(img))`
   gives that article the poster — so the scaffold's job is done by one rule instead of
   two, and a thumbnail-less show renders exactly one poster rather than two stacked. */

/* Actualités: `.ec-strip` again but at three even columns and a shorter image, and the
   date reads as the card's meta line rather than as prose.
   The section header carries no "toutes les actualités" link, and that is a site fact
   rather than an omission: `show_on_front` is `posts` and `page_for_posts` is 0, so this
   install has no posts-archive URL at all — /actualites/ 404s. (Moved here from
   front-page.html, whose comments ship to the browser.) */
.eclat-news .wp-block-post-date { font-size: .8125rem; color: var(--eclat-meta); }
.eclat-news .wp-block-post-date a { text-decoration: none; color: inherit; }

/* --- 11. home: temps-fort split ------------------------------ (Task 6) --- */
/* `.ec-feat`: `1.15fr .85fr` (= 57.5% / 42.5%), full bleed, the image flush against a
   dark panel with a brand-coloured label above the title. Encre #2B1409 replaces the
   mockup's #1A1033 ground; `secondary` #FF9E1B is Éclat's equivalent of the mockup's
   bright label colour and measures 8.42:1 on that ground.
   The dark panel is LOCAL to this block, as plan constraint "Éclat's dark ground is
   local to archive-spectacle" requires of the home: it comes from the columns block's
   own colour attributes, not from any root override, and `base` stays #FFFFFF.

   [6B5 2026-08-23, client item 3: the panel described above is no longer dark, and the label
   no longer carries `secondary`. The band now takes the palette's `primary` -- the client's
   "Couleur principale (corail)" -- with `contrast` ink on it, and the label simply inherits
   that ink, so the 8.42:1 figure above describes a pairing this block no longer ships. Two
   things DO survive: the colour still comes from the columns block's own attributes rather
   than any root override, and `base` still stays #FFFFFF. Ink on the unaltered coral is
   4.28:1, a knowingly shipped sub-AA residual (ruling R2). The label figures are given for
   BOTH palettes, since a bare ratio here depends on which one is loaded: the live secondary
   #FFEC80 would have been 3.40:1 on the live coral, and the default #FF9E1B 1.59:1 on the
   default coral -- ink beats both, which is why the label's colour attribute was dropped
   rather than retuned.]
   [6B6 2026-08-23, item 1: "the colour still comes from the columns block's own attributes"
   no longer holds for INK -- script 46 took textColor off this block, so ink now comes from
   this rule's own `color` declaration below (var(--ekla-ink-alaune, ...)), not from a block
   attribute. The ground half is unchanged: `backgroundColor:"primary"` still lives on the
   block.] */
/* No `align-items` here: core ships `.wp-block-columns{align-items:normal!important}`,
   which on a flex container already behaves as stretch, so the media column fills the
   row height. A rule that cannot win is a rule that lies about what the file does. */
.eclat-feature {
  margin-block-start: var(--wp--preset--spacing--60);
  /* Core gives any `.wp-block-columns.has-background` a `padding: 1.25em 2.375em`,
     which measured 21px / 40px here and inset the photograph from the full-bleed edge
     it is supposed to run to. The panel supplies its own padding. */
  padding: 0;
  /* 6b6 item 1: same mechanism as .eclat-footer above (§8) -- textColor is off this
     block's attributes now (script 46), so this plain rule can win over Core's
     `!important` preset class, and an unset `alaune_ink` slot stays invisible. */
  color: var(--ekla-ink-alaune, var(--wp--preset--color--contrast));
}
/* The figure is taken OUT OF FLOW deliberately. Measured with it in flow: `width: 100%`
   plus the file's own 1536x1053 aspect made the image 1280px tall on a wide viewport,
   which set the row height, which the panel then matched — a 400px band rendered at
   1323px. Absolutely positioned, the image cannot inflate the row: the height comes
   from `min-height` and from the panel's own content, and the image fills whatever
   that turns out to be. */
.eclat-feature__media { position: relative; min-height: 25rem; }   /* .ec-feat .ph { height: 400px } */
.eclat-feature__media .wp-block-image { position: absolute; inset: 0; margin: 0; }
.eclat-feature__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.eclat-feature__panel {
  padding: var(--wp--preset--spacing--50);            /* .ec-feat .b { padding: 52px 48px } */
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.eclat-feature__label {
  text-transform: uppercase;
  letter-spacing: .16em;
  font-weight: 700;
  margin-block-end: .875rem;
}
/* .ec-feat p { opacity: .8 } — expressed as a colour rather than an opacity so the
   value is auditable: base at 80% over encre measured 11.39:1.
   [6B5 2026-08-23, client item 3: STALE and removed. That 11.39:1 was white-over-encre, and
   this panel's ground is now the coral with ink text; an 80% ink lands at 3.38:1 against 4.28:1
   undimmed. Same call as the footer prose in Task 3 — at this contrast there is nothing left to
   spend on softening, so the colour is inherited from the block's textColor and only the
   measure and the leading are set here.] */
.eclat-feature__text {
  max-width: 42ch;
  line-height: 1.6;
  color: inherit;
}

@media (max-width: 781px) {
  .eclat-feature__media { min-height: 15rem; }
  .eclat-feature__panel { padding: var(--wp--preset--spacing--40); }
}

/* --- 12. home: CTA band -------------------------------------- (Task 6) --- */
/* `.ec-band`: the brand gradient at full bleed, centred, one heading and one button.
   The ink is `contrast`, declared on the group in the template: white on the soleil
   ramp measures 3.29:1 at the coral stop and 2.07:1 at the soleil stop, and a gradient
   passes only if its worst stop passes. Human ruling, Task 5 — the gradient and the
   palette stand, the ink changes.

   [6B4 2026-08-22, client ruling: the band is FLAT secondary now. The contrast ink and
   the encre-filled CTA below were designed against the ramp's worst stop and hold even
   better on flat secondary (encre 14.55:1; white-on-encre CTA unchanged).]
   [6B6 2026-08-23, item 1: "declared on the group in the template" no longer holds -- this
   task's textColor removal (front-page.html) means the group's own attributes no longer set
   ink at all. The ink is still `contrast` by default, but now comes from this rule's own
   `color` declaration below (var(--ekla-ink-band, ...)), which the client can set
   independently of the global `contrast` slot.] */
.eclat-band {
  margin-block-start: var(--wp--preset--spacing--60);
  padding-block: var(--wp--preset--spacing--60);
  background: var(--wp--preset--color--secondary);
  text-align: center;
  /* 6b6 item 1: same mechanism as .eclat-footer above (§8) -- textColor is off this
     block's attributes now, so this plain rule can win over Core's `!important` preset
     class, and an unset `band_ink` slot stays invisible. The heading inherits this; the
     CTA button below keeps its own explicit colours untouched. */
  color: var(--ekla-ink-band, var(--wp--preset--color--contrast));
}
/* The default CTA rode this same gradient when this was written, so on this ground it would
   have been invisible [6C 2026-08-24: both halves of that sentence are historical now -- the
   6B4 ruling above took the band itself off the gradient (flat secondary), and §4's 6B4
   ruling took the default button off it too (flat secondary). Neither rides a gradient any
   more; the reasoning that led to this dedicated encre CTA still holds (it needed to read
   against the band regardless of gradient vs flat) and the ratios below are unaffected.]
   eclat-couleurs.html option A already carries a second, encre-filled CTA
   (`--cta2: #2B1409`), and that is what the band uses. White on encre is 17.4:1, and
   the pill's own edge against the ground measures 5.29:1 at the coral stop and 8.42:1
   at the soleil stop. A white pill would have been 2.07:1 against the soleil stop and
   would have failed WCAG 1.4.11 for the boundary of a control. */
.eclat-band .wp-element-button {
  background: var(--wp--preset--color--contrast);
  color: var(--wp--preset--color--base);
  box-shadow: 0 4px 14px rgba(43, 20, 9, .25);
}
.eclat-band .wp-element-button:hover { box-shadow: 0 6px 20px rgba(43, 20, 9, .32); }

/* §8 gives `.eclat-footer` 5rem of air because most pages end in white prose and a dark
   band needs separating from it. When the page ENDS in a full-bleed colour band, that
   same air renders as a white stripe between two coloured bands — measured at 80px on
   the home. The band should meet the footer.
   [6B5 2026-08-23: "a dark band" is now the coral band -- client item 3. The reasoning is
   unchanged; only the colour word was wrong.] */
main:has(> .eclat-band:last-child) + footer .eclat-footer { margin-block-start: 0; }

/* --- 13. agenda: the poster wall ----------------------------- (Task 7) --- */
/* agenda-paradigmes.html, Éclat column: a dark ground carrying an irregular mosaic of
   posters, each title set over its picture with a coloured age pastille under it. This
   is the page that separated Éclat from the other two identities [6C 2026-08-24: Papier and
   Jardin were retired in Plan 6b Task 3; kept as the historical reason for this page's
   distinct treatment, not a claim that they still exist].

   THE DARK GROUND IS LOCAL, and that is a plan constraint rather than a preference:
   `base` stays #FFFFFF, so the home, the show pages and every content page remain white.
   Every rule below is therefore scoped under `.eclat-agenda`, a class only
   ekla-vivant/templates/archive-spectacle.html carries.

   CONTRAST — every ratio quoted in this section comes from one script, the same one §9
   describes, extended with this page's surfaces. Two points of method decide most of the
   values below:
     · alpha is composited over the REAL opaque ancestor. Outside a tile that is the
       #12100F ground, never white — measuring these chips against white would have
       flattered every one of them and hidden three failures. [6B4: the wall is now
       color-mix(accent 45%, contrast) — re-measured in the 6b4 browser pass]
     · a gradient passes only if its WORST stop passes.
   The soleil ramp and the palette do not change; where text lands on the ramp the ink is
   `contrast`, never white (human ruling, Task 5 — white is 3.29:1 / 2.07:1). */
.eclat-agenda {
  /* [6C2 2026-08-25, client item 1: the ground and its white ink used to sit HERE, on
     <main>, and painted the whole page — title, édito, filter card and wall alike. The
     client asked for the title + édito on white, with the poster grid unchanged, so the
     pair moved down to `.eclat-agenda__wall` below; this rule keeps only the margin
     reset. §5's filter card is drawn for a white page again (and now from palette slots);
     the §13 dark card overrides and the édito's forced `secondary` link colour were
     deleted with the ground they compensated for.] */
  /* WordPress gives every .wp-site-blocks child a spacing--40 top margin. Left alone it
     renders as a 1.75rem white stripe between the header's hairline and the wall. */
  margin-block-start: 0;
}
/* The rust ground, now local to the query block (wall + pagination + empty state), which
   archive-spectacle.html marks with this class.
   [6B4 2026-08-22, client ruling (variant D of 4 screenshot candidates): the wall was the
   only literal near-black in the build and belonged to NO palette — the exact defect the
   client flagged. Token-driven: accent mixed into the ink, so the wall follows the season
   palette. Under today's palette this computes to rgb(93,29,12); white ink on it ≈ 12.8:1
   (re-measured live in the 6b4 browser pass). color-mix has in-file precedent
   (--eclat-soleil-chip).] */
.eclat-agenda__wall {
  background: color-mix(in srgb, var(--wp--preset--color--accent) 45%, var(--wp--preset--color--contrast));
  color: var(--wp--preset--color--base);
}
/* …and the same stripe, 5rem of it, between the wall and the dark footer. §12 solves the
   identical problem for the home's CTA band; this is that rule for this page.
   [6B5 2026-08-23: the footer is coral, not dark, since client item 3. The stripe problem
   and this rule are unaffected.] */
main.eclat-agenda + footer .eclat-footer { margin-block-start: 0; }

/* The title moved out to .eclat-agenda__head in 5b Task 2, leaving this wrapper holding
   the filter card alone -- which is what gave the (since-removed) sticky below real travel.
   Both are direct children of main.eclat-agenda, so THIS element's containing block is the
   whole poster wall rather than its own height. 5a shipped the sticky on .ekla-filters
   instead, where travel measured 0.00px at every scroll position tested.
   UPDATE (Task 6, D12, 2026-08-19): position:sticky was removed from .eclat-agenda__util
   below -- client decision, the pinned card covered the poster wall on scroll -- but this
   head/util split is KEPT: .eclat-agenda__head still owns the title's own spacing separately
   from the filter card's. */
.eclat-agenda__head {
  max-width: var(--wp--style--global--wide-size);
  margin-inline: auto;
  /* Replaces core's .has-global-padding, which the constrained layout adds -- otherwise
     the head carries two sets of gutters. Bottom padding is 0: the util below owns the
     gap down to the wall. */
  padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--40) 0;
}

.eclat-agenda__util {
  max-width: var(--wp--style--global--wide-size);
  margin-inline: auto;
  /* The shorthand deliberately replaces core's `.has-global-padding`, which the group's
     constrained layout adds. The 28px inline gutter is the page's ONLY horizontal gutter;
     `display: contents` here pins the card correctly but destroys it, which is why the
     template was restructured instead. */
  padding: var(--wp--preset--spacing--40);
}

/* Panel is STATIC by client decision (D12, 2026-08-19): the pinned card covered the poster
   wall on scroll (measured in a browser at 1440x760, scrollY 820, Plan 5b post-plan visual
   pass -- this wrapper computed `background-color: rgba(0, 0, 0, 0)` and the only painted
   layer inside it was §13's `.ekla-filters` at `rgba(255,255,255,.06)`, 6% white, no
   backdrop-filter, so the wall scrolled UNDER the pinned card at 94% strength and all six
   show cards overlapped it, by up to 705x380px, chip labels drawn straight through card
   titles). Task 6 adds édito content above this panel; the client believed that alone would
   stop the overlap. It does not -- a sticky panel just pins further down the page once
   there is more content above it. position:sticky, top and z-index are removed here
   explicitly rather than relying on content to defuse the bug. Still scoped to >=64rem only
   because that is where this rule historically lived (Plan 5b Task 2); nothing about being
   static requires the breakpoint, but narrowing scope further is out of Task 6's remit. */
@media (min-width: 64rem) {
  .eclat-agenda__util {
    /* THE OPAQUE GROUND IS KEPT DELIBERATELY even though the panel no longer moves: it is
       harmless while static, and correct insurance if this panel is ever re-pinned.
       `inherit` rather than a second `#12100F` literal: the parent IS main.eclat-agenda
       (§13's `.eclat-agenda` rule declares that ground [6C 2026-08-24: was "line 1122" --
       a fragile absolute pointer that had already drifted; named by selector instead, which
       cannot rot the same way]), and that parent relationship is not an assumption
       -- test-eclat-agenda.php asserts it by DOM parentNode identity. So this tracks the
       wall automatically and cannot drift from it, which two comments in this file
       (`#12100F ground`, `the #12100F wall`) would otherwise be able to do.
       Composited appearance is UNCHANGED: 6% white over #12100F is the same colour it
       always was, so every ratio §13 quotes against this card still holds.
       [6B4: the wall is now color-mix(accent 45%, contrast) — re-measured in the 6b4
       browser pass] */
    background-color: inherit;
  }
}

/* CSS grid, not `columnCount`: the mosaic needs tiles that span, and an nth-child cadence
   gives the large ones a rhythm that reads as art direction without one line of per-post
   logic.
   THE GRID ITEM IS THE <li>, NOT THE ARTICLE — read out of the live archive rather than
   assumed. core/post-template wraps every rendered pattern in `<li class="wp-block-post">`,
   so the geometry has to live on the li: `grid-column` on the article would style a box
   that is not a grid item at all, and `:nth-child` on the article counts inside its own
   li, where every tile is child 1 and a 9n+1 rule matches EVERY tile. The article is then
   stretched over its li and keeps all of the painting. */
.eclat-wall {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 5px;
  padding: 0 5px 5px;
  margin: 0;
  list-style: none;
}
@media (max-width: 1100px) { .eclat-wall { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .eclat-wall { grid-template-columns: 1fr; } }

.eclat-wall > li { position: relative; overflow: hidden; aspect-ratio: 4 / 3; margin: 0; }
/* Irregular rhythm without per-post logic. 4-col only — it collapses to uniform below
   1100px, where two columns of spanning tiles would be one column of very tall ones.
   THE 2x2 TILE KEEPS THE 4/3 ABOVE — do not give it `aspect-ratio: auto`, which is what
   the plan drew and what shipped in the first pass. Every child of the tile is
   absolutely positioned, so the li has NO in-flow content and `auto` leaves its height to
   be derived entirely from the two grid rows it spans. Those rows are sized by the OTHER
   tiles in them, so on a filtered page that returns one result there are no other tiles
   and the row height is zero: measured live, a single-result agenda rendered its only
   show at 1713x5 px, invisible inside `overflow: hidden`. Twelve live facet values return
   exactly one show (lieu=gazometre, age=des-4-mois, …) and three return two, so this is
   the faceting the plan exists to demonstrate, not an edge case. With the definite 4/3 the
   li supports itself and also feeds row sizing; the mosaic is unchanged to within 1.25px,
   because (2col + gap) * 3/4 and 2 * (col * 3/4) + gap differ only by gap/4.
   `9n+6` and `9n+9` already carry a definite 16/9 and were never exposed. */
@media (min-width: 1101px) {
  .eclat-wall > li:nth-child(9n+1) { grid-column: span 2; grid-row: span 2; }
  .eclat-wall > li:nth-child(9n+6) { grid-column: span 2; aspect-ratio: 16 / 9; }
  .eclat-wall > li:nth-child(9n+9) { grid-column: span 2; aspect-ratio: 16 / 9; }
}
/* The tile itself is a SELF-CONTAINED poster card, because `ekla/show-card` is not the
   agenda's alone: the child pattern wins by slug site-wide, so the parent's
   `ekla/show-grid` — the home's "À venir" rail (§10) — renders these too. It therefore
   carries its own 4:3 box and the card radius §10 gives every other surface on the home.
   Inside the wall the li owns the geometry and the mosaic is edge-to-edge, so there the
   tile is stretched over its li and gives the radius back. */
.eclat-tile {
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  margin: 0;
  border-radius: var(--eclat-radius);
}
.eclat-wall > li > .eclat-tile {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  border-radius: 0;
}

.eclat-tile__img,
.eclat-tile__img img { position: absolute; inset: 0; width: 100%; height: 100%; }
.eclat-tile__img { margin: 0; }
.eclat-tile__img img { object-fit: cover; }
/* The tile scrim is the SHAPE — bottom-weighted, so the top of the frame stays a
   photograph and the caption lands on something solid. Transcribed unchanged. */
.eclat-tile::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(0deg, rgba(10, 8, 8, .92) 4%, rgba(10, 8, 8, .25) 46%, rgba(10, 8, 8, 0) 74%);
}
.eclat-tile__cap { position: absolute; inset-inline: 15px; bottom: 14px; z-index: 2; }
/* …and the caption carries its own FLOOR, which is a measurement rather than decoration.
   The scrim above has stops in % of TILE height, so how much of it lands under the text
   depends on how tall the tile is and how many lines the title takes — neither of which
   this page controls. Measured on Task 6's BOUND (white text over the scrim above a
   hypothetical pure-white photograph: no image file, so it reproduces from arithmetic and
   holds for any picture the client later drops in), a two-line title at 1280px sits at
   2.80:1 against a 3.0 floor and a four-line one at 1.52:1 — 14 of the 49 measured caption
   positions failed. This layer is anchored in PIXELS to the caption box instead, so it
   cannot decay with tile height; the worst position becomes 5.27:1 and none fails. The two
   compose to α .60–.96, and the visible shape is still the ::after's.
   A ::before, and z-index -1: the caption is `z-index: 2`, i.e. its own stacking context,
   so a negative-z-index pseudo-element paints under the caption's own text while the whole
   caption still paints over the tile scrim. */
.eclat-tile__cap::before {
  content: "";
  position: absolute;
  inset: -44px -15px -14px;
  z-index: -1;
  background: linear-gradient(0deg,
    rgba(10, 8, 8, .55) 0,
    rgba(10, 8, 8, .55) calc(100% - 44px),
    rgba(10, 8, 8, 0) 100%);
}
.eclat-tile__title { font-size: 1.2rem; margin: 0 0 .45rem; line-height: 1.06; }
/* `.eclat-tile` is repeated here on purpose, for (0,2,1) rather than (0,1,1). §10's
   `.eclat-sec .wp-block-post-title a { color: contrast }` and
   `.eclat-sec .wp-block-post-terms a { background: tertiary }` were written for the
   PARENT's show-card, which after this task renders nowhere; at (0,2,1) they would paint
   encre over the photograph on the home's rail — about 1.5:1 — and give the same component
   two different pills on two pages. Out-specifying them keeps the fix inside this section
   rather than editing Task 6's. */
.eclat-tile .eclat-tile__title a {
  color: var(--wp--preset--color--base);
  text-decoration: none;
  text-shadow: 0 1px 12px rgba(0, 0, 0, .5);
}
.eclat-wall > li:nth-child(9n+1) .eclat-tile__title { font-size: 1.95rem; }
/* The date line (fix2). §6 gives `.ekla-daterange` --eclat-meta #8A5A44, which is right on a
   white page and measures 1.66:1 over the caption backdrop — so the poster contexts need
   their own treatment. It is deliberately NOT scoped to `.eclat-agenda`: the same overlaid
   tile renders on the taxonomy archives, which are light pages.
   `base@86%` rather than flat white, so the date reads as meta beneath the title. Measured
   at the DATE's own position in the caption stack (not the title's): 7.65:1 at the worst
   caption in the sweep, 13.25:1 at the best.
   TWO DECLARATIONS, the same idiom §3 and §4 use. A UA that cannot parse color-mix()
   drops that declaration at parse time, and with only one here the cascade did NOT fall
   back to plain `base` — it fell through to §6's `.ekla-daterange { color: --eclat-meta }`,
   #8A5A44 over the caption backdrop, which measures 1.66:1. The plain declaration below
   is what makes the documented degradation real: plain `base` is 9.64:1 at this position.
   Adding this line makes the caption ~30px taller and pushes the title up into weaker
   scrim. The whole sweep was re-measured with it and still has ZERO failures; the worst
   title row is 4.45:1 against a 3.0 floor. */
.eclat-tile__cap .ekla-daterange {
  color: var(--wp--preset--color--base);
  color: color-mix(in srgb, var(--wp--preset--color--base) 86%, transparent);
  margin-block-end: .35rem;
}
/* With no photograph the date sits on the soleil ramp, where every dimmed white fails:
   base@86% is 2.77:1 / 1.87:1 and --eclat-tint 1.93:1 / 1.21:1. Encre is 5.29:1 / 8.42:1 —
   the same ruling as the title above it. (0,2,1), so it beats the rule above at (0,2,0). */
.eclat-tile:not(:has(img)) .ekla-daterange { color: var(--wp--preset--color--contrast); }
/* core/post-terms sets its terms inline with a `__separator` span between them, so the
   pills need a flex row and the separator gone — the same two rules §10 needs on the home.
   Task 10: `.eclat-tile__cap` became a real `wp:group` (flow) so the Site Editor stops
   flagging it invalid, which puts this row under core's zero-specificity
   `:where(.is-layout-flow) > *{margin-block-start:var(--wp--preset--spacing--40)}` —
   1.75rem it never carried before, which would grow the caption into the weaker scrim
   §7's ratios were measured against. `margin-block-start: 0` here holds the caption at
   its pre-existing height; the title (own `margin`) and `.ekla-daterange`
   (foundation.css `margin:0`) already out-specify that rule so they needed nothing. */
.eclat-tile__meta { gap: .35rem; margin-block-start: 0; }
.eclat-tile__meta .wp-block-post-terms { display: flex; flex-wrap: wrap; gap: .35rem; }
.eclat-tile__meta .wp-block-post-terms__separator { display: none; }
/* The same hairline ruling as §10, measured against THIS section's ground, which is
   not §10's: the pill sits on the caption backdrop, so the bound is Task 7's — a pure
   white photograph under `.eclat-tile__cap::before` rgba(10,8,8,.55), i.e. #787777.
   (The ::after scrim only ever darkens that further, so it is a genuine worst case and
   it reproduces from arithmetic, with no image file.) Against it the glass fill
   separates the pill by 1.42:1 and the age pill's secondary fill by 2.15:1 — both
   under the 3:1 WCAG 1.4.11 asks. A dimmed white at .75 composites to 3.49:1 over the
   glass fill and 3.71:1 over the age fill; §13's own `rgba(255,255,255,.38)` chip edge
   was tried first and reaches only 2.32:1 here (and .55 only 2.82:1), because this
   ground is eight times brighter in luminance than the filter card's.
   Padding compensated by the border's 1px, as in §10.

   [6B4 2026-08-22: edge DELETED with the rest of the tag-pill hairlines (client
   ruling). The glass fill + blur + white ink remain the pill's affordance; the
   sub-3:1 fill separation is accepted by the same ruling.] */
.eclat-tile .eclat-tile__meta a {
  font-size: .65rem;
  font-weight: 700;
  padding: .2rem .55rem;
  border-radius: var(--eclat-pill);
  background: rgba(255, 255, 255, .19);
  backdrop-filter: blur(6px);
  color: var(--wp--preset--color--base);
  text-decoration: none;
}
.eclat-tile .eclat-tile__age a {
  background: var(--wp--preset--color--secondary);
  color: var(--wp--preset--color--contrast);   /* 8.42:1, and its own opaque fill */
}

/* A show with no photograph. 31 of the 232 have none, and on the DEFAULT view of this
   archive ("à venir") that is currently every single one — so on this page it is the
   common case, not the exception.
   `:has()` keeps it declarative: core/post-featured-image emits nothing at all when there
   is no thumbnail, so the absence of an <img> IS the condition and no per-post PHP is
   needed. Without :has() the tile degrades to a plain dark box; the plan says explicitly
   not to replace it with JS.
   --eclat-soleil-fill rather than --eclat-soleil: §1 records the mockup's own distinction
   — 140deg for large surfaces, because across a 4:3 poster the 115deg ramp bands sideways
   instead of falling down the tile. §7 already uses it for exactly this job.
   THE INK IS `contrast`, and that is §7's finding repeated: on a tile with no photograph
   the title IS the poster, and white measures 3.29:1 at the coral stop and 2.07:1 at the
   soleil stop. Encre measures 5.29:1 and 8.42:1. */
.eclat-tile:not(:has(img)) { background: var(--eclat-soleil-fill); }
.eclat-tile:not(:has(img))::after { display: none; }
.eclat-tile:not(:has(img)) .eclat-tile__cap::before { display: none; }
.eclat-tile:not(:has(img)) .eclat-tile__title a {
  color: var(--wp--preset--color--contrast);
  text-shadow: none;                            /* a dark glow under dark ink on a bright
                                                   ground is smudge, not legibility */
}
/* The pills invert with the ink: `base@19%` over the ramp measures 2.73:1 / 1.81:1 for
   white, so on a poster they become an encre chip — 17.40:1, with the chip's own edge at
   5.29:1 / 8.42:1 against the ramp. The age pill gives up its soleil fill here for the
   plainest of reasons: on this tile the soleil IS the ground. */
.eclat-tile:not(:has(img)) .eclat-tile__meta a {
  background: var(--wp--preset--color--contrast);
  backdrop-filter: none;
  color: var(--wp--preset--color--base);
}

/* --- the home rail: the approved card, not the poster ------------- (fix2) --- */
/* Because this card wins by slug everywhere, the first pass silently redesigned the home's
   "À venir" rail: Task 6 built it from homepage-directions.html `.ec-card` — photo, then
   title BELOW it — and it started rendering as an overlaid poster. Human ruling: keep one
   pattern and let context decide, so the agenda stays the poster wall and `.eclat-strip`
   gets the approved anatomy back. CSS only; the markup already carries what both need.
   Two of these rules are not cosmetic. Inherited from the poster above, on a white card the
   title would be `base` on `base` and the pills `base@19%` on white — both measure 1.00:1,
   i.e. invisible. §10 already holds the right values for this context and they are restored
   here rather than by editing Task 6's section. Values from `.ec-card`:
   `.ph { height: 250px; border-radius: 16px; margin-bottom: 14px }` — the radius and the
   margin are already §10's; only the height and the return to flow are new — and
   `h3 { font-size: 20px; margin: 0 0 8px; weight 700 }`, whose margin is also already §10's.
   250px is written as 15.625rem for the same reason §11 writes `.ec-feat .ph`'s 400px as
   25rem: this file keeps mockup pixels in rem. */
.eclat-strip .eclat-tile {
  position: static;
  aspect-ratio: auto;
  overflow: visible;
  border-radius: 0;      /* the radius belongs to the photo here, not to the card */
}
.eclat-strip .eclat-tile::after,
.eclat-strip .eclat-tile__cap::before { display: none; }   /* no scrim: nothing overlays */
.eclat-strip .eclat-tile__img {
  position: relative;
  inset: auto;
  height: 15.625rem;     /* .ec-card .ph { height: 250px } */
}
.eclat-strip .eclat-tile__cap { position: static; inset: auto; }
.eclat-strip .eclat-tile__title { font-size: 1.25rem; }    /* .ec-card h3 { font-size: 20px } */
.eclat-strip .eclat-tile .eclat-tile__title a {
  color: var(--wp--preset--color--contrast);               /* 17.40:1 on white */
  text-shadow: none;
}
.eclat-strip .eclat-tile__cap .ekla-daterange { color: var(--eclat-meta); }   /* 5.79:1 on white */
.eclat-strip .eclat-tile .eclat-tile__meta a {
  background: var(--wp--preset--color--tertiary);
  backdrop-filter: none;
  color: var(--eclat-ink);                                 /* 9.75:1 on tertiary */
  font-size: .71875rem;                                    /* .ec-pills span: 11.5px */
  padding: .25rem .625rem;
}
.eclat-strip .eclat-tile .eclat-tile__age a {
  background: var(--wp--preset--color--secondary);
  color: var(--wp--preset--color--contrast);               /* 8.42:1 */
}
/* With no photograph the gradient fills the IMAGE SLOT, not the whole card — the card body
   is white here and the title sits on it, so painting the article would put encre text on a
   coral ground it was never measured against. This is the shape Task 6's scaffold had; it is
   correct in this layout and wrong in the poster one, which is why it is scoped rather than
   restored globally. */
.eclat-strip .eclat-tile:not(:has(img)) { background: none; }
.eclat-strip .eclat-tile:not(:has(img))::before {
  content: "";
  display: block;
  height: 15.625rem;
  border-radius: var(--eclat-radius);
  background: var(--eclat-soleil-fill);
  margin-block-end: .875rem;
}

/* Pagination and the empty state. §2 points every prose link at `accent` #D92D1C, which is
   the accessible choice on a white page and measures 3.92:1 on this ground; §8's dark
   footer already answers the same question with `base`. */
.eclat-pag { padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--40); }
.eclat-pag a { color: var(--wp--preset--color--base); }                              /* 18.97:1 */
/* `.wp-block-query-no-results` and not `.wp-block-query > p`: core/query-no-results does
   emit a wrapper div in WP 7.0.2, so the paragraph is a grandchild. Measured in the live
   DOM at /spectacles/?quand=avenir&age=des-10-ans — with the wrong selector the empty
   state's link stayed `accent` at 3.92:1 and the text sat flush against the full-bleed
   edge. Reading core was not enough; the rendered page was. */
.eclat-agenda .wp-block-query-no-results {
  /* The query is `alignfull` for the wall's sake, so without a measure the empty state
     starts at the viewport edge while the filter card above it sits in the 1280px column. */
  max-width: var(--wp--style--global--content-size);
  margin-inline: auto;
  padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--40);
}
.eclat-agenda .wp-block-query-no-results a { color: var(--wp--preset--color--secondary); }  /* 9.18:1 */

/* The one focus indicator on this page the foundation rule cannot deliver.
   `.eclat-tile` and `.eclat-wall > li` are both `overflow: hidden`, and the image link
   fills the tile exactly -- so an indicator drawn OUTSIDE the border box (outline offset
   +2, halo 0-6px out) is clipped away on all four sides. The title (15px inset) and pill
   (14px inset) links have room; only this one is affected. Drawing it inward is the only
   option that survives the clip: -5px offset puts the 3px ring fully inside, and an inset
   halo replaces the outer one. `!important` matches foundation's halo, which is
   `!important` and would otherwise repaint over this at equal specificity. */
.eclat-tile__img a:focus-visible {
  outline-offset: -5px;
  box-shadow: inset 0 0 0 8px var(--wp--preset--color--base) !important;
}

/* --- 14. the show page ----------------------------------------- (Task 8) --- */
/* single-spectacle.html: a full-bleed hero (photo, title, date range), then a
   two-column body — content left, a "Séances" card right that stays in view as
   the article scrolls. THE PAGE IS LIGHT: unlike the agenda (§13), nothing here
   scopes a dark ground — `base` stays #FFFFFF, exactly as it does on the home.

   Task 7 left this section stubbed "responsive breakpoints"; it is retitled here
   because nothing of that shape was ever written into it, and an empty heading
   describing content that isn't there would be the same kind of lie about what
   the file does that §11 already declines to ship. The five media queries §6's
   minors flagged as living outside it are unrelated to this page and untouched,
   per surgical-change scope; a sweep is still 5b's, not this task's. */

/* The hero's only darkening layer is the cover block's OWN dim (dimRatio:60,
   overlayColor:contrast, set in the template) — there is no second custom scrim
   here the way §9's home hero and §13's tiles each add one. core centres cover
   content by default; the title sits at the bottom, same technique as .eclat-hero
   (§9). The cover's own `layout:"constrained"` (not §9's "default") is the
   template's choice, transcribed as given: it centres the title and the date
   range in the 1280px content measure instead of left-anchoring them, so no
   horizontal rule is needed here — core's own container styles already do that. */
.eclat-show__hero {
  align-items: flex-end;
  padding-block-end: var(--wp--preset--spacing--50);
  /* Core ships `.wp-block-cover { padding: 1em }`, and with this theme's fluid typography
     that `em` DRIFTS: measured 14.16px at 390 / 15.09 at 782 / 17.01 at 3440, tracking the
     cover's own font-size exactly, while the page's root padding is a flat 1rem. Above the
     1280 measure the inner container is centred so the padding never shows; below it the
     hero is edge-to-edge and the title sat 1.84px left of the article at 390. Inline only --
     the block-end value above and core's vertical 1em are both left alone. (0,1,0) ties
     core's selector and wins on source order, the same device §8b's `word-break` uses.) */
  padding-inline: var(--wp--style--root--padding-left);
}

/* CONTRAST — the brief's own prose asks for ".ekla-daterange in secondary on the
   scrim"; measured, it fails badly and is not shipped that way.
   The bound: dimRatio:60 + overlayColor:contrast means the cover paints its
   .wp-block-cover__background at opacity .6 over the photograph (WP core,
   wp-includes/blocks/cover/style.css:143-144, "has-background-dim-60{opacity:.6}").
   The photograph is unknowable, so — same convention as §9/§13 — every figure here
   is the worst case over a hypothetical PURE-WHITE photograph: #80726B. That
   really is the worst case, not merely a stand-in for one: relative luminance is
   a non-negative-weighted sum of three independently-maximised channels, so no
   real photo can push the composite lighter than white does.
     · secondary #FF9E1B on #80726B = 2.24:1 — fails even the 3.0 a large line
       would get, let alone the 4.5 a 15px one needs. Not shipped.
     · base #FFFFFF (the title's own colour) = 4.63:1 — clears the 4.5 a 15px
       line needs. Both colours here are fixed hexes, not sampled from a photo,
       so the ratio is exact and stable; it is simply a closer margin than most
       pairs in this file because the floor for normal-size text sits close to
       what a strong-but-not-black scrim can deliver. Same script and method as
       §9/§13: composite the overlay's alpha over a pure-white stand-in for the
       photograph, then take WCAG relative luminance of the result. */
.eclat-show__hero .ekla-daterange {
  color: var(--wp--preset--color--base);
}

.eclat-show__body {
  padding-block: var(--wp--preset--spacing--60);
}

/* The "Séances" card. tertiary is this card's OWN ground — the first time either
   `.ekla-sessions` or `.wp-block-post-terms` has had to render on it instead of
   the white page §6/§2 each assumed, which breaks two pairs measured below.
   ALIGN-SELF IS LOAD-BEARING, not tidiness: core ships
     .wp-block-columns { align-items: initial !important }
   (wp-includes/blocks/columns/style.css:100-101), whose initial value on a flex
   container is "normal", which computes to STRETCH. Left alone, this column
   would be forced to the height of its sibling — usually a much longer article —
   filling that box top to bottom with no free space, so the sticky rule below
   would have nowhere to move and would never visibly stick. Opting out is
   unconditional (not only inside the sticky breakpoint) because the same stretch
   already runs between 782px and 1100px, where the columns sit side by side but
   are not yet sticky: without this the card would be a tall, half-empty
   rectangle there too. */
.eclat-show__meta {
  background: var(--wp--preset--color--tertiary);
  border-radius: var(--eclat-radius);
  padding: var(--wp--preset--spacing--40);
  align-self: flex-start;
}
/* "Above 1100px" is the brief's own number, and the same breakpoint §13's mosaic
   uses. Verified live, not assumed: the header renders 63.4px tall at this width
   (≈ --eclat-header-h's 4rem — confirmed against a real DOM measurement, not the
   token alone), so 6rem of offset leaves 2rem (32px) of clearance. Because
   .eclat-show__meta's containing block is `.eclat-show__body` and not the
   document, position:sticky cannot carry it past that row's own bottom edge —
   it structurally has nowhere left to go once the row (and the article beside
   it) ends, and the footer sits outside `.eclat-show__body` entirely, so it
   cannot be overlapped. Both — clears the header, stops short of the footer —
   were confirmed by scrolling a real rendered show page and reading the card's
   top and bottom out of the DOM, not left as arithmetic. */
/* [6C3 2026-08-25, latent defect closed: `top: 6rem` (96px) was tuned when the header measured
   63px; since 6c T11 the header is 89.39px at 1024-1278 and 127px at >=1280, so the card's top
   31px slid UNDER the header at >=1280. The offset now derives from the same token and formula
   as html{scroll-padding-top} (§2): 8.5rem + spacing-30 = 152px -> 25px of clearance at >=1280,
   63px at 1101-1278. One token, two consumers, no second copy of the header's height.
   Measured live on a show page after the change (6c3 ledger). The 63.4px figure quoted above is
   historical.] */
@media (min-width: 1101px) {
  .eclat-show__meta { position: sticky; top: calc(var(--eclat-header-h) + var(--wp--preset--spacing--30)); }
}

/* §6's list divider, --eclat-hair, was tuned for a white page and measures only
   1.07:1 on tertiary — WCAG 1.4.11 asks 3:1 of a boundary. Re-pointed to
   --eclat-chip-line, the token §1 already keeps as "the lightest step on the
   coral ramp that still complies": 3.07:1 here. Both hexes are fixed, so — as
   with the hero above — the margin is exact, just closer to its floor than most
   pairs in this file; --eclat-ink (this card's other text colour) was tried as a
   tint toward tertiary and rejected: diluted enough to read as a hairline, it
   had already dropped under 3:1 (2.11:1 at 40%, falling further at less). */
.eclat-show__meta .ekla-sessions li {
  border-color: var(--eclat-chip-line);
}

/* post-terms (âge / public / lieu) inherit §2's plain `accent` link by default,
   which measures 4.34:1 here — under the 4.5 an 11.5px label needs, and the one
   place on this page that would otherwise still read as a bare underlined list
   rather than the pill language §10/§13 already established everywhere else this
   component renders. Same recipe, background only swapped: tertiary is this
   card's own ground and a tertiary pill would vanish into it, so the fill is
   `base` instead — --eclat-ink measures 10.86:1 on base (and already 9.75:1 on
   tertiary, for the plain sessions text above). */
.eclat-show__meta .wp-block-post-terms { display: flex; flex-wrap: wrap; gap: .375rem; }
.eclat-show__meta .wp-block-post-terms__separator { display: none; }
/* …and the hairline ruling reaches the third and last ground. Here the pill is the
   REVERSE of §10's — a base fill on the tertiary card — and it separates from that card
   by the same 1.11:1, because the pair is the same two hexes the other way round. The
   edge is not: --eclat-chip-line measures 3.07:1 against tertiary, against 3.42:1
   on §10's white, which is why each section is measured rather than one value copied.
   3.07 is the same margin the sessions divider six rules above already accepts on this
   exact ground, and for the same reason — both hexes are fixed.

   [6B4 2026-08-22, client ruling: tag pills are BORDERLESS — the client reversed the
   Task 10 hairline ruling; the mockup's borderless pills win after all. The 1.4.11
   boundary argument had already lapsed: --eclat-chip-line measures ≈2.16:1 against the
   season palette's lilac tertiary, under the 3:1 it existed to provide. Padding
   re-absorbs the border's 1px so the rendered pill size is unchanged.] */
.eclat-show__meta .wp-block-post-terms a {
  text-decoration: none;
  font-size: .71875rem;
  font-weight: 600;
  padding: .25rem .625rem;
  border-radius: var(--eclat-pill);
  background: var(--wp--preset--color--base);
  color: var(--eclat-ink);
}

/* --- 14b. the show's prose column -------------------------- (2026-07-31) --- */
/* WHY THIS SECTION EXISTS AT ALL. Until now this page had NO prose styling of any
   kind: §14 dressed the hero and the Séances card and stopped, so the description —
   the thing a visitor actually reads — fell through to §2's globals and rendered as
   an unmeasured ribbon. Papier (.pa-show__body) and Jardin (.ja-show__body) each
   styled their own [6C 2026-08-24: both retired since Plan 6b Task 3 -- present tense
   corrected, the historical gap analysis this section was built to close is unaffected];
   Éclat was the only identity of the three that did not, which is
   the same shape of gap as the layout bug recorded on the template above.

   THE MEASURE GOES ON THE CONTAINER, NOT ON THE PARAGRAPH, and that is the one place
   this section deliberately departs from Papier's equivalent rule. §2 ships a global
   `p { max-width: 72ch }`, which caps PARAGRAPHS while leaving every other child of
   the article — blockquote, heading, list, separator — free to run the column's full
   992px. Measured live at contentSize: paragraphs 772px against a blockquote at 992px,
   i.e. two different right edges in one article, which is visible and reads as broken
   rather than as a measure. Capping the container instead gives every child the same
   two edges, so `max-width: none` below is not a loosening — it hands the constraint
   up one level rather than removing it. 68ch is Papier's own number, reused so the two
   identities' articles do not disagree about what a comfortable line is. */
.eclat-show .wp-block-post-content {
  max-width: 68ch;
  line-height: 1.7;
}
.eclat-show .wp-block-post-content p { max-width: none; }

/* The Joomla-neutralising !important block that stood here was deleted in Plan 5d: the
   inline styles it fought no longer exist in post_content. Four of its rules keyed on
   [style*="..."], which can no longer match anything, and the three heading-restoration
   rules below them existed only to undo the first four. */

/* The pull quote. 210 of 233 shows carry one, and it is nearly always the company's
   own note about the piece, so it is the article's one moment of voice and had been
   rendering as core's bare indent. The rule is --eclat-chip-line, the token §1 keeps
   as the lightest step on the coral ramp that still complies (3.07:1 on tertiary,
   3.42:1 on white) — a decorative rule carries no 1.4.11 duty, but reusing the
   complying token costs nothing and keeps one coral edge in this file rather than two.
   --eclat-ink is 10.86:1 on base, already measured for the terms pills in §14. */
.eclat-show .wp-block-post-content blockquote {
  margin-block: 2rem;
  padding-inline-start: 1.5rem;
  border-inline-start: 3px solid var(--eclat-chip-line);
  font-style: italic;
  color: var(--eclat-ink);
}
.eclat-show .wp-block-post-content blockquote p:last-child {
  font-style: normal;
  font-size: .875rem;
  color: var(--eclat-meta);          /* 5.20:1 on base */
}

/* 225 of 233 shows use `<hr>` to fence the practical information off from the
   description. --eclat-hair is this identity's own rule colour, the one §3/§8 already
   draw under the header and above the footer, so the article's separators and the
   page's chrome agree. */
.eclat-show .wp-block-post-content hr {
  margin-block: 2.5rem;
  border: 0;
  border-top: 1px solid var(--eclat-hair);
}

/* --- 16. the show's practical coda --------------------------- (Plan 5e) --- */
/* WHY THIS SECTION EXISTS. Plan 5d moved three repeated boilerplate blocks out of 252
   post_content rows into parent-theme blocks. 5d's spec never listed CSS, so all three
   shipped unstyled -- measured 2026-08-02, `ekla-after` appeared in exactly ONE file in
   the whole themes/ tree: functions.php, where it is emitted. On the live page the block
   read as naked text floating between the article and the footer, which is what the
   client reported: "it doesn't look like it belongs to the show description."

   HUMAN RULING, 2026-08-02: it does not belong to the description. It is an
   after-the-visit note to teachers, identical on all 233 shows and relevant to the 207
   carrying `Scolaire`. So it gets its own ground and reads as a deliberate module -- the
   separation becomes intentional rather than accidental.

   ⚠ THE OFFSET THIS COMMENT USED TO DEFEND IS GONE, AND NOTHING HERE CHANGED TO REMOVE IT.
   Until 2026-08-02 this band sat 160px right of the article, and that gap was argued for
   here as "a module aligns to the page; a column aligns to its grid". The client read it as
   the description not lining up with the block, and overruled it. The fix was NOT made in
   this section: the band was already sitting exactly on the page's content measure (the
   Plan 5e browser pass measured deltaX = 0). It was the ARTICLE that was off-measure --
   `.eclat-show__body` carried `align="wide"`, so the prose began at x=913 while the hero
   title, this band and the footer all began at x=1073. Dropping `alignwide` from the
   template moved the column onto the page's spine and the 160px closed from the other side.
   The prose measure itself is untouched: §14b's 68ch cap is narrower than either column
   width, so the paragraphs are the same 729px they always were -- only their position moved.

   THE GROUND IS `tertiary`, AND CREAM WAS MEASURED AND REJECTED. Éclat's page is pure
   white and --eclat-cream is 1.06:1 against it -- too faint to read as a band at all.
   Same script that reproduces §14's 4.63:1:
       cream     #FFF7F1   1.06 vs page   ink 10.25   chip edge 3.23
       tertiary  #FFF0E6   1.11 vs page   ink  9.75   chip edge 3.07
       hair      #FFE6D8   1.20 vs page   ink  9.08   chip edge 2.86  <- fails 1.4.11
       hair-warm #FFE0CE   1.25 vs page   ink  8.69   chip edge 2.74  <- fails 1.4.11
   The two grounds with more presence break the chip edge below the 3:1 WCAG 1.4.11 asks
   of a boundary, and both are HAIRLINE tokens -- a rule colour used as a ground is a
   category error in this file. tertiary is also the Séances card's own ground (§14), so
   this page's two practical surfaces are made of one material and every ink pair on it
   was already measured there. 1.11 also sits in the same range as Papier's 1.14 and
   Jardin's 1.08: the three bands are equally quiet by measurement, not by coincidence.
   NOT --eclat-soleil. That gradient was §12's home CTA band [6C 2026-08-24: §12 has been
   flat `secondary` since the 6B4 client ruling -- this disambiguation is historical, not
   a live "elsewhere in this file" pointer, but the choice it explains (tertiary here, not
   the brand ramp) is unaffected], and a questionnaire note for
   teachers must not shout louder than the show it follows. */
.ekla-after {
  margin-block-start: var(--wp--preset--spacing--60);
  padding-block: var(--wp--preset--spacing--50);
  /* Jardin's §16 device: the root padding this alignfull band cancels, handed straight back.
     Éclat's article carries NO gutter of its own -- its text sits on the root padding -- so
     here the term Jardin adds to its gutter is the whole value. It was spacing--40 (28px),
     which centring hid above the 1280 measure and which showed up as a 12px step below it
     (band 28 vs article 16). Vertical air is untouched; only the inline edge moved. */
  padding-inline: var(--wp--style--root--padding-left);
  background: var(--wp--preset--color--tertiary);
}

/* 40px was page-title scale on a practical footnote -- the loudest single part of the
   defect, and the same 40px in all three identities because nothing had touched it.
   1.5rem keeps it a heading without competing with the h1 sitting in the hero above. */
/* [6D 2026-09-04] the heading now comes from the keyed page as a plain h2 — same scale. */
.ekla-after__title, .ekla-after h2 {
  font-size: 1.5rem;
  margin-block: 0 1rem;
  color: var(--eclat-ink);              /* 9.75:1 on tertiary */
}

.ekla-after p {
  color: var(--eclat-ink);              /* 9.75:1 on tertiary */
  margin-block: 0 .75rem;
}
.ekla-after p:last-child { margin-block-end: 0; }

/* The links stay INLINE inside their sentences: the copy is byte-extracted from #4794 and
   may not be restructured, so there is no button to make here. --eclat-ink is 9.75:1 on
   this ground and the underline carries the affordance, which means the coral does not
   have to be spent on a teachers' footnote. */
.ekla-after a {
  color: var(--eclat-ink);
  text-underline-offset: .2em;
}

/* §8 gives .eclat-footer 5rem of air because most pages end in white prose. This page now
   ends in a coloured band, so that air renders as a white stripe between two grounds --
   §12 hit exactly this on the home and this is its rule, re-keyed to the new band.
   `.ekla-after` really is main's last child in all four single-spectacle templates
   (checked in the rendered document, not read off the template). */
main:has(> .ekla-after:last-child) + footer .eclat-footer { margin-block-start: 0; }

/* THE COMPANY LINK ON THE HERO -- A CONTRAST FAILURE, NOT A PREFERENCE.
   `.ekla-compagnie` renders inside the cover (template line 9) and its anchor inherited
   the GLOBAL link colour #D92D1C, because §14b's encre link override is scoped to
   `.wp-block-post-content` and this block is not in post-content. The cover paints
   `contrast` #2B1409 at dimRatio:60 over the photograph, so -- same convention as
   §9/§13/§14, worst case over a pure-white stand-in for the unknowable photo:
       #D92D1C on #80726B (white photo, WORST case)   1.04:1
       #D92D1C on #1A0C05 (black photo, BEST case)    3.95:1
   It fails the 4.5 a 17px normal line needs against EVERY possible photograph, best case
   included -- on 180 shows carrying _compagnie_url, on the active default identity.
   `base` is the fix and it is not a new measurement: §14 already re-coloured
   `.ekla-daterange` -- 20px below this line, on the same scrim -- to base for the same
   reason, at 4.63:1. The underline stays: on a photograph, colour alone cannot carry the
   affordance. Both selectors are needed because 47 of the 227 shows carrying `_compagnie`
   have no `_compagnie_url`, so the name renders as bare text with no anchor to inherit. */
.eclat-show__hero .ekla-compagnie,
.eclat-show__hero .ekla-compagnie a {
  color: var(--wp--preset--color--base);
}

/* `.ekla-booking` is the page's conversion point and was rendering as a trailing sentence
   28px under the session list with nothing between them. It becomes the Séances card's
   call to action: the card's own hairline above it, in --eclat-chip-line, which measures
   3.07:1 on tertiary -- the value §14 already accepts on this exact ground, and the reason
   --eclat-hair is not used here (1.07:1 on tertiary, measured in §14). The phone number
   already carries <strong> from the callback; nothing here restructures the copy. */
.eclat-show__meta .ekla-booking {
  margin-block-start: 1rem;
  padding-block-start: 1rem;
  border-block-start: 1px solid var(--eclat-chip-line);
  color: var(--eclat-ink);              /* 9.75:1 on tertiary */
}
.eclat-show__meta .ekla-booking a { color: var(--eclat-ink); }

/* §16 MUST STAY LAST IN THIS FILE. verify-5e.php extracts it with
   /\* --- 16\..*
   /s -- one pattern, split across those two lines on purpose: typed as a single string its
   closing delimiter would end THIS comment and leave the rest of the sentence as invalid
   CSS. The match is greedy and unbounded, so it runs to EOF. Append a §17 and every
   §16-scoped assertion in that gate silently widens to cover it -- which fails CLOSED
   (stricter), never open, but is not what the assertion claims. */

/* --- 17. the consent banner ---------------------------------- (Plan 5f) --- */
/* Complianz exposes 34 CSS custom properties; overriding them under this identity's body class
   is the only route to three looks, because the plugin's own Appearance settings are GLOBAL and
   this build switches identity by cookie. Verified live before choosing this route: overriding
   --cmplz_button_accept_background_color repainted the button on the next frame.

   SPECIFICITY, CHECKED RATHER THAN ASSUMED. Complianz declares these as `:root { … }` in the
   generated stylesheet (uploads/complianz/css/banner-5-optin.css, read from source), specificity
   (0,1,0). `body.wp-child-theme-ekla-vivant .cmplz-cookiebanner` is (0,2,1); the .cmplz-title
   rule below is (0,3,1) against the base rule's (0,2,0). Both win outright on specificity, so it
   does not matter that Complianz's own stylesheet is injected by JS after the page loads
   (complianz.js reads an inline "css_file" template and appends a <link> at runtime) and could
   in principle land after this one -- specificity beats source order regardless.

   CONTRAST, MEASURED:
     white #FFFFFF on --eclat-ink #5A3324  =  10.86:1   (the accept button; needs >= 4.5)
   NOT --eclat-chip-line (the plan's own first draft, #E36253): measures 3.42:1 for white text,
   below the floor -- already on record in §1 as "3.42:1 against the white chip fill", the same
   number restated here as the reason it fails rather than as a success.
   NOT `accent` #D92D1C either, even though flat white-on-D92D1C independently measures 4.84:1 in
   isolation (also already on record, §3: "reads 4.84:1 on white"): F5 below forbids `accent` for
   a graphic on EVERY identity by human ruling (2026-08-02), no escape hatch for Éclat. The plan's
   OWN citation for D92D1C here -- "the file's existing complying coral, §14 measured it at
   4.63:1 on the hero scrim" -- is itself wrong: §14 shows D92D1C is what FAILED on that scrim
   (1.04:1 worst case / 3.95:1 best case) and was REPLACED by `base`, which is the value actually
   measured at 4.63:1 there. D92D1C is the file's existing FAILING coral, not a complying one --
   corrected here rather than propagated.
   NOT `primary` #FF4D3D (3.29:1) or `secondary` #FF9E1B (2.07:1) either: both fail, matching this
   file's own §4 citations for the same two hexes exactly ("Encre... White on the soleil ramp
   measures 3.29:1 at the coral stop and 2.07:1 at the soleil stop").
   --eclat-ink is Éclat's existing UI-ink token -- "navigation and chip labels" per §1 -- the same
   interactive role a button plays, not a new token invented to chase the number.

   ADDED BEYOND THE PLAN'S DRAFT: --cmplz_hyperlink_color. Not in the brief's Step 3 text, added
   after the browser check the brief itself mandates (Global Constraint 11) showed why it was
   needed: the "Politique cookies" / "Vie privée" links inside the banner rendered in Complianz's
   own stock #3B29FF (measured live, getComputedStyle) against everything else in this banner
   being on-brand -- exactly the "stock plugin dialog" impression this whole task exists to
   remove. --eclat-ink again: it is text inside the SAME white banner ground the accept button's
   text ratio was already measured against, so 10.86:1 applies unchanged.

   ⚠ FIX ROUND 1, CRITICAL 2 (parallel finding, this identity). Neither the plan nor the first
   draft ever gave this banner a boundary: --cmplz_banner_border_width defaults to 0 in
   Complianz's own generated stylesheet, so the banner's only edge against the page (Éclat's
   `base` is white everywhere, so banner ground = page ground = 1.00:1) was the stock box-shadow,
   measured under 2:1. Decided, deliberately, alongside Papier's fix rather than leaving it
   unexamined: added a real 1px border. --eclat-chip-line is not a new choice -- it is the SAME
   token rejected for the accept button earlier in this comment for being too light FOR TEXT
   (3.42:1, below the 4.5 floor); a 1px border needs only WCAG 1.4.11's 3:1, which 3.42:1 clears
   with margin, and the number is already on record in §1, not re-derived.

   ⚠ FIX ROUND 1, IMPORTANT 1. All four banner buttons (.cmplz-btn: accept/deny/view-preferences/
   save-preferences) computed `font-family: Arial` live, on every identity, including this one --
   `font-family` on `.cmplz-cookiebanner` does not reach a <button>; cookiebanner.css:365 sets no
   family on `.cmplz-btn` and the UA stylesheet's own button font wins by default. Fixed with the
   same `inherit` idiom foundation.css already uses for `.ekla-cookie-link`'s "font: inherit" --
   proven pattern for the identical "form controls don't inherit like everything else" gap.

   ⚠ FIX ROUND 1, IMPORTANT 2. Deny and Préférences were stock Complianz grey (#f9f9f9/#222/#f2f2f2)
   on every identity, with an edge against the white banner ground measured at 1.05:1 -- for a
   task whose purpose is removing exactly that impression, leaving two of three buttons stock was
   the wrong place to stop, and it is also the "refuse harder to find than accept" shape EU
   regulators flag. --wp--preset--color--tertiary background is a warm, near-white ground distinct
   from pure `base`; --eclat-chip-line for the border reuses the SAME token as the banner's own new
   edge (3.42:1 against `base`, one hairline colour for both); --eclat-ink for text is already on
   record at 9.75:1 on tertiary (§14). Deny and Préférences/Enregistrer share this treatment within
   Éclat -- both are secondary actions of the same weight, so one look for both is not blending
   across identities, only across two buttons that play the same role in this one. */
body.wp-child-theme-ekla-vivant .cmplz-cookiebanner {
  --cmplz_banner_background_color: var(--wp--preset--color--base);
  --cmplz_banner_border_color: var(--eclat-chip-line);
  --cmplz_banner_border_width: 1px 1px 1px 1px;
  --cmplz_text_color: var(--eclat-ink);
  --cmplz_hyperlink_color: var(--eclat-ink);
  --cmplz_button_accept_background_color: var(--eclat-ink);
  --cmplz_button_accept_border_color: var(--eclat-ink);
  --cmplz_button_accept_text_color: var(--wp--preset--color--base);
  --cmplz_button_deny_background_color: var(--wp--preset--color--tertiary);
  --cmplz_button_deny_border_color: var(--eclat-chip-line);
  --cmplz_button_deny_text_color: var(--eclat-ink);
  --cmplz_button_settings_background_color: var(--wp--preset--color--tertiary);
  --cmplz_button_settings_border_color: var(--eclat-chip-line);
  --cmplz_button_settings_text_color: var(--eclat-ink);
  --cmplz_button_border_radius: 999px 999px 999px 999px;
  --cmplz_banner_border_radius: 16px 16px 16px 16px;
  font-family: var(--wp--preset--font-family--body);
}
body.wp-child-theme-ekla-vivant .cmplz-cookiebanner .cmplz-title {
  font-family: var(--wp--preset--font-family--display);
}
body.wp-child-theme-ekla-vivant .cmplz-cookiebanner .cmplz-btn {
  font-family: inherit;
}

/* THE MAP PLACEHOLDER (carried from Task 4). Complianz's own asset for the blocked OSM embed on
   /ekla/ is a Google-Maps-styled JPEG -- the plugin ships no OpenStreetMap-specific placeholder
   family. VERIFIED LIVE rather than assumed: the wrapping element is already server-rendered as
   <div class="cmplz-placeholder-parent"> around the iframe (class-cookie-blocker.php's rewrite),
   so this hooks the STABLE markup rather than the class complianz.js adds after the fact on the
   SAME element (.cmplz-blocked-content-container, added on IntersectionObserver) -- hooking the
   stable class means the tint applies from first paint, with no flash of the plugin's own white.
   THE IMAGE CANNOT BE COVERED BY A NORMAL RULE. complianz.js sets it as
   `.cmplz-placeholder-N { background-image: url(…) !important; }` (cmplz_append_css, read from
   source, N a runtime-assigned index) -- only another !important could out-rank that, and Global
   Constraint 5 forbids one here. This does not fight it: an opaque ::before is a SEPARATE box,
   painted above its own parent's background regardless of z-index, so it covers the image
   without ever touching the property the plugin marked with that flag. Reuses §13's
   no-image-fallback gradient exactly: a missing map is the same situation as a missing show
   photograph. The notice text sits above it at the plugin's own z-index:98
   (assets/css/cookieblocker.css), so nothing here needs to restate that stacking.

   ⚠ FIX ROUND 1, CRITICAL 1. THE FIRST VERSION OF THIS RULE HID THE MAP FOREVER, EVEN AFTER
   CONSENT. .cmplz-placeholder-parent is server-rendered and Complianz NEVER removes it --
   confirmed by reading cmplz_remove_placeholder() (complianz.js:886-893), which strips only
   .cmplz-blocked-content-container and the numbered .cmplz-placeholder-N class from the SAME
   element, never .cmplz-placeholder-parent itself. An unconditional ::before on that class
   therefore painted over the real iframe permanently once it loaded. Verified live before this
   fix: clicked Accepter via `.click()` (a real bubbling click event, which is what Complianz's
   own delegated listener responds to), then confirmed `document.elementFromPoint()` at the map's
   centre returned the DIV, not the IFRAME, with the iframe's own src already the real OSM URL.
   FIXED WITH :has(), NOT A BODY CLASS. Complianz DOES add a `cmplz-marketing` class to <body> on
   accept (confirmed live, cmplz_set_category_as_body_class(), complianz.js:388-409) and that was
   the other option the review offered -- but :has() ties the rule to the exact DOM fact that
   changes (cmplz_enable_category() removes .cmplz-blocked-content-notice from the DOM the moment
   ITS OWN category/service is granted, complianz.js:739-755), rather than to a body-wide class
   that would need a second, separately-maintained category name if this project ever blocks a
   second third party. :has() is not a new technique here: this identity's own §7 no-image
   fallback already depends on it 17 times. */
body.wp-child-theme-ekla-vivant .cmplz-placeholder-parent {
  position: relative;
}
body.wp-child-theme-ekla-vivant .cmplz-placeholder-parent:has(.cmplz-blocked-content-notice)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--eclat-soleil-fill);
  border-radius: 3px;          /* matches .cmplz-blocked-content-container's own radius */
}
body.wp-child-theme-ekla-vivant .cmplz-placeholder-parent .cmplz-blocked-content-notice {
  background-color: var(--wp--preset--color--contrast);
  color: var(--wp--preset--color--base);   /* 17.40:1 on this notice's own contrast ground.
                                               [6B5 2026-08-23: the cross-reference that stood
                                               here -- "the same pair .eclat-footer uses" --
                                               died with client item 3, which moved the footer
                                               to coral with ink text. This notice keeps its
                                               dark ground and its ratio; only the comparison
                                               to the footer is gone.] */
  border-radius: var(--eclat-radius-sm);
  font-family: var(--wp--preset--font-family--body);
}

/* THE FOOTER "Gérer les cookies" BUTTON (carried from Task 5's foundation.css review item; see
   that file's own comment for the full measurement). `color: inherit` already lands the button
   on rgb(255,255,255) -- full, undimmed white -- at 17.40:1 on .eclat-footer's contrast ground.
   The sibling Mentions légales / Vie privée links are the SAME white at opacity:.82 (still
   comfortably safe): a near-match, the button reading slightly crisper rather than wrong, so the
   colour is left as a deliberate choice rather than forced to an exact match nothing requires.
   What was genuinely missing: the button had NO hover or focus feedback at all, while every
   sibling link gets one (.eclat-footer a:hover restores opacity to 1 and underlines). Opacity is
   already at its ceiling here, so only the underline is new -- matched to that same rule.
   [6B5 2026-08-23, client item 3: the 17.40:1 above is STALE — it measured white on the ink
   ground this footer no longer has. `color: inherit` (foundation.css:498) still does the right
   thing without any change here: the button now inherits the same ink as its sibling links, at
   the same 4.28:1, so the "near-match" this comment describes is now an exact match. The rule
   itself is unchanged and MUST stay — verify-5f.php:433-441 uses
   `.eclat-footer .ekla-cookie-link:focus-visible` as the structural anchor bounding section 17.] */
.eclat-footer .ekla-cookie-link:hover,
.eclat-footer .ekla-cookie-link:focus-visible {
  text-decoration: underline;
}

/* 5G-ECLAT-BEGIN */
/* =====================================================================
   ORPHAN SURFACE (Plan 5g). Body-class scoped: these surfaces share ONE
   parent markup, so body classes are the only identity-blind axis (spec
   section 5). Equal-specificity wins over foundation on source order via
   the ['ekla-foundation'] dependency -- by construction, not luck.
   Meta ink is --eclat-meta, NOT primary: prose-size primary measures
   3.29:1 (foundation's own prose-links note) and the label is smaller.
   ===================================================================== */
body.page .ekla-page__title,
body.single-post .ekla-article__title,
body.category .ekla-news__title,
body.search .ekla-search__title,
body.error404 .ekla-404__title {
  font-family: var(--wp--preset--font-family--display);
  letter-spacing: -0.02em;
  line-height: 1.05;
  font-size: clamp(2.25rem, 4.2vw, 3.75rem);
}
body.page .ekla-page__head,
body.single-post .ekla-article__head {
  border-block-end: 1px solid var(--wp--preset--color--contrast);
  margin-block-end: var(--wp--preset--spacing--40);
}
body.single-post .ekla-article__label,
body.single-post .ekla-article__label a,
body.single-post .ekla-article__date,
body.category .ekla-news__card-date {
  color: var(--eclat-meta);
}
body.category .ekla-news__card-title a,
body.search .ekla-hit__title a {
  font-family: var(--wp--preset--font-family--display);
  color: var(--wp--preset--color--contrast);
}
body.error404 .ekla-404__title { font-size: clamp(3rem, 8vw, 6rem); }
body.single-post .ekla-article__back a { color: var(--wp--preset--color--contrast); }
/* 5G-ECLAT-END */

/* 5G2-ECLAT-BEGIN */
/* Forms accent (Plan 5g-2). Vars only, plus the one bespoke gesture the
   contract cannot carry (the lift). The button quotes .eclat-btn (this
   file, ~461): soleil gradient ground, and THE RULING -- encre ink, never
   white; white measures 3.29:1 at the coral stop and 2.07:1 at the soleil
   stop, encre 5.29:1 / 8.42:1. --ekla-form-check is encre too: Éclat's
   coral primary sits at the 3:1 boundary on a white field ground.

   [6B4 2026-08-22: the ramp figures above are historical — the button now rides flat
   secondary; encre on secondary = 14.55:1.] */
.forminator-ui.forminator-custom-form[data-design=default] {
  --ekla-form-field-radius: 10px;
  --ekla-form-check: var(--wp--preset--color--contrast);
  --ekla-form-btn-bg: var(--wp--preset--color--secondary);   /* [6B4: flat, see §4] */
  --ekla-form-btn-bg-hover: var(--wp--preset--color--secondary);
  --ekla-form-btn-fg: var(--wp--preset--color--contrast);
  --ekla-form-btn-fg-hover: var(--wp--preset--color--contrast);
  --ekla-form-btn-radius: var(--eclat-pill);
  --ekla-form-btn-shadow: var(--eclat-shadow);
  --ekla-form-btn-shadow-hover: 0 6px 20px color-mix(in srgb, var(--wp--preset--color--primary) 45%, transparent);
}
.forminator-ui.forminator-custom-form[data-design=default] .forminator-button.forminator-button-submit:hover {
  transform: translateY(-1px);
}
.forminator-ui.forminator-custom-form[data-design=default] .forminator-button.forminator-button-submit:active {
  transform: translateY(0);
}
/* 5G2-ECLAT-END */

/* 6B-DOORS-BEGIN — the hero's four entry doors (client decision D2, 2026-08-19).
   Cards sit ON the photo, so each carries its own SOLID ground: text never rides
   the scrim (the 5e lesson — nothing passes 4.5:1 against an arbitrary photo).
   Encre on base measures 15.9:1 [CORRECTED 2026-08-21 (6b3 fix round 3): stale figure.
   Task 9's browser pass live-measured this exact pair (base = #FFFFFF, contrast =
   #2B1409, matching this comment's own colors) at 17.40:1, independently re-verified by
   the WCAG relative-luminance formula against those two hex values -- 17.40:1 is the
   correct ratio, not 15.9. Both numbers clear the 4.5:1 floor by a wide margin, so this
   was a documentation-only error, not a live contrast defect]. The accent bar is the
   soleil gradient — decoration, not ink, so the worst-stop rule does not apply to it
   [6B4: bar deleted.].
   2026-08-19: shipped the ::before-strip variant, NOT border-image — verified by
   screenshot in Brave (zoomed on the card's top-left corner) that border-image + the
   card's own border-radius produces a hard SQUARE corner (border-image ignores
   border-radius), exactly the conflict the brief flagged. [6B4 2026-08-22: the strip is
   DELETED (client item 3). Its "follows the card's curve" claim above was wrong — CSS
   corner-overlap scaling caps a 4px-tall box's radii at ~4px against the card's 12px,
   and with no overflow:hidden the squarer corners protruded past the card silhouette
   as gradient "ears" on the photo; the strip's negative margins also ate the top
   padding (content 20px from the top vs 28px bottom). The clean card stands alone.] */
.eclat-doors {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--wp--preset--spacing--30);
  margin-block-start: var(--wp--preset--spacing--50);
}
/* [6B5 2026-08-23, client item 1: "the 4 activities don't have any style now, maybe a nice
   all corner border".] The border is the RESOLUTION of the conflict the fence documents
   above, not a reopening of it: `border-image` could not follow `border-radius`, which is
   why the 6b4 strip produced protruding gradient "ears" -- but a plain `border` follows the
   radius on all four corners by definition, costs no pseudo-element and eats no padding.
   `accent`, not `primary`: measured on the white card, accent #992910 is 7.84:1 while
   primary is 4.06:1 on the live coral and only 3.29:1 on the default one -- 0.29 above the
   WCAG 1.4.11 floor of 3:1 for the boundary of a control, which is no headroom at all for a
   client who edits this palette every season. accent is also the palette's own
   "Titres & liens" slot, so the edge reads as the same family as the card's link colour. */
.eclat-door {
  display: block;
  position: relative;   /* [6C 2026-08-24: inert -- the 6B4-deleted accent strip was the only
                            positioned descendant that needed this containing block. Harmless,
                            kept rather than removed (comment-hygiene work does not touch
                            executable lines); flagged for a future cleanup task. */
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--contrast);
  border: 2px solid var(--wp--preset--color--accent);
  border-radius: .75rem;
  box-shadow: var(--eclat-shadow);
  padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--30);
  text-decoration: none;
  transition: transform .15s ease, box-shadow .15s ease;
}
.eclat-door:hover,
.eclat-door:focus-visible { transform: translateY(-3px); box-shadow: 0 10px 28px rgba(43, 20, 9, .28); }
.eclat-door strong { display: block; font-size: 1.0625rem; line-height: 1.2; }
.eclat-door span { display: block; margin-top: .35rem; font-size: .8125rem; line-height: 1.35; opacity: .82; }
@media (max-width: 63.9375rem) { .eclat-doors { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 39.9375rem) { .eclat-doors { grid-template-columns: 1fr; } }
/* 6B-DOORS-END */

/* 6B-LOGO-BEGIN — the header ships the WP custom logo, not a text wordmark (client
   decision D10, 2026-08-19). wp:site-logo replaces wp:site-title in parts/header.html;
   the seed file (docs/client/meeting-prep/logo.png, scripts/migrate/33-seed-site-logo.php)
   is monochrome black on a transparent ground, and Sarah replaces it seasonally from the
   admin — nothing here may couple the mark's colour to a palette token. */
/* TASK 14 (browser pass, 2026-08-19): THE MARK IS NOT ELASTIC.
   `.eclat-logo` is a flex item of `.eclat-header__inner` (space-between), and flex items
   default to `flex-shrink: 1` — so between 640px and ~1023px, where the nav block drops out
   of its hamburger (WP's own overlay breakpoint is 600px) and lays nine items out inline,
   the nav's demand for space was being paid for by the logo. MEASURED, all five pass pages,
   before this rule: the declared 90px rendered as **53.86px at 640** and **66.06px at 768**
   (a 150x94 mark drawn 53.86x33.75, i.e. the "ART POUR TOUS" line ~4px tall — illegible),
   and the mark got SMALLER as the viewport got BIGGER: 110 → 54 → 66 → 90 → 150. Task 7
   measured only 1024/1280/1440/3440, so this band had never been looked at.
   Cost, measured rather than assumed: at 640 the nav takes a THIRD row (navH 50 → 78);
   768 stays at 2 rows; 1024 and 1280+ stay at ONE row, so Task 7's required floor is
   untouched, and horizontal overflow stays 0 at all ten widths on all five pages.
   85px would have held 2 rows at 640, but only via a third, ~640–767-wide media band
   fitted to one measured width — the 5b lesson (a working per-width fix deleted in favour
   of one shared rule) says don't. One declaration, stated once. */
.eclat-logo {
  flex-shrink: 0;
}
/* [T17 2026-09-07: the client's 2026-27 mark (#15988) is SQUARE, the seeded one was 150x94.
   Sized by HEIGHT now, at the exact heights the three width rules below used to yield for
   the 150/90/110px-wide mark (94 / 56 / 69px), so every header height this file measured
   -- --eclat-header-h 8.5rem, the 1024px one-row nav -- stays as measured. With a square
   mark the width-based rule drew it 150px tall and the header grew to 183px (measured).] */
.eclat-logo img {
  display: block;
  height: 94px;
  width: auto;
}
/* FIX ROUND 1 (task review Finding 2): the fixed 150px logo is wider than the text
   site-title it replaced, and nothing shrinks it below 1280px, so nine nav items wrapped
   to 2 rows at 1024px — the exact regression class this header has produced twice before
   (identity.css:449's C1 history: "1 row at 1024-1400px" was the target this broke).
   Same lever §3 already uses for this symptom (the 1400px nav step-down), scoped to the
   band below the required 1280px floor. MEASURED, not guessed, with the Step 5 re-measure's
   own same-origin-iframe technique — real CSS edits + fresh navigates per candidate
   (a JS-injected inline override on the <img> gave nonsense mid-flex-shrink readings and
   was abandoned as unreliable on this host): 100px still wrapped Contact to a second line
   at 1024px (measured 99.4688px computed, 2 rows); 95px still wrapped (measured
   94.9844px computed, 2 rows, last item at top:49 vs top:21 for the rest); 90px was the
   first value that held 1 row (all 9 items at the same top, verified at 1024px); 80px and
   60px also held, confirmed only to establish 90px still has margin, not because a
   smaller value was needed. 90px ships — closest to the 150px default that measurably
   holds 1 row, not the smallest value that does. */
@media (max-width: 79.9375rem) {
  .eclat-logo img { height: 56px; }
}
@media (max-width: 39.9375rem) {
  .eclat-logo img { height: 69px; }
}
/* 6B-LOGO-END */

/* 6B2-CHAPTERS-BEGIN — Art à l'École (page 5124) reads as chapters, not as one column of
   text (client tracker 2026-08-13: "les chapitres ne sont pas clairs, textes trop
   linéaires — accentuer les sections, créer des divisions").

   PAINT, NOT STRUCTURE. Plan 5c already gave this page its four chapters and wired their
   anchors to the menu; nothing here touches content, markup or the anchors. What was
   missing is that a chapter head and a sub-head were drawn IDENTICALLY, so the page had
   no visible seams: thirteen h3 heads in one flow, nine of them subordinate.

   SCOPE, AND WHY THE SELECTOR LOOKS LIKE THIS — every part of it was measured against the
   rendered page before it was written, not assumed:
     · `h3`, NOT `h2`. This page renders ZERO h2 inside its post-content (measured: 0; the
       only h2 on the document are the three footer column heads). A rule written on h2 —
       the obvious guess, and this task's first draft — would have matched nothing and
       shipped invisible.
     · `> ` (child), not a descendant. The four heads are DIRECT children of
       .wp-block-post-content, which is what keeps the treatment out of the accordions,
       quotes and galleries nested further down.
     · `[id]` is the chapter test, and it is a meaning rather than a trick: on this page an
       id is exactly what makes a head a chapter — it is the target the sub-navigation
       jumps to. Measured: 13 direct h3 children, 4 of them anchored. The nine sub-heads
       carry no id and are deliberately left alone; if Sarah anchors a fifth chapter from
       the editor it inherits the treatment with no CSS change.
     · ÉDITO is deliberately NOT included. It has no anchor because it is the page's
       preamble, not one of the four chapters the menu names.

   THE TREATMENT is one rule, three levers, in Éclat's existing vocabulary:
     · a full-column rule in `primary` — the division the client asked for. `primary`, not
       a hex, so Task 9's client-editable season palette repaints it (same rationale as
       §1's gradients). 4px is the weight §13 already uses for Éclat's heavier accents;
       #FF4D3D on white measures 3.29:1, so the divider clears WCAG 1.4.11's 3:1 even
       though a purely decorative rule is not held to it;
     · spacing--60 above the rule, spacing--30 below it. The flow layout's default blockGap
       here is spacing--40 (1.75rem) and WordPress zeroes margin-block-end on flow children,
       so this is not a collapsed guess: the gap above a chapter goes from 1.75rem to 5rem,
       nearly three times the gap between any two ordinary blocks. The padding is
       DELIBERATELY the smaller token, and it was chosen by looking rather than by
       symmetry — at spacing--40 the rule rendered 34px above the chapter's cap and the
       sub-head sat 38px below it, so the bar floated midway between the two and read as a
       loose divider; at spacing--30 it renders 22px above the cap against the same 38px
       below, and the bar reads as the chapter's own. Measured in the iframe harness at
       1280px, both values rendered, before either was committed;
     · one step up the type scale (large → x-large) at §2's h2 weight. The size gap is
       what actually separates chapter from sub-head at a glance — 40px vs 24px at 1280px,
       28px vs 20px at 360px, both ends of the clamp measured. The heading LEVEL is not
       changed: that is markup, and this task does not write markup.

   [6C 2026-08-24] The prediction above fired: "A rule written on h2 -- the obvious guess,
   and this task's first draft -- would have matched nothing and shipped invisible" is no
   longer true. Plan 6c-lite Task 4 (script 49-aae-h2.php) promotes the four anchored
   chapter h3 heads to real h2; the nine sub-heads stay h3. Selector moved
   h3.wp-block-heading[id] -> h2.wp-block-heading[id] to follow the markup -- nothing else
   in the rule changes (the size/weight levers above were already tuned to "§2's h2
   weight"). See test-ekla-page-sections.php's 6b2-T4 section for the updated DOM proof. */
.page-id-5124 .wp-block-post-content > h2.wp-block-heading[id] {
  margin-block-start: var(--wp--preset--spacing--60);
  padding-block-start: var(--wp--preset--spacing--30);
  border-block-start: 4px solid var(--wp--preset--color--primary);
  font-size: var(--wp--preset--font-size--x-large);
  font-weight: 800;
}
/* 6B2-CHAPTERS-END */

/* --- 6B3-BIBLIO-CARDS-BEGIN (2026-08-21, review round item 6) -----------------
   /bibliotheque-numerique/ tiles as Éclat cards. Paint only — the grid's
   structure stays in foundation's 6B2 fence; test-biblio-grid B7* guards this
   fence. No hover lift: the card is not clickable (B1 pins the 43-href multiset,
   so no wrapping link may ever be added). Covers letterbox on a quiet ground
   instead of cropping — same ruling as the partner logos (D-B). */
.ekla-cover-grid__item {
  background: var(--eclat-cream);
  border-radius: var(--eclat-radius);
  box-shadow: var(--eclat-shadow-pop);
  padding: var(--wp--preset--spacing--30);
  /* ADDED 2026-08-21 (6b3 fix round 3). Task 9's 360px width sweep found a real 2.42px
     horizontal overflow on /bibliotheque-numerique/: grid 0's "Bilan en vidéo" tile
     carries a pre-existing migrated credit line, `© CapCanal/Séquence/ EAL/ékla – 2013`,
     and the token `CapCanal/Séquence/` (19 chars, only slashes -- not a CSS break
     opportunity) doesn't fit the 112.5px text column this fence's own padding leaves at
     the base 2-col breakpoint. Scoped to the ITEM, not just its <p>s: core's "Télécharger"
     /"Découvrir" buttons are unaffected -- checked, they carry no width rule anywhere in
     foundation.css or this file, so their box sizes to its own (short) label rather than
     being squeezed by the tile, and `overflow-wrap` only ever acts on a box narrower than
     its content. */
  overflow-wrap: anywhere;
}
.ekla-cover-grid__item .wp-block-image {
  height: 16rem;
  background: var(--wp--preset--color--base);
  border-radius: var(--eclat-radius-sm);
}
.ekla-cover-grid__item .wp-block-image img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
/* --- 6B3-BIBLIO-CARDS-END --- */

/* --- 6C-EKLA-TITLE-BEGIN (Plan 6c-lite A2, 2026-08-24, ruling D6c-4) ---------
   /ekla/ (page 5128) keeps its h1 for assistive tech and outline but paints
   nothing: the sr-only pattern, WITHOUT !important (this file's comment-stripped
   count is gate-pinned at exactly 1). Specificity (0,2,1) ties the body.page
   rules above and wins on source order (R2: the earlier (0,2,0) claim was wrong
   for the __head rule -- fixed by adding the body element to both selectors).
   The __head override kills the otherwise-empty ruled band (1px border +
   padding) the hidden h1 leaves. */
body.page-id-5128 .ekla-page__title {
  position: absolute;
  clip-path: inset(50%);
  width: 1px;
  height: 1px;
  overflow: hidden;
  margin: -1px;
  padding: 0;
  border: 0;
}
body.page-id-5128 .ekla-page__head {
  border-block-end: 0;
  padding-block: var(--wp--preset--spacing--40) 0;
  margin-block-end: 0;
}
/* --- 6C-EKLA-TITLE-END --- */

/* --- 6C2-SOCIAL-BEGIN (2026-08-25, client item 2) -----------------------------
   The home « Suivre ékla » zone carries the official Facebook Page Plugin iframe,
   500px wide by Facebook's own contract (adapt_container_width only adapts at load).
   Inside its column it must never widen the page: on a 360px phone an uncapped
   500px iframe is the only thing on the home that would scroll sideways. Complianz
   swaps the iframe for a placeholder before consent; the cap applies to both. */
.eclat-social iframe { max-width: 100%; }
/* --- 6C2-SOCIAL-END ----------------------------------------------------------- */

/* --- 6C3-SOCIAL-BEGIN (2026-08-25, client: "improve the visual of Suivre ékla") -------
   Same white page, more structure: the Facebook feed becomes a raised card, the lead
   sentence takes the display face, the follow buttons carry brand glyphs, the newsletter
   note sits in a quiet tertiary box, and a wide « Sur Instagram » row is reserved for the
   Smash Balloon grid. Every colour is a palette slot or a §1 token; nothing is a literal.
   The buttons are plain markup inside a wp:html block (the script-48 precedent): core's
   RichText cannot round-trip an inline <svg>, so they are not core/button blocks and get
   their flex layout here rather than from the layout support. */
.eclat-social__card {
  background: var(--wp--preset--color--tertiary);
  border: 1px solid color-mix(in srgb, var(--wp--preset--color--accent) 35%, transparent);
  border-radius: var(--eclat-radius);
  box-shadow: var(--eclat-shadow-pop);
  overflow: hidden;              /* the 500x600 iframe (or Complianz's placeholder) cannot poke out of the rounding */
  /* [6E-2 2026-09-11 evening] Both feeds are this card, in two EQUAL columns; 500px is the
     Facebook Page plugin's hard maximum, so both cards stop there and centre -- twins. */
  max-width: 500px;
  margin-inline: auto;
}
.eclat-social__card iframe { display: block; }
.eclat-social__lead {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 700;
  font-size: 1.375rem;
  line-height: 1.25;
  letter-spacing: -.02em;
  margin-block: 0 var(--wp--preset--spacing--30);
}
.eclat-social__buttons { display: flex; flex-wrap: wrap; gap: .75rem; }
.eclat-social__buttons .wp-block-button__link { display: inline-flex; align-items: center; gap: .55rem; }
.eclat-social__buttons svg { width: 1.15em; height: 1.15em; fill: currentColor; flex: none; }
/* [6E 2026-09-11] The 6c3 note box and the dormant-row rule are retired: the newsletter
   moved into the band below and the Instagram column is never empty now (verify-6e B10). */
/* --- 6C3-SOCIAL-END ------------------------------------------------------------------- */

/* --- 6E-SOCIAL-BEGIN (2026-09-11, client after session 1: "present the Suivre section
   better — Facebook and Instagram side by side, the newsletter above, the Brevo form is raw")
   Zone order: lead → newsletter band → feeds (Facebook card | Instagram grid). The band is
   the same tertiary card language as the Facebook card. The Brevo plugin's legacy form
   cannot be re-templated (bare-text labels inside <p>, the input sharing the <p>'s class,
   a `sib-default-btn` submit), so the CSS lays it out: a 4-track grid on the fields box,
   label over field, the site's field radius (10px, the 5g-2 Forminator value), and a submit
   that quotes .eclat-btn (~577) — flat secondary, encre ink, never white (6b4 ruling). The
   plugin registers its shortcode only with an API key; the mu-plugin fallback then renders
   nothing and `:not(:has(form))` takes the empty band out of flow (verify-6e B2/C). */
.eclat-social__news {
  background: var(--wp--preset--color--tertiary);
  border-radius: var(--eclat-radius);
  padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--50);
  margin-block-end: var(--wp--preset--spacing--40);
}
.eclat-social__news:not(:has(form)) { display: none; }
.eclat-social__news > h3 { margin-block: 0 .35rem; }
.eclat-social__news > p { margin-block: 0 var(--wp--preset--spacing--30); }
.eclat-social__news .sib_signup_box_inside_1 {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: .75rem 1rem;
  align-items: end;
}
.eclat-social__news .sib_msg_disp { grid-column: 1 / -1; }
/* `:has()` rather than `:empty`: the plugin serves the slot with a whitespace text node. */
.eclat-social__news .sib_msg_disp:not(:has(*)) { display: none; }
.eclat-social__news .sib_signup_form p {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  margin: 0;
  font-size: .875rem;
  font-weight: 600;
}
.eclat-social__news .sib_signup_form p.sib-alert-message { font-weight: 500; border-radius: 10px; }
.eclat-social__news .sib_signup_form input:not([type="hidden"]):not([type="submit"]) {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-weight: 400;
  padding: .8rem 1rem;
  border: 1px solid color-mix(in srgb, var(--wp--preset--color--contrast) 30%, transparent);
  border-radius: 10px;
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--contrast);
}
.eclat-social__news .sib_signup_form input:not([type="hidden"]):not([type="submit"]):focus-visible {
  outline: 2px solid var(--wp--preset--color--accent);
  outline-offset: 2px;
}
.eclat-social__news .sib_signup_form input[type="submit"] {
  display: inline-block;
  border-radius: var(--eclat-pill);
  font: inherit;
  font-weight: 700;
  font-size: .9375rem;
  line-height: 1.2;
  padding: .9rem 1.75rem;
  background-color: var(--wp--preset--color--secondary);
  color: var(--wp--preset--color--contrast);
  border: 0;
  box-shadow: var(--eclat-shadow);
  cursor: pointer;
  transition: box-shadow var(--eclat-ease), transform var(--eclat-ease);
}
.eclat-social__news .sib_signup_form input[type="submit"]:hover {
  box-shadow: 0 6px 20px color-mix(in srgb, var(--wp--preset--color--primary) 45%, transparent);
  transform: translateY(-1px);
}
.eclat-social__news .sib_signup_form input[type="submit"]:active { transform: translateY(0); box-shadow: var(--eclat-shadow-chip); }
.eclat-social__feeds .eclat-social__buttons { margin-block-start: var(--wp--preset--spacing--30); justify-content: center; }
/* The Instagram card pads its grid and heading; the Facebook one is edge-to-edge by the plugin's design. */
.eclat-social__insta .eclat-social__card { padding: var(--wp--preset--spacing--30); }
.eclat-social__insta .eclat-social__card > h3 { margin-block: 0 var(--wp--preset--spacing--20); }
@media (max-width: 781px) {
  .eclat-social__news .sib_signup_box_inside_1 { grid-template-columns: 1fr; }
  .eclat-social__news .sib_signup_form input[type="submit"] { width: 100%; }
}
/* --- 6E-SOCIAL-END ---------------------------------------------------------------------- */

/* --- 6C3-NEWS-LETTERBOX-BEGIN (2026-08-25, client: "the actualités images are cropped") ---
   Both Actualités grids now ask core for a 16:9 box with `scale: contain` (pattern
   ekla/latest-actualites + index.html), so the wide event banners are shown whole. The
   home figure already had a tertiary ground (§10); the category page's card figure gets
   the same one here so the letterbox bands read as a designed surface, not a gap. Same
   ruling as the partner logos and the biblio covers (D-B: letterbox on a quiet ground). */
body.category .ekla-news__card .wp-block-post-featured-image {
  background: var(--wp--preset--color--tertiary);
  border-radius: var(--eclat-radius-sm);
  overflow: hidden;
}
/* --- 6C3-NEWS-LETTERBOX-END ---------------------------------------------------------- */
