/* ==========================================================================
   Layout — page shell, grid, section rhythm.
   Structure only. Anything with a visual identity belongs in components.css.
   ========================================================================== */

.site {
  position: relative;
  overflow: clip;
}

/* Swup swaps the contents of this element on navigation. It must stay in the
   DOM across transitions, so it wraps main only — never the header or footer. */
#swup {
  position: relative;
}


/* --- Horizontal rhythm ---------------------------------------------------
   The XD's artboard grid: 26px margins, 12 columns, 16px gutter, on a 1440
   canvas. .shell is the single place that measurement lives. */

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

.shell--flush { padding-inline: 0; }

.grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr));
  gap: var(--grid-gutter);
}

/* Column spans used by the templates. Named by span rather than by
   start/end so a block can be moved without renaming it. */
.col-1  { grid-column: span 1; }
.col-2  { grid-column: span 2; }
.col-3  { grid-column: span 3; }
.col-4  { grid-column: span 4; }
.col-5  { grid-column: span 5; }
.col-6  { grid-column: span 6; }
.col-7  { grid-column: span 7; }
.col-8  { grid-column: span 8; }
.col-9  { grid-column: span 9; }
.col-10 { grid-column: span 10; }
.col-12 { grid-column: 1 / -1; }

.col-start-7 { grid-column-start: 7; }
.col-start-8 { grid-column-start: 8; }


/* --- Vertical rhythm -----------------------------------------------------
   Section padding is deliberately coarse. Exact offsets between a heading and
   the block beneath it are the component's business, not the section's. */

.section {
  position: relative;
  padding-block: var(--s-8);
}

/* Landing target for a menu item that links to a section rather than a page.
   The header is sticky and 83px tall, so without the margin the heading it
   scrolled to ends up underneath it. */
.section-anchor { scroll-margin-top: 83px; }

.section--tight { padding-block: var(--s-6); }
.section--loose { padding-block: var(--s-9); }
.section--flush-top { padding-block-start: 0; }
.section--flush-bottom { padding-block-end: 0; }


/* --- Bands ---------------------------------------------------------------
   The design alternates two dark values down the page. Setting --c-bg on the
   band rather than `background` directly means nested components that read the
   token (card borders, input fills) stay in step automatically. */

/* Off the home page there is no alternation: the whole document sits on the
   deeper dark. Setting the token on the body rather than the background means
   body, every `.band` and anything else reading `--c-bg` move together, and
   `.band--deep` below simply resolves to the same value it already had. The
   class comes from z1_body_classes(); see the note there for why it is spelled
   as "not home" rather than listed per template. */
body.is-deep { --c-bg: var(--c-panel-deep); }

.band {
  background: var(--c-bg);
}

.band--deep {
  --c-bg: var(--c-panel-deep);
  background: var(--c-bg);
}

.band--accent {
  --c-bg: var(--c-green);
  --c-text: var(--c-panel);
  --c-text-display: var(--c-panel);
  background: var(--c-bg);
  color: var(--c-text);
}


/* --- Hero ----------------------------------------------------------------
   Full-bleed image with the display heading over it and the lead pinned to the
   right-hand columns.

   Offsets below are measured from the XD's Accueil artboard rather than chosen:

     background image   0,0    1440 x 1057
     title             26,201  w 1053   (columns 1-9)
     lead            1079,592  w 335    (columns 10-12)
     scroll hint       26,646
     rule               0,715  w 1440   full bleed
     button          1079,781

   The band is a fixed height in the design, so the hero is taller than a 1080p
   viewport and the button sits below the fold. That is intentional. Revisited
   in the responsive pass. */

/* The header is now in normal flow and occupies 83px (25 top + 58 tall), so the
   hero's own offsets are the artboard values minus that: band 1057-83, title
   201-83, rule 715-83. */
.hero {
  position: relative;
  min-height: 974px;
  isolation: isolate;
}

/* Corner glows — three soft radial highlights that lift the hero off flat black
   and give the sphere something to sit in.

   These were written in the old stylesheet but almost certainly never rendered:
   the comment block above them opened with `\*` instead of `/*`, and that parse
   error takes the following rule down with it. Same defect that hid the
   preloader spinner. Values are carried over exactly. */
.hero::before {
  content: '';
  position: absolute;

  /* Extends up behind the header row. The header is transparent and in flow, so
     without this the top 83px sits outside the hero box and reads as a flat
     black bar cutting across the ambience. In the design the backdrop runs to
     the very top of the artboard. */
  inset: -83px 0 0 0;

  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(circle at 100% 0%, rgb(255 255 255 / 0.085), transparent 55%),
    radial-gradient(circle at 0% 100%, rgb(255 255 255 / 0.03), transparent 55%),
    radial-gradient(circle at 59% 100%, rgb(255 255 255 / 0.01), transparent 55%);
}

/* Film grain over the sphere.
 *
 * New rather than restored: the old theme only ever mentioned it in a comment,
 * so there is no previous implementation to match. Generated rather than an
 * image — feTurbulence tiles at 180px, which keeps it fine-grained at any zoom
 * and costs one data URI instead of a request.
 *
 * Deliberately faint. `overlay` lets it lift the mid-tones without touching the
 * blacks, so the flat panels stay flat and only the sphere's gradients pick it
 * up. Delete this rule and the ::after on .section-sphere to remove it. */
.hero::after,
.section-sphere::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.09;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

/* Matches the glow layer, which runs up behind the transparent header. */
.hero::after { inset: -83px 0 0 0; }

@media (prefers-reduced-motion: reduce) {
  .hero::after,
  .section-sphere::after { display: none; }
}

/* Hero content sits above the glows. */
.hero > * { position: relative; z-index: 1; }

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
}

.hero__media img,
.hero__media canvas {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The scrim exists to keep 80px Thin legible over a photograph — the lightest
   strokes vanish against highlights. With no featured image the hero is flat
   dark by design, and painting a gradient over flat dark only muddies it, so
   this is scoped to the case where an image is actually present. */
.hero__media--image::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgb(33 33 33 / 0.55) 0%, rgb(33 33 33 / 0.15) 45%, rgb(33 33 33 / 0.85) 100%);
}

.hero__inner {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr));
  gap: var(--grid-gutter);
  padding-block-start: 118px;   /* title at y201, less the 83px header row */
}

.hero__main { grid-column: 1 / 10; }

/* The right-hand column is anchored by its foot, not its head.

   The artboard sets the lead at y592 over two 23px lines, ending at y638, with
   the rule at y715 and the button at y781. Expressed as a top margin those two
   gaps only survive while the copy takes the design's two lines. It does not:
   the live lead runs to four, which carried its last line down to y684 and the
   button to y827, and in the closing band at 1025 — where this column is 228
   wide — the lead reached six lines and crossed its own rule.

   Fixing the column at the design's lead bottom and pushing the lead down to it
   makes extra lines grow upward, into the empty half of the band, instead of
   down into the rule. The button's gap is then measured from a point that no
   longer moves with the copy, so it holds y781 at every width.

   The column box below IS the lead's box: it ends where the design's lead ends,
   and the button hangs off that edge rather than sitting inside it. Keeping the
   button out of the flex flow matters — an auto margin shares out the
   container's free space, so with the button still in the box the lead would be
   pushed to the middle of it rather than onto its foot. */
.hero__side {
  grid-column: 10 / -1;
  position: relative;
  display: flex;
  flex-direction: column;
  height: 437px;            /* column top y201 down to the design's lead bottom, y638 */
}

.hero__title { max-width: 1053px; }

/* The title occupies four 75px lines from y=201, ending at 501; the hint sits
   at 646. */
.hero__hint { margin-block-start: 145px; }

.hero__lead {
  flex: none;                  /* a short column must not squash the copy */
  margin-block-start: auto;    /* sits on the column's foot, y638 */
}

.hero__cta {
  position: absolute;
  inset-block-start: calc(100% + 143px);   /* 143 below the lead's foot — y781 */
  inset-inline: 0;
}

/* The alpha lives in the colour rather than on `opacity`, because opacity
   applies to the whole element — including the split-plus drawn on it below,
   which the design keeps at full-strength green. */
.hero__rule {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 632px;   /* y715 less the 83px header row */
  height: 1px;

  /* A one-colour gradient and not that colour, so the line can be drawn in from
     the left — see Rule draw in components.css. background-size is what does
     the drawing there, and it has no effect on a background-color, which always
     fills the box whatever else is asked of it. */
  background: linear-gradient(rgb(255 255 255 / 0.35) 0 0) no-repeat;
}


/* --- Closing CTA band ----------------------------------------------------
   Same construction as the hero. Offsets from the band top (y=4923):
     heading 216 (x=311, w 819)   lead 561   rule 688   button 743 */

.cta {
  position: relative;
  min-height: 974px;
  isolation: isolate;
}

.cta__inner {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr));
  gap: var(--grid-gutter);
  padding-block-start: 216px;
}

/* Both pinned to row 1 so the lead's offset below still measures from the band
   top, as it does in the design.

   `.cta__main` spans the whole content box rather than columns 1-9. The
   percentage below was written against the content box — 20.5% of 1388 is the
   285px that puts the heading at x=311 — but it resolves against whatever
   `.cta__main` is, and at 9 columns that landed the heading at 236. The tablet
   rule already spans 1/-1, which is why it hits x=116 there and only the
   desktop value was wrong. */
.cta__main { grid-column: 1 / -1; grid-row: 1; }

/* Bottom-anchored, exactly as the hero's right column and for the same reason —
   see the note there. This band is where the failure actually showed: at 1025
   the lead ran to six lines and crossed the rule beneath it by 11px. */
.cta__side {
  grid-column: 10 / -1;
  grid-row: 1;
  position: relative;
  display: flex;
  flex-direction: column;
  height: 391px;   /* column top (band+216) down to the design's lead bottom, band+607 */
}

/* The block is centred on the content box in all three artboards — 311+819/2,
   116+537/2 and 21+331/2 each land on the middle of their page — and the text
   is centred inside it.

   `margin-inline: auto` states that centring directly. The 20.5% it replaces
   only resolved to the design's x=311 while the content box was exactly 1388;
   below 1440 it kept its percentage while the box shrank, so by 1280 the
   heading sat 52px right of the centre it is supposed to hold. */
.cta__title {
  margin-inline: auto;
  max-width: 819px;
  text-align: center;
}

/* The lead now sits in a reveal wrapper (see home.php), which is the flex item;
   it takes the foot-anchoring the lead used to carry. Inside a block wrapper the
   lead's own `margin-block-start: auto` below resolves to 0, so the responsive
   overrides that restate it are harmless. */
.cta__lead-reveal {
  flex: none;
  margin-block-start: auto;    /* sits on the column's foot, band+607 */
}

.cta__lead {
  flex: none;
  margin-block-start: auto;    /* sits on the column's foot, band+607 */
}

.cta__action {
  position: absolute;
  inset-block-start: calc(100% + 153px);   /* band+760 */
  inset-inline: 0;
}

.cta__rule {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 688px;
  height: 1px;
  background: linear-gradient(rgb(255 255 255 / 0.35) 0 0) no-repeat;   /* see .hero__rule */
}


/* --- The split-plus on a full-bleed rule ---------------------------------
   Both hero rules carry a green cross with a gap at its centre, drawn in the
   artboards as four 8px strokes: horizontals at 1068-1076 and 1084-1092,
   verticals at 703-711 and 719-727, all 2px in #ade542. It marks where the
   right-hand column begins, and it was missing from both rules entirely.

   The rule is full bleed but the mark is not: it belongs to the content box, so
   past 1440 it has to stay with the centred content rather than drift out with
   the line. `--rule-box` is the content box's own width — the viewport until
   the shell caps out — which makes one expression correct at every width:
   1080 at 1440, 571 at 768, 274 at 375, and 50% + 360 on anything wider. */

.hero__rule,
.cta__rule {
  --rule-box: min(100%, var(--page-max));

  /* x1080 in a content box that runs 26 to 1414. The two narrower boards put it
     at 573 and, for the closing band, 252 — responsive.css carries those. */
  --plus-ratio: 0.7594;

  --rule-plus-x: calc(
    50% - var(--rule-box) / 2
    + var(--page-margin)
    + (var(--rule-box) - var(--page-margin) * 2) * var(--plus-ratio)
  );
}

.hero__rule::before,
.hero__rule::after,
.cta__rule::before,
.cta__rule::after {
  content: '';
  position: absolute;
  top: 50%;
  left: var(--rule-plus-x);
  translate: -50% -50%;
  color: var(--c-accent);
}

/* Horizontal arms — 8px, an 8px gap, 8px. */
.hero__rule::before,
.cta__rule::before {
  width: 24px;
  height: 2px;
  background: linear-gradient(90deg,
    currentColor 0 8px, transparent 8px 16px, currentColor 16px 24px);
}

/* Vertical arms. */
.hero__rule::after,
.cta__rule::after {
  width: 2px;
  height: 24px;
  background: linear-gradient(180deg,
    currentColor 0 8px, transparent 8px 16px, currentColor 16px 24px);
}



/* --- À propos hero -------------------------------------------------------
   Title on the flat band, photograph inset beneath it rather than behind. */

.about-hero { padding-block-start: 118px; }   /* y201 less the 83px header row */

.about-hero__title { max-width: 1170px; }

.about-hero__media {
  margin: 118px 0 0;
  aspect-ratio: 1388 / 621;
  border-radius: var(--radius-card);
  overflow: hidden;
}

.about-hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Black and white on À propos only, at the client's request. Scoped to the
   page because the Expertises hero borrows .about-hero__media for its frame. */
.page-about .about-hero__media img { filter: grayscale(1); }


/* --- Separator -----------------------------------------------------------
   A hairline with a plus glyph on it, between the À propos sections. The plus
   sits at the left, aligned with the label column (x=143), not centred. */

/* Alpha in the colour, not on `opacity`: the plus is a child, and a parent's
   opacity dims the whole group — `.separator__plus { opacity: 1 }` could never
   win it back, so the green mark was rendering at 35% too. */
.separator {
  position: relative;
  height: 1px;
  background: linear-gradient(rgb(255 255 255 / 0.35) 0 0) no-repeat;   /* see .hero__rule */
  margin-block: var(--s-7) 46px;

  /* The legal page's aside uses an <hr> for this, and a UA <hr> carries a
     border of its own — which would paint the whole line at once, under the one
     being drawn. */
  border: 0;

  --rule-box: min(100%, var(--page-max));

  /* Where the plus sits, as a fraction of the content box. The artboards do not
     agree on one value — 143 at 1440, 299 at 768, 253 at 375, which is 8.43%,
     38.2% and 69.6% — so each breakpoint carries its own rather than being
     interpolated from the others. responsive.css sets the other two. */
  --plus-ratio: 0.0843;

  /* A separator nested inside a card is already inset by the page margin, so it
     measures the offset straight across its own box. The full-bleed one below
     has to add the margin back. */
  --plus-x: calc(100% * var(--plus-ratio));
}

/* The artboards run this line edge to edge (x0.5, w1440) while the copy it
   separates stays inside the content box, so it breaks out of the shell's
   padding. The copy in the results panel keeps its own width — that separator
   is nested deeper and never matches this selector. */
.shell > .separator {
  width: 100vw;
  margin-inline: calc(50% - 50vw);

  /* Now that the line runs the full viewport, the mark has to be measured back
     off the centred content box — the same expression the hero rules use. */
  --plus-x: calc(
    50% - var(--rule-box) / 2
    + var(--page-margin)
    + (var(--rule-box) - var(--page-margin) * 2) * var(--plus-ratio)
  );
}

.separator__plus {
  position: absolute;
  top: 50%;
  left: var(--plus-x);
  translate: -50% -50%;
  width: 24px;
  height: 24px;
  color: var(--c-accent);
}

/* Split, like the mark on the hero rules — four 8px arms around an 8px gap,
   not a solid cross. */
.separator__plus::before,
.separator__plus::after {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
}

.separator__plus::before {
  width: 24px;
  height: 2px;
  background: linear-gradient(90deg,
    currentColor 0 8px, transparent 8px 16px, currentColor 16px 24px);
}

.separator__plus::after {
  width: 2px;
  height: 24px;
  background: linear-gradient(180deg,
    currentColor 0 8px, transparent 8px 16px, currentColor 16px 24px);
}

/* The rule that closes the À propos band — the same component, but its plus
   sits over the right-hand column rather than the label column, and the
   artboard's gaps are 85 above and 55 below. The gap below is doubled to 110
   on request, now that a display title sits under the rule rather than a
   button. In flow rather than absolute: this band has no fixed height to
   measure an offset from. */
.separator--band {
  --plus-ratio: 0.7594;
  margin-block: 85px 110px;
}

/* Under the legal page's title. Same component again — the artboards give its
   mark x1080, the rule itself on y453, and the body on 513.

   123 above, not the 102 that was here: the title's box runs 255 to 330 for its
   one line, and 330 to the rule at 453 is 123. The 60 below is right — 454 to
   the body at 513. */
.separator--legal {
  --plus-ratio: 0.7594;
  margin-block: 123px 60px;
}


/* --- Two-column editorial (À propos) -------------------------------------
   XD: label x=143 w=335 (columns 2-4), body x=611 w=686 (columns 6-11).
   Both are set at 24/32, so the label is a peer of the body rather than a
   smaller heading above it. */

/* No foot of its own. This carried 122px, which the separator's own 96px top
   margin then added to: 218px between the last line of a block and the rule
   under it, against the 77 the artboard leaves (body 1244 + seven lines ends
   1463, rule 1540). The case study already set this to 0 for the same reason,
   so the two pages now space their editorial blocks the same way. */
.editorial {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr));
  gap: var(--grid-gutter);
}

/* --c-mist, not the inherited --c-off-white. The token comments set the general
   rule — mist for display, off-white for body — but all four boards that draw
   these blocks (À propos at 1440/768/375, Étude de cas at 1440) put them at
   #b4c3bc. They are the longest paragraphs on the site and the boards hold them
   back from the running text. The Résultats panel overrides this to white in
   pages.css, which is what its own board asks for. */
.editorial__label {
  grid-column: 2 / 5;
  font-weight: var(--fw-medium);
  line-height: var(--t-editorial-lh);
  color: var(--c-mist);
}

.editorial__body {
  grid-column: 6 / 12;
  line-height: var(--t-editorial-lh);
  color: var(--c-mist);
}

/* À propos renders its body as separate paragraphs (so each can arrive on its
   own); the gap is the one blank line the field's <br><br> used to make. */
.editorial__body > p + p { margin-top: var(--t-editorial-lh); }
.expertise-hero__lead > p + p { margin-top: var(--t-lead-lh); }

.editorial__aside { grid-column: 11 / -1; }


/* --- Stacks --------------------------------------------------------------
   Owl selector: spacing lives between siblings, so a block never carries a
   margin that has to be cancelled when it lands first or last in a container. */

.stack > * + * { margin-top: var(--s-3); }
.stack--sm > * + * { margin-top: var(--s-1); }
.stack--lg > * + * { margin-top: var(--s-5); }
.stack--xl > * + * { margin-top: var(--s-7); }

.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
}

.push-right { margin-left: auto; }


/* --- Small desktop -------------------------------------------------------
   Between the tablet breakpoint and about 1200 the CTA band crowds itself.

   The heading is a fixed 819 centred on the content box, so it wraps to the
   same four lines at 1026 as it does at 1440 — what changes is everything
   around it. The lead is pinned to the foot of a 391px column, which put its
   top at the row top + 299 while the four-line heading runs to + 300: they are
   vertically adjacent at every width. That is fine while the heading's last
   line ends far to the left of the lead's column, which it does at 1440, where
   the ink stops 64px short of it. The clearance is 0.25 of the content width
   less 278, so it closes as the page narrows — 4px at 1200, and at 1026 the
   last line runs 39px INTO the lead's column with nothing between them.

   Dropping the lead is the fix rather than narrowing the heading: the heading
   is centred on the page and pulling it in would have to take 180px off each
   side to clear the column, which costs another two lines. 451 gives the lead
   the same 59px of air the wider layouts have, and the button below it still
   lands 94px inside the band's 974.
   -------------------------------------------------------------------------- */

/* Up to 1340, not 1200. The clearance formula above puts the heading's last
   line 40px clear of the lead's column only once the content box reaches 1274,
   which is a 1341 viewport. Stopping at 1200 fixed the width that was reported
   and left 1201-1340 with the same 4-to-35px near-miss, plus a 60px jump in the
   lead's position the moment the query let go. */
@media (min-width: 1025px) and (max-width: 1340px) {
  .cta__side { height: 451px; }
}
