/* ==========================================================================
   Components: header, footer, buttons, links, project list, lang switch
   ========================================================================== */

/* Button ------------------------------------------------------------------ */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: var(--control-height);
  padding-inline: var(--control-padding);
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) ease,
    border-color var(--duration-fast) ease,
    color var(--duration-fast) ease;
}
.button .icon { flex-shrink: 0; transition: transform var(--duration-base) var(--ease-out); }

/* Hover effects follow :root:not(.reduce-motion) rather than .motion: the
   footer toggle can turn them back on without a reload, while .motion (the
   reveals) only returns on the next page. */

.button--primary {
  background: var(--color-primary);
  color: var(--color-inverse);
}
.button--primary:hover { background: var(--color-primary-hover); }

.button--secondary {
  background: var(--color-bg);
  border-color: var(--color-border-strong);
  color: var(--color-text);
}
.button--secondary:hover { border-color: var(--color-text); }

/* Tertiary — text weight at rest, secondary's stroke on hover.
   The base .button already reserves a transparent 1px border at the pill
   radius, so the outline appears without shifting anything.
   Emphasis is carried by fill and stroke, not size: tertiary keeps the control
   height, so when its outline appears beside a secondary or primary button the
   two pills match. Only the horizontal padding is tighter. */
.button--tertiary {
  padding-inline: var(--space-4);
  gap: var(--space-2);
  background: transparent;
  color: var(--color-text);
}
.button--tertiary:hover { border-color: var(--color-border-strong); }
.button--tertiary .icon {
  color: var(--color-text-subtle);
  width: 18px;
  height: 18px;
  transition: color var(--duration-fast) ease;
}
.button--tertiary:hover .icon { color: var(--color-text); }

/* Size modifier, independent of tier — for controls that sit among small text
   with no neighbouring button to match (e.g. the footer animations toggle). */
.button--sm {
  min-height: auto;
  padding-block: var(--space-2);
}


/* Header ------------------------------------------------------------------ */

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding-block: var(--space-6) var(--space-5);
  position: relative;
}
.site-header__inner::after {
  content: "";
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  bottom: 0;
  border-bottom: 1px solid var(--color-border);
}
@media (min-width: 768px) {
  .site-header__inner { padding-block: var(--space-7) var(--space-5); }
}

.site-header__logo {
  position: relative;
  z-index: 1;
  font-family: var(--font-serif);
  font-size: var(--text-logo);
  line-height: var(--leading-tight);
  white-space: nowrap;
}
/* "AG", still: like every link, the hover label says where it leads */
.monogram { display: inline-flex; }

/* Pages, next to the name: plain text links (not buttons, so they read as
   navigation); the current one in black. Actions, on the right: the CV as
   the secondary button, "Get in touch" as the primary. */
.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  margin-left: var(--space-8);
  transition: opacity var(--duration-base) var(--ease-out);
}
.site-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem; /* 44px touch target */
  font-weight: var(--weight-medium);
  white-space: nowrap; /* "À propos" stays on one line */
  color: var(--color-text-subtle);
  transition: color var(--duration-fast);
}
.site-nav a:hover,
.site-nav a[aria-current] { color: var(--color-text); }
.site-actions { display: flex; align-items: center; gap: var(--space-3); margin-left: auto; }
.site-header__contact { cursor: pointer; }
/* Phones: the pages and "Get in touch" only; the CV is in the footer and on
   the About page */
@media (max-width: 767px) {
  .site-nav { gap: var(--space-4); margin-left: var(--space-4); }
  .site-actions__cv { display: none; }
}
@media (max-width: 479px) {
  .site-header__inner { gap: var(--space-3); }
  .site-header__contact { min-height: 2.75rem; padding-inline: var(--space-4); }
}
/* The smallest phones (French labels are the longest): tighter spacing,
   and "Contact" instead of the full label in the header */
.contact__short { display: none; }
@media (max-width: 374px) {
  .site-nav { gap: var(--space-3); margin-left: var(--space-3); }
  .site-header__contact { padding-inline: var(--space-4); }
}
@media (max-width: 359px) {
  .site-header__contact .contact__full { display: none; }
  .site-header__contact .contact__short { display: inline; }
}

/* Status pill -------------------------------------------------------------- */

/* One shape for every status on the site ("On request" on locked projects):
   a thin outline, small type in full black, a leading sign. Set apart from
   paragraphs by its shape and from links and buttons by its size. */
.status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 1.75rem;
  padding: 0.3rem 0.75rem;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  line-height: 1.3;
  color: var(--color-text);
}
.status .icon { flex: none; width: 14px; height: 14px; }

.availability__dot {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--color-available);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-available) 20%, transparent);
}

/* Footer ------------------------------------------------------------------ */

/* Footer, in three tiers (no hairline of its own: pages already end on one,
   the project list's or the timeline's): a sign-off (availability and a
   large "Get in touch"), three named columns on the 12-column grid
   (contact, preferences, elsewhere), then the copyright. Every item in a
   column is one row of the same height, links and toggles alike, so they
   share the same lines. Phones: the columns stack, rows grow to 44px. */
.site-footer {
  --row: 2rem; /* 32 */
  margin-top: var(--space-13);
  padding-bottom: var(--space-9);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  color: var(--color-text);
}
.site-footer__inner {
  position: relative;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--grid-gap);
  padding-top: var(--space-9);
}

/* Sign-off: availability and "Get in touch" on the left, "Rate this
   portfolio" on the right, on the same baseline; it drops below when narrow */
.site-footer__signoff {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-5);
  padding-bottom: var(--space-11);
}
.site-footer__signoff-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
}
.site-footer__status { display: flex; align-items: center; gap: var(--space-2); }
/* Section-heading size: a clear sign-off, well below the project titles */
.site-footer__cta {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: var(--text-h2);
  line-height: var(--leading-heading);
  transition: color var(--duration-fast) ease;
}
.site-footer__cta:hover { color: var(--color-text-muted); }

/* Columns: a Departure Mono heading on a hairline, then one row per item */
.site-footer__col {
  grid-column: span 4;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.site-footer__heading {
  align-self: stretch;
  display: flex;
  align-items: center;
  height: var(--row);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 400;
  color: var(--color-text-muted);
}
.site-footer__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: var(--row);
  cursor: pointer;
}
.site-footer__link:hover > span:first-child,
.site-footer__link:hover:not(:has(> span)) { text-decoration: underline; text-underline-offset: 0.2em; }
.site-footer__link .icon { width: 14px; height: 14px; color: var(--color-text-muted); }

/* Preferences: the language, then the two toggles. The toggles keep their
   button look (pill on hover); their padding is pulled back so their icon
   starts on the column edge like every other row. The globe is 16px wide
   and the dots 8px: each dot gets a 16px slot (centred under the globe),
   so "En", "Dark mode" and "Animations" start on the same line. */
.site-footer__prefs .lang-switch { height: var(--row); margin-right: 0; }
.site-footer__prefs .footer-toggle {
  height: var(--row);
  padding-block: 0;
  margin-left: calc((var(--space-3) + 1px) * -1);
}
.site-footer__prefs .footer-toggle__dot { margin-inline: 4px; }

/* Copyright */
.site-footer__strip {
  grid-column: 1 / -1;
  margin-top: var(--space-11);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-caption);
  color: var(--color-text-muted);
}

/* Narrower screens: one column. (A 12-column grid's eleven gaps alone are
   wider than a phone and pushed the whole page sideways.) */
@media (max-width: 1023px) {
  .site-footer__inner { grid-template-columns: minmax(0, 1fr); }
  .site-footer__col { grid-column: 1 / -1; margin-bottom: var(--space-7); }
  .site-footer__strip { margin-top: var(--space-3); }
}
/* Phones: every link and toggle gets a 44px-tall touch target. The language
   links are only as wide as their word plus a little padding, so En and Fr
   stay close together; the first one's padding is pulled back so the globe
   and "En" sit on the column edge like every other row. */
@media (max-width: 767px) {
  .site-footer { --row: 2.75rem; }
  .site-footer .lang-switch ul { gap: 0; }
  .site-footer .lang-switch a { display: inline-flex; align-items: center; height: var(--row); padding-inline: var(--space-2); }
  .site-footer .lang-switch li:first-child a { margin-left: calc(var(--space-2) * -1); }
}

/* Language: both options shown, the current one in full black */
.lang-switch { display: inline-flex; align-items: center; gap: var(--space-2); margin-right: var(--space-2); }
.lang-switch > .icon { width: 16px; height: 16px; color: var(--color-text); }
.lang-switch ul { display: flex; gap: var(--space-2); }
.lang-switch a { color: var(--color-text-subtle); }
.lang-switch a[aria-current],
.lang-switch a:hover { color: var(--color-text); }

/* Dark mode and animations: tertiary toggles with a dot on the left, filled
   when on, then the name and the state */
.footer-toggle { gap: var(--space-2); padding-inline: var(--space-3); font-size: inherit; color: var(--color-text); }
.footer-toggle__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  border: 1px solid currentColor;
}
.footer-toggle[aria-pressed="true"] .footer-toggle__dot { background: currentColor; }
.footer-toggle__state { color: var(--color-text-muted); }
.no-js .footer-toggle { display: none; }

/* Project list ------------------------------------------------------------ */

.project-row { border-bottom: 1px solid var(--color-border); }
.project-list { border-top: 1px solid var(--color-border); } /* frames the list as one block */

/* Phones and tablets: thumbnail, title and chevron on the first line, the
   description under the title, so every piece of text shares one left
   edge. No hover there, so the thumbnail stays and the result is left to
   the case study's recap. */
.project-row__link {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  column-gap: var(--space-4);
  row-gap: var(--space-2);
  padding-block: clamp(1rem, 0.6rem + 1.2vw, 1.375rem);
}

/* Same pill as the home portrait */
.project-row__thumb {
  grid-row: 1;
  grid-column: 1;
  width: clamp(3.5rem, 2.5rem + 3vw, 5rem);
  aspect-ratio: 194 / 116;
  margin-top: 0.05em; /* centred on the title's first line */
  overflow: hidden;
  border-radius: var(--radius-pill);
}
/* The hover reveal runs on this inner layer: the outer pill's opacity belongs
   to the scroll reveal (GSAP sets it inline, which would win over :hover) */
.project-row__thumb-fill {
  display: block;
  height: 100%;
  background: var(--color-placeholder);
  transition: opacity var(--duration-base) var(--ease-out), clip-path var(--duration-base) var(--ease-out);
}
.project-row__thumb .picture { width: 100%; height: 100%; object-fit: cover; }
/* Locked without a public cover: a quiet placeholder, nothing to reveal */
.project-row__thumb--empty .project-row__thumb-fill { background: linear-gradient(180deg, var(--color-placeholder), var(--color-text-dimmed)); }

.project-row__title {
  grid-row: 1;
  grid-column: 2;
  font-family: var(--font-serif);
  font-size: var(--text-display-m); /* 36 → 88, 108 on wide screens */
  line-height: 0.95;
  letter-spacing: -0.005em;
  text-wrap: balance;
  transition: transform var(--duration-base) var(--ease-out);
}
.project-row__link:hover .project-row__title { transform: translateX(var(--space-3)); } /* 12px, per Figma */

.project-row__aside { grid-row: 2; grid-column: 2 / -1; }
.project-row__index { display: none; }
/* Large number, a direct child of the row (not inside the description
   block, which the scroll reveal moves): only used by layouts that show it,
   e.g. templates/home-stepped */
.project-row__number { display: none; }
/* What the project is, for someone who hasn't opened it */
.project-row__tagline {
  display: block;
  max-width: 34rem;
  font-size: var(--text-caption);
  line-height: 1.4;
  color: var(--color-text-muted);
  text-wrap: pretty;
  transition: opacity var(--duration-base) var(--ease-out);
}

/* A bare chevron instead of a button: the whole row is the link, and on
   desktop the row's hover already says so. Kept for touch screens, where
   nothing else hints that a row can be tapped. */
.project-row__chevron {
  grid-row: 1;
  grid-column: 3;
  display: inline-flex;
  margin-top: 0.3rem; /* on the title's first line */
  color: var(--color-text-subtle);
  transition: color var(--duration-base) var(--ease-out), transform var(--duration-base) var(--ease-out);
}
.project-row__chevron .icon { width: 24px; height: 24px; }
.project-row__link:is(:hover, :focus-visible) .project-row__chevron { color: var(--color-text); transform: translateX(var(--space-2)); }

.project-row__outcome { display: none; }
.project-row__value {
  font-family: var(--font-serif);
  font-size: var(--text-quote);
  line-height: 1;
  white-space: nowrap;
}
.project-row__measure {
  font-size: var(--text-caption);
  line-height: 1.3;
  color: var(--color-text-muted);
}
.project-row--locked .project-row__value { color: var(--color-text-dimmed); }

/* Wide screens: the title starts the row on the left; the number and
   description sit after it, in the last columns. On hover the thumbnail
   opens before the title (which steps aside to make room) and the result
   takes the description's place: one image, one number, never both
   descriptions and results at once. The other rows step back. */
@media (min-width: 1024px) {
  .project-row__link {
    --thumb-w: clamp(7rem, 2rem + 6vw, 10rem); /* 112 → 160 */
    grid-template-columns: repeat(12, minmax(0, 1fr));
    align-items: center;
    column-gap: var(--grid-gap);
    padding-block: clamp(1.25rem, 0.5rem + 1.2vw, 2rem);
  }
  .project-row__title { grid-column: 1 / span 8; }
  .project-row__aside {
    grid-row: 1;
    grid-column: 9 / span 3; /* same axis as the intro above */
    align-self: center;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }
  .project-row__index {
    display: block;
    font-size: var(--text-caption);
    font-variant-numeric: tabular-nums;
    color: var(--color-text-subtle);
  }
  .project-row__tagline { font-size: var(--text-caption); max-width: 17rem; }
  .project-row__chevron { grid-column: 12; justify-self: end; margin-top: 0; }
  /* Out of the flow at rest, so the title owns the left edge. grid-area:
     auto makes the whole link its containing block; with the phone grid
     placement (row 1, col 1) it was a single grid cell instead, which put
     the pill ~10px below the title's centre. */
  .project-row__thumb {
    grid-area: auto;
    position: absolute;
    margin-top: 0;
    top: 50%;
    left: 0;
    width: var(--thumb-w);
    transform: translateY(-50%);
    pointer-events: none;
  }
}
@media (hover: hover) and (min-width: 1024px) {
  .project-row { transition: opacity var(--duration-base) var(--ease-out); }
  .project-list:has(.project-row__link:is(:hover, :focus-visible)) .project-row:not(:has(.project-row__link:is(:hover, :focus-visible))) { opacity: 0.3; }

  /* Room for the step aside, so a wrapping title never runs into the
     description column */
  .project-row__title { max-width: calc(100% - var(--thumb-w) - var(--space-5)); }
  .project-row__thumb-fill { clip-path: inset(0 100% 0 0 round 999px); }
  .project-row__link:is(:hover, :focus-visible) .project-row__thumb-fill { clip-path: inset(0 0 0 0 round 999px); }
  .project-row__link:is(:hover, :focus-visible) .project-row__title { transform: translateX(calc(var(--thumb-w) + var(--space-5))); }

  /* Description out, result in, in the same place: a quick crossfade.
     Children, not the aside itself: the reveals own the aside's inline
     visibility. */
  .project-row__link:is(:hover, :focus-visible) .project-row__aside > * { opacity: 0; }
  .project-row__outcome {
    grid-row: 1;
    grid-column: 9 / span 3;
    align-self: center;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    pointer-events: none;
  }
  .project-row__outcome > * { opacity: 0; }
  .project-row__link:is(:hover, :focus-visible) .project-row__outcome > * { opacity: 1; }
  :root:not(.reduce-motion) :is(.project-row__aside, .project-row__outcome) > * {
    transition: opacity var(--duration-base) var(--ease-out);
  }
  :root:not(.reduce-motion) .project-row__outcome > * + * { transition-delay: 40ms; } /* the measure a beat after the value */
  .project-row__measure { max-width: 15rem; }
}

/* Lightbox ---------------------------------------------------------------- */

.lightbox-trigger {
  display: block;
  width: 100%;
  cursor: zoom-in;
  border-radius: var(--radius-media);
}

.has-lightbox { overflow: hidden; }

.lightbox {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: var(--space-5) var(--gutter);
  border: 0;
  background: var(--color-bg);
  color: var(--color-text);
  display: none;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: var(--space-4);
}
.lightbox[open] { display: grid; }
.lightbox::backdrop { background: transparent; }

.lightbox__close { justify-self: end; }

.lightbox__figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 0;
  gap: var(--space-3);
}
.lightbox__image {
  max-width: 100%;
  max-height: 100%;
  min-height: 0;
  object-fit: contain;
  border-radius: var(--radius-media);
  background: var(--color-placeholder);
  user-select: none;
  -webkit-user-drag: none;
}
.lightbox__caption {
  font-size: var(--text-caption);
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: center;
}
.lightbox__caption:empty { display: none; }

.lightbox__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-5);
}
.lightbox__nav[hidden] { display: none; }
.lightbox__counter {
  min-width: 4em;
  text-align: center;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}

.lightbox__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-height);
  height: var(--control-height);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  background: var(--color-bg);
  transition: border-color var(--duration-fast), background-color var(--duration-fast), color var(--duration-fast);
}
.lightbox__button:hover {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-inverse);
}

/* Project switcher (project pages) ---------------------------------------- */

.project-nav {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.project-switch { position: relative; }

/* summary renders as our tertiary button, so the default marker has to go */
.project-switch__toggle { cursor: pointer; list-style: none; }
.project-switch__toggle::-webkit-details-marker { display: none; }

/* Open reads like hover: the stroke is the affordance in both states */
.project-switch[open] > .project-switch__toggle { border-color: var(--color-border-strong); }
.project-switch[open] > .project-switch__toggle .icon { transform: rotate(180deg); }

.project-switch__menu {
  position: absolute;
  z-index: 15;
  top: calc(100% + var(--space-2));
  inset-inline-start: 0;
  min-width: max(100%, 11rem);
  padding: var(--space-2);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-media);
  text-align: start; /* the hero is centred; the menu must not inherit that */
  animation: fade-in var(--duration-base) var(--ease-out);
}

/* Number, name, then type · year under the name */
.project-switch__item {
  display: grid;
  grid-template-columns: 2em auto;
  align-items: baseline;
  column-gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-card);
  font-size: var(--text-body);
  line-height: 1.3;
  white-space: nowrap;
  color: var(--color-text-muted);
  transition: color var(--duration-fast) ease;
}
.project-switch__num { font-family: var(--font-mono); font-size: var(--text-mono); }
.project-switch__meta {
  grid-column: 2;
  margin-top: 2px;
  font-size: var(--text-caption);
  font-weight: 400;
  color: var(--color-text-muted);
}
.project-switch__item:hover { color: var(--color-text); }
.project-switch__item--current { color: var(--color-text); cursor: default; }

/* Text field ----------------------------------------------------------------
   The secondary button's pill and stroke, so a field and its submit button
   read as one control. */
.field {
  min-height: var(--control-height);
  padding-inline: var(--space-5);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  background: var(--color-bg);
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-body);
  transition: border-color var(--duration-fast) ease;
}
.field:hover,
.field:focus,
.field[aria-invalid="true"] { border-color: var(--color-text); }

/* Locked for this visitor: the title steps back, the way in (arrow) doesn't */
.project-row--locked .project-row__title {
  color: var(--color-text-dimmed);
  transition: color var(--duration-fast) ease, transform var(--duration-base) var(--ease-out);
}
.project-row--locked .project-row__link:hover .project-row__title { color: var(--color-text); }
/* "On request" in the description column: the title stays pure type, the
   status pill sits with the other small print */
.project-row__status { align-self: flex-start; margin-top: var(--space-3); }

/* Hover label (main.js) ----------------------------------------------------
   One small line saying what a link does — the page it opens, a file and its
   size, an external site, an email, enlarging an image. It follows the
   pointer; with the keyboard it sits under the focused element. It types
   itself in. The accent here is under 4.5:1 for 11px text, accepted: the
   label only repeats what the element already says, and is aria-hidden. */
.hover-label {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 900; /* under the page transition panel */
  padding: 2px 6px;
  border-radius: 2px;
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  line-height: 16px;
  font-weight: 400;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate3d(var(--x, 0), var(--y, 0), 0);
  transition: opacity var(--duration-fast) var(--ease-out);
  will-change: transform;
}
.hover-label.is-on { opacity: 1; }
