.container {
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: var(--space-md);
}

@media (max-width: 640px) {
  .container { padding-inline: var(--space-sm); }
}

main { position: relative; z-index: 2; }

section {
  padding-block: var(--space-xl);
  position: relative;
}

/* ============================================================
   NAV
   ============================================================ */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: rgba(28, 27, 33, 0);
  border-bottom: 1px solid transparent;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.nav.is-stuck {
  background: rgba(28, 27, 33, 0.86);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom-color: var(--color-border);
}


.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  height: var(--nav-height);
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: var(--space-md);
}

/* Wordmark as plain text, never uppercase (charter 01) */
.nav__brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.nav__links {
  display: none;
  gap: var(--space-md);
  font-size: var(--size-small);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 600;
  font-family: var(--font-body);
}

.nav__links a {
  color: var(--color-main-dim);
  position: relative;
  padding-block: 0.35em;
  transition: color var(--dur-fast) var(--ease);
}

.nav__links a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--color-highlight);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}

.nav__links a:hover { color: var(--color-main); }
.nav__links a:hover::after,
.nav__links a.is-active::after { transform: scaleX(1); }
.nav__links a.is-active { color: var(--color-main); }

/* Reading progress */
.nav__progress {
  position: absolute;
  left: 0;
  bottom: -1px;
  height: 2px;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left;
  background: var(--color-highlight);
}

/* Language switch, FR / EN */
.lang-switch {
  display: flex;
  align-items: center;
  gap: 0.45em;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--color-main-faint);
}

.lang-switch a {
  color: var(--color-main-faint);
  padding: 0.2em 0.1em;
  transition: color var(--dur-fast) var(--ease);
}

.lang-switch a:hover { color: var(--color-accent); }
.lang-switch a.is-active { color: var(--color-main); }

/* The panel stacks full width links: the switch sits on its own row */
.lang-switch--panel {
  justify-content: center;
  padding-top: var(--space-sm);
  font-size: 0.8rem;
}

/* Mobile menu button */
.nav__toggle {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius);
  transition: border-color var(--dur-fast) var(--ease);
}

.nav__toggle:hover { border-color: var(--color-accent); }

.nav__toggle span {
  display: block;
  width: 16px;
  height: 2px;
  background: var(--color-main);
  transition: transform var(--dur) var(--ease), opacity var(--dur-fast) var(--ease);
}

.nav__toggle span + span { margin-top: 3px; }

.nav__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

@media (min-width: 800px) {
  .nav__links { display: flex; }
  .nav__toggle { display: none; }
}

/* Mobile panel */
.nav__panel {
  position: fixed;
  inset: var(--nav-height) 0 auto 0;
  background: rgba(24, 24, 29, 0.97);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-md);
  display: grid;
  gap: var(--space-xs);
  z-index: 99;
  transform: translateY(-12px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
}

.nav__panel.is-open { opacity: 1; transform: none; visibility: visible; }

.nav__panel a {
  font-family: var(--font-display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 1.15rem;
  padding-block: 0.6em;
  border-bottom: 1px solid var(--color-border);
  color: var(--color-main-dim);
  display: flex;
  align-items: baseline;
  gap: 0.7em;
}

.nav__panel a::before {
  content: attr(data-idx);
  font-family: var(--font-body);
  font-size: 0.7rem;
  color: var(--color-accent);
  letter-spacing: 0.1em;
}

.nav__panel a:hover, .nav__panel a:focus-visible { color: var(--color-main); }

@media (min-width: 800px) { .nav__panel { display: none; } }

/* ============================================================
   AXES (Créer / Collaborer / Transmettre)
   ============================================================ */
.axis {
  display: grid;
  gap: var(--space-lg);
  /* The panel aligns to the axis digit, not to the middle of the text */
  align-items: start;

  /* One slant for the whole section, asked for on 31/08/2026. It is the run of
     the oblique per unit of height, that is the tangent of its angle to the
     vertical. The image mask, the button mask and the hover sweep all derive
     from this single number, so the angle is changed here, once. It went from
     0.62 to 0.52 when the line was made continuous: it now crosses the whole
     column instead of the image alone, and the sharper angle was carrying the
     subject of the picture away with it. */
  --axis-slant: 0.52;
  --axis-cta-h: 4rem;
  --axis-cta-rim: 2px;
  --axis-gap: var(--space-xs);
  /* What stands under the image in its own column. The oblique of the image has
     to reach the top of the button already shifted by this much, otherwise the
     two edges are merely parallel and the eye reads two shapes instead of one
     line. */
  --axis-below: calc(var(--axis-gap) + var(--axis-cta-h));
  /* A rim of `--axis-cta-rim` measured square to the oblique edge is wider than
     that measured horizontally, by 1 / cos(atan(slant)) = sqrt(1 + slant^2).
     Without it the outline would thin out on the diagonal and hold its width on
     the three straight edges, which is exactly what the eye catches. */
  --axis-cta-rim-x: calc(var(--axis-cta-rim) * 1.176);
  --axis-cta-run: calc(var(--axis-slant) * var(--axis-cta-h));
}

/* A phone shows the image at the full width of the screen, where the desktop
   angle would cut away nearly half the picture. The composition survives a
   softer slant, the picture does not survive the sharp one. */
@media (max-width: 859.98px) {
  .axis { --axis-slant: 0.34; }
}

/* The visual column is the wider of the two, asked for on 31/08/2026: the
   trapeze has to reach the edge of the text block, a margin apart, instead of
   stopping a gutter short of it. That margin is the grid gap, and it is what
   used to be a `--space-xl` void. The text loses nothing readable, its lead is
   capped at 46ch anyway. The ratio flips with the layout, since `order` moves
   the text to the second column but not the track that sizes it. */
@media (min-width: 860px) {
  .axis { grid-template-columns: 1fr 1.2fr; gap: var(--space-md); }
  .axis--reverse { grid-template-columns: 1.2fr 1fr; }
  .axis--reverse .axis__text { order: 2; }
}

.axis__number {
  font-family: var(--font-display);
  font-size: clamp(3.5rem, 8vw, 5.5rem);
  font-weight: 700;
  line-height: 0.9;
  display: block;
  /* The top of the digit meets the top of the image next to it. The value is
     not eyeballed: with `line-height: 0.9`, Jost puts the cap top 0.081em below
     the box top (half-leading plus the ascender gap), measured in the browser.
     It stays in `em` so it follows the clamp above. The old -0.13em overshot by
     4.3px, and the image sat that much lower than the digit. */
  margin-top: -0.081em;
  margin-bottom: var(--space-xs);
  /* Stroke first, glyph body over it: hides the seams Jost leaves inside
     3 and 8. The body matches the ground it sits on, hence the variable. */
  paint-order: stroke fill;
  color: var(--axis-number-fill, var(--color-bg));
  -webkit-text-stroke: 2px var(--color-border-strong);
}

.axis__text h2 { font-size: var(--size-h2); margin-top: 0.15em; }

.axis__lead {
  color: var(--color-main-dim);
  margin-top: var(--space-sm);
  max-width: 46ch;
}

/* Animated visual, shown only when it has a column of its own */

/* --- illustration d'une section d'accueil -------------------------------
   Elle prend la place de l'ancien panneau decoratif, mais elle mene quelque
   part : une section de l'accueil annonce une page, l'image est le chemin le
   plus court vers elle. */
.axis__shot {
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--color-bg-elevated-2);
  /* One single line runs from the top of the image to the bottom of the button,
     asked for on 31/08/2026. The height of the image is three quarters of its
     width (aspect-ratio above) and the first coordinate of a polygon is read
     against the width, hence the 75%; `--axis-below` is what the line still has
     to travel under the image, so the bottom of the image is already shifted by
     that much and the button picks the line up exactly where it left it. */
  clip-path: polygon(
    calc(var(--axis-slant) * (75% + var(--axis-below))) 0,
    100% 0,
    100% 100%,
    calc(var(--axis-slant) * var(--axis-below)) 100%
  );
}

/* Image on the left, text on the right: the oblique mirrors, so that it always
   opens towards the text instead of turning its back on it. */
.axis--reverse .axis__shot {
  clip-path: polygon(
    0 0,
    calc(100% - var(--axis-slant) * (75% + var(--axis-below))) 0,
    calc(100% - var(--axis-slant) * var(--axis-below)) 100%,
    0 100%
  );
}

.axis__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.9);
  transition: transform var(--dur) var(--ease), filter var(--dur) var(--ease);
}

/* The image no longer lifts on hover: it would carry its half of the oblique
   away from the other, and the line is only one line while it stays put. */
.axis__aside:hover .axis__shot img,
.axis__aside:focus-within .axis__shot img { transform: scale(1.04); filter: saturate(1.05); }

/* --- visual column: the image, then the button it announces --------------
   The button moved under the image on 31/08/2026, at Jerome's request. It used
   to close the text column, where nothing held it to the right; under the image
   it closes the visual column, and both lead to the same page.

   The two are one block, not two neighbours: the oblique crosses the gap, so
   the hover crosses it too. Pointing at either half lights both, and the
   keyboard gets the same through `:focus-within`. */
.axis__aside {
  display: grid;
  gap: var(--axis-gap);
}

/* --- the button, in the continuation of the trapeze ---------------------
   Asked for on 31/08/2026. The bar repeats the oblique of the image on its own
   height, outline included. Nothing here is drawn with a `border`: a border is
   clipped away by `clip-path`, so the accent is painted on the element itself
   and the ground of the section is laid over it, inset by the rim. What is left
   showing around that inset is the outline. */
.axis__cta {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.6em;
  height: var(--axis-cta-h);
  padding-inline: var(--space-md);
  background: var(--color-accent);
  clip-path: polygon(var(--axis-cta-run) 0, 100% 0, 100% 100%, 0 100%);
  /* `clip-path` hides what spills, it does not stop it counting: the sweep is
     wider than the bar by one run, and that width would still push a horizontal
     scrollbar onto the page. `overflow` is what actually contains it. */
  overflow: hidden;
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: var(--size-small);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  transition: color var(--dur) var(--ease);
}

.axis--reverse .axis__cta {
  justify-content: flex-start;
  clip-path: polygon(0 0, calc(100% - var(--axis-cta-run)) 0, 100% 100%, 0 100%);
}

/* The ground of the section, inset by the rim. Its own oblique is the button's
   oblique moved `--axis-cta-rim-x` to the right: parallel, never converging,
   which an `inset` alone would not give. */
.axis__cta::before {
  content: '';
  position: absolute;
  inset: var(--axis-cta-rim);
  z-index: 0;
  background: var(--axis-number-fill, var(--color-bg));
  clip-path: polygon(
    calc(var(--axis-slant) * (var(--axis-cta-h) - var(--axis-cta-rim))
         + var(--axis-cta-rim-x) - var(--axis-cta-rim)) 0,
    100% 0,
    100% 100%,
    calc(var(--axis-slant) * var(--axis-cta-rim)
         + var(--axis-cta-rim-x) - var(--axis-cta-rim)) 100%
  );
}

.axis--reverse .axis__cta::before {
  clip-path: polygon(
    0 0,
    calc(100% - var(--axis-slant) * (var(--axis-cta-h) - var(--axis-cta-rim))
         - var(--axis-cta-rim-x) + var(--axis-cta-rim)) 0,
    calc(100% - var(--axis-slant) * var(--axis-cta-rim)
         - var(--axis-cta-rim-x) + var(--axis-cta-rim)) 100%,
    0 100%
  );
}

/* The hover fill. It arrives on the oblique too, and it is wider than the bar by
   one run so that its slanted edge has left the frame when it lands. */
.axis__cta-sweep {
  position: absolute;
  z-index: 2;
  top: var(--axis-cta-rim);
  bottom: var(--axis-cta-rim);
  left: var(--axis-cta-rim);
  width: calc(100% - 2 * var(--axis-cta-rim) + var(--axis-cta-run));
  background: var(--color-accent);
  clip-path: polygon(0 0, 100% 0, calc(100% - var(--axis-cta-run)) 100%, 0 100%);
  transform: translateX(-100%);
  transition: transform var(--dur) var(--ease);
}

.axis--reverse .axis__cta-sweep {
  left: auto;
  right: var(--axis-cta-rim);
  /* Leading edge on the left here, and it leans the other way: parallel to the
     oblique of this button, which is the mirror of the one opposite. */
  clip-path: polygon(0 0, 100% 0, 100% 100%, var(--axis-cta-run) 100%);
  transform: translateX(100%);
}

.axis__cta-label,
.axis__cta-arrow {
  position: relative;
  z-index: 3;
}

.axis__cta-arrow { transition: transform var(--dur) var(--ease); }

.axis__aside:hover .axis__cta,
.axis__aside:focus-within .axis__cta { color: var(--axis-number-fill, var(--color-bg)); }

.axis__aside:hover .axis__cta-sweep,
.axis__aside:focus-within .axis__cta-sweep { transform: translateX(0); }

.axis__aside:hover .axis__cta-arrow,
.axis__aside:focus-within .axis__cta-arrow { transform: translateX(4px); }

/* `clip-path` cuts an outline away, so the focus ring cannot be one: the rim
   thickens instead, and every shape derived from it follows on its own. */
.axis__cta:focus-visible {
  --axis-cta-rim: 5px;
  outline: none;
}

.axis__panel {
  display: none;
  aspect-ratio: 4 / 3;
  border: var(--border);
  border-radius: var(--radius);
  background:
    radial-gradient(circle at 70% 20%, rgba(1, 181, 255, 0.10), transparent 55%),
    var(--color-bg-elevated-2);
  position: relative;
  overflow: hidden;
}

/* After the base rule on purpose: at equal specificity, order decides */
@media (min-width: 860px) { .axis__panel { display: block; } }

.axis__panel svg { width: 100%; height: 100%; }

/* --- axis visuals: branching, network, waves --- */
.net-link {
  stroke: var(--color-border-strong);
  stroke-width: 1;
  fill: none;
}

.net-flow {
  stroke: var(--color-accent);
  stroke-width: 1.5;
  fill: none;
  stroke-dasharray: 6 54;
  animation: link-flow 3s linear infinite;
}

.net-node {
  fill: var(--color-accent);
  transform-box: fill-box;
  transform-origin: center;
  animation: node-pulse 3.4s var(--ease-out) infinite;
}

.net-node--warm { fill: var(--color-highlight); }

.net-ring {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1;
}

/* --- Transmettre visual: broadcast waves --- */
.wave {
  fill: none;
  stroke: var(--color-accent);
  stroke-width: 1.5;
  transform-box: fill-box;
  transform-origin: center;
  animation: ring-out 4s var(--ease-out) infinite;
}

.wave--warm { stroke: var(--color-highlight); }

.seed {
  fill: var(--color-highlight);
  transform-box: fill-box;
  transform-origin: center;
  animation: node-pulse 3s var(--ease-out) infinite;
}

.orbit {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1;
  stroke-dasharray: 3 6;
  transform-box: fill-box;
  transform-origin: center;
  animation: spin-slow 40s linear infinite;
}

/* ============================================================
   CREATIONS GRID
   ============================================================ */
/* One card per row */
.card-rows {
  display: grid;
  gap: var(--space-md);
  grid-template-columns: 1fr;
  margin-top: var(--space-lg);
}

/* ============================================================
   OFFERS GRID
   ============================================================ */
.offer-grid {
  display: grid;
  gap: var(--space-sm);
  grid-template-columns: 1fr;
  margin-top: var(--space-lg);
}

@media (min-width: 700px) {
  .offer-grid { grid-template-columns: repeat(3, 1fr); }
  .offer-grid--two { grid-template-columns: repeat(2, 1fr); }
}
