/* ==========================================================================
   base.css — styles shared by every page.
   Load order: base.css first, then the page stylesheet (index.css, …).
   Breakpoint: mobile layout at max-width 810px (keep in sync with common.js).
   ========================================================================== */

/* ==========================================================================
   Fonts
   Only the five upright weights the site renders are shipped. A weight
   without its own face uses the nearest one (600 renders with Bold); a
   future italic would be synthesised until its face is added here.
   Served as WOFF (the OTF tables, zlib-compressed: ≈ 70 KB per weight
   instead of ≈ 150 KB). The Bold face, used by every page title, is
   preloaded in each page's <head> so titles render in Larsseit at once.
   ========================================================================== */
@font-face {
  font-family: 'Larsseit';
  src: url('../fonts/larsseit/larsseit-light.woff') format('woff');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Larsseit';
  src: url('../fonts/larsseit/larsseit-regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Larsseit';
  src: url('../fonts/larsseit/larsseit-medium.woff') format('woff');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Larsseit';
  src: url('../fonts/larsseit/larsseit-bold.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Larsseit';
  src: url('../fonts/larsseit/larsseit-extrabold.woff') format('woff');
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================================
   Design tokens
   ========================================================================== */

:root {
  --color-bg: #fafafa;
  --color-surface: #ffffff;
  --color-text: #000000;
  --color-text-muted: #4e4e4e;
  --color-nav-idle: #929292;
  --color-accent: #711ff5;
  --color-highlight: #ffe600;
  --color-dark: #272727;
  --color-text-subtle: #6b6b6b; /* secondary text, ≥ 4.5:1 on --color-bg */
  --color-line: rgba(0, 0, 0, 0.08);
  --color-error: #c62f2f;       /* form errors, ≥ 4.5:1 on white */

  /* Scale. The desktop design is drawn in viewport units for a 1920px
     reference screen. Beyond it, sizes keep growing but four times slower,
     so a larger screen brings breathing room rather than larger elements;
     the extra width becomes side margin around a centred design canvas.
     Below 1920px every token equals its plain counterpart (1vw, 1px, 100vw).
     - --vw:     use instead of vw for desktop sizes (= 1vw up to 1920px);
     - --u:      use instead of px for fixed desktop sizes (= 1px up to 1920px);
     - --canvas: width of the design canvas, centred in the viewport. */
  --vw: min(1vw, 14.4px + 0.25vw);
  --u: max(1px, var(--vw) / 19.2);
  --canvas: calc(100 * var(--vw));
  --canvas-offset: calc((100vw - var(--canvas)) / 2);

  /* Layout */
  --nav-height: calc(80 * var(--u)); /* sticky navbar: the visible area starts below it */
  --viewport-height: 100vh;          /* upgraded to the dynamic viewport below */
  --page-gutter: calc(var(--canvas-offset) + 6.5 * var(--vw)); /* horizontal page margin */
  --radius-media: calc(0.5 * var(--vw)); /* corner radius of images and of the
                                            surfaces that sit beside them */
  /* Mobile card radii, proportional to the card: white cards are large soft
     surfaces, so they get rounder corners than flat images. Fluid with the
     screen, floored and capped so the curve stays balanced at any width. */
  --radius-surface: clamp(1.25rem, 6vw, 2rem);      /* large cards (≈ 380 × 300) */
  --radius-surface-sm: clamp(1rem, 4.5vw, 1.5rem);  /* compact cards (≈ 340 × 220) */

  /* Width of the wide page blocks (CTA card, portfolio project cards), so their
     edges align exactly. Use on a direct child of <main> (outside
     .InnerPadding): the mobile value is relative to the full page width. */
  --block-wide: calc(80 * var(--vw));

  /* Typography */
  --text-min: 0.9375rem;    /* 15px: floor for running text on any screen */

  /* Motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}

/* 100dvh follows the real visible height on mobile (address bar shown/hidden). */
@supports (height: 100dvh) {
  :root {
    --viewport-height: 100dvh;
  }
}

/* ==========================================================================
   Reset & base
   ========================================================================== */

* {
  margin: 0;
  box-sizing: border-box;
}

html body {
  overflow-x: hidden;
}

body {
  font-family: 'Larsseit', sans-serif;
  font-weight: 400;
  background-color: var(--color-bg);
}

ul {
  list-style: none;
}

/* Images carry width/height attributes so the browser reserves their box
   before they load (no layout shift). The attributes give the ratio; the
   rendered size stays the one set by the CSS (rules setting a height win
   over this one by specificity). */
img {
  height: auto;
}

/* Hidden visually but still announced by screen readers. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link: first focusable element of every page, off-screen until it
   receives keyboard focus, then shown above everything (same pill look as
   the solid buttons). Jumps to <main id="contenu">. */
.skip-link {
  position: fixed;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 200;
  padding: 0.75em 1.4em;
  border-radius: 999px;
  background-color: var(--color-dark);
  color: #fff;
  font-weight: 500;
  font-size: var(--text-min);
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus-visible {
  transform: none;
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

/* The skip target receives focus programmatically only: no outline on it. */
main:focus {
  outline: none;
}

/* ==========================================================================
   Custom cursor
   The native cursor is only hidden once common.js has detected a mouse
   (html.has-custom-cursor), so it never disappears if JS fails or on touch.
   ========================================================================== */

.has-custom-cursor,
.has-custom-cursor * {
  cursor: none;
}

#cursor {
  z-index: 30;
  position: fixed;
  top: -20px;  /* centre the 40px disc on the pointer */
  left: -20px;
  width: 40px;
  height: 40px;
  background: white;
  border-radius: 50%;
  mix-blend-mode: difference;
  pointer-events: none;
  opacity: 0;
  transition: scale 0.15s ease-in-out;
}

.has-custom-cursor #cursor.is-visible {
  opacity: 1;
}

#cursor.is-pressed {
  scale: 0.5;
}

/* ==========================================================================
   Text selection & highlight
   ========================================================================== */

::selection {
  background-color: var(--color-highlight);
  color: #000000;
}

.highlight {
  background: linear-gradient(transparent 60%, var(--color-highlight) 0%);
}

/* ==========================================================================
   Navbar
   The current page link carries aria-current="page".
   ========================================================================== */

.navbar {
  background-color: var(--color-bg);
  margin-right: calc(var(--canvas-offset) + 20 * var(--u));
  margin-left: calc(var(--canvas-offset) + 20 * var(--u));
  top: 0;
  z-index: 10;
  position: sticky;
  display: flex;
  align-items: center;
  padding: calc(20 * var(--u));
  height: var(--nav-height);
  font-size: calc(16 * var(--u));
}

.menu {
  font-weight: 300;
  display: flex;
  position: absolute;
  right: 0;
  align-items: center;
}

.logo {
  display: block;
  margin-top: 2px;
}

.logo img {
  position: relative;
  width: calc(70 * var(--u));
}

.accueil {
  padding-left: calc(40 * var(--u));
}

.nav-links {
  padding-left: calc(15 * var(--u));
  justify-content: right;
  position: relative;
  display: flex;
  gap: calc(15 * var(--u));
}

.accueil a,
.nav-links a {
  text-decoration: none;
  color: var(--color-nav-idle);
  transition: color 0.3s ease;
}

.accueil a:hover,
.nav-links a:hover,
.accueil a[aria-current="page"],
.nav-links a[aria-current="page"] {
  color: #000;
}

.contact-btn {
  padding-left: calc(15 * var(--u));
}

.contact-btn a {
  margin-right: calc(20 * var(--u));
  padding: calc(12 * var(--u)) calc(20 * var(--u)) calc(10 * var(--u));
  border: 1px solid var(--color-accent);
  border-radius: 50px;
  text-decoration: none;
  color: var(--color-accent);
  transition: background-color 0.45s ease;
}

.contact-btn a:hover,
.contact-btn a[aria-current="page"] {
  background-color: var(--color-accent);
  color: #fff;
}

/* Burger (visible on mobile only) */

.burger {
  display: none;
  background: none;
  border: none;
  position: absolute;
  right: 20px;
  width: 40px;
  height: 40px;
}

.burger span {
  display: block;
  height: 2px;
  width: 24px;
  background: #000;
  margin: 6px auto;
  transition: transform 0.25s ease, opacity 0.25s ease;
}

.burger span:nth-child(2) {
  transform-origin: left;
}

.burger.active span:nth-child(1) {
  transform: translateY(8px) rotate(45deg);
}

.burger.active span:nth-child(2) {
  transform: scaleX(0);
  opacity: 0;
}

.burger.active span:nth-child(3) {
  transform: translateY(-8px) rotate(-45deg);
}

/* ==========================================================================
   Layout helpers & shared typography
   ========================================================================== */

.InnerPadding {
  padding-left: var(--page-gutter);
  padding-right: var(--page-gutter);
}

/* Two-line page title used by the secondary pages. */
.page-title {
  text-align: center;
  margin-top: calc(5 * var(--vw));
  font-weight: 700;
  font-size: min(5 * var(--vw), 90 * var(--u));
}

.page-title__line {
  display: block;
}

/* Optional decorative strokes centred above the title. Sized in em, so it
   always keeps the same proportion to the title, whatever its font-size
   (desktop, mobile and large screens alike). */
.page-title__strokes {
  display: block;
  width: 2.5em;
  height: auto;
  /* Negative top margin lifts the whole title block (only when the strokes
     are present); the bottom margin keeps the strokes close to the text. */
  margin: -0.5em auto -0.05em;
}

.page-title__line--accent {
  color: var(--color-accent);
}

.subtitle {
  margin: auto;
  text-align: center;
  margin-top: calc(2 * var(--vw));
  font-size: max(var(--text-min), 1.3 * var(--vw));
  font-weight: 300;
  color: var(--color-text-muted);
  max-width: calc(40 * var(--vw));
}

/* Longer introduction paragraphs: airier line spacing on every screen and
   balanced line breaks (no single word left on the last line). */
.subtitle--lead {
  line-height: 1.5;
  text-wrap: pretty;
}

/* Page introductions: same width as .subtitle on smaller desktops, then
   progressively wider on large screens (no jump: both formulas meet at
   ~890px), keeping lines within roughly 70–85 characters. */
.subtitle--wide {
  max-width: max(40 * var(--vw), 64 * var(--vw) - 160 * var(--u));
}

/* Section heading inside a page (same scale as the home page section titles). */
.section-heading {
  text-align: center;
  font-weight: 600;
  font-size: min(4.5 * var(--vw), 90 * var(--u));
  margin-bottom: calc(3 * var(--vw));
}

/* ==========================================================================
   "Learn more" link-button
   ========================================================================== */

.btn-container {
  font-family: sans-serif; /* the buttons use the system sans-serif by design */
  font-size: calc(16 * var(--u)); /* the button below is sized in em */
  line-height: 1.5;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100 * var(--u));
}

.learn-more {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  width: 12em;
  text-decoration: none;
}

.learn-more .circle {
  transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
  position: relative;
  display: block;
  width: 3em;
  height: 3em;
  background: var(--color-dark);
  border-radius: 1.625em;
}

.learn-more .icon {
  transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
  position: absolute;
  top: -0.3125em;
  bottom: 0;
  margin: auto;
}

.learn-more .icon.arrow {
  left: 0.625em;
  width: 1.125em;
  height: 0.125em;
  background: none;
}

.learn-more .icon.arrow::before {
  position: absolute;
  content: "";
  top: -0.15em;
  right: 0.0625em;
  width: 0.625em;
  height: 0.625em;
  border-top: 0.125em solid #ffffff;
  border-right: 0.125em solid #ffffff;
  transform: rotate(45deg);
}

.learn-more .button-text {
  transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
  position: absolute;
  inset: 0;
  padding: 0.75em 0;
  margin: 0 0 0 1.85em;
  color: #000000;
  font-weight: 700;
  line-height: 1.6;
  text-align: center;
  text-transform: uppercase;
}

/* The hover/focus animation only exists on real links (a[href]): a button
   without destination yet (e.g. a file not published) never looks clickable. */
.learn-more[href]:hover .circle,
.learn-more[href]:focus-visible .circle {
  width: 100%;
}

.learn-more[href]:hover .icon.arrow,
.learn-more[href]:focus-visible .icon.arrow {
  transform: translate(0.5em, 0);
}

.learn-more[href]:hover .button-text,
.learn-more[href]:focus-visible .button-text {
  color: var(--color-bg);
}

/* ==========================================================================
   Pill button — compact solid button: black pill, white icon + label.
   Markup: <a class="btn-pill" href="…"><svg class="btn-pill__icon" aria-hidden="true">…</svg>Label</a>
   Hover/focus only on real links (a[href]), like the other buttons: the
   pill takes the accent colour (same as the navbar contact pill).
   ========================================================================== */

.btn-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.85em 1.6em;
  border-radius: 999px;
  background-color: var(--color-dark);
  color: #fff;
  font-size: max(var(--text-min), 0.9 * var(--vw));
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  transition: background-color 0.45s ease;
  -webkit-tap-highlight-color: transparent;
}

.btn-pill__icon {
  flex: none;
  width: 1.1em;
  height: 1.1em;
}

.btn-pill[href]:hover,
.btn-pill[href]:focus-visible {
  background-color: var(--color-accent);
}

.btn-pill:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .btn-pill,
  .learn-more {
    transition: none;
  }
}

/* Mobile: the "learn more" buttons take the pill look (same values as
   .btn-pill above; keep both in sync). The disc and its hover animation
   are a pointer interaction; on a phone a solid, obvious target reads
   better. Same markup, so nothing changes in the pages: the disc is hidden,
   an arrow icon (drawn as a mask, like the project cue) leads the label. */
@media (max-width: 810px) {
  .btn-container {
    font-family: inherit; /* the site typeface, like the pill buttons */
  }

  .learn-more {
    display: inline-flex;
    align-items: center;
    gap: 0.6em;
    width: auto;
    padding: 0.85em 1.6em;
    border-radius: 999px;
    background-color: var(--color-dark);
    color: #fff;
    font-size: var(--text-min);
    font-weight: 500;
    line-height: 1.2;
    white-space: nowrap;
    transition: background-color 0.45s ease;
    -webkit-tap-highlight-color: transparent;
  }

  .learn-more::before {
    content: "";
    flex: none;
    width: 1.1em;
    height: 1.1em;
    background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
  }

  .learn-more .circle {
    display: none;
  }

  .learn-more .button-text,
  .learn-more[href]:hover .button-text,
  .learn-more[href]:focus-visible .button-text {
    position: static;
    margin: 0;
    padding: 0;
    color: inherit;
    font-weight: inherit;
    line-height: inherit;
    text-transform: none;
  }

  .learn-more[href]:active,
  .learn-more[href]:focus-visible {
    background-color: var(--color-accent);
  }

  .learn-more:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 3px;
  }
}

/* ==========================================================================
   3D tilt card stack (driven by common.js via [data-tilt])
   ========================================================================== */

.card-stack {
  height: calc(50 * var(--vw));
  width: calc(87 * var(--vw));
  max-width: calc(1200 * var(--u));
  max-height: calc(800 * var(--u));
  font-size: calc(16 * var(--u)); /* tilt perspective and depth are in em */
  position: relative;
  transform-style: preserve-3d;
  transition: 0.3s ease;
  margin: calc(3 * var(--vw)) auto;
}

.card-stack__img {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: calc(80 * var(--vw));
  max-width: calc(1100 * var(--u));
  height: auto;
  filter: drop-shadow(10px 10px 50px rgba(153, 153, 153, 0.418));
}

.card-stack__img--front {
  transform: translate(-50%, -50%) translateZ(3em);
  z-index: 10;
  transform-style: preserve-3d;
  transition: 0.3s ease;
}

.card-stack.is-tilting .card-stack__img--front {
  filter: drop-shadow(5px 5px 15px rgba(168, 168, 168, 0.62));
}

/* Slightly smaller stack than on the home page (contact, about). */
.card-stack--compact {
  max-width: calc(1000 * var(--u));
  max-height: calc(700 * var(--u));
}

/* ==========================================================================
   Project card (shared component)
   Markup: li.project-card > article > a.project-card__link[href] >
     .project-card__visual (media + cue) and .project-card__info (optional
     .project-card__category, then .project-card__title).
   Two layouts, each set by its container (only geometry and behaviour
   differ, everything below is common):
   - .project-stack (portfolio.css): large sticky stacking cards;
   - .project-grid (below): compact cards, three per row, normal scroll.
   Tokens read here, set by the container:
   - --card-ratio       media aspect ratio;
   - --card-title-size  title size (the info block is sized in em from it);
   - --media-frame      page-coloured frame around the visual (0 = none);
   - --media-max-height optional media height cap (stack only);
   - --cue-size         diameter of the hover cue.
   ========================================================================== */

.project-card {
  font-size: var(--card-title-size);
}

/* Visual wrapper: holds the media (placeholder or <img>) and the hover cue,
   so the cue is centred on the visual itself, not on the whole card. */
.project-card__visual {
  position: relative;
}

/* Media: real image or placeholder, same box either way.
   The frame (page colour) reads as a margin around the visual and cleanly
   separates a stacked card from the one it slides over. The outer radius
   grows by the frame width, so the visible image keeps --radius-media.
   The frame is part of the box (border-box): ratio and size are unchanged. */
.project-card__media {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--card-ratio);
  max-height: var(--media-max-height, none);
  object-fit: cover;
  border: var(--media-frame) solid var(--color-bg);
  border-radius: calc(var(--radius-media) + var(--media-frame));
}

/* Temporary visual: neutral tones of the site palette, one per position so
   consecutive projects are easy to tell apart. */
.project-card__media--placeholder {
  background: linear-gradient(135deg, var(--placeholder-from), var(--placeholder-to));
  --placeholder-from: #ececf1;
  --placeholder-to: #e2e2ea;
}

.project-card:nth-child(4n + 2) .project-card__media--placeholder {
  --placeholder-from: #ebe7f6;
  --placeholder-to: #dfd8f2;
}

.project-card:nth-child(4n + 3) .project-card__media--placeholder {
  --placeholder-from: #eeeeea;
  --placeholder-to: #e4e4dd;
}

.project-card:nth-child(4n + 4) .project-card__media--placeholder {
  --placeholder-from: #e8ecf2;
  --placeholder-to: #dce2ec;
}

/* Info: optional category (accent, small caps-style) over the title, one
   line each, aligned on the visible image (inside the media frame). */
.project-card__info {
  display: flex;
  flex-direction: column;
  gap: 0.15em;
  padding-top: 0.6em;
  padding-inline: var(--media-frame);
}

.project-card__category,
.project-card__title {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.project-card__category {
  font-size: max(0.75rem, 0.5em);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
}

.project-card__title {
  font-size: 1em;
  font-weight: 600;
  line-height: 1.2;
}

/* Card link and "open project" cue
   The cue belongs to the card component, not to its (temporary) content:
   a dark disc with an arrow pointing right, centred on the visual,
   in the same language as the round "learn more" buttons of the site.
   The arrow is part of the disc, so both appear with one single animation.
   It only exists on real links (a[href]): a card without a destination is
   not clickable, so it never looks clickable.
   - pointer with hover: the disc and its arrow zoom in together (no fade):
     a lively entrance with a slight overshoot, a shorter accelerating exit;
   - touch (no hover: phones, tablets): no cue at all, the whole card is
     the link and a cue that never reacts would only cover the visual;
   - keyboard: same as hover on :focus-visible, plus a visible outline. */

.project-card__link {
  position: relative;
  display: block;
  border-radius: var(--radius-media);
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}

.project-card__link:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 4px;
}

.project-card__cue {
  --cue-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E");

  display: none;
  position: absolute;
  inset: 0;
  margin: auto; /* centred on the visual */
  width: var(--cue-size, clamp(2.75rem, 4.2 * var(--vw), 84 * var(--u)));
  height: var(--cue-size, clamp(2.75rem, 4.2 * var(--vw), 84 * var(--u)));
  border-radius: 50%;
  background-color: var(--color-dark);
  color: #fff;
  pointer-events: none;
}

/* Arrow → drawn as a mask, so it takes the text colour and stays crisp. */
.project-card__cue::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: currentColor;
  -webkit-mask: var(--cue-arrow) center / 42% no-repeat;
  mask: var(--cue-arrow) center / 42% no-repeat;
}

@media (hover: hover) {
  /* The cue only exists where it can react: pointer devices with hover. */
  a.project-card__link[href] .project-card__cue {
    display: block;
  }

  /* Rest state + exit: zoom out, short and accelerating (ease-in). */
  .project-card__cue {
    transform: scale(0);
    transition: transform 0.22s cubic-bezier(0.55, 0, 1, 0.45);
  }

  /* Entrance: zoom in, quick start, ~6% overshoot, soft settle. */
  .project-card__link[href]:hover .project-card__cue,
  .project-card__link[href]:focus-visible .project-card__cue {
    transform: scale(1);
    transition: transform 0.45s cubic-bezier(0.34, 1.5, 0.64, 1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .project-card__cue {
    transition: none;
  }
}

/* ==========================================================================
   Project grid — compact project cards
   Same component as the large cards, minimal content (visual only, no info
   block: give the link an aria-label with the project name), no sticky
   behaviour: a plain grid in the normal page flow.
   Columns follow the room available (same breakpoints as the footer):
   3 per row from 1200px, 2 from 600px, then 1. Width: --block-wide, like the
   other wide blocks; place it outside .InnerPadding (its mobile width is
   relative to the full page width). Beyond 1920px the --vw / --u scale
   keeps the cards from growing much.
   ========================================================================== */

.project-grid {
  --card-ratio: 3 / 2;
  --card-title-size: max(1.0625rem, 1.15 * var(--vw)); /* if a title is added */
  --media-frame: 0px;       /* no stacking here: the visual fills its card */
  --cue-size: clamp(2.5rem, 3.2 * var(--vw), 62 * var(--u));

  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: calc(3 * var(--vw)) calc(2 * var(--vw));
  width: var(--block-wide);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

/* Two per row: drawn on 4 tracks, each card spanning 2, so a card left alone
   on the last row (odd count) can sit centred instead of hanging left. */
@media (max-width: 1199px) {
  .project-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: clamp(1.75rem, 4vw, 2.5rem) clamp(1rem, 3vw, 1.5rem);
  }

  .project-grid > .project-card {
    grid-column: span 2;
  }

  .project-grid > .project-card:last-child:nth-child(odd) {
    grid-column: 2 / span 2;
  }
}

@media (max-width: 599px) {
  .project-grid {
    --card-title-size: clamp(1.0625rem, 4.5vw, 1.25rem);
    grid-template-columns: minmax(0, 1fr);
  }

  .project-grid > .project-card,
  .project-grid > .project-card:last-child:nth-child(odd) {
    grid-column: auto;
  }
}

/* ==========================================================================
   Logos carousel (shared: home, about)
   Each .logos-slide holds the same logos; all slides shift by their own width
   so the loop is seamless. font-size: 0 removes inter-image whitespace so the
   spacing is exactly the image margins (otherwise the loop jumps by one space).
   Markup: the 3 slides must stay identical and adjacent (no whitespace
   between them). Spacing around the carousel is set by each page.
   ========================================================================== */

@keyframes slide {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-100%);
  }
}

.logos {
  overflow: hidden;
  white-space: nowrap;
  position: relative;
}

.logos::before,
.logos::after {
  position: absolute;
  top: 0;
  bottom: 0;
  width: calc(300 * var(--u));
  content: "";
  z-index: 2;
}

.logos::before {
  left: 0;
  background: linear-gradient(to left, rgba(250, 250, 250, 0), var(--color-bg));
}

.logos::after {
  right: 0;
  background: linear-gradient(to right, rgba(250, 250, 250, 0), var(--color-bg));
}

.logos-slide {
  display: inline-block;
  font-size: 0;
  animation: 16.8s slide infinite linear;
}

/* +1.65px per side = the whitespace (3.3px) that used to separate the logos,
   so the visual spacing is unchanged. */
.logos-slide img {
  margin: 0 calc(1.2 * var(--vw) + 1.65 * var(--u));
  width: auto; /* sized by its height; the width attribute only gives the ratio */
  height: min(2.5 * var(--vw), 50 * var(--u));
}

/* ==========================================================================
   "Vous avez un projet ?" CTA card (shared: home, portfolio)
   Place it directly in <main>, outside .InnerPadding: its mobile width is
   relative to the full page width.
   ========================================================================== */

.projet {
  gap: calc(2 * var(--vw));
  padding-top: calc(4 * var(--vw));
  border-radius: var(--radius-media);
  filter: drop-shadow(10px 10px 50px rgba(153, 153, 153, 0.082));
  margin: calc(8 * var(--vw)) auto;
  width: var(--block-wide);
  min-height: calc(34 * var(--vw)); /* grows instead of overflowing if the text wraps more */
  display: flex;
  flex-direction: column;
  justify-content: center;
  background-color: var(--color-surface);
}

.projettitle {
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: min(4.5 * var(--vw), 90 * var(--u));
  margin-top: min(var(--vw), 10 * var(--u));
  color: var(--color-accent);
  text-align: center;
}

.projetsubtitle {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
  margin-top: calc(2 * var(--vw));
  font-size: max(var(--text-min), 1.3 * var(--vw));
  font-weight: 300;
  color: var(--color-text-muted);
  max-width: calc(40 * var(--vw));
}

/* ==========================================================================
   Footer
   Three compositions, chosen by the room the link columns need:
   - desktop (≥ 1200px): logo column + three link columns;
   - tablet (600–1199px): logo row, then the three columns on the full width;
   - mobile (< 600px): logo row, two link columns, social card, legal line.
   ========================================================================== */

.stroke {
  background-color: #838383;
  height: 1px;
  margin: calc(5 * var(--vw)) auto;
  width: calc(80 * var(--vw));
}

.footer {
  margin: calc(7 * var(--vw)) auto calc(4 * var(--vw));
  width: calc(70 * var(--vw));
  display: flex;
  gap: calc(10 * var(--vw));
}

.footer1 {
  flex: none;
  width: calc(10 * var(--vw));
}

.footer-logo {
  display: inline-block;
}

.logofooter {
  width: calc(5 * var(--vw));
}

.footer2 {
  flex: none;
  width: calc(50 * var(--vw));
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: calc(6 * var(--vw));
}

.FooterTitle {
  font-weight: 500;
  font-size: clamp(1.0625rem, 1.5 * var(--vw), 25 * var(--u));
  margin-bottom: calc(1.4 * var(--vw));
}

.footerlink {
  display: flex;
  flex-direction: column;
  gap: calc(0.5 * var(--vw));
  font-weight: 300;
  padding: 0;
}

.footerlink li,
.footerlink li a {
  font-size: clamp(0.875rem, var(--vw), 18 * var(--u));
  color: #000;
}

.footerlink li a {
  text-decoration: none;
  transition: color 0.2s ease;
}

.footerlink li a:hover,
.footerlink li a:focus-visible {
  color: var(--color-accent);
}

.reseauximg {
  padding: 0;
  margin-top: var(--vw);
  display: flex;
}

.reseauximg li a img {
  margin-right: calc(0.7 * var(--vw));
  width: min(1.2 * var(--vw), 28 * var(--u));
}

.reseauximg li a img.tiktok {
  width: min(1.1 * var(--vw), 25 * var(--u));
}

.footer-legal {
  width: calc(70 * var(--vw));
  margin: 0 auto calc(4 * var(--vw));
  padding-top: calc(1.5 * var(--vw));
  border-top: 1px solid var(--color-line);
  font-size: clamp(0.75rem, 0.85 * var(--vw), 14 * var(--u));
  font-weight: 300;
  color: var(--color-text-subtle);
}

/* Tablet: the desktop columns would be too narrow for readable text. */
@media (max-width: 1199px) {
  .stroke {
    width: auto;
    margin: clamp(3rem, 8vw, 5rem) var(--page-gutter) 0;
  }

  .footer {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 2.5rem 2rem;
    width: auto;
    margin: 2.5rem var(--page-gutter) 0;
  }

  .footer1 {
    grid-column: 1 / -1;
    width: auto;
  }

  .logofooter {
    width: 70px; /* same as the navbar logo */
  }

  /* Its three blocks become direct grid items of .footer. */
  .footer2 {
    display: contents;
  }

  .FooterTitle {
    font-size: 1.125rem;
    margin-bottom: 0.75rem;
  }

  .footerlink {
    gap: 0.125rem;
  }

  .footerlink li,
  .footerlink li a {
    font-size: var(--text-min);
    line-height: 1.4;
  }

  /* Comfortable touch targets without changing the visual rhythm. */
  .footerlink li a {
    display: inline-block;
    min-width: 2.75rem;
    padding-block: 0.3rem;
  }

  .footer-note {
    color: var(--color-text-subtle);
    padding-block: 0.3rem;
  }

  .reseauximg {
    gap: 0.75rem;
    margin-top: 0.75rem;
  }

  .reseauximg li a {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border-radius: 50%;
    background: var(--color-surface);
    box-shadow: 0 6px 20px rgba(153, 153, 153, 0.18);
    transition: transform 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
  }

  .reseauximg li a:hover,
  .reseauximg li a:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 10px 24px rgba(113, 31, 245, 0.18);
  }

  .reseauximg li a img {
    width: 1.25rem;
    margin: 0;
  }

  .reseauximg li a img.tiktok {
    width: 1.125rem;
  }

  .footer-legal {
    width: auto;
    margin: 3rem var(--page-gutter) 0;
    padding: 1.25rem 0 calc(1.5rem + env(safe-area-inset-bottom));
    font-size: 0.8125rem;
  }
}

/* Mobile: two link columns side by side, social links in a card (same
   white-card language as the "Vous avez un projet ?" block). */
@media (max-width: 599px) {
  /* "A propos" holds the longest labels, so it gets the wider column. */
  .footer {
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
    gap: 2.25rem 1.5rem;
  }

  .reseaux {
    grid-column: 1 / -1;
    padding: 1.5rem;
    border-radius: var(--radius-surface-sm);
    background: var(--color-surface);
    box-shadow: 0 10px 40px rgba(153, 153, 153, 0.14);
  }

  .reseauximg li a {
    background: var(--color-bg);
    box-shadow: none;
  }
}

/* ==========================================================================
   Responsive (≤ 810px)
   ========================================================================== */

@media (max-width: 810px) {
  :root {
    /* Same as the home page portfolio previews (.Ui, .anime): page gutters. */
    --block-wide: calc(100% - 2 * var(--page-gutter));
  }

  /* Navbar: aligned on the page gutter. Its height stays --nav-height
     (the phone preview on the home page is centred relative to it). */

  .navbar {
    margin: 0;
    padding: 0 var(--page-gutter);
  }

  /* 44px touch target; the 24px lines end exactly on the gutter. */
  .burger {
    display: block;
    right: calc(var(--page-gutter) - 10px);
    width: 44px;
    height: 44px;
    padding: 0;
    -webkit-tap-highlight-color: transparent;
  }

  /* Full-height panel under the navbar, faded in (visibility keeps the
     closed menu out of the tab order and away from screen readers). */
  .menu {
    position: absolute;
    top: 100%;
    right: 0;
    left: 0;
    height: calc(var(--viewport-height) - var(--nav-height));
    display: flex;
    flex-direction: column;
    align-items: stretch;
    padding: clamp(1rem, 4vh, 2.5rem) var(--page-gutter) calc(clamp(1.5rem, 5vh, 3rem) + env(safe-area-inset-bottom));
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--color-bg);
    border-top: 1px solid var(--color-line);
    counter-reset: nav;
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.35s var(--ease-out), visibility 0s linear 0.35s;
  }

  .menu.open {
    visibility: visible;
    opacity: 1;
    transition: opacity 0.35s var(--ease-out);
  }

  /* Page scroll is locked while the menu is open (class set by common.js). */
  .menu-open body {
    overflow: hidden;
  }

  .accueil,
  .nav-links {
    display: flex;
    flex-direction: column;
    gap: 0;
    padding: 0;
  }

  /* Links rise in one after the other. */
  .menu li {
    opacity: 0;
    transform: translateY(0.75rem);
    transition: opacity 0.3s var(--ease-out), transform 0.3s var(--ease-out);
  }

  .menu.open li {
    opacity: 1;
    transform: none;
    transition-duration: 0.45s, 0.6s;
    transition-delay: calc(80ms + var(--i, 0) * 55ms);
  }

  .accueil li { --i: 0; }
  .nav-links li:nth-child(1) { --i: 1; }
  .nav-links li:nth-child(2) { --i: 2; }
  .contact-btn li { --i: 3; }

  /* Large editorial links: index number, label, chevron. */
  .accueil a,
  .nav-links a {
    display: flex;
    align-items: baseline;
    gap: 0.875rem;
    padding: 1rem 0;
    border-bottom: 1px solid var(--color-line);
    font-size: clamp(1.75rem, 8.5vw, 2.5rem);
    font-weight: 600;
    line-height: 1.15;
    color: #000;
  }

  .accueil a::before,
  .nav-links a::before {
    counter-increment: nav;
    content: counter(nav, decimal-leading-zero);
    min-width: 1.25rem;
    font-size: 0.75rem;
    font-weight: 400;
    color: var(--color-accent);
  }

  .accueil a::after,
  .nav-links a::after {
    content: "";
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    margin: 0 0.25rem 0 auto;
    border-top: 2px solid currentColor;
    border-right: 2px solid currentColor;
    transform: translateY(-0.2em) rotate(45deg);
    color: var(--color-nav-idle);
    transition: transform 0.3s var(--ease-out), color 0.3s ease;
  }

  .accueil a:hover,
  .nav-links a:hover,
  .accueil a:active,
  .nav-links a:active {
    color: var(--color-accent);
  }

  .accueil a:hover::after,
  .nav-links a:hover::after {
    color: var(--color-accent);
    transform: translate(0.25rem, -0.2em) rotate(45deg);
  }

  /* Contact: the desktop pill, full width at the bottom of the panel. */
  .contact-btn {
    margin-top: auto;
    padding: 2rem 0 0;
  }

  .contact-btn a {
    display: block;
    margin: 0;
    padding: 1rem 1.5rem;
    font-size: 1.125rem;
    text-align: center;
  }

  .contact-btn a:active {
    background-color: var(--color-accent);
    color: #fff;
  }

  /* CTA card: height follows its content, generous padding instead of a
     fixed vw height that left the text cramped in a narrow column. */
  .projet {
    min-height: 0;
    margin: 12vw auto;
    padding: clamp(2.5rem, 11vw, 4rem) 1.5rem clamp(1.25rem, 5vw, 2rem);
    gap: 1rem;
    border-radius: var(--radius-surface);
  }

  .projettitle {
    margin-top: 0;
    font-size: clamp(1.625rem, 7.5vw, 2.5rem);
  }

  .projetsubtitle {
    margin-top: 0;
    font-size: clamp(var(--text-min), 3.8vw, 1.125rem);
    line-height: 1.5;
    max-width: 30ch;
  }

  /* Logos carousel */

  .logos::before,
  .logos::after {
    width: 100px;
  }

  .logos-slide img {
    margin: 15vw calc(3vw + 1.65px);
    height: 6vw;
  }

  /* Shared typography */

  .page-title {
    margin-top: 10vw;
    font-size: 8vw;
  }

  .section-heading {
    font-size: clamp(1.625rem, 7.5vw, 2.5rem);
    margin-bottom: clamp(1.25rem, 5vw, 2rem);
  }

  .subtitle {
    margin-top: 7vw;
    font-size: clamp(var(--text-min), 3.8vw, 1.125rem);
    line-height: 1.5;
    max-width: min(80vw, 36rem);
  }
}

/* ==========================================================================
   Page motion — opening animation and page transitions
   One system, one visual language: two full-screen curtains in the site
   colours (accent below, dark on top) moving upwards on the same ease.
   Only transform is animated (compositor-friendly, fine on phones). The
   state is a class on <html>, set by the inline script in <head> (before
   the first paint) and by common.js:

   - .is-intro     arrival from outside the site or reload: the curtains,
                   covering the page at first paint, lift off (dark first,
                   then accent), revealing the page already rendered
                   underneath. Nothing waits for images or fonts.
   - .is-leaving   an internal link was followed: the curtains rise from
                   the bottom and cover the page (accent first, then dark),
                   then the browser navigates.
   - .is-entering  the page reached through such a link: same lift as the
                   opening, slightly quicker, continuing the leaving motion.

   Without JavaScript, or with reduced motion, no class is set: links
   navigate normally and pages simply show. The curtains never take
   pointer events, so the page underneath stays usable.
   ========================================================================== */

:root {
  --motion-ease: cubic-bezier(0.76, 0, 0.24, 1);
}

.is-intro::before,
.is-intro::after,
.is-entering::before,
.is-entering::after,
.is-leaving::before,
.is-leaving::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 100;
  pointer-events: none;
}

/* Accent curtain under the dark one. */
.is-intro::before,
.is-entering::before,
.is-leaving::before {
  background-color: var(--color-accent);
}

.is-intro::after,
.is-entering::after,
.is-leaving::after {
  background-color: var(--color-dark);
}

/* Opening: dark lifts first, uncovering the accent, which follows. */
.is-intro::after {
  animation: curtain-lift 0.8s var(--motion-ease) 0.1s both;
}

.is-intro::before {
  animation: curtain-lift 0.8s var(--motion-ease) 0.22s both;
}

/* Entering: the same lift, a little quicker. */
.is-entering::after {
  animation: curtain-lift 0.7s var(--motion-ease) both;
}

.is-entering::before {
  animation: curtain-lift 0.7s var(--motion-ease) 0.12s both;
}

/* Leaving: accent rises first, the dark curtain closes on top of it.
   Fully covered at 0.6s (common.js navigates then). */
.is-leaving::before {
  animation: curtain-cover 0.5s var(--motion-ease) both;
}

.is-leaving::after {
  animation: curtain-cover 0.5s var(--motion-ease) 0.1s both;
}

@keyframes curtain-lift {
  to {
    transform: translateY(-100%);
  }
}

@keyframes curtain-cover {
  from {
    transform: translateY(100%);
  }
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .is-intro::before,
  .is-intro::after,
  .is-entering::before,
  .is-entering::after,
  .is-leaving::before,
  .is-leaving::after {
    display: none;
  }

  #cursor,
  .menu,
  .menu.open,
  .menu li,
  .menu.open li {
    transition: none;
  }

  .menu li,
  .menu.open li {
    transform: none;
  }
}
