/* ==========================================================================
   Paris Partners Softwares — v3, "brand-led mosaic"
   ---------------------------------------------------------------------------
   Design language borrowed from inetum.com, palette taken from PPS.

   What is adopted from Inetum:
     - a saturated gradient top bar with uppercase navigation
     - full-bleed hero imagery
     - a mosaic of unequal tiles mixing solid brand colours with imagery
     - an uppercase category label above every tile title
     - offset solid blocks bleeding off the left and right edges
     - the small-square motif, and the "EN SAVOIR PLUS ->" link pattern

   What is deliberately NOT adopted:
     - Inetum puts its <h1> BELOW a full-height blurred image, so the fold
       communicates nothing. Here the proposition sits on the hero.
     - Inetum ships four <h1> elements and zero <section>. This uses one <h1>
       per page and real landmarks.
     - Inetum sets magenta on yellow at 3.61:1. Every pair here was measured.

   Colour mapping: Inetum's magenta -> PPS red #dc2f2a; its navy -> PPS deep
   indigo #141a4a; its electric yellow has no PPS equivalent, so the structural
   role it plays (bright offset blocks) is taken by a pale indigo tint. There is
   no invented third hue.

   Single fixed look, no theme toggle: v3 is brand-led, and a brand this
   saturated does not have a quiet mode.
   ========================================================================== */

/* --------------------------------------------------------------- 1. tokens */

:root {
  color-scheme: light;

  --f-display: "Archivo", "Segoe UI", system-ui, sans-serif;
  --f-body: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  /* Brand.
     Three separate roles, because one saturated red cannot carry all of them:
       --accent  working colour: buttons, labels, links, card edges. Indigo,
                 9.03:1 on white, and it reads as stability rather than alarm —
                 which matters when the product being sold is reliability.
       --emph    headline emphasis and the one red tile. The brand red, used
                 sparingly so it still lands.
       --danger  errors only. Never used as decoration, so a red field border
                 means exactly one thing. */
  --red: #dc2f2a;
  --emph: #dc2f2a;
  --danger: #c0231e;
  --purple: #8e2f8c;
  --indigo: #2f3f9e;
  --accent: #2f3f9e;
  --accent-dark: #141a4a;
  --ground: #141a4a;
  --ground-2: #0e1338;
  --grad: linear-gradient(104deg, #dc2f2a 0%, #8e2f8c 52%, #2f3f9e 100%);
  /* The same sweep at 55%, for tiles that carry body text: over --ground it
     lands between #84293b and #26317a, dark enough to keep --on-dark-2 at
     4.54:1 while giving each tile an actual hue instead of flat navy. */
  --grad-wash: linear-gradient(104deg, rgba(220, 47, 42, .55) 0%, rgba(142, 47, 140, .55) 52%, rgba(47, 63, 158, .55) 100%);
  --grad-soft: linear-gradient(104deg, rgba(220, 47, 42, .92), rgba(47, 63, 158, .92));
  /* For gradient text on the deep-indigo grounds. The brand gradient cannot be
     used there: its purple end measures 2.30:1 and its indigo end 1.82:1
     against #141a4a. These lifted stops all clear 7:1. */
  --grad-light: linear-gradient(104deg, #ff8a80 0%, #e29ada 52%, #9daaf3 100%);
  /* The working accent, lifted for the deep-indigo grounds. --accent itself
     (#2f3f9e) measures 1.82:1 on #141a4a and is unusable there. Same value as
     the indigo stop of --grad-light above, so this names a colour rather than
     introducing one. 7.43:1 on the darkest band. */
  --accent-light: #9daaf3;

  /* Surfaces */
  --paper: #ffffff;
  --paper-2: #f4f5fb;
  --tint: #e7eaf9;
  --ink: #10132a;
  --ink-2: #474d69;
  --ink-3: #666c8a;   /* 4.73:1 sur --paper-2, le pire fond où il apparaît */
  --rule: #dfe2ef;
  --on-dark: #ffffff;
  --on-dark-2: #c3c9e6;

  /* Type scale */
  --t-mi: .6875rem;
  --t-xs: .8125rem;
  --t-sm: .9375rem;
  --t-base: clamp(1rem, .98rem + .1vw, 1.0625rem);
  --t-md: clamp(1.0625rem, 1.02rem + .2vw, 1.1875rem);
  --t-lg: clamp(1.2rem, 1.12rem + .35vw, 1.45rem);
  --t-xl: clamp(1.45rem, 1.3rem + .7vw, 2rem);
  --t-2xl: clamp(1.8rem, 1.45rem + 1.6vw, 2.85rem);
  --t-3xl: clamp(2.2rem, 1.6rem + 2.9vw, 4rem);

  --sp-1: .25rem;
  --sp-2: .5rem;
  --sp-3: .75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 2.75rem;
  --sp-8: 3.5rem;
  --sp-9: 5rem;

  --gutter: clamp(1.15rem, .5rem + 3vw, 3.5rem);
  --maxw: 1380px;
  --r: 0px;            /* Inetum's tiles are hard-cornered. So are these. */
  --head-h: 4.25rem;
  --ease: cubic-bezier(.32, .72, .35, 1);
}

/* ---------------------------------------------------------- 2. reset & base */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--head-h) + 1.5rem);
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-body);
  font-size: var(--t-base);
  line-height: 1.66;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

img, svg { display: block; max-width: 100%; height: auto; }

h1, h2, h3, h4 {
  font-family: var(--f-display);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -.015em;
  text-wrap: balance;
}

h1 { font-size: var(--t-3xl); }
h2 { font-size: var(--t-2xl); }
h3 { font-size: var(--t-lg); line-height: 1.2; }
h4 { font-size: var(--t-md); }

p { text-wrap: pretty; }
a { color: inherit; }

ul, ol { padding-left: 1.15rem; }
li + li { margin-top: .35em; }

strong { font-weight: 700; }

abbr[title] { text-decoration: underline dotted; text-underline-offset: .18em; cursor: help; }

:where(button, input, select, textarea) { font: inherit; color: inherit; }
button { cursor: pointer; }

::selection { background: var(--accent); color: #fff; }

:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 2px;
}

:focus:not(:focus-visible) { outline: none; }

/* ------------------------------------------------------------ 3. primitives */

.shell {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.band {
  position: relative;
  padding-block: clamp(3rem, 2rem + 5vw, 5.75rem);
  overflow: hidden;
}

.band--tight { padding-block: clamp(2rem, 1.5rem + 3vw, 3.5rem); }
.band--paper2 { background: var(--paper-2); }
.band--tint { background: var(--tint); }
.band--dark { background: var(--ground); color: var(--on-dark); }
.band--dark h1, .band--dark h2, .band--dark h3, .band--dark h4 { color: var(--on-dark); }

/* ...but not inside a light tile. .tile--tint keeps its pale fill on any band,
   so the blanket rule above put white on #e7eaf9 at 1.20:1. The band says what
   the BAND's ground is, not what every surface inside it is. */
.band--dark .tile--tint :is(h1, h2, h3, h4) { color: var(--ink); }
.band--dark .tile--tint { color: var(--ink); }

/* Inetum's signature device — solid blocks bleeding off the band edges — was
   tried here and removed. It works there because the blocks are large and
   overlap content, so they read as a layer in a composition. Absolutely
   positioned in a section's margin, with nothing behind or over them, they
   read as stray rectangles instead. Both the red and the pale variants were
   reported as looking like bugs, which is the correct reading.

   The square motif (.squares) is kept: it only ever sits on the hero plate or
   the CTA gradient, so it has a real ground and reads as a motif rather than
   as a floating shape. That difference is the whole lesson.

   Half true, as it turned out. On the hero plate the brand cells read; on the
   CTA the ground IS the brand gradient, so brand-coloured cells measured
   1.02:1 and the motif was simply not there. See .cta .squares below. */

.band > .shell { position: relative; z-index: 1; }

/* The square motif — a 2x2 reading of the PPS mark.

   The mark is two triangles split on the anti-diagonal: red over the upper-left
   half (M3 3 h34 L3 37 Z) and indigo over the lower-right (M41 7 v34 h-34).
   With the mark on a unit square that is red where x+y < 1 and indigo where
   x+y > 1: the top-left cell spans 0..1 so it is wholly red, the bottom-right
   spans 1..2 so it is wholly indigo, and the other two span 0.5..1.5 — the
   split line runs corner to corner across each of them. So rather than
   approximating with the blend hue, they carry the edge itself: the triangle
   above-left of the line red, the one below-right indigo. The gradient runs
   perpendicular to that line, top-left to bottom-right, hence 135deg.

   Previously six cells in an arbitrary red / indigo / tint / purple / gap / red
   arrangement, which read as decoration with no source. */
.squares {
  position: absolute;
  z-index: 0;
  display: grid;
  grid-template-columns: repeat(2, 1.75rem);
  gap: .35rem;
  pointer-events: none;
}

.squares span { aspect-ratio: 1; }
.squares span:nth-child(1) { background: var(--red); }     /* haut gauche  */
/* The half-pixel feather keeps the diagonal edge from stepping. */
.squares span:nth-child(2),
.squares span:nth-child(3) {
  background: linear-gradient(135deg,
    var(--red) 0 calc(50% - .5px), var(--indigo) calc(50% + .5px) 100%);
}
.squares span:nth-child(4) { background: var(--indigo); }  /* bas droite   */

.eyebrow {
  display: block;
  font-family: var(--f-display);
  font-size: var(--t-mi);
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
}

.eyebrow--on-dark { color: var(--accent-light); }

/* --accent on --ground is 1.82:1. That was left to the author remembering
   .eyebrow--on-dark, and the first section moved to a dark band forgot it.
   Inside a band it is no longer a choice. The modifier stays for .hero, .phero
   and .cta, which are not .band. */
.band--dark .eyebrow { color: var(--accent-light); }

.deck {
  max-width: 62ch;
  font-size: var(--t-md);
  color: var(--ink-2);
}

.band--dark .deck { color: var(--on-dark-2); }

.note { font-size: var(--t-sm); color: var(--ink-3); }
.measure { max-width: 64ch; }
.center { text-align: center; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  top: .5rem; left: 50%;
  z-index: 300;
  padding: .65rem 1.2rem;
  background: var(--ink);
  color: #fff;
  font-weight: 700;
  font-size: var(--t-sm);
  text-decoration: none;
  transform: translate(-50%, -220%);
  transition: transform .2s var(--ease);
}

.skip-link:focus-visible { transform: translate(-50%, 0); }

/* Section heading: uppercase, centred, with a short gradient rule beneath —
   Inetum centres its section titles in magenta caps. */
.shead {
  max-width: 52rem;
  margin-inline: auto;
  margin-bottom: clamp(2rem, 1.5rem + 2vw, 3.25rem);
  text-align: center;
}

.shead h2 {
  text-transform: uppercase;
  letter-spacing: -.01em;
}

.shead h2 .em { color: var(--emph); }
.band--dark .shead h2 .em { color: #ff8a80; }

.shead::after {
  content: "";
  display: block;
  width: 4rem;
  height: 4px;
  margin: var(--sp-5) auto 0;
  background: var(--grad);
}

.shead .deck { margin-top: var(--sp-5); margin-inline: auto; }
.shead .eyebrow { margin-bottom: var(--sp-3); }

.shead--left { margin-inline: 0; text-align: left; }
.shead--left::after { margin-inline: 0; }
.shead--left .deck { margin-inline: 0; }

/* -------------------------------------------------------------- 4. controls */

.btn {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  padding: .85rem 1.6rem;
  border: 2px solid transparent;
  border-radius: var(--r);
  background: var(--accent);
  color: #fff;
  font-family: var(--f-display);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color .18s var(--ease), color .18s var(--ease),
              border-color .18s var(--ease);
}

.btn:hover { background: var(--accent-dark); color: #fff; }

.btn--ghost { background: transparent; border-color: var(--ink); color: var(--ink); }
.btn--ghost:hover { background: var(--ink); color: #fff; border-color: var(--ink); }

.btn--on-dark { background: #fff; color: var(--ground); }
/* Was: fill turns red on hover. On the CTA band that is a red button on a
   red-to-indigo gradient — 1.00:1 at the red end, so the button's boundary
   vanished, and the label dropped from 16.45:1 to 4.68:1. Hovering made the
   control *less* visible.
   No dark fill works either: deep indigo only reaches 1.82:1 against the
   gradient's indigo end. This band tolerates light fills only. So the surface
   stays white (4.68:1 boundary at worst) and hover is signalled by the label
   plus an inset ring. */
.btn--on-dark:hover {
  /* The surface shifts to the pale brand blue — the conventional button-hover
     signal. Earlier attempts and why they were dropped:
       - red fill:  1.00:1 against the red end of the CTA gradient;
       - dark fill: 1.82:1 against its indigo end, so no dark works either;
       - red label + red ring: passed at 4.68:1 but read as a validation error
         on a white field.
     This keeps a light surface (3.91:1 boundary, above the 3:1 required),
     raises the label to 13.74:1, and introduces no colour that is not already
     in the palette. */
  background: var(--tint);
  color: var(--ground);
}

.btn--ghost-dark { background: transparent; border-color: #fff; color: #fff; }
.btn--ghost-dark:hover { background: #fff; color: var(--ground); }

.btn svg { width: 1.05em; height: 1.05em; flex: none; }

.actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }

/* The "EN SAVOIR PLUS ->" pattern */
.more {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-family: var(--f-display);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--accent);
}

.more svg { width: 1.4em; height: 1.4em; flex: none; transition: transform .22s var(--ease); }
.more:hover svg { transform: translateX(5px); }
.more--light { color: #fff; }
.more--ink { color: var(--ink); }

/* ---------------------------------------------------------------- 5. topbar */

.topbar {
  position: sticky;
  top: 0;
  z-index: 200;
  background: var(--grad);
  color: #fff;
}

.topbar__inner {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-height: var(--head-h);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  margin-right: auto;
  color: #fff;
  text-decoration: none;
  flex: none;
}

.brand__mark { width: 2rem; height: 2rem; flex: none; }

.brand__name {
  font-family: var(--f-display);
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -.01em;
}

.brand__tag {
  font-size: .625rem;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: #fff;   /* rgba(.8) donnait 3,46:1 sur le rouge du dégradé */
  display: block;
  line-height: 1.3;
}

@media (max-width: 44rem) { .brand__tag { display: none; } }

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

/* The in-drawer CTA duplicates the one in .bar-tools; each viewport shows
   exactly one. (Third time writing this rule across the three versions — it is
   the standard trap of putting the same button in two places.) */
.nav > .btn { display: none; }

.nav__list {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  margin: 0; padding: 0;
  list-style: none;
}

.nav__list li + li { margin-top: 0; }

.nav__link {
  position: relative;
  display: block;
  padding: .4rem 0;
  color: #fff;
  font-family: var(--f-display);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  /* Was opacity .82 = 3,57:1 on the red end of the bar. Opaque at rest; hover
     is signalled by an underline rather than by opacity. */
  opacity: 1;
  border-bottom: 2px solid transparent;
  transition: border-color .18s var(--ease);
}

.nav__link:hover { border-bottom-color: rgba(255, 255, 255, .8); }
.nav__link[aria-current="page"] { opacity: 1; }

.nav__link[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -.1rem;
  height: 3px;
  background: #fff;
}

.bar-tools { display: flex; align-items: center; gap: var(--sp-3); flex: none; }

.icon-btn {
  display: grid;
  place-items: center;
  width: 2.4rem; height: 2.4rem;
  /* .55 donnait 2,30:1 ; WCAG 1.4.11 demande 3:1 sur une bordure d'interface */
  border: 2px solid rgba(255, 255, 255, .8);
  background: transparent;
  color: #fff;
  transition: background-color .18s var(--ease), border-color .18s var(--ease);
}

.icon-btn:hover { background: rgba(255, 255, 255, .16); border-color: #fff; }
.icon-btn svg { width: 1.1rem; height: 1.1rem; }

.nav-toggle { display: none; }

.nav-toggle .bars { position: relative; width: 1.1rem; height: 2px; background: currentColor; }
.nav-toggle .bars::before,
.nav-toggle .bars::after {
  content: "";
  position: absolute; left: 0;
  width: 100%; height: 2px;
  background: currentColor;
  transition: transform .22s var(--ease);
}
.nav-toggle .bars::before { top: -6px; }
.nav-toggle .bars::after { top: 6px; }
.nav-toggle[aria-expanded="true"] .bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .bars::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .bars::after { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 66rem) {
  .nav-toggle { display: grid; }

  .nav {
    position: fixed;
    inset: var(--head-h) 0 0;
    z-index: 199;
    display: block;
    padding: var(--sp-6) var(--gutter) var(--sp-9);
    background: var(--ground);
    overflow-y: auto;
    visibility: hidden;
    opacity: 0;
    transition: opacity .22s var(--ease), visibility .22s;
  }

  .nav.is-open { visibility: visible; opacity: 1; }

  .nav__list { flex-direction: column; align-items: stretch; gap: 0; }

  .nav__link {
    padding: 1rem 0;
    border-bottom: 1px solid rgba(255, 255, 255, .16);
    font-size: var(--t-sm);
  }

  .nav__link[aria-current="page"]::after { left: 0; right: auto; width: 2rem; bottom: -1px; }
  .nav > .btn {
    display: inline-flex;
    margin-top: var(--sp-6);
    width: 100%;
    justify-content: center;
  }
  .nav__link { white-space: normal; }
  .bar-tools .btn { display: none; }
}

/* ------------------------------------------------------------------ 6. hero */

.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ground);
  color: #fff;
  padding-block: clamp(3.5rem, 2rem + 7vw, 7.5rem);
}

.hero__plate {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Substitutes for Inetum's hero video: a slow drift of the generated plate.
   Disabled entirely under prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .hero__plate {
    animation: drift 46s ease-in-out infinite alternate;
    transform-origin: 60% 40%;
  }
}

@keyframes drift {
  from { transform: scale(1.04) translate3d(0, 0, 0); }
  to   { transform: scale(1.12) translate3d(-1.5%, -1.5%, 0); }
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(96deg, rgba(14, 19, 56, .93) 0%, rgba(14, 19, 56, .74) 48%, rgba(14, 19, 56, .36) 100%);
}

.hero__inner { max-width: 46rem; }
.hero h1 { text-transform: uppercase; }
.hero h1 .em { color: #ff8a80; }

.hero__deck {
  margin-top: var(--sp-6);
  max-width: 54ch;
  font-size: var(--t-lg);
  line-height: 1.5;
  color: var(--on-dark-2);
}

.hero .actions { margin-top: var(--sp-7); }

.hero .squares { right: 8%; bottom: 12%; }

@media (max-width: 60rem) { .hero .squares { display: none; } }

/* Stat ribbon riding the bottom edge of the hero */
.ribbon {
  position: relative;
  z-index: 2;
  background: var(--grad);
  color: #fff;
}

.ribbon__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

.ribbon__grid > div {
  padding: var(--sp-5) var(--sp-4);
  border-left: 1px solid rgba(255, 255, 255, .25);
}

.ribbon__grid > div:first-child { border-left: 0; padding-left: 0; }

.ribbon dt {
  font-family: var(--f-display);
  font-size: var(--t-mi);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #fff;   /* rgba(.82) donnait 3,57:1 sur le rouge du dégradé */
}

.ribbon dd {
  margin: .4rem 0 0;
  font-family: var(--f-display);
  font-size: clamp(1.45rem, 1.1rem + 1.4vw, 2.2rem);
  font-weight: 700;
  line-height: 1;
}

.ribbon dd small {
  display: block;
  margin-top: .35rem;
  font-family: var(--f-body);
  font-size: var(--t-xs);
  font-weight: 500;
  color: #fff;   /* rgba(.82) donnait 3,57:1 sur le rouge du dégradé */
}

@media (max-width: 52rem) {
  .ribbon__grid { grid-template-columns: repeat(2, 1fr); }
  .ribbon__grid > div:nth-child(2n+1) { border-left: 0; padding-left: 0; }
  .ribbon__grid > div:nth-child(n+3) { border-top: 1px solid rgba(255, 255, 255, .25); }
}

/* Compact hero for inner pages */
.phero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ground);
  color: #fff;
  padding-block: clamp(2.75rem, 2rem + 4vw, 5rem);
}

.phero__plate {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .85;
}

.phero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(96deg, rgba(14, 19, 56, .94), rgba(14, 19, 56, .6));
}

.phero h1 { text-transform: uppercase; max-width: 30ch; }
.phero .deck { margin-top: var(--sp-5); color: var(--on-dark-2); }
.phero .eyebrow { color: var(--accent-light); margin-bottom: var(--sp-3); }

/* ---------------------------------------------------------------- 7. mosaic */

/* Unequal tiles mixing solid brand colours with generated imagery. The four
   variants give the grid its rhythm, exactly as Inetum alternates yellow
   solids with duotone photographs. */
.mosaic {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(6, 1fr);
}

.tile {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: 15rem;
  padding: var(--sp-6);
  grid-column: span 2;
  text-decoration: none;
  transition: transform .25s var(--ease);
}

.tile:hover { transform: translateY(-4px); }

.tile__plate {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Fallback ground. The plate is a child <img> and the scrim is ::after, so
   without this a failed image request leaves white text on the band colour —
   invisible. With it, the tile degrades to a solid deep-indigo card. */
.tile--plate { background: var(--ground); }

.tile--plate::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to top, rgba(14, 19, 56, .93) 6%, rgba(14, 19, 56, .45) 62%, rgba(14, 19, 56, .2) 100%);
}

.tile--plate,
.tile--dark,
.tile--grad { color: #fff; }

/* Flat --ground read as plain navy — and identically on every tile. Three
   layers now: a slight white lift, a window on the washed brand sweep, and the
   ground beneath as the opaque base. The window steps red / purple / indigo on
   a 3-cycle, so neighbouring tiles never share a hue whatever the mosaic holds
   — no dependency on how many tiles there are. */
.tile--dark {
  background:
    linear-gradient(158deg, rgba(255, 255, 255, .09) 0%, rgba(255, 255, 255, .015) 100%),
    var(--grad-wash),
    var(--ground);
  background-size: auto, 300% 100%, auto;
}

.mosaic > .tile--dark:nth-child(3n + 1) { background-position: 0 0, 0% 0, 0 0; }
.mosaic > .tile--dark:nth-child(3n + 2) { background-position: 0 0, 50% 0, 0 0; }
.mosaic > .tile--dark:nth-child(3n + 3) { background-position: 0 0, 100% 0, 0 0; }

/* .tile--dark is --ground, and so is .band--dark: on a dark band the tile
   measured 1.00:1 against it — not faint, identical — so seven tiles across
   offres and cas-clients had no edge at all and only their text showed. Same
   fault as .tile--tint on a white band: the fill is the band's own token.
   Lifted off it with the white wash the dark step cards already use, plus the
   1px outline the other dark cards carry.

   .tile--plate is included because its background is only a fallback for a
   failed image request — on a dark band that fallback was invisible too. */
.band--dark :is(.tile--dark, .tile--plate) { border: 1px solid rgba(255, 255, 255, .16); }

/* .tile--plate's background is only a fallback for a failed image request, and
   --ground is invisible on a dark band. It inherits the same wash. */
.band--dark .tile--plate {
  background:
    linear-gradient(158deg, rgba(255, 255, 255, .09) 0%, rgba(255, 255, 255, .015) 100%),
    var(--grad-wash),
    var(--ground);
}
/* Was a flat brand-red panel: the 11px label measured 3.79:1 on flat red, and
   a whole red card read as an alert panel once red had been demoted to
   emphasis-only. Replaced by the brand gradient.

   It was first oriented indigo-first, so the small label would land on the
   indigo end. That reason disappeared when every text in the tile was made
   opaque white (>= 4.68:1 on any stop), leaving the tile running the opposite
   way to the top bar, the ribbon and the CTA band for no reason. Now red-first
   like all of them — which is also the order of the PPS mark itself: red
   triangle top-left, indigo triangle bottom-right. The angle stays diagonal
   because the tile is a card, not a horizontal band. */
.tile--grad {
  background: linear-gradient(155deg, var(--emph) 0%, var(--purple) 52%, var(--indigo) 100%);
}
/* The pale fill is 1.20:1 on a white band and 1.10:1 on --paper-2, so on its
   own it draws no edge whatever: the tile dissolves into the band and the
   mosaic stops reading as a mosaic. No colour in this range fixes that — even
   --rule only reaches 1.19:1 — so the boundary has to be drawn, not tinted. */
.tile--tint {
  background: var(--tint);
  color: var(--ink);
  border: 1px solid var(--rule);
}

/* Same 4px brand edge as the step cards and the rule under each section title,
   so the tile is anchored by the design's own device rather than by a grey box
   outline. */
.tile--tint::before {
  content: "";
  position: absolute;
  inset: -1px -1px auto -1px;
  height: 4px;
  background: var(--grad);
}

.tile__label {
  font-family: var(--f-display);
  font-size: var(--t-mi);
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* Content used to be bottom-anchored on every variant, by justify-content:
   flex-end on .tile plus margin-bottom: auto on the label. That is right on a
   plate tile, where the space it opens is filled by the artwork — and wrong
   everywhere else, where it opens nothing and starts each tile's text at a
   different height according to how long its own paragraph is: in one row the
   three headings sat at 2080 / 2105 / 2081. Bottom-anchoring is now the plate
   tile's behaviour only. */
.tile--plate {
  justify-content: flex-end;
}

.tile--plate .tile__label { margin-bottom: auto; }

.tile--plate .tile__label { color: var(--accent-light); }
/* --accent-light fell to 4.49:1 on the red end of the wash; white is also what
   .tile--grad already uses on a coloured ground. */
.tile--dark .tile__label { color: #fff; }
.tile--grad .tile__label { color: #fff; }
.tile--tint .tile__label { color: var(--accent); }

.tile h3 { margin-top: var(--sp-5); }
.tile p { margin-top: var(--sp-3); font-size: var(--t-sm); }
.tile--plate p, .tile--dark p { color: var(--on-dark-2); }
.tile--grad p { color: #fff; }   /* rgba(.92) donnait 4,29:1 sur l'extrémité rouge */
.tile--tint p { color: var(--ink-2); }
/* With the body at the top, the footing pins to the bottom so a row of tiles
   lines its links and chips up regardless of paragraph length — the same rule
   .post__body .more and .ref__stack already use. Not on plate tiles, whose
   label already claims the free space. */
.tile .more { margin-top: var(--sp-5); }
.tile:not(.tile--plate) :is(.more, .chips) { margin-top: auto; padding-top: var(--sp-5); }
.tile--plate .more, .tile--dark .more, .tile--grad .more { color: #fff; }

/* Spans that produce the uneven rhythm */
.tile--wide { grid-column: span 4; }
.tile--tall { grid-row: span 2; min-height: 32rem; }
.tile--full { grid-column: span 6; }

@media (max-width: 68rem) {
  .mosaic { grid-template-columns: repeat(4, 1fr); }
  .tile--wide { grid-column: span 4; }
  .tile--tall { grid-row: span 1; min-height: 18rem; }
  .tile--full { grid-column: span 4; }
}

@media (max-width: 44rem) {
  .mosaic { grid-template-columns: 1fr; }
  .tile, .tile--wide, .tile--full { grid-column: span 1; }
  .tile--tall { min-height: 15rem; }
}

/* --------------------------------------------------------- 8. duotone photos */

/* Not used yet: PPS has no photography. When real photographs arrive, wrapping
   one in .duotone applies the same red/indigo treatment the plates carry, so
   the imagery stays a single visual family. */
.duotone {
  position: relative;
  overflow: hidden;
  background: var(--ground);
}

.duotone img {
  width: 100%;
  filter: grayscale(1) contrast(1.15);
  mix-blend-mode: luminosity;
}

.duotone::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--grad-soft);
  mix-blend-mode: color;
  pointer-events: none;
}

/* ----------------------------------------------------------- 9. numbered set */

.steps {
  display: grid;
  gap: var(--sp-4);
  /* Six steps is a known, fixed count, so the column count is declared rather
     than left to auto-fit. auto-fit cannot guarantee a split: at 1268px of
     content even minmax(19rem) resolved to four columns, leaving 4+2 with two
     empty cells. Declared, it is always 3+3, then 2+2+2, then 6. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  counter-reset: st;
  margin: 0;
  padding: 0;
  list-style: none;
}

.steps > li {
  position: relative;
  margin-top: 0;
  padding: var(--sp-6);
  /* A shallow diagonal rather than one flat colour. Previously an opaque fill
     on light bands and a flat rgba(255,255,255,.06) wash on dark ones — six
     identical dead rectangles in a row. */
  background: linear-gradient(158deg, var(--paper) 0%, var(--paper-2) 100%);
  counter-increment: st;
  transition: transform .2s var(--ease), background .2s var(--ease);
}

/* The 4px edge carries the brand gradient — the same device as the rule under
   each section title. Rather than repeating the whole red→indigo sweep six
   times, each card shows a window onto it, sliding one fifth further along per
   step: the procedure reads as one continuous run from the first box to the
   last. Indexed on nth-child rather than on the column, so it stays continuous
   at 3, 2 and 1 columns alike. */
.steps > li::before {
  content: "";
  position: absolute;
  /* -1px so the edge bleeds over the 1px card outline and reads flush, the
     way the plain 4px border-top does on .post and .panel. */
  inset: -1px -1px auto -1px;
  height: 4px;
  background: var(--grad);
  background-size: 300% 100%;
}

.steps > li:nth-child(1)::before { background-position: 0% 0; }
.steps > li:nth-child(2)::before { background-position: 20% 0; }
.steps > li:nth-child(3)::before { background-position: 40% 0; }
.steps > li:nth-child(4)::before { background-position: 60% 0; }
.steps > li:nth-child(5)::before { background-position: 80% 0; }
.steps > li:nth-child(6)::before { background-position: 100% 0; }

.steps > li:hover { transform: translateY(-3px); }

.band--dark .steps > li {
  background: linear-gradient(158deg, rgba(255, 255, 255, .11) 0%, rgba(255, 255, 255, .025) 100%);
}

.band--dark .steps > li:hover {
  background: linear-gradient(158deg, rgba(255, 255, 255, .155) 0%, rgba(255, 255, 255, .05) 100%);
}

.steps__n {
  display: block;
  font-family: var(--f-display);
  font-size: var(--t-mi);
  font-weight: 700;
  letter-spacing: .16em;
  color: var(--accent);
}

.band--dark .steps__n { color: var(--accent-light); }
.steps__n::before { content: "ÉTAPE 0" counter(st); }

.steps h3 { margin-top: var(--sp-3); font-size: var(--t-md); text-transform: uppercase; }
.steps p { margin-top: var(--sp-3); font-size: var(--t-sm); color: var(--ink-2); }
.band--dark .steps p { color: var(--on-dark-2); }

/* Six steps, so the split is declared at every width: 3+3, 2+2+2, then 6. */
@media (max-width: 62rem) {
  .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 40rem) {
  .steps { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------ 10. logo band */

.logos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-5) var(--sp-8);
}

.logos img {
  width: auto;
  max-width: 7rem;
  max-height: 2rem;
  object-fit: contain;
  filter: grayscale(1);
  opacity: .55;
  transition: filter .18s var(--ease), opacity .18s var(--ease);
}

.logos img:hover { filter: none; opacity: 1; }

/* ------------------------------------------------------------ 11. reference */

.refs {
  display: grid;
  gap: var(--sp-4);
  /* Eight references, a known count — declared rather than auto-fit, as for
     .steps and .tgrid. 4+4, then 2x4, then 8. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 72rem) { .refs { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 40rem) { .refs { grid-template-columns: 1fr; } }

/* Head plate then body, the same two-part rhythm as .tcard — but the head here
   holds a CLIENT logo, not our own title, so it cannot take the brand gradient
   the way .tcard's does. Nine of the ten logo files are dark artwork (mean
   luminance .03 to .20): on the sweep they measure 2.07:1 at the indigo end
   against 18.73:1 on white. The plate stays white for that reason. */
.ref {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--paper);
}

/* The 4px edge carries a window on the brand sweep, one eighth further along
   per card, so the eight references run red to indigo like the step cards. */
.ref::before {
  content: "";
  position: absolute;
  inset: -1px -1px auto -1px;
  height: 4px;
  background: var(--grad);
  background-size: 800% 100%;
}

.ref:nth-child(1)::before { background-position: 0% 0; }
.ref:nth-child(2)::before { background-position: 14.3% 0; }
.ref:nth-child(3)::before { background-position: 28.6% 0; }
.ref:nth-child(4)::before { background-position: 42.9% 0; }
.ref:nth-child(5)::before { background-position: 57.1% 0; }
.ref:nth-child(6)::before { background-position: 71.4% 0; }
.ref:nth-child(7)::before { background-position: 85.7% 0; }
.ref:nth-child(8)::before { background-position: 100% 0; }

/* Centred in a fixed band with one max-height for every file: the logos are
   supplied at wildly different scales and left-aligning them at their own size
   made the row look ragged. Greyscale keeps ten client brands from fighting. */
.ref__logo {
  display: grid;
  place-items: center;
  height: 6rem;
  margin: 0;
  padding: var(--sp-4);
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}

.ref__logo img {
  width: auto;
  max-width: 70%;
  max-height: 2.4rem;
  object-fit: contain;
  filter: grayscale(1);
  opacity: .9;
}

.ref__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--sp-5);
  background: linear-gradient(158deg, var(--paper-2) 0%, #eef1f9 100%);
}

.ref h3 { font-size: var(--t-md); text-transform: uppercase; }
.ref p { margin-top: var(--sp-3); font-size: var(--t-sm); color: var(--ink-2); }
.ref__stack { margin-top: auto; padding-top: var(--sp-5); }

.chips { display: flex; flex-wrap: wrap; gap: .35rem; margin: 0; padding: 0; list-style: none; }
.chips li { margin-top: 0; }

.chip {
  display: inline-block;
  padding: .2rem .55rem;
  background: var(--tint);
  /* The tint fill alone is 1.20:1 on a white card and 1.10:1 on a --paper-2
     panel, so on a panel the pill shape all but disappears. The hairline is
     what makes it read as a chip on any ground. */
  border: 1px solid var(--accent-light);
  color: var(--indigo);
  font-family: var(--f-display);
  font-size: var(--t-mi);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ---------------------------------------------------------------- 12. lists */

.split {
  display: grid;
  gap: clamp(2rem, 1rem + 4vw, 4rem);
  grid-template-columns: 1fr;
}

@media (min-width: 64rem) {
  .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .split--rail { grid-template-columns: minmax(0, 1fr) minmax(0, 22rem); align-items: start; }
  /* A short intro beside a tall card: top-aligned it leaves the column looking
     abandoned, so the text sits against the middle of the card instead. */
  .split--middle > :first-child { align-self: center; }
  /* Weighted towards the card: a fact sheet is nine label/value pairs, and at
     an even split its value column was 317px, so most rows wrapped to two or
     three lines and the card ran 884px against 275px of intro. Giving the card
     the wider share lets the values sit on one line and takes the height out. */
  .split--factsheet { grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); }
}

.panel {
  position: relative;
  padding: clamp(1.4rem, 1rem + 1.6vw, 2.25rem);
  /* A shallow diagonal instead of one flat grey, and the 4px edge moved to
     ::before so it can carry the brand gradient — the same treatment the step
     cards got, otherwise the panels read as inert boxes beside them. */
  background: linear-gradient(158deg, var(--paper-2) 0%, #eef1f9 100%);
}

.panel::before {
  content: "";
  position: absolute;
  inset: -1px -1px auto -1px;
  height: 4px;
  background: var(--grad);
}

/* A PAIR of panels takes successive windows on the sweep, so the two edges
   read as one run rather than two identical bars. Guarded by the same quantity
   query as the step cards: in a .split--rail the first child is the form or the
   prose, not a panel, so the lone rail panel fails :first-child and keeps the
   whole sweep instead of showing only its indigo tail. */
.split > .panel:first-child:nth-last-child(2)::before,
.split > .panel:first-child:nth-last-child(2) ~ .panel::before { background-size: 200% 100%; }
.split > .panel:first-child:nth-last-child(2) ~ .panel::before { background-position: 100% 0; }

.band--paper2 .panel { background: linear-gradient(158deg, var(--paper) 0%, #f7f8fc 100%); }
.band--dark .panel {
  background: linear-gradient(158deg, rgba(255, 255, 255, .1) 0%, rgba(255, 255, 255, .03) 100%);
}
.panel .eyebrow { margin-bottom: var(--sp-4); }

/* ------------------------------------------------------------- 13b. tcard */
/* Eight small cards rather than two large panels: one technology group each,
   a plate head carrying the title and the chip list beneath. .post already
   pairs a plate with a body, so this is that arrangement with the title moved
   onto the plate and centred. */
.tgrid {
  display: grid;
  gap: var(--sp-4);
  /* Eight is a known, fixed count. Declared rather than auto-fit, for the same
     reason as .steps: auto-fit cannot guarantee the split. 4+4, 2x4, then 8. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 68rem) { .tgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 34rem) { .tgrid { grid-template-columns: 1fr; } }

.tcard {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rule);
  overflow: hidden;
}

.tcard__head {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  padding: var(--sp-4);
  text-align: center;
  /* Each head is a window one eighth further along the brand sweep, so the row
     runs red to indigo across the eight cards — the same device as the step
     card edges. */
  background: var(--grad);
  background-size: 800% 100%;
}

.tcard:nth-child(1) .tcard__head { background-position: 0% 0; }
.tcard:nth-child(2) .tcard__head { background-position: 14.3% 0; }
.tcard:nth-child(3) .tcard__head { background-position: 28.6% 0; }
.tcard:nth-child(4) .tcard__head { background-position: 42.9% 0; }
.tcard:nth-child(5) .tcard__head { background-position: 57.1% 0; }
.tcard:nth-child(6) .tcard__head { background-position: 71.4% 0; }
.tcard:nth-child(7) .tcard__head { background-position: 85.7% 0; }
.tcard:nth-child(8) .tcard__head { background-position: 100% 0; }

/* Texture in CSS rather than a plate image. The generated plates are drawn for
   large surfaces: dropped into a 300x170 head, object-fit: cover magnifies a
   fragment into flat colour, and stretching the whole plate in brings visible
   edge banding. Eight image requests for nothing either way. A fine dot field
   echoes the matrix plate, costs no request, and cannot fail to load.
   The flat tint underneath is the scrim that keeps the title off the red end. */
.tcard__head::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: rgba(14, 19, 56, .3);
  background-image: radial-gradient(rgba(255, 255, 255, .17) 1px, transparent 1.4px);
  background-size: 13px 13px;
}

.tcard__head h3 {
  position: relative;
  z-index: 1;
  margin: 0;
  color: #fff;
  font-size: var(--t-md);
  text-transform: uppercase;
}

.tcard__body {
  flex: 1;
  padding: var(--sp-5);
  background: linear-gradient(158deg, var(--paper-2) 0%, #eef1f9 100%);
}

/* Not every group is a list of names — the in-house library is a sentence. */
.tcard__note { margin: 0; font-size: var(--t-sm); color: var(--ink-2); }

.facts { margin: 0; }

.facts > div {
  display: grid;
  grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
  gap: var(--sp-2) var(--sp-5);
  padding-block: var(--sp-4);
  border-bottom: 1px solid var(--rule);
}

.facts > div:first-child { border-top: 1px solid var(--rule); }

/* Inside a panel the table is a card, not a page-width block: the label track
   drops to v1's 9rem, the rows tighten, and the first and last shed the padding
   that pushed the card away from its own edges. */
.panel .facts > div {
  grid-template-columns: minmax(0, 9rem) minmax(0, 1fr);
  padding-block: var(--sp-3);
}

.panel .facts > div:first-child { padding-top: 0; border-top: 0; }
.panel .facts > div:last-child { padding-bottom: 0; border-bottom: 0; }

.facts dt {
  font-family: var(--f-display);
  font-size: var(--t-mi);
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding-top: .2rem;
}

.facts dd { margin: 0; }

.band--dark .facts > div { border-color: rgba(255, 255, 255, .18); }
.band--dark .facts dt { color: var(--on-dark-2); }

@media (max-width: 36rem) {
  .facts > div { grid-template-columns: 1fr; }
}

/* The rail column is 22rem, so a .facts table inside it has ~278px of content:
   the 12rem label track plus the 24px gap left 62px for the value, and the
   address wrapped one word per line. The viewport media query above cannot
   catch this — the viewport is wide, the container is not. Scoped to the same
   breakpoint that creates the rail: below it the panel is full width and the
   two-column form is fine again. */
@media (min-width: 64rem) {
  .split--rail .facts > div { grid-template-columns: 1fr; }
  .split--rail .facts dt { padding-top: 0; }
}

.prose { max-width: 68ch; }
.prose > * + * { margin-top: var(--sp-4); }
.prose h2 { margin-top: var(--sp-8); font-size: var(--t-xl); text-transform: uppercase; }
.prose h3 { margin-top: var(--sp-6); font-size: var(--t-md); text-transform: uppercase; }
.prose p, .prose ul, .prose ol { color: var(--ink-2); }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: .18em; }
.prose hr { height: 1px; border: 0; background: var(--rule); margin-block: var(--sp-6); }

.prose code {
  padding: .1em .35em;
  background: var(--tint);
  font-family: ui-monospace, Consolas, monospace;
  font-size: .9em;
}

.rich { max-width: 66ch; font-size: var(--t-md); }
.rich > * + * { margin-top: var(--sp-5); }
.rich h2 { margin-top: var(--sp-8); font-size: var(--t-xl); text-transform: uppercase; }
.rich h3 { margin-top: var(--sp-6); font-size: var(--t-md); text-transform: uppercase; }
.rich p, .rich ul, .rich ol { color: var(--ink-2); }
.rich > p:first-of-type { color: var(--ink); font-weight: 500; }
.rich li + li { margin-top: var(--sp-2); }
.rich a { color: var(--accent); text-decoration: underline; text-underline-offset: .18em; }
.rich blockquote { padding-left: var(--sp-5); border-left: 4px solid var(--accent); color: var(--ink-2); }
.rich hr { height: 1px; border: 0; background: var(--rule); }

/* ---------------------------------------------------------------- 13. posts */

.posts {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
}

.post {
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border-top: 4px solid var(--accent);
  overflow: hidden;
}

.post__plate { aspect-ratio: 16 / 7; }
.post__plate img { width: 100%; height: 100%; object-fit: cover; }
.post__body { display: flex; flex-direction: column; flex: 1; padding: var(--sp-6); }
.post__body h3 { margin-top: var(--sp-3); font-size: var(--t-md); text-transform: uppercase; }
.post__body h3 a { text-decoration: none; }
.post__body h3 a:hover { color: var(--accent); }
.post__body p { margin-top: var(--sp-3); font-size: var(--t-sm); color: var(--ink-2); }
.post__body .more { margin-top: auto; padding-top: var(--sp-5); }

/* ---------------------------------------------------------------- 14. forms */

.field { display: grid; gap: .4rem; }

.field label {
  font-family: var(--f-display);
  font-size: var(--t-mi);
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: .8rem .9rem;
  border: 2px solid var(--rule);
  border-radius: var(--r);
  background: var(--paper);
  color: var(--ink);
  font-size: var(--t-base);
  transition: border-color .18s var(--ease);
}

.field textarea { min-height: 8.5rem; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: var(--ink-3); }

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: none;
  border-color: var(--indigo);
}

.field .hint {
  font-family: var(--f-body);
  font-size: var(--t-xs);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-3);
}

.field .err { font-size: var(--t-xs); color: var(--danger); font-weight: 600; }
.field:not([data-invalid]) .err { display: none; }

.field[data-invalid] input,
.field[data-invalid] select,
.field[data-invalid] textarea { border-color: var(--danger); }

/* One field per row. The same auto-fit rule in v1 happens to resolve to a
   single column because v1's form column is only 424px; here it is 863px, so
   auto-fit produced three across. Declared, so it does not depend on how wide
   the column happens to be. */
.form-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: 1fr;
  max-width: 36rem;
}

.form-grid .full { grid-column: 1 / -1; }

.consent { display: flex; gap: .65rem; align-items: flex-start; font-size: var(--t-sm); color: var(--ink-2); }
.consent input { width: 1.05rem; height: 1.05rem; margin-top: .35rem; flex: none; accent-color: var(--accent); }
.consent a { color: var(--accent); }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.status {
  padding: var(--sp-4);
  border-left: 4px solid var(--rule);
  background: var(--paper-2);
  font-size: var(--t-sm);
}

.status:empty { display: none; }
.status[data-state="ok"] { border-color: #1f6b3f; color: #1a5c36; }
.status[data-state="error"] { border-color: var(--danger); color: var(--danger); }

.map { overflow: hidden; background: var(--paper-2); aspect-ratio: 16 / 10; }
.map iframe { display: block; width: 100%; height: 100%; border: 0; }

/* ------------------------------------------------------------------ 15. cta */

.cta {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--grad);
  color: #fff;
  padding-block: clamp(2.75rem, 2rem + 3vw, 4.5rem);
}

.cta__inner {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
}

.cta h2 { max-width: 26ch; font-size: var(--t-xl); text-transform: uppercase; }
.cta p { margin-top: var(--sp-3); max-width: 48ch; color: #fff; }   /* rgba(.9) donnait 4,04:1 sur le rouge */
/* The motif used to float at right:4%/top:18% over a band whose height varies
   with its copy, while the buttons stay vertically centred — so whether the two
   collided was luck: measured, it overlapped on offres, a-propos and blog, and
   cleared index by 9px. Left as a plain flex item it stopped overlapping but
   dropped onto its own line once the row ran out of width.

   So the row reserves a strip for it and it is positioned into that strip: the
   content box simply does not extend that far, which makes non-overlap a
   property of the layout rather than of the copy's length. */
@media (min-width: 60.01rem) {
  .cta__inner { padding-right: calc(var(--gutter) + 5.75rem); }

  .cta .squares {
    right: var(--gutter);
    top: 50%;
    transform: translateY(-50%);
  }
}

/* The CTA ground is the brand gradient itself, so red on red and indigo on
   indigo cancel out — every cell measured 1.02:1, at .5 opacity or at full.
   No brand colour can work here. The header already answers this: on the
   coloured topbar .brand__mark drops to a white fill plus a white outline, so
   the motif keeps the mark's geometry and gives up its palette. Same here —
   solid where the red triangle covers, outlined where the indigo one does. */
.cta .squares span { background: rgba(255, 255, 255, .45); }
.cta .squares span:nth-child(1) { background: #fff; }

/* Same split in monochrome: solid where the red triangle covers, faint where
   the indigo one does — one mapping, used everywhere in the motif.

   The bottom-right cell was an outline, echoing .brand__mark, which drops the
   indigo triangle to a stroke on the coloured topbar. That held while it was
   the only indigo in the motif. Once the diagonal cells were split and started
   rendering indigo as a faint fill, the same region of the mark was being drawn
   two different ways in one figure, and the outlined cell read as an empty box
   rather than as part of the shape. It takes the faint fill like the rest. */
.cta .squares span:nth-child(2),
.cta .squares span:nth-child(3) {
  background: linear-gradient(135deg,
    #fff 0 calc(50% - .5px), rgba(255, 255, 255, .3) calc(50% + .5px) 100%);
}
.cta .squares span:nth-child(4) { background: rgba(255, 255, 255, .3); }

@media (max-width: 60rem) { .cta .squares { display: none; } }

/* --------------------------------------------------------------- 16. footer */

.site-footer {
  background: var(--ground-2);
  color: var(--on-dark-2);
  padding-block: var(--sp-9) var(--sp-5);
}

.site-footer__grid {
  display: grid;
  gap: var(--sp-7);
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
}

.fcol h2 {
  margin-bottom: var(--sp-4);
  font-family: var(--f-display);
  font-size: var(--t-mi);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #fff;
}

.fcol ul { margin: 0; padding: 0; list-style: none; display: grid; gap: .55rem; font-size: var(--t-sm); }
.fcol li { margin-top: 0; }
.fcol a { color: var(--on-dark-2); text-decoration: none; }
.fcol a:hover { color: #fff; text-decoration: underline; }
.fcol p { font-size: var(--t-sm); }
.fcol address { font-style: normal; font-size: var(--t-sm); line-height: 1.8; }
.fcol address a { color: var(--on-dark-2); }

.site-footer__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: 1px solid rgba(255, 255, 255, .16);
  font-size: var(--t-xs);
}

.site-footer__foot nav { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
.site-footer__foot a { color: var(--on-dark-2); text-decoration: none; }
.site-footer__foot a:hover { color: #fff; }

/* ------------------------------------------------- 16b. card outlines (fix)

   .ref, .post, .steps > li and .panel are white-backed cards that appear on
   white, grey AND dark bands. Relying on the parent for contrast meant that on
   a plain white band the body vanished and only the 4px accent edge showed —
   reading as a detached coloured bar rather than a card. A 1px outline in
   addition to the accent edge makes them read correctly on every ground.
   -------------------------------------------------------------------------- */

.ref,
.post,
.steps > li,
.panel {
  border: 1px solid var(--rule);
}

/* Re-assert the accent edge after the shorthand above. */
.post { border-top: 4px solid var(--accent); }
/* .steps > li and .panel paint their edge with ::before (brand gradient) and
   keep only the 1px outline from the shared rule above. */

.band--dark :is(.ref, .post, .steps > li, .panel) {
  border-color: rgba(255, 255, 255, .16);
}

.band--dark .post { border-top-color: var(--accent-light); }

/* ----------------------------------------------------------------- 17. util */

.pillnav { display: flex; flex-wrap: wrap; gap: .4rem; margin: 0; padding: 0; list-style: none; }
.pillnav li { margin-top: 0; }

.pillnav a {
  display: inline-block;
  padding: .45rem .95rem;
  background: rgba(255, 255, 255, .12);
  color: #fff;
  font-family: var(--f-display);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color .18s var(--ease);
}

.pillnav a:hover { background: var(--accent); }

.mt-3 { margin-top: var(--sp-3); }
.mt-4 { margin-top: var(--sp-4); }
.mt-5 { margin-top: var(--sp-5); }
.mt-6 { margin-top: var(--sp-6); }
.mt-7 { margin-top: var(--sp-7); }
.mt-8 { margin-top: var(--sp-8); }

/* ------------------------------------------------- 17b. gradient emphasis

   Emphasised headline words carry the brand gradient instead of a flat red.
   Flat brand red was the last large red surface left on a light band, and a
   gradient reads as brand rather than as alarm — the same reasoning that moved
   the solid tile and the CTA hover.

   Applied ONLY to large type. A gradient across ten characters of 11px
   uppercase reads as muddy noise, not as a gradient, so the small labels
   (.eyebrow, .tile__label, .steps__n) keep their flat light red. Error text
   keeps flat --danger, which is the entire point of that role.

   The flat colour declarations above remain the base: `color: transparent` with
   an unsupported background-clip would make the words disappear entirely, which
   is the same "relying on something else to supply the result" failure that hit
   the cards, the plate tiles and the translucent labels.

   Contrast, measured per stop rather than guessed — an auditor only sees
   `transparent` here:
     light grounds  red 4.68:1 · purple 7.15:1 · indigo 9.03:1
     dark grounds   7.21:1 · 7.73:1 · 7.43:1
   -------------------------------------------------------------------------- */

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .shead h2 .em {
    background: var(--grad);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }

  .band--dark .shead h2 .em,
  .hero h1 .em {
    background: var(--grad-light);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

/* --------------------------------------------------------------- 18. motion */

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

/* ---------------------------------------------------------------- 19. print */

@media print {
  .topbar, .site-footer, .cta, .icon-btn, .nav-toggle, .map, .squares { display: none !important; }
  body { background: #fff; color: #000; }
  .hero, .phero, .tile--dark, .tile--grad, .tile--plate { background: #fff !important; color: #000 !important; }
  .hero__plate, .phero__plate, .tile__plate { display: none !important; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .85em; }
}
