/* =========================================================
   Jean Sébastien Allet · Portfolio · style.css
   ---------------------------------------------------------
   One file, read from top to bottom:

   1.  Fonts (self-hosted, no external request)
   2.  Design tokens: colours, type, shapes, spacing
       Change the look of the whole site here.
   3.  Base and layout
   4.  Background (aurora + grain)
   5.  Components (used everywhere)
       5.1  Glass card     5.2  Buttons (+ round arrow)  5.3  Badge
       5.4  Tags           5.5  Icons          5.6  Text styles
       5.7  Section head   5.8  Image frame    5.9  Reveal animation
       5.10 Results tiles  5.11 Fill-in marks  5.12 Data table
   6.  Navigation
   7.  Hero, profile photo and key facts
   8.  Key numbers (gearbox page)
   9.  Timeline (Experience, Education, gearbox steps)
   10. Projects
   11. Skills (11.1 toolbox dock)
   12. Beyond work (12.1 photo carousel)
   13. Contact
   14. Footer
   15. Case study page (gearbox.html): looping video, sections, 15.6 3D model
   16. Gallery and picture viewer
   17. Responsive (tablet, phone)
   18. Reduced motion

   Design rules that keep the page consistent:
   - Colours: 4 brand colours (blue, teal, orange, pink) + green
     for "available / now" only. Everything else is ink or white.
   - Fonts: Geist for all text, Instrument Serif italic ONLY for the
     coloured word in big titles, Geist Mono ONLY for small labels
     and dates (class .eyebrow and .meta).
   - Shapes: every box is a .card (same glass, same radius).
     Pills are only buttons, .badge (status) and .tag (keywords).
   ========================================================= */


/* =========================================================
   1. FONTS
   Licences in assets/fonts (SIL Open Font License).
   ========================================================= */
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../assets/fonts/geist-latin-wght-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../assets/fonts/geist-latin-ext-wght-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../assets/fonts/geist-mono-latin-wght-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../assets/fonts/geist-mono-latin-ext-wght-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Instrument Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/instrument-serif-latin-400-italic.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Instrument Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/instrument-serif-latin-ext-400-italic.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* =========================================================
   2. DESIGN TOKENS
   ========================================================= */

/* The 4 background colours are animated, so they are registered as colours */
@property --a1 { syntax: "<color>"; inherits: true; initial-value: #2F8CFF; }
@property --a2 { syntax: "<color>"; inherits: true; initial-value: #FF5A8A; }
@property --a3 { syntax: "<color>"; inherits: true; initial-value: #FF9150; }
@property --a4 { syntax: "<color>"; inherits: true; initial-value: #13B9A1; }

:root {
  /* Colours: neutrals */
  --bg: #EEF2F7;                         /* page background */
  --nav-room: 96px;                      /* height kept free under the fixed menu (118px on 2 rows) */
  --ink: #0C1424;                        /* main text */
  --ink-2: #46536A;                      /* secondary text */
  --ink-3: #56627A;                      /* labels, dates (4.5:1 on the page background) */
  --line: rgba(12, 20, 36, .09);         /* thin separators */

  /* Colours: brand (the only accents of the site) */
  --blue: #2F8CFF;
  --teal: #13B9A1;
  --orange: #FF9150;
  --pink: #FF5A8A;
  --green: #17B26A;                      /* only for "available" and "now" */

  /* Colour order teal → blue → pink → orange: every step blends into a clean colour */
  --grad-brand: linear-gradient(100deg, var(--teal) 0%, var(--blue) 35%, var(--pink) 70%, var(--orange) 100%);
  --grad-cool: linear-gradient(135deg, var(--blue), var(--teal));

  /* Glass */
  --glass-bg: linear-gradient(160deg, rgba(255, 255, 255, .76), rgba(255, 255, 255, .44));
  --glass-border: rgba(255, 255, 255, .8);
  --glass-blur: blur(24px) saturate(170%);
  --shadow-card: 0 24px 60px -30px rgba(20, 44, 90, .38), 0 2px 8px rgba(20, 44, 90, .05), inset 0 1px 0 rgba(255, 255, 255, .95);

  /* Type */
  --font-sans: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-serif: "Instrument Serif", ui-serif, Georgia, "Times New Roman", serif;
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --fs-h1: clamp(3rem, 6.3vw, 5.6rem);   /* "Jean Sébastien" stays on one line */
  --fs-h2: clamp(2.1rem, 4.8vw, 3.6rem);
  --fs-h3: 1.3rem;
  --fs-body: 1.0625rem;
  --fs-small: .9rem;
  --fs-meta: .76rem;                    /* uppercase mono labels: 12 px, still readable on phones */
  --weight-title: 620;

  /* Shapes */
  --radius-card: 28px;                   /* every card */
  --radius-inner: 18px;                  /* photos and boxes inside a card */
  --radius-icon: 12px;
  --radius-pill: 999px;

  /* Spacing */
  --gutter: clamp(16px, 4vw, 40px);      /* left/right page margin */
  --max-width: 1180px;
  --section-space: clamp(64px, 9vw, 116px);
  --card-pad: clamp(22px, 3vw, 34px);
  --gap: 20px;

  /* Motion */
  --ease: cubic-bezier(.2, .75, .2, 1);

  color-scheme: light;
}


/* =========================================================
   3. BASE AND LAYOUT
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--nav-room);   /* anchors and keyboard focus stay below the fixed menu */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

h1, h2, h3, h4, p, figure, dl, dd { margin: 0; }
img { display: block; max-width: 100%; }
a { color: inherit; }
ul, ol { margin: 0; padding: 0; list-style: none; }
[hidden] { display: none !important; }   /* hidden always wins over a component's display */

:focus-visible {
  outline: 2.5px solid var(--blue);
  outline-offset: 3px;
  border-radius: 10px;
}

/* Page wrapper: side margins for everything except the fixed elements */
.page {
  padding-inline: var(--gutter);
  overflow-x: clip;
}

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

.section {
  padding-block: var(--section-space);
}

/* A section without a title that follows on from the one above
   (gearbox page call to action): one short gap instead of two big ones */
.section--follow { padding-top: 0; }
.section:has(+ .section--follow) { padding-bottom: clamp(40px, 5vw, 64px); }

/* "Skip to content" link, visible only with the keyboard */
.skip-link {
  position: fixed;
  z-index: 100;
  left: 16px;
  top: -80px;
  padding: 10px 16px;
  border-radius: var(--radius-pill);
  background: var(--ink);
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  transition: top .2s;
}
.skip-link:focus { top: 16px; }

/* The icon sprite itself is invisible */
.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* Text for screen readers only (e.g. an empty table corner) */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}


/* =========================================================
   4. BACKGROUND
   Four soft colour blobs drifting slowly. main.js changes their
   colours (--a1 to --a4) when you scroll into another section.
   ========================================================= */
.aurora {
  position: fixed;
  inset: 0;
  z-index: -2;
  overflow: hidden;
  pointer-events: none;
  transition: --a1 1.8s ease, --a2 1.8s ease, --a3 1.8s ease, --a4 1.8s ease;
}

.aurora i {
  position: absolute;
  width: 64vmax;
  height: 64vmax;
  border-radius: 50%;
  opacity: .6;
  will-change: transform;
}

.aurora i:nth-child(1) { left: -10vmax; top: -18vmax; background: radial-gradient(closest-side, var(--a1), transparent); animation: drift-1 26s ease-in-out infinite alternate; }
.aurora i:nth-child(2) { right: -16vmax; top: -4vmax; background: radial-gradient(closest-side, var(--a2), transparent); animation: drift-2 31s ease-in-out infinite alternate; }
.aurora i:nth-child(3) { left: 16vw; bottom: -30vmax; background: radial-gradient(closest-side, var(--a3), transparent); animation: drift-3 35s ease-in-out infinite alternate; }
.aurora i:nth-child(4) { right: -8vmax; bottom: -22vmax; background: radial-gradient(closest-side, var(--a4), transparent); animation: drift-4 29s ease-in-out infinite alternate; }

@keyframes drift-1 { to { transform: translate3d(14vw, 12vh, 0) scale(1.15); } }
@keyframes drift-2 { to { transform: translate3d(-12vw, 16vh, 0) scale(.9); } }
@keyframes drift-3 { to { transform: translate3d(10vw, -14vh, 0) scale(1.1); } }
@keyframes drift-4 { to { transform: translate3d(-16vw, -8vh, 0) scale(1.2); } }

/* Fine paper grain on top of the colours */
.grain {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .22;
  mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}


/* =========================================================
   5. COMPONENTS
   ========================================================= */

/* 5.1 Glass card ------------------------------------------
   The base of every box. A soft light follows the mouse
   (main.js part 11 sets --mx and --my). */
.card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 0%), rgba(255, 255, 255, .85), transparent 60%);
  opacity: 0;
  transition: opacity .3s;
  pointer-events: none;
}

.card:hover::before { opacity: 1; }

.card-title {
  font-size: var(--fs-h3);
  font-weight: var(--weight-title);
  letter-spacing: -.02em;
  line-height: 1.2;
}

/* Card heading with an icon tile in front */
.card-head {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 1.08rem;
  font-weight: var(--weight-title);
  letter-spacing: -.015em;
  line-height: 1.25;
}


/* 5.2 Buttons --------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55em;
  height: 50px;
  padding: 0 24px;
  border: 0;
  border-radius: var(--radius-pill);
  font: 600 .95rem/1 var(--font-sans);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: transform .3s var(--ease), box-shadow .3s, background .3s, color .3s;
}

.btn:active { transform: scale(.97); }

.btn--primary {
  background: var(--ink);
  color: #fff;
  box-shadow: 0 12px 26px -12px rgba(12, 20, 36, .7);
}
.btn--primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 18px 32px -14px rgba(12, 20, 36, .6);
}

.btn--glass {
  background: rgba(255, 255, 255, .62);
  color: var(--ink);
  border: 1px solid var(--glass-border);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}
.btn--glass:hover {
  background: rgba(255, 255, 255, .9);
  transform: translateY(-2px);
}

/* Arrow icons move a little to the right on hover */
.btn .icon--arrow { transition: transform .3s var(--ease); }
.btn:hover .icon--arrow { transform: translateX(3px); }

.btn--small {
  height: 36px;
  padding: 0 14px;
  font-size: .8rem;
}

/* Copy buttons turn green for a moment */
.btn.is-done {
  background: var(--green);
  border-color: transparent;
  color: #fff;
}

/* Round arrow over a carousel or the dock (placed by its own class) */
.arrow-btn {
  position: absolute;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border: 1px solid var(--glass-border);
  border-radius: 50%;
  background: rgba(255, 255, 255, .8);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  box-shadow: 0 10px 22px -12px rgba(12, 20, 36, .5);
  color: var(--ink);
  cursor: pointer;
  transition: opacity .3s, transform .3s var(--ease), background .3s;
}

.arrow-btn:hover { background: #fff; transform: scale(1.06); }
.arrow-btn:disabled { opacity: 0; pointer-events: none; }


/* 5.3 Badge -----------------------------------------------
   Small white pill for a status or an achievement,
   with a green dot (.badge__dot) or an icon in front. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px 7px 10px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, .72);
  border: 1px solid var(--glass-border);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  color: var(--ink);
  font-size: .84rem;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
}

.badge--small {
  gap: 6px;
  padding: 4px 10px 4px 8px;
  font-size: .72rem;
  vertical-align: middle;
}

.badge .icon {
  width: 15px;
  height: 15px;
  color: var(--orange);
}

.badge__dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
  animation: pulse 2.4s infinite;
}

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(23, 178, 106, .55); }
  70%  { box-shadow: 0 0 0 9px rgba(23, 178, 106, 0); }
  100% { box-shadow: 0 0 0 0 rgba(23, 178, 106, 0); }
}


/* 5.4 Tags (keywords) ------------------------------------- */
.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.tag {
  padding: 6px 11px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, .72);
  border: 1px solid var(--line);
  color: var(--ink-2);
  font-size: .8rem;
  font-weight: 500;
  line-height: 1.3;
  transition: background .3s, color .3s;
}

.tag:hover {
  background: #fff;
  color: var(--ink);
}


/* 5.5 Icons ------------------------------------------------
   .icon      line icon from the sprite, takes the text colour
   .icon-tile coloured square with a white icon (card headings) */
.icon {
  flex: none;
  width: 1.15em;
  height: 1.15em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon-tile {
  flex: none;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-icon);
  background: var(--grad-cool);
  color: #fff;
}

.icon-tile .icon {
  width: 21px;
  height: 21px;
}


/* 5.6 Text styles ----------------------------------------- */

/* Small uppercase label (Geist Mono) */
.eyebrow {
  font: 500 var(--fs-meta)/1.3 var(--font-mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Dates and small technical details (Geist Mono) */
.meta {
  font: 500 .78rem/1.3 var(--font-mono);
  color: var(--ink-3);
}


/* 5.7 Section head ---------------------------------------- */
.section-head {
  max-width: 780px;
  margin-bottom: clamp(28px, 4vw, 48px);
}

.kicker {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font: 500 var(--fs-meta)/1 var(--font-mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.kicker::before {
  content: "";
  width: 24px;
  height: 3px;
  border-radius: 3px;
  background: var(--grad-brand);
}

.h2 {
  margin-top: 14px;
  font-size: var(--fs-h2);
  font-weight: var(--weight-title);
  line-height: 1.02;
  letter-spacing: -.038em;
  text-wrap: balance;
}

/* The coloured italic word in big titles (the only serif on the site) */
.h2 em,
.hero__name em,
.case-title em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -.01em;
  padding-right: .06em;
  background: var(--grad-brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}


/* 5.8 Image frame (.media) ---------------------------------
   Shows the photo if the file exists, otherwise a placeholder
   with the expected content (main.js part 9). */
.media {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-inner);
  background: linear-gradient(135deg, color-mix(in oklab, var(--blue) 16%, white), color-mix(in oklab, var(--teal) 14%, white));
}

.media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--focus, 50% 50%);   /* style="--focus: 50% 30%" keeps the top part in view */
  transition: transform .5s var(--ease);
}

.card:hover .media img { transform: scale(1.04); }

/* With a mouse, each photo sits on its own GPU layer from the start:
   the zoom starts on the first frame, and the moving card light never
   has to redraw the photo */
@media (hover: hover) and (pointer: fine) {
  .media img { will-change: transform; }
}

.media--square { aspect-ratio: 1 / 1; }
.media--4x3    { aspect-ratio: 4 / 3; }
.media--16x10  { aspect-ratio: 16 / 10; }
.media--16x9   { aspect-ratio: 16 / 9; }

.media__placeholder {
  position: absolute;
  inset: 10px;
  display: none;
  place-items: center;
  align-content: center;
  gap: 8px;
  padding: 16px;
  border: 1.5px dashed rgba(12, 20, 36, .18);
  border-radius: calc(var(--radius-inner) - 6px);
  color: var(--ink-2);
  font-size: .84rem;
  font-weight: 600;
  text-align: center;
}

.media__placeholder .icon {
  width: 26px;
  height: 26px;
}

/* Photo credit inside a picture frame (bottom right) */
.media__credit {
  position: absolute;
  right: 8px;
  bottom: 8px;
  padding: 2px 8px;
  border-radius: 6px;
  background: rgba(12, 20, 36, .55);
  color: #fff;
  font-size: .68rem;
  line-height: 1.5;
  pointer-events: none;
}

.media.is-empty img,
.media.is-empty .media__credit { display: none; }
.media.is-empty .media__placeholder { display: grid; }

/* Setting in main.js: hide empty frames completely */
.hide-empty-media .media.is-empty { display: none; }

/* Drawings, renders, exploded views: show the whole picture, never crop */
.media--contain:not(.is-empty) { background: #fff; }
.media--contain img { object-fit: contain; }

/* Cut-out pictures (transparent PNG): whole, floating on the colour background */
.media--cutout img {
  padding: 5% 0 3%;
  object-fit: contain;
  filter: drop-shadow(0 16px 18px rgba(12, 20, 36, .2));
}

/* Very wide pictures (exploded view) */
.media--panorama { aspect-ratio: 11 / 4; }

/* Looping render on a white background (gearbox project card): the white
   frame and the render's own white background read as one */
.media--loop:not(.is-empty) { background: #fff; }
.media.media--loop video { background: #fff; object-fit: contain; transform: scale(1.12); }

/* Video frame: same placeholder system as the photos (main.js part 9) */
.media video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: var(--ink);
}

.media.is-empty video { display: none; }


/* 5.9 Reveal animation -------------------------------------
   Elements with .reveal slide up a little when they appear.
   They stay readable at all times (no fade from invisible). */
.reveal {
  transition: transform 1s var(--ease);
  transition-delay: calc(var(--delay, 0) * 90ms);
}

.reveal.is-pending { transform: translateY(40px) scale(.985); }
.section-head.reveal.is-pending { transform: translateY(18px); }


/* 5.10 Results tiles (.outcomes) --------------------------
   One number + one short line, at the end of every project.
   Same look as the .spec table (white tiles, thin lines). */
.outcomes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  width: 100%;
  margin-top: 6px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-inner);
  background: var(--line);
}

.outcome {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: 13px 15px;
  background: rgba(255, 255, 255, .72);
}

.outcome__value {
  align-self: flex-start;
  padding-bottom: .04em;
  background: var(--grad-cool);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-size: clamp(1.4rem, 2.1vw, 1.8rem);
  font-weight: var(--weight-title);
  letter-spacing: -.035em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.outcome__value small {
  margin-left: 3px;
  font-size: .5em;
  letter-spacing: 0;
}

.outcome__value sup { font-size: .55em; }

.outcome__label {
  color: var(--ink-2);
  font-size: .82rem;
  line-height: 1.35;
}

/* An odd number of results: the last one takes the full row */
.outcome:last-child:nth-child(odd) { grid-column: 1 / -1; }


/* 5.11 Fill-in marks ---------------------------------------
   <span class="fill-in">x</span> = a value still to write.
   Orange dashed box, easy to spot. Search "fill-in" in the
   HTML files to find them all (README, section 5). */
.fill-in {
  display: inline-block;
  min-width: 1.5em;
  padding: 0 .3em;
  border: 1.5px dashed var(--orange);
  border-radius: 8px;
  background: color-mix(in oklab, var(--orange) 12%, white);
  color: #B4541A;
  -webkit-text-fill-color: #B4541A;
  font: 600 .8em/1.3 var(--font-mono);
  letter-spacing: 0;
  text-align: center;
  vertical-align: middle;
}

/* A whole paragraph still to write */
.fill-in--block {
  display: block;
  padding: 14px 16px;
  font: 500 .95rem/1.5 var(--font-sans);
  text-align: left;
}


/* 5.12 Data table ------------------------------------------
   Technical tables of the gearbox page. Wrapped in .table-wrap
   so they scroll sideways on small phones. */
.table-wrap {
  width: 100%;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-inner);
  background: rgba(255, 255, 255, .72);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: .93rem;
}

.data-table th,
.data-table td {
  padding: 11px 16px;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: middle;
}

.data-table thead th {
  font: 500 var(--fs-meta)/1.3 var(--font-mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.data-table tbody th {
  color: var(--ink-2);
  font-weight: 550;
}

.data-table td {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.data-table tbody tr:last-child > * { border-bottom: 0; }

.data-table tfoot > tr > * {
  border-top: 1px solid var(--line);
  border-bottom: 0;
  color: var(--ink);
  font-weight: 700;
}

.data-table--budget td { text-align: right; }

/* Highlighted row (my part of the budget) */
.data-table .is-mine > * {
  background: color-mix(in oklab, var(--blue) 9%, transparent);
  color: var(--ink);
}


/* =========================================================
   6. NAVIGATION
   ========================================================= */
.nav {
  position: fixed;
  z-index: 50;
  top: calc(env(safe-area-inset-top, 0px) + 14px);
  left: 50%;
  transform: translateX(-50%);
  width: min(1060px, calc(100% - 24px));
}

.nav__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 7px 7px 12px;
  border-radius: var(--radius-pill);
}

/* Wordmark: first name in bold */
.nav__brand {
  flex: none;
  padding: 6px 6px 6px 10px;
  border-radius: var(--radius-pill);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.25;
  white-space: nowrap;
  text-decoration: none;
}

.nav__links {
  position: relative;
  display: flex;
  gap: 2px;
  min-width: 0;
  margin-inline: auto;
  overflow-x: auto;
  scrollbar-width: none;
}
.nav__links::-webkit-scrollbar { display: none; }

/* Phones: when the links scroll sideways, a soft fade on the side where
   more links are hidden (main.js part 4 sets the classes) */
.nav__links.fade-end {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent);
}
.nav__links.fade-start {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 40px);
          mask-image: linear-gradient(to right, transparent, #000 40px);
}
.nav__links.fade-start.fade-end {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 40px, #000 calc(100% - 40px), transparent);
          mask-image: linear-gradient(to right, transparent, #000 40px, #000 calc(100% - 40px), transparent);
}

.nav__links a {
  position: relative;
  z-index: 1;
  padding: 8px 13px;
  border-radius: var(--radius-pill);
  color: var(--ink-2);
  font-size: .875rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: color .3s;
}

.nav__links a:hover,
.nav__links a.is-active { color: var(--ink); }

/* White pill that slides under the active link */
.nav__indicator {
  position: absolute;
  z-index: 0;
  top: 0;
  left: 0;
  width: 0;
  height: 100%;
  border-radius: var(--radius-pill);
  background: #fff;
  box-shadow: 0 1px 2px rgba(12, 20, 36, .08), 0 8px 18px -8px rgba(12, 20, 36, .25);
  opacity: 0;
  transition: transform .55s var(--ease), width .55s var(--ease), opacity .3s;
}

/* EN / FR / DE */
.lang-switch {
  flex: none;
  display: flex;
  padding: 3px;
  border-radius: var(--radius-pill);
  background: rgba(12, 20, 36, .06);
}

.lang-switch button {
  padding: 8px 9px;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink-3);
  font: 600 .72rem/1 var(--font-mono);
  letter-spacing: .06em;
  cursor: pointer;
  transition: background .3s, color .3s;
}

.lang-switch button[aria-pressed="true"] {
  background: #fff;
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(12, 20, 36, .14);
}

/* Reading progress, under the menu */
.nav__progress {
  position: absolute;
  left: 22px;
  right: 22px;
  bottom: 0;
  height: 2px;
  border-radius: 2px;
  background: var(--grad-brand);
  transform-origin: left;
  transform: scaleX(var(--progress, 0));
}


/* =========================================================
   7. HERO, PROFILE PHOTO AND KEY FACTS
   .hero is shared by both pages. .hero--home (index.html) adds
   the key facts strip (.glance) under the text and the photo.
   ========================================================= */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, .88fr);
  align-items: center;
  gap: clamp(28px, 5vw, 72px);
  padding-block: clamp(128px, 15vh, 168px) clamp(48px, 7vw, 88px);
}

.hero__name {
  margin-top: 24px;
  font-size: var(--fs-h1);
  font-weight: 640;
  line-height: .92;
  letter-spacing: -.05em;
}

.hero__name span,
.hero__name em { display: block; }

/* The surname: bigger, gradient slowly moving */
.hero__name em {
  display: inline-block;
  font-size: 1.12em;
  line-height: .95;
  padding: 0 .1em .06em 0;
  background-size: 220% 100%;
  animation: sheen 8s ease-in-out infinite alternate;
}

@keyframes sheen { to { background-position: 100% 0; } }

.hero__role { margin-top: 22px; }

.hero__lead {
  max-width: 34em;
  margin-top: 12px;
  color: var(--ink-2);
  font-size: clamp(1.06rem, 1.35vw, 1.2rem);
  text-wrap: pretty;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 30px;
}

/* Profile photo in a glass frame, caption underneath (never on the face) */
.profile {
  justify-self: center;
  width: 100%;
  max-width: 400px;
  padding: 12px;
  transition: transform .5s var(--ease);
}

.profile__caption {
  display: grid;
  justify-items: start;
  gap: 8px;
  padding: 16px 10px 8px;
}

/* "Now" badge on the left, logo of where I am on the right */
.profile__now {
  justify-self: stretch;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}

.profile__logo {
  width: auto;
  height: 24px;
}

.profile__title {
  font-size: 1.15rem;
  font-weight: var(--weight-title);
  letter-spacing: -.01em;
  line-height: 1.25;
}

.profile__text {
  color: var(--ink-2);
  font-size: var(--fs-small);
  line-height: 1.45;
}

.profile .tags { margin-top: 4px; }

/* Main page: everything essential in the first screen */
.hero--home {
  row-gap: clamp(20px, 3vh, 30px);
  padding-block: clamp(104px, 14vh, 140px) clamp(40px, 6vw, 72px);
}

.hero--home .profile { max-width: 360px; }

/* Key facts strip: 4 cells side by side (2×2 on tablets, stacked on phones) */
.glance {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.glance > div {
  display: grid;
  align-content: start;
  gap: 8px;
  min-width: 0;
  padding: 18px clamp(16px, 2vw, 24px);
  border-right: 1px solid var(--line);
}

.glance > div:last-child { border-right: 0; }

.glance dt {
  display: flex;
  align-items: center;
  gap: 8px;
}

.glance dt .icon {
  width: 16px;
  height: 16px;
  color: var(--blue);
}

.glance dd {
  font-size: .95rem;
  font-weight: 560;
  line-height: 1.4;
  text-wrap: pretty;
}



/* 7.1 Mountain hero (main page) ---------------------------
   The Dents du Midi in winter. Layers: sky, first name, the
   mountain cut out of the same photo (sky made transparent),
   then "Allet" and the peak labels in front. The hero is taller
   than the screen: its stage stays pinned while you scroll and
   main.js (part 17) writes the progress in --p (0 to 1): the view
   moves into the peaks, the first name sinks behind them and the
   scene dissolves into the page.
   Photo: Christian David, CC BY-SA 4.0 (credit in the hero).
   Peak positions: camera position found by matching the ridge
   with swisstopo elevation data. */
.peaks-hero {
  --p: 0;
  position: relative;
  height: 140svh;
  margin-inline: calc(-1 * var(--gutter));
}

.peaks-hero__pin {
  /* --ry: height of the ridge on the screen. The photo (3:2, ridge at
     28.4 % of its height) is placed so its ridge is there, and is big
     enough to reach the bottom of the screen. Above it, the sky goes on
     in the photo's own colour. */
  --ry: 50cqh;
  --fw: max(100cqw, 108cqh);
  --fh: calc(var(--fw) / 1.5);
  --ftop: calc(var(--ry) - var(--fh) * .284);
  --fleft: clamp(100cqw - var(--fw), 50cqw - var(--fw) * .49, 0px);
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  container-type: size;
  color: #fff;
}

/* Seamless: the sky, the photo and the name sit in one layer whose
   foot turns transparent (a mask, not a colour laid on top). The page
   background shows through, so nothing marks where the hero ends.
   The fade grows while you scroll (30 % of the screen, then 80 %),
   with a soft S-curve so it has no visible start line. */
.peaks-hero__scene {
  --fade: calc(30% + var(--p) * 50%);
  --mask: linear-gradient(180deg,
    #000 calc(100% - var(--fade)),
    rgba(0, 0, 0, .9) calc(100% - var(--fade) * .8),
    rgba(0, 0, 0, .65) calc(100% - var(--fade) * .6),
    rgba(0, 0, 0, .35) calc(100% - var(--fade) * .4),
    rgba(0, 0, 0, .1) calc(100% - var(--fade) * .2),
    transparent);
  position: absolute;
  inset: 0;
  /* Sky above the photo: same colours as its top edge (darker on the left) */
  background:
    linear-gradient(180deg, rgba(8, 40, 90, .16), rgba(8, 40, 90, 0) 45%),
    linear-gradient(90deg, #2273B0, #3C89C2 50%, #5399CF);
  -webkit-mask-image: var(--mask);
          mask-image: var(--mask);
}

/* Light mist over the foot of the photo, so the snow fades to white
   first and the page colours only show at the very bottom */
.peaks-hero__scene::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  /* As tall as --fade (30 % to 80 %), drawn by a scale and not a height, so
     scrolling never moves the layout (no layout shift for search engines) */
  height: 80%;
  transform: scaleY(calc((30 + var(--p) * 50) / 80));
  transform-origin: 50% 100%;
  background: linear-gradient(180deg, rgba(238, 242, 247, 0), rgba(238, 242, 247, .7));
  pointer-events: none;
}

.peaks-hero__frame {
  position: absolute;
  left: var(--fleft);
  top: var(--ftop);
  width: var(--fw);
  height: var(--fh);
  transform: scale(calc(1 + var(--p) * .16));
  transform-origin: 50% 28%;
  will-change: transform;
}

.peaks-hero__frame img {
  display: block;
  width: 100%;
  height: 100%;
  animation: peaks-settle 2.4s var(--ease) backwards;
}

@keyframes peaks-settle {
  from { transform: scale(1.08); filter: brightness(.8) saturate(.8); }
}

/* The top of the photo fades into the sky above it, no visible edge */
.peaks-hero__frame--sky {
  z-index: 0;
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 9%);
  mask-image: linear-gradient(180deg, transparent, #000 9%);
}
.peaks-hero__frame--front { z-index: 2; }

/* The name. The h1 is not a layer itself, so its two parts can sit
   on different layers: the first name behind the mountain (z 1),
   the surname in front of it (z 3). */
.peaks-hero__name {
  position: absolute;
  inset: 0;
  margin: 0;
  pointer-events: none;
}

.peaks-hero__first,
.peaks-hero__last {
  position: absolute;
  display: block;
  white-space: nowrap;
  animation: peaks-rise 1.6s var(--ease) backwards;
}

@keyframes peaks-rise {
  from { opacity: 0; translate: 0 .35em; }
}

/* Only the foot of the letters goes behind the peaks */
.peaks-hero__first {
  z-index: 1;
  left: 0;
  right: 0;
  top: calc(var(--ry) - .66em);
  font-size: 12.4cqw;
  font-weight: 640;
  line-height: .9;
  letter-spacing: -.05em;
  text-align: center;
  text-shadow: 0 4px 40px rgba(10, 50, 100, .35);
  transform: translateY(calc(var(--p) * 9cqh));
  opacity: calc(1 - var(--p) * 1.5);
  animation-delay: .45s;
}

.peaks-hero__last {
  z-index: 3;
  right: 8cqw;
  top: calc(var(--ry) + 6.6cqw);
  color: #fff;
  font-family: var(--font-serif);
  font-size: 9.5cqw;
  font-style: italic;
  font-weight: 400;
  line-height: .9;
  text-shadow: 0 6px 30px rgba(8, 24, 48, .45);
  transform: translateY(calc(var(--p) * 9cqh));
  opacity: calc(1 - var(--p) * 2.6);
  animation-delay: .75s;
}

/* Peak labels: dot on the summit, thin line, name on the rock.
   Hover (or tap) shows the height. Positions in % of the photo. */
.peaks-hero__peaks {
  position: absolute;
  inset: 0;
  z-index: 1;
  margin: 0;
  padding: 0;
  list-style: none;
  opacity: calc(1 - var(--p) * 4);
}

.peak {
  position: absolute;
  left: var(--x);
  top: var(--y);
  display: grid;
  justify-items: center;
  padding: 0 4px 4px;
  border: 0;
  background: none;
  color: #fff;
  font: inherit;
  cursor: default;
  pointer-events: none;
  transform: translateX(-50%);
  animation: peak-in .9s var(--ease) backwards;
  animation-delay: calc(1.3s + var(--i) * .09s);
}

@keyframes peak-in {
  from { opacity: 0; translate: 0 -8px; }
}

.peak::before {
  content: "";
  width: 7px;
  height: 7px;
  margin-top: -11.5px;
  border: 8px solid transparent;   /* bigger target for the pointer */
  border-radius: 50%;
  background: #fff padding-box;
  pointer-events: auto;
  filter: drop-shadow(0 0 3px #fff) drop-shadow(0 0 8px rgba(255, 255, 255, .8));
}

.peak__line {
  width: 1px;
  height: var(--len);
  background: linear-gradient(rgba(255, 255, 255, .95), rgba(255, 255, 255, .4));
  transition: height .45s var(--ease);
}

.peak__label {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  margin-top: 2px;
  padding: 4px 8px;
  border: 1px solid transparent;
  border-radius: 8px;
  font-size: .78rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: .015em;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(5, 20, 40, .7), 0 0 12px rgba(5, 20, 40, .55);
  transition: background .3s, border-color .3s;
}

/* The height slides out next to the name */
.peak__alt {
  display: inline-block;
  max-width: 0;
  overflow: hidden;
  opacity: 0;
  color: #FFD2B0;
  font: 500 .7rem/1.2 var(--font-mono);
  transition: max-width .45s var(--ease), margin .45s var(--ease), opacity .3s;
}

.peak:hover .peak__alt,
.peak:focus-visible .peak__alt,
.peak.is-open .peak__alt {
  max-width: 5.5em;
  margin-left: 8px;
  opacity: 1;
}

/* No backdrop-filter here: the labels sit inside the masked hero layer,
   and a blur switched on at hover makes the browser rebuild that layer
   (the foot of the hero flashed). A darker plain background instead. */
.peak:hover .peak__label,
.peak:focus-visible .peak__label,
.peak.is-open .peak__label {
  border-color: rgba(255, 255, 255, .3);
  background: rgba(10, 22, 40, .66);
  text-shadow: none;
}

.peak:hover .peak__line,
.peak.is-open .peak__line { height: calc(var(--len) + 6px); }
.peak:hover,
.peak:focus-visible,
.peak.is-open { z-index: 2; }
.peak:focus-visible { outline: none; }
.peak:focus-visible .peak__label { outline: 2px solid #fff; outline-offset: 2px; }
.peak.is-out { visibility: hidden; }   /* would be cut by the screen edge (main.js part 17) */

/* Foot: what I do, in its own glass tile, then the scroll cue */
.peaks-hero__foot {
  position: absolute;
  z-index: 5;
  left: 0;
  right: 0;
  bottom: clamp(30px, 5vh, 52px);
  display: grid;
  justify-items: center;
  gap: 12px;
  padding-inline: var(--gutter);
  opacity: calc(1 - var(--p) * 3);
  animation: peaks-rise 1.2s 1.1s var(--ease) backwards;
}

.peaks-hero__tile {
  display: grid;
  justify-items: center;
  gap: 8px;
  padding: 14px 22px;
  border-radius: var(--radius-inner);
  color: var(--ink);
  text-align: center;
}

.peaks-hero__tile .eyebrow { color: var(--ink); }

/* Where I live, under the role, in spaced capitals */
.peaks-hero__tile .peaks-hero__where {
  color: var(--ink-2);
  letter-spacing: .2em;
  white-space: nowrap;
}

/* Phones: less spacing, so the line stays on one row */
@media (max-width: 480px) {
  .peaks-hero__tile .peaks-hero__where { letter-spacing: .13em; }
}

.peaks-hero__cue {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: var(--ink-2);
  font-size: .78rem;
  font-weight: 560;
  text-decoration: none;
}

.peaks-hero__cue .icon {
  width: 18px;
  height: 18px;
  animation: peaks-cue 2s ease-in-out infinite;
}

@keyframes peaks-cue {
  0%, 100% { transform: translateY(0); opacity: .6; }
  50% { transform: translateY(6px); opacity: 1; }
}

.peaks-hero__credit {
  position: absolute;
  z-index: 5;
  right: var(--gutter);
  bottom: 12px;
  max-width: 46%;
  color: var(--ink-3);
  font: 500 .62rem/1.4 var(--font-mono);
  letter-spacing: .02em;
  text-align: right;
  opacity: calc(1 - var(--p) * 3);
}

.peaks-hero__credit a { color: inherit; }

/* Intro under the hero: photo, what I do, buttons, key facts.
   The photo is as tall as the text next to it. */
/* It comes up over the faded foot of the hero, so the two flow together */
.intro {
  position: relative;
  z-index: 1;
  margin-top: -22svh;
  padding-top: clamp(24px, 4vw, 48px);
  scroll-margin-top: calc(124px - var(--nav-room));   /* 124 px in all: the intro slides over the hero */
}

.intro__grid {
  display: grid;
  grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr);
  gap: clamp(28px, 5vw, 64px);
}

.intro__photo {
  display: flex;
  margin: 0;
  padding: 10px;
}

.intro__photo img {
  width: 100%;
  height: 100%;
  min-height: 260px;
  border-radius: var(--radius-inner);
  object-fit: cover;
  object-position: 50% 32%;
}

.intro__text {
  display: grid;
  align-content: center;
  justify-items: start;
  gap: 16px;
}

.intro__lead {
  max-width: 30em;
  color: var(--ink);
  font-size: clamp(1.2rem, 1.9vw, 1.5rem);
  font-weight: 480;
  line-height: 1.45;
  letter-spacing: -.012em;
  text-wrap: pretty;
}

.intro .hero__actions { margin-top: 6px; }
.intro .glance { margin-top: clamp(28px, 4vw, 44px); }
.glance--5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }


/* =========================================================
   8. KEY NUMBERS
   Used by the gearbox page (strip under the hero).
   ========================================================= */

/* Key numbers strip (gearbox page) */
.stats {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid var(--glass-border);
  background: rgba(255, 255, 255, .26);
}

.stat {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px clamp(18px, 2.4vw, 30px);
  border-right: 1px solid var(--line);
}

.stat:last-child { border-right: 0; }

.stat__number {
  padding-bottom: .05em;
  background: var(--grad-cool);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-size: clamp(2.2rem, 4vw, 3.1rem);
  font-weight: var(--weight-title);
  letter-spacing: -.045em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.stat__number small {
  margin-left: 3px;
  font-size: .42em;
  letter-spacing: 0;
}

.stat__label {
  color: var(--ink-2);
  font-size: .88rem;
  line-height: 1.35;
}


/* =========================================================
   9. TIMELINE (EXPERIENCE AND EDUCATION)
   The grey rail runs from the first dot to the last one.
   The coloured fill grows with the scroll, BEHIND the dots,
   and each dot lights up when the fill reaches its centre.
   main.js part 8 measures the dots and sets the variables.
   ========================================================= */
.timeline {
  --pad: 50px;                           /* space on the left for the line */
  --rail-x: 17.5px;                      /* horizontal position of the line */
  --rail-top: 40px;                      /* set by main.js */
  --rail-height: 0px;                    /* set by main.js */
  --fill: 0px;                           /* set by main.js */
  position: relative;
  padding-left: var(--pad);
}

.timeline__rail,
.timeline__fill {
  position: absolute;
  top: var(--rail-top);
  left: calc(var(--rail-x) - 1.5px);
  width: 3px;
  border-radius: 3px;
}

.timeline__rail {
  height: var(--rail-height);
  background: rgba(12, 20, 36, .1);
}

/* The gradient is as tall as the whole rail, so the colour at each
   point stays the same while the fill grows. main.js sets
   --timeline-gradient from SETTINGS.timelineColours. */
.timeline__fill {
  height: var(--fill);
  background-image: var(--timeline-gradient, linear-gradient(180deg, var(--teal), var(--blue), var(--pink), var(--orange)));
  background-size: 3px var(--rail-height);
  background-repeat: no-repeat;
}

/* Glowing point at the end of the fill */
.timeline__head {
  position: absolute;
  top: calc(var(--rail-top) - 7px);
  transform: translateY(var(--fill));   /* moved by a transform: no layout shift */
  left: calc(var(--rail-x) - 7px);
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 3px var(--head-color, var(--blue)), 0 0 22px 6px color-mix(in oklab, var(--head-color, var(--blue)) 50%, transparent);
  opacity: 0;
  transition: opacity .35s;
}

.timeline.is-moving .timeline__head { opacity: 1; }

.timeline__list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);   /* wide content (formula, tables) never stretches the page */
  gap: var(--gap);
}

.timeline__item { position: relative; }

.timeline__dot {
  position: absolute;
  top: 30px;
  left: calc(var(--rail-x) - var(--pad) - 10px);
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  border: 3px solid #D3DAE6;
  transition: border-color .45s, background-color .45s, box-shadow .45s, transform .45s var(--ease);
}

/* One-time ripple when the dot lights up */
.timeline__dot::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 2px solid var(--dot, var(--blue));
  opacity: 0;
  pointer-events: none;
}

.timeline__item.is-lit .timeline__dot {
  border-color: var(--dot, var(--blue));
  background-color: var(--dot, var(--blue));
  box-shadow: inset 0 0 0 3px #fff, 0 0 0 6px color-mix(in oklab, var(--dot, var(--blue)) 18%, transparent);
  transform: scale(1.12);
}

.timeline__item.is-lit .timeline__dot::after { animation: ripple 1s var(--ease); }

@keyframes ripple {
  0%   { opacity: .9; transform: scale(.8); }
  100% { opacity: 0; transform: scale(2.4); }
}

/* Entry card (one job or one school) */
.entry { padding: var(--card-pad); }

.entry--media {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(200px, 280px);
  align-items: center;
  gap: 26px;
}

.entry__head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px 16px;
}

/* Company or school logo above the title. Its width/height in the HTML set the size:
   wide logos get a lower height, so every logo has about the same visual weight. */
.entry__logo {
  display: block;
  height: auto;
  margin-bottom: 12px;
}

.entry__title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  font-size: var(--fs-h3);
  font-weight: var(--weight-title);
  letter-spacing: -.022em;
  line-height: 1.2;
}

.entry__sub {
  margin-top: 5px;
  color: var(--ink-2);
  font-size: .95rem;
}

/* Highlighted part of the line under the title (e.g. the study track) */
.entry__sub strong {
  color: var(--ink);
  font-weight: 600;
}

.entry__date {
  padding-top: 4px;
  white-space: nowrap;
}

.entry__text {
  max-width: 68ch;
  margin-top: 12px;
  color: var(--ink-2);
}

.entry .tags,
.entry__badges { margin-top: 16px; }

/* Awards: one badge each. A long badge wraps inside its pill on phones */
.entry__badges {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.entry__badges .badge {
  max-width: 100%;
  white-space: normal;
  border-radius: 14px;
}

.entry .outcomes,
.entry .partners,
.entry__figure { margin-top: 16px; }

.entry__figure { max-width: 640px; }     /* a picture inside a step stays secondary */

/* "Gearbox case study" button inside an entry */
.entry__link { margin-top: 18px; }


/* =========================================================
   10. PROJECTS
   6-column grid. Every image sits inside the card with the
   same inner radius as the other photos of the site.
   ========================================================= */
.projects {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--gap);
}

.project {
  display: flex;
  flex-direction: column;
  padding: 12px;
}

.project--large { grid-column: span 4; grid-row: span 2; }
.project--half  { grid-column: span 2; }
.project--wide  { grid-column: span 4; flex-direction: row; gap: 4px; }

.project--wide .media {
  flex: none;
  width: 42%;
  min-height: 280px;
}

/* Never an empty gap inside a card. A card can be taller than its
   content (the big gearbox card is as tall as the two cards next to it,
   and that changes with the language). The picture frame takes the
   extra height, never the space under the text. Its aspect ratio stays
   the minimum; the render or cut-out stays whole and centred. */
.project--large > .media,
.project--half > .media { flex: 1 0 auto; }
.project--large > .project__body,
.project--half > .project__body { flex: 0 0 auto; }

/* Wide card (picture on the side): the text sets the height and the
   photo fills it. A tall photo never stretches the card past its text. */
.project--wide .media img {
  position: absolute;
  inset: 0;
}

.project__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: 16px 12px 14px;
}

.project__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
}

.project__title {
  font-size: clamp(1.3rem, 2vw, 1.6rem);
  font-weight: var(--weight-title);
  letter-spacing: -.028em;
  line-height: 1.15;
}

.project--large .project__title { font-size: clamp(1.6rem, 2.8vw, 2.2rem); }

.project__sub {
  color: var(--ink);
  font-weight: 500;
  line-height: 1.4;
}

.project__text {
  margin-bottom: auto;                   /* results sit at the bottom: side-by-side cards line up */
  color: var(--ink-2);
  font-size: .98rem;
}

/* Who the project was made with: small label + logo */
.partners {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px 32px;
  margin-top: 8px;
}

.partner {
  display: grid;
  justify-items: start;
  gap: 8px;
}

.partner img { height: auto; }          /* size: width/height in the HTML */

/* The flagship card has room for its 4 results on one line */
/* 4 results: all on one line (2 results: the normal 2 columns) */
.project--large .outcomes:has(> .outcome:nth-child(4)) { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.project--large .outcome { grid-column: auto; }

.project__link { margin-top: 8px; }

/* Two cards that share the last row (thesis and Vault, .project--duo).
   Wide screens: half the row each, picture on top. Both pictures can take
   extra height, so the taller card sets the row and the other picture
   fills it: never an empty gap under a text. Narrow screens: they are
   .project--wide cards (picture on the side), see part 17. */
@media (min-width: 981px) {
  .project--duo { grid-column: span 3; flex-direction: column; gap: 0; }
  .project--duo .media {
    width: auto;
    min-height: 0;
    flex: 1 0 auto;
    aspect-ratio: 16 / 10;
  }
  .project--duo > .project__body { flex: 0 0 auto; }
}

/* Key/value tiles (gearbox page: hero facts and technical data) */
.spec {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  width: 100%;
  margin-top: 6px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-inner);
  background: var(--line);
}

.spec div {
  min-width: 0;
  padding: 12px 14px;
  background: rgba(255, 255, 255, .7);
}

.spec dd {
  margin-top: 5px;
  font-size: .9rem;
  font-weight: 550;
  line-height: 1.35;
}


/* =========================================================
   11. SKILLS
   ========================================================= */
.skills {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
}

/* 11.1 Toolbox dock -------------------------------------------
   The software I use, like the macOS dock: the icon under the
   mouse grows (main.js part 15 sets --mag from 0 to 1 on each
   item). When the icons don't all fit, the row scrolls sideways
   with arrows on the sides and soft fading edges. */
.dock {
  --icon: 54px;                          /* icon size at rest */
  --room: 34px;                          /* space above the icons to grow into */
  position: relative;
  margin-bottom: var(--gap);
  padding: 4px 8px;
}

.dock__track {
  display: flex;
  gap: 2px;
  padding: var(--room) 12px 14px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.dock__track::-webkit-scrollbar { display: none; }
.dock__track:focus-visible { outline-offset: -3px; }

/* Centred when they all fit, without cutting the first ones off when they don't */
.dock__item:first-child { margin-left: auto; }
.dock__item:last-child { margin-right: auto; }

.dock__item {
  flex: none;
  display: grid;
  align-content: start;
  justify-items: center;
  gap: 10px;
  width: 76px;
}

.dock__icon {
  width: var(--icon);
  height: var(--icon);
  object-fit: contain;
  transform-origin: 50% 100%;
  transform: translateY(calc(var(--mag, 0) * -6px)) scale(calc(1 + var(--mag, 0) * .5));
  transition: transform .18s var(--ease);
  filter: drop-shadow(0 8px 12px rgba(12, 20, 36, .16));
}

/* A logo without its own tile gets a white app tile */
.dock__icon--plate {
  padding: var(--pad, 11px);
  border: 1px solid rgba(12, 20, 36, .06);
  border-radius: 14px;
  background: #fff;
}

/* A square icon gets the rounded corners of an app tile */
.dock__icon--tile { border-radius: 14px; }

/* Square tiles: a plain box shadow looks the same as the drop shadow,
   and is much lighter for the browser (no filter to render) */
.dock__icon--plate,
.dock__icon--tile {
  filter: none;
  box-shadow: 0 8px 12px rgba(12, 20, 36, .16);
}

.dock__label {
  display: grid;
  color: var(--ink-2);
  font-size: .74rem;
  font-weight: 550;
  line-height: 1.2;
  text-align: center;
  white-space: nowrap;
  transition: color .2s;
}

.dock__item:hover .dock__label { color: var(--ink); }

/* Arrows centred on the icons; fading edges where more icons hide */
.dock__arrow { top: calc(4px + var(--room) + var(--icon) / 2 - 21px); }
.dock__arrow--prev { left: 10px; }
.dock__arrow--next { right: 10px; }

.dock.is-scrollable .dock__track {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 56px, #000 calc(100% - 56px), transparent);
          mask-image: linear-gradient(90deg, transparent, #000 56px, #000 calc(100% - 56px), transparent);
}

.dock.is-scrollable.at-start .dock__track {
  -webkit-mask-image: linear-gradient(90deg, #000, #000 calc(100% - 56px), transparent);
          mask-image: linear-gradient(90deg, #000, #000 calc(100% - 56px), transparent);
}

.dock.is-scrollable.at-end .dock__track {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 56px, #000);
          mask-image: linear-gradient(90deg, transparent, #000 56px, #000);
}

.skill-card { padding: var(--card-pad); }
.skill-card .tags { margin-top: 18px; }
.skill-card--wide { grid-column: span 2; }

/* Language levels on the A1 to C2 scale */
.levels { margin-top: 10px; }

.levels__scale,
.levels__row {
  display: grid;
  grid-template-columns: minmax(80px, 130px) minmax(0, 1fr) minmax(64px, auto);
  align-items: center;
  gap: 16px;
}

.levels__scale {
  margin-top: 8px;
  color: var(--ink-3);
  font: 500 .64rem/1 var(--font-mono);
  letter-spacing: .08em;
}

.levels__steps,
.levels__bar {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 5px;
}

.levels__steps span { text-align: center; }

.levels__row {
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.levels__row:last-child { border-bottom: 0; }

.levels__name { font-weight: 600; }

.levels__bar i {
  height: 9px;
  border-radius: 5px;
  background: rgba(12, 20, 36, .08);
}

.levels__bar i.on { background: var(--grad-cool); }

.levels__level { text-align: right; }


/* =========================================================
   12. BEYOND WORK
   Photo carousel on the left (two rows high), two text cards
   on the right. The carousel works on its own (12.1).
   ========================================================= */
.life {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: var(--gap);
}

.life__gallery {
  grid-row: span 2;
  display: flex;
  flex-direction: column;
  padding: 12px;
}

.life-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: var(--card-pad);
}

.life-card p { color: var(--ink-2); }
.life-card p strong { color: var(--ink); font-weight: 600; }


/* 12.1 Photo carousel ---------------------------------------
   Swipe, arrows or dots (main.js part 16). The photos fill the
   frame: 3:2 at least, taller if the card next to it is taller. */
.carousel__viewport {
  position: relative;
  flex: 1 0 auto;
  aspect-ratio: 3 / 2;
  border-radius: var(--radius-inner);
}

.carousel__track {
  position: absolute;
  inset: 0;
  display: flex;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  border-radius: inherit;
}

.carousel__track::-webkit-scrollbar { display: none; }
.carousel__track:focus-visible { outline-offset: -3px; }

.carousel__slide {
  flex: 0 0 100%;
  height: 100%;
  border-radius: 0;                      /* the track rounds the corners */
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

.carousel__caption {
  position: absolute;
  z-index: 1;
  left: 12px;
  bottom: 12px;
}

.carousel__arrow { top: 50%; margin-top: -21px; }
.carousel__arrow--prev { left: 12px; }
.carousel__arrow--next { right: 12px; }

/* With a mouse, the arrows show up when the pointer is over the photos */
@media (hover: hover) and (pointer: fine) {
  .carousel__arrow { opacity: 0; }
  .carousel:hover .carousel__arrow:not(:disabled),
  .carousel__arrow:focus-visible { opacity: 1; }
}

/* Dots: 8 px, with a 24 px touch area around each */
.carousel__dots {
  display: flex;
  justify-content: center;
  padding-top: 6px;
}

.carousel__dot {
  box-sizing: content-box;
  width: 8px;
  height: 8px;
  padding: 8px;
  border: 0;
  border-radius: var(--radius-pill);
  background: rgba(12, 20, 36, .2);
  background-clip: content-box;
  cursor: pointer;
  transition: width .35s var(--ease), background-color .35s;
}

.carousel__dot[aria-current="true"] {
  width: 24px;
  background-color: var(--blue);
}


/* =========================================================
   13. CONTACT
   ========================================================= */
.contact {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
}

.contact__info { padding: clamp(24px, 4vw, 48px); }

.contact__lead {
  max-width: 44ch;
  margin-top: 12px;
  color: var(--ink-2);
}

.contact__list { margin-top: 18px; }

.contact-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  padding: 13px 0;
  border-bottom: 1px solid var(--line);
}

.contact-row > .icon {
  width: 22px;
  height: 22px;
  color: var(--blue);
}

.contact-row .eyebrow { display: block; margin-bottom: 4px; }

.contact-row__value {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 560;
  text-decoration: none;
  overflow-wrap: anywhere;
}

a.contact-row__value:hover { text-decoration: underline; }

.refs { margin-top: 22px; }

.refs ul {
  display: grid;
  gap: 6px;
  margin-top: 10px;
}

.refs li { color: var(--ink-2); }
.refs strong { color: var(--ink); font-weight: 600; }

.refs p {
  margin-top: 8px;
  color: var(--ink-3);
  font-size: .85rem;
}

/* Form */
.contact__form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: clamp(24px, 4vw, 48px);
  background: rgba(255, 255, 255, .42);
  border-left: 1px solid var(--glass-border);
}

.field {
  display: grid;
  gap: 7px;
}

.field label {
  font-size: .84rem;
  font-weight: 600;
}

.field input,
.field textarea {
  width: 100%;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: rgba(255, 255, 255, .85);
  color: var(--ink);
  font: inherit;
  font-size: .98rem;
  transition: border-color .25s, box-shadow .25s;
}

.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 4px rgba(47, 140, 255, .16);
}

.field textarea {
  min-height: 140px;
  resize: vertical;
}

.contact__form .btn { align-self: flex-start; }

.form-note {
  padding: 12px 14px;
  border-radius: 12px;
  background: rgba(47, 140, 255, .1);
  font-size: .88rem;
}

.form-note.is-error { background: rgba(255, 90, 138, .12); }


/* =========================================================
   14. FOOTER
   ========================================================= */
.footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding-block: 28px 44px;
  border-top: 1px solid var(--line);
  color: var(--ink-3);
  font-size: .86rem;
}

.footer a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ink-2);
  font-weight: 500;
  text-decoration: none;
}


/* =========================================================
   15. CASE STUDY PAGE (gearbox.html)
   Same building blocks as the main page: hero, cards, timeline,
   key numbers. Only the layouts below are specific.
   ========================================================= */

/* Hero: smaller title than the name on the main page */
.case-title {
  margin-top: 18px;
  font-size: clamp(2.6rem, 6vw, 5rem);
  font-weight: 640;
  line-height: .98;
  letter-spacing: -.045em;
  text-wrap: balance;
}

/* Text on the left, the car on track (vertical video, 4:5) on the right, numbers below */
.case-hero {
  grid-template-columns: minmax(0, 1fr) minmax(0, .72fr);
  row-gap: clamp(20px, 3vw, 36px);
  padding-block: clamp(112px, 14vh, 148px) 0;
}

.case-hero .hero__lead { margin-top: 18px; }
.case-hero__status { margin-top: 18px; }
.case-hero__stats { grid-column: 1 / -1; }

.case-hero__video {
  justify-self: end;
  width: 100%;
  max-width: 440px;
  padding: 10px;
}

.case-hero__video .media { aspect-ratio: 4 / 5; border-radius: calc(var(--radius-card) - 10px); }
.case-hero__video video { object-fit: cover; }
.case-hero__video .gallery__caption { padding-bottom: 2px; }

/* 15.1 Looping video --------------------------------------
   The renders have a white background. "multiply" makes the white
   disappear, so the parts float on the page colours. The mask
   softens the edges where parts leave the frame.
   No transform, filter or opacity on the parents: they would cut
   the video off from the background and bring the white back. */
.loop { position: relative; }

/* Wide loop that runs to the right edge of the screen */
.loop--bleed {
  --bleed: calc(var(--gutter) + max(0px, (100vw - 2 * var(--gutter) - var(--max-width)) / 2));
  aspect-ratio: 16 / 9;
  margin-right: calc(-1 * var(--bleed));
  margin-left: -4%;
}

.loop__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 18%, #000 86%, transparent), linear-gradient(transparent, #000 10%, #000 88%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(90deg, transparent, #000 18%, #000 86%, transparent), linear-gradient(transparent, #000 10%, #000 88%, transparent);
  mask-composite: intersect;
}

/* Pause / play: small glass button in the corner */
.loop__toggle {
  position: absolute;
  right: 14px;
  bottom: 14px;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--glass-border);
  border-radius: 50%;
  background: rgba(255, 255, 255, .7);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 6px 16px -8px rgba(20, 44, 90, .4);
  color: var(--ink);
  cursor: pointer;
  opacity: .75;
  transition: opacity .2s, transform .2s var(--ease);
}

.loop__toggle:hover,
.loop__toggle:focus-visible { opacity: 1; transform: scale(1.06); }
.loop__toggle .icon { width: 16px; height: 16px; }
.loop__icon-play,
.loop.is-paused .loop__icon-pause { display: none; }
.loop.is-paused .loop__icon-play { display: block; }

/* On the wide loop the button stays inside the page width */
.loop--bleed .loop__toggle { right: max(14%, calc(var(--bleed) + 8px)); bottom: 4%; }
.media.is-empty .loop__toggle { display: none; }

/* Short intro under a section title */
.section-lead {
  max-width: 40em;
  margin-top: 16px;
  color: var(--ink-2);
  font-size: clamp(1.04rem, 1.3vw, 1.15rem);
  text-wrap: pretty;
}

/* 15.2 Results: text on the left, the exploded wheel corner looping on the right */
.race {
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
  align-items: center;
  gap: clamp(20px, 3vw, 40px);
}

/* The four competitions: logos on white tiles, all the same height */
.events {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  max-width: 640px;
  margin-top: 26px;
}

.events__item {
  display: grid;
  place-items: center;
  height: 84px;
  padding: 10px 12px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-inner);
  background: #fff;
  box-shadow: 0 8px 20px -14px rgba(20, 44, 90, .35);
  position: relative;
  /* Grows under the mouse like the toolbox dock of the main page (main.js) */
  transform-origin: 50% 100%;
  transform: translateY(calc(var(--mag, 0) * -6px)) scale(calc(1 + var(--mag, 0) * .32));
  transition: transform .18s var(--ease), box-shadow .18s var(--ease);
}

.events__item:hover { z-index: 1; box-shadow: 0 16px 30px -16px rgba(20, 44, 90, .45); }

.events__item img {
  width: auto;
  max-width: 100%;
  height: auto;
  max-height: 58px;
}

/* Link inside a sentence (team website) */
.text-link {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--blue) 55%, transparent);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  transition: text-decoration-color .2s;
}

.text-link:hover { text-decoration-color: var(--blue); }

/* 15.3 The brief: what changed on the 2026 car on the left, the section
   drawing of the wheel corner on the right */
.brief {
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  align-items: center;
  gap: var(--gap) clamp(24px, 4vw, 56px);
}

.brief__text .case-note { margin-top: 22px; }
.brief__drawing { padding: 10px; }

/* 15.4 The gearbox: render and the headline facts in one card */
.gearbox {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  align-items: center;
  gap: clamp(20px, 4vw, 48px);
  padding: 10px clamp(10px, 3vw, 34px) 10px 10px;
}

.gearbox__body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-block: 18px;
}

.gearbox__body .h2 { margin-top: 0; }

/* Key numbers in their own glass card */
.stats-card .stats { border-top: 0; background: transparent; }

/* Short highlighted note (the brief: what changed in 2026) */
.case-note {
  display: grid;
  gap: 6px;
  padding: 14px 18px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--blue);
  border-radius: var(--radius-inner);
  background: rgba(255, 255, 255, .72);
  color: var(--ink-2);
  font-size: .95rem;
  line-height: 1.55;
}

.case-note__list { display: grid; gap: 8px; }
.case-note__list li { padding-left: 16px; position: relative; }
.case-note__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .62em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--grad-cool);
}
.case-note strong { color: var(--ink); font-weight: 620; }

/* Generic text card */
.case-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: var(--card-pad);
}

.case-text { color: var(--ink-2); }

/* 15.5 Design: two pictures side by side in a step, then the drawings */
.entry__pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  max-width: 640px;
  margin-top: 16px;
}

/* A step's picture, with on its right the software used (the same app
   tiles as the toolbox dock of the main page) or the machine shop */
.step__row {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-top: 16px;
}

.step__row > .entry__figure,
.step__row > .entry__pair {
  flex: 1 1 0;
  min-width: 0;
  margin-top: 0;
}

.step__aside {
  --icon: 54px;
  flex: 0 0 210px;
  align-self: stretch;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 14px;
  padding: 18px 14px 14px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-inner);
  background: rgba(255, 255, 255, .5);
  text-align: center;
}

.step__apps {
  display: flex;
  justify-content: center;
  gap: 4px;
}

/* Under the mouse the icon grows a little, like in the dock */
.step__apps .dock__item:hover { --mag: .3; }

/* The machine shop: its logo, big, on a white tile */
.step__aside--logo {
  flex-basis: 240px;
  gap: 18px;
  padding-block: 18px 22px;
  background: #fff;
}

.step__logo {
  width: min(100%, 200px);
  height: auto;
}

/* Assembly step: the two competitions I was at */
.step__events {
  display: grid;
  justify-items: center;
  gap: 16px;
}

.step__events img {
  width: min(100%, 150px);
  height: auto;
}

/* A3 drawings (297 × 210) */
.media--a3 { aspect-ratio: 297 / 210; }

/* Caption with a link on the right (drawing name · PDF) */
.gallery__caption--row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}

.gallery__caption--row a {
  color: var(--blue);
  font-weight: 600;
  text-decoration: none;
}

.gallery__caption--row a:hover { text-decoration: underline; }

/* Last section: call to action, centred */
.case-cta {
  align-items: center;
  max-width: 720px;
  margin-inline: auto;
  text-align: center;
}

.case-cta .hero__actions { justify-content: center; margin-top: 4px; }

.case-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--ink-2);
  font-weight: 500;
  text-decoration: none;
}

.case-back:hover { color: var(--ink); }


/* 15.6 3D model (js/gearbox-3d.js) ------------------------
   Model on the left, controls and readout on the right.
   The part colours match the legend dots. */
.gear3d {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
}

.gear3d__stage {
  position: relative;
  min-height: 460px;
  border-right: 1px solid var(--line);
  background: radial-gradient(circle at 50% 45%, rgba(255, 255, 255, .7), rgba(255, 255, 255, 0) 70%);
}

.gear3d__canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  cursor: grab;
  opacity: 0;
  transition: opacity .6s var(--ease);
}

.gear3d__canvas:active { cursor: grabbing; }
.gear3d.is-ready .gear3d__canvas { opacity: 1; }

.gear3d__fallback {
  display: none;
  position: absolute;
  inset: 0;
  place-content: center;
  padding: var(--card-pad);
  color: var(--ink-2);
  text-align: center;
}

.gear3d.is-failed .gear3d__fallback { display: grid; }
.gear3d.is-failed .gear3d__canvas,
.gear3d.is-failed .gear3d__legend,
.gear3d.is-failed .gear3d__slowmo,
.gear3d.is-failed .gear3d__reset,
.gear3d.is-failed .gear3d__explode,
.gear3d.is-failed .gear3d__labels,
.gear3d.is-failed .gear3d__hint { display: none; }

.gear3d__slowmo {
  position: absolute;
  top: 16px;
  left: 18px;
  pointer-events: none;
}

.gear3d__legend {
  position: absolute;
  left: 18px;
  bottom: 16px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin: 0;
  padding: 0;
  list-style: none;
  pointer-events: none;
}

.gear3d__legend li { display: inline-flex; align-items: center; gap: 7px; }

.gear3d__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--dot);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--dot) 22%, transparent);
}

.gear3d__dot--sun { --dot: var(--orange); }
.gear3d__dot--planet { --dot: var(--blue); }
.gear3d__dot--ring { --dot: var(--teal); }
.gear3d__dot--carrier { --dot: var(--pink); }

.gear3d__panel {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: var(--card-pad);
}

.gear3d__label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.gear3d__value {
  font-size: 1.15rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.gear3d__value small { color: var(--ink-3); font-size: .8rem; font-weight: 500; }

/* Speed slider: brand gradient up to the thumb (--fill set by the script) */
.gear3d__range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 28px;
  margin: 8px 0 0;
  background: transparent;
  cursor: pointer;
}

.gear3d__range::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--orange), var(--pink)) 0 0 / var(--fill, 30%) 100% no-repeat, rgba(12, 20, 36, .1);
}

.gear3d__range::-moz-range-track {
  height: 6px;
  border-radius: var(--radius-pill);
  background: rgba(12, 20, 36, .1);
}

.gear3d__range::-moz-range-progress {
  height: 6px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--orange), var(--pink));
}

.gear3d__range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 22px;
  height: 22px;
  margin-top: -8px;
  border: 3px solid #fff;
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 4px 12px -4px rgba(12, 20, 36, .6);
}

.gear3d__range::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: 3px solid #fff;
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 4px 12px -4px rgba(12, 20, 36, .6);
}

.gear3d__range:focus-visible { outline: 2px solid var(--blue); outline-offset: 4px; border-radius: 8px; }

.gear3d__scale { display: flex; justify-content: space-between; margin-top: 2px; }

.gear3d__buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.gear3d__buttons .btn[aria-pressed="true"] { background: var(--ink); color: #fff; border-color: transparent; }

/* Play / pause label follows the state */
.gear3d[data-state="playing"] .gear3d__when-paused,
.gear3d[data-state="paused"] .gear3d__when-playing { display: none; }

/* Explode / assemble label follows the view */
.gear3d[data-view="assembled"] .gear3d__when-exploded,
.gear3d[data-view="exploded"] .gear3d__when-assembled { display: none; }

/* Part names in the exploded view, placed by gearbox-3d.js */
.gear3d__labels {
  position: absolute;
  inset: 0;
  overflow: hidden;
  opacity: var(--labels, 0);
  pointer-events: none;
}

.gear3d__tag {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(-50%, -130%);
  color: var(--ink);
  font-size: .78rem;
  font-weight: 600;
  white-space: nowrap;
  text-shadow: 0 0 4px #fff, 0 0 8px #fff, 0 0 12px #fff;
}

.gear3d__outcomes { margin-top: 0; font-variant-numeric: tabular-nums; }
.gear3d__note { color: var(--ink-3); font-size: .82rem; line-height: 1.55; }


/* =========================================================
   16. GALLERY AND PICTURE VIEWER
   ========================================================= */
.gallery {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
  margin-top: var(--gap);
}

.gallery__item,
.gallery__video {
  display: flex;
  flex-direction: column;
  padding: 10px;
}

.gallery__item--wide { grid-column: 1 / -1; }

.gallery__caption {
  padding: 12px 8px 6px;
  color: var(--ink-2);
  font-size: .9rem;
  line-height: 1.45;
}

/* Pictures open bigger on click (main.js part 10) */
[data-lightbox] .media:not(.is-empty) img { cursor: zoom-in; }

.lightbox {
  width: min(1200px, calc(100vw - 32px));
  max-width: none;
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);       /* phones: never under the browser bars */
  padding: 14px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-card);
  background: rgba(255, 255, 255, .94);
  box-shadow: var(--shadow-card);
  color: var(--ink);
}

.lightbox::backdrop {
  background: rgba(12, 20, 36, .55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* Opens with a short fade and grow (opacity and transform only: smooth) */
@media (prefers-reduced-motion: no-preference) {
  .lightbox[open] { animation: lightbox-in .3s var(--ease); }
  .lightbox[open]::backdrop { animation: lightbox-fade .3s ease; }
}

@keyframes lightbox-in { from { opacity: 0; transform: scale(.96); } }
@keyframes lightbox-fade { from { opacity: 0; } }

.lightbox__figure {
  display: grid;
  gap: 10px;
}

.lightbox__image {
  width: 100%;
  max-height: calc(100vh - 150px);
  max-height: calc(100dvh - 150px);
  object-fit: contain;
  border-radius: var(--radius-inner);
  background: #fff;
}

.lightbox__caption {
  padding: 0 52px 2px 6px;
  color: var(--ink-2);
  font-size: .92rem;
}

.lightbox__close {
  position: absolute;
  right: 22px;
  bottom: 18px;
}


/* =========================================================
   17. RESPONSIVE
   ========================================================= */

/* Tablet */
@media (max-width: 980px) {
  .gear3d { grid-template-columns: minmax(0, 1fr); }
  .gear3d__stage { min-height: 0; aspect-ratio: 4 / 3; border-right: 0; border-bottom: 1px solid var(--line); }
  .hero { grid-template-columns: 1fr; padding-top: 136px; }
  .profile { order: -1; max-width: 380px; }

  /* Main page: text first, then the key facts, then the photo */
  .hero--home .profile { order: 1; }
  .glance { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .glance > div:nth-child(2n) { border-right: 0; }
  .glance > div:nth-child(-n + 2) { border-bottom: 1px solid var(--line); }

  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat:nth-child(2) { border-right: 0; }
  .stat:nth-child(-n + 2) { border-bottom: 1px solid var(--line); }

  .projects { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .project--large { grid-column: 1 / -1; grid-row: auto; }
  .project--half { grid-column: span 1; }
  .project--wide { grid-column: 1 / -1; }

  .skills { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .skill-card--wide { grid-column: 1 / -1; }

  /* Beyond work: photos on top, the two cards side by side below */
  .life { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .life__gallery { grid-column: 1 / -1; grid-row: auto; }

  .contact { grid-template-columns: 1fr; }
  .contact__form { border-left: 0; border-top: 1px solid var(--glass-border); }

  /* Gearbox page: video on top, then the text, full width */
  .case-hero { padding-top: 112px; }
  .case-hero__video { justify-self: center; max-width: 420px; }
  .loop--bleed { margin-inline: calc(-1 * var(--gutter)); }
  .loop--bleed .loop__toggle { right: calc(var(--gutter) + 6%); }
  .brief { grid-template-columns: 1fr; }
  .gearbox { grid-template-columns: 1fr; padding: 10px; }
  .race { grid-template-columns: 1fr; }
  .gearbox__body { padding: 8px 14px 14px; }
  .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Short laptop screens: the main hero is more compact so the key facts
   strip stays in the first screen (or at least starts to show) */
@media (min-width: 981px) and (max-height: 800px) {
  .hero--home { padding-top: 96px; row-gap: 18px; }
  .hero--home .hero__name { font-size: min(var(--fs-h1), 5rem); }
  .hero--home .hero__lead { font-size: 1.06rem; }
  .hero--home .profile { max-width: 300px; }
  .hero--home .profile__caption { gap: 6px; padding-top: 12px; }
  .hero--home .profile .tags { display: none; }
}

/* Tablet and phone: the menu links go on a second line (on one line
   they no longer fit next to the name, the languages and the button) */
@media (max-width: 940px) {
  .nav__bar { flex-wrap: wrap; row-gap: 6px; padding: 8px; border-radius: 24px; }
  .nav__brand { margin-right: auto; }
  .nav__links { order: 3; width: 100%; margin: 0; }
  .nav__links a { padding: 7px 11px; font-size: .84rem; }

  :root { --nav-room: 118px; }
}

/* Small tablet */
@media (max-width: 720px) {
  .hero { padding-top: 128px; }
  .profile { max-width: 340px; }

  .entry--media { grid-template-columns: 1fr; }
  .entry--media .media { aspect-ratio: 16 / 9; }

  /* Gearbox steps: the software (or the machine shop) as one row
     between the text and the picture */
  .step__row { flex-direction: column; align-items: stretch; }
  .step__row > .entry__figure,
  .step__row > .entry__pair { flex: none; }
  .step__aside { order: -1; flex: none; grid-auto-flow: column; justify-content: start; align-items: center; gap: 16px; padding: 10px 16px; text-align: left; }
  .step__aside--logo { padding-block: 14px; }
  .step__logo { width: 150px; }
  .step__events { grid-auto-flow: column; align-items: center; gap: 14px; }
  .step__events img { width: 88px; }

  .project--large .outcomes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Small phones: in a step tile the label goes above the icons or logos */
@media (max-width: 480px) {
  .step__aside { grid-auto-flow: row; justify-items: start; gap: 10px; }
}

/* Phone */
@media (max-width: 600px) {
  .gear3d__stage { aspect-ratio: 1 / 1; }
  .gear3d__labels { display: none; }   /* too crowded: the legend names the parts */
  body { font-size: 1rem; }

  .glance { grid-template-columns: 1fr; }
  .glance > div { gap: 6px; padding: 14px 18px; border-right: 0; border-bottom: 1px solid var(--line); }
  .glance > div:last-child { border-bottom: 0; }

  .dock { --icon: 46px; --room: 22px; padding-inline: 4px; }
  .dock__item { width: 68px; }
  .dock__icon--plate { padding: calc(var(--pad, 11px) * .85); border-radius: 12px; }
  .dock__icon--tile { border-radius: 12px; }

  .timeline { --pad: 34px; --rail-x: 10px; }

  .projects,
  .skills,
  .life,
  .gallery { grid-template-columns: 1fr; }
  .project--half { grid-column: auto; }
  .project--wide { flex-direction: column; }
  .project--wide .media { width: 100%; min-height: 0; aspect-ratio: 16 / 10; }
  .spec { grid-template-columns: 1fr; }
  .events { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .data-table th,
  .data-table td { padding: 10px 12px; }
  .data-table td { white-space: normal; }

  .levels__scale,
  .levels__row { grid-template-columns: 74px minmax(0, 1fr) auto; gap: 10px; }

  .contact-row { grid-template-columns: auto minmax(0, 1fr); }
  .contact-row .btn { grid-column: 2; justify-self: start; }

  /* Hero buttons share each row evenly when they wrap */

  .hero--home .hero__actions .btn,
  .case-hero .hero__actions .btn { flex: 1 1 auto; justify-content: center; }
  .case-hero .hero__actions .btn--primary { flex-basis: 100%; }

  /* Lighter effects keep scrolling smooth on phones: still background,
     no blended grain, softer glass blur */
  .aurora i { animation: none; }
  .grain { mix-blend-mode: normal; opacity: .08; }
  .card { -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }
}

/* Phones: the first row of the menu (name, languages, button) stays on one line */
@media (max-width: 420px) {
  .nav__bar { column-gap: 8px; }
  .nav__brand { padding-inline: 8px 4px; font-size: .95rem; }
  .lang-switch button { padding-inline: 7px; }
}

/* Gearbox page: the "Portfolio" button keeps only its arrow (the label is still read aloud) */
@media (max-width: 389px) {
  .nav__action--back { width: 36px; padding: 0; }
  .nav__action--back span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

/* Small phones: result values ("Dec 2026") still fit in their tile */
@media (max-width: 360px) {
  .outcome { padding-inline: 12px; }
  .outcome__value { font-size: 1.25rem; }
}

/* Very small phones: the menu's CV button gives way (the hero has "My CV") */
@media (max-width: 349px) {
  .nav__action[data-cv] { display: none; }
}


/* =========================================================
   18. REDUCED MOTION
   Visitors who ask their system for less motion get a still page.
   ========================================================= */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* Mountain hero: phones and tablets held upright. The photo is shown
   wider than the screen (middle of the massif), name on two lines. */
@media (max-aspect-ratio: 4/5) {
  .peaks-hero__pin { --ry: 47cqh; --fw: 230cqw; }
  .peaks-hero__first { top: calc(var(--ry) - 1.62em); font-size: 15cqw; line-height: .86; white-space: normal; }
  .peaks-hero__last { right: 6cqw; top: calc(var(--ry) + 27cqw); font-size: 17cqw; }
  .peak__label { padding: 4px 6px; font-size: .66rem; }
  .peaks-hero__credit { left: var(--gutter); max-width: none; text-align: left; }
  .peaks-hero__foot { bottom: 64px; }
}

@media (max-width: 980px) {
  .intro__grid { grid-template-columns: 1fr; }
  .intro__photo { max-width: 520px; }
  .intro__photo img { height: auto; aspect-ratio: 3 / 2; }
  .glance--5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .glance--5 > div { border-bottom: 1px solid var(--line); }
  .glance--5 > div:nth-child(2n) { border-right: 0; }
  .glance--5 > div:last-child { grid-column: 1 / -1; border-bottom: 0; }
}

@media (max-width: 600px) {
  .glance--5 { grid-template-columns: 1fr; }
  .glance--5 > div { border-right: 0; }
}

/* Less motion: no pinning, no scroll effects, no opening animation */
@media (prefers-reduced-motion: reduce) {
  .peaks-hero { height: 100svh; }
  .intro { margin-top: 0; }
  .peaks-hero__frame img, .peaks-hero__first, .peaks-hero__last, .peak, .peaks-hero__foot, .peaks-hero__cue .icon { animation: none; }
}
