/* ==========================================================================
   federicabruschi.design — static rebuild
   Design tokens sampled from the live Framer site (computed styles).
   Tweak everything from :root; nothing below hardcodes a colour or family.
   ========================================================================== */

:root {
  /* Colour — sampled from the live site */
  --bg: #ffffff;
  --ink: #000000;
  --ink-soft: #050505;
  --grey: #616161;          /* lead paragraphs, nav links */
  --grey-mid: #8f8f8f;      /* section labels, captions */
  --grey-light: #9ba1a5;    /* metadata */
  --line: rgba(0, 0, 0, 0.08);
  --line-strong: rgba(0, 0, 0, 0.14);
  --surface: #f6f6f6;
  --accent: #6f9bd1; /* accordion +/- icons — the one non-greyscale colour on the site */

  /* Inverted footer */
  --on-dark: #ffffff;
  --on-dark-muted: rgba(255, 255, 255, 0.72);
  --on-dark-faint: rgba(255, 255, 255, 0.5);
  --line-on-dark: rgba(255, 255, 255, 0.16);
  --shadow-card: 0 24px 48px -32px rgba(0, 0, 0, 0.35);
  --shadow-lift: 0 32px 60px -28px rgba(0, 0, 0, 0.28);

  /* Type — Inter for everything, Mona Sans for the display name */
  --font-sans: "Inter", "Inter Placeholder", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Helvetica, Arial, sans-serif;
  --font-display: "Mona Sans", "Mona Sans Placeholder", var(--font-sans);

  /* Layout */
  --measure: 1120px;
  --gutter: clamp(20px, 5vw, 64px);
  --section-y: clamp(88px, 13vw, 180px);
  --radius: 12px;
  --radius-lg: 24px;

  /* Motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-expand: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-hover: 0.3s;
  --dur-reveal: 0.6s;
  --dur-expand: 0.8s;
  /* CSS has no true spring physics. These two curves stand in for the hero
     fan's real framer-motion springs — see animations.js for where each is
     used and why. */
  --ease-spring: cubic-bezier(0.32, 0.72, 0, 1);   /* stands in for bounce: 0 */
  --ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); /* stands in for bounce: 0.6, the pop-in overshoot */
}

/* --------------------------------------------------------------------------
   Reset / base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 18px;
  line-height: 1.5;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }

a { color: inherit; text-decoration: none; }

h1, h2, h3, h4, p, figure, ul, ol, blockquote { margin: 0; }

ul, ol { padding: 0; list-style: none; }

button, input, textarea { font: inherit; color: inherit; }

::selection { background: var(--ink); color: var(--bg); }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 4px;
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   Layout primitives
   -------------------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* A narrow reading column that still starts at the page gutter, so headings
   line up with the nav logo and the footer instead of floating in the middle. */
.col { max-width: 640px; }

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

.section + .section { padding-top: 0; }

/* --------------------------------------------------------------------------
   Typography
   -------------------------------------------------------------------------- */

.h-hero {
  font-size: clamp(2.5rem, 7.2vw, 5.25rem);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: -0.055em;
  text-wrap: balance;
}

.h-page {
  font-size: clamp(2rem, 5vw, 3.25rem);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.04em;
}

.h-display {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 6vw, 4.25rem);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.03em;
}

.h-sub {
  font-size: clamp(1.375rem, 2.4vw, 1.875rem);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.03em;
}

/* “✩ Selected projects” eyebrow */
.label {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--grey-mid);
  margin-bottom: 1.25rem;
}

.label::before {
  content: "✩";
  margin-right: 0.5em;
}

.lead {
  font-size: clamp(1.0625rem, 1.6vw, 1.25rem);
  line-height: 1.45;
  color: var(--grey);
  text-wrap: pretty;
}

.body-text {
  color: var(--grey);
  line-height: 1.6;
  text-wrap: pretty;
}

.body-text + .body-text { margin-top: 1.25em; }

.meta {
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--grey-mid);
}

.link-underline {
  color: var(--ink);
  border-bottom: 1px solid var(--line-strong);
  transition: border-color var(--dur-hover) ease, opacity var(--dur-hover) ease;
}

.link-underline:hover { border-color: var(--ink); }

/* Spacing utilities — keeps the markup free of inline styles. */
.mt-2 { margin-top: 1.5rem; }
.mt-3 { margin-top: 2.25rem; }
.mt-4 { margin-top: clamp(32px, 4vw, 56px); }
.mt-5 { margin-top: clamp(48px, 6vw, 88px); }

/* Bulleted lists on project pages use the site’s ◦ marker */
.dot-list { display: grid; gap: 0.875rem; }

.dot-list li {
  position: relative;
  padding-left: 1.5em;
  color: var(--grey);
  line-height: 1.55;
}

.dot-list li::before {
  content: "◦";
  position: absolute;
  left: 0;
  color: var(--grey-mid);
}

/* --------------------------------------------------------------------------
   Sticky nav
   -------------------------------------------------------------------------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--line);
}

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  min-height: 68px;
  padding-block: 0.75rem;
}

.nav__logo {
  font-size: clamp(1.125rem, 2.2vw, 1.5rem);
  font-weight: 700;
  letter-spacing: -0.045em;
  white-space: nowrap;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(0.875rem, 2.6vw, 2.25rem);
  flex-wrap: wrap;
  justify-content: flex-end;
}

.nav__link {
  font-size: clamp(0.9375rem, 1.4vw, 1.125rem);
  color: var(--grey);
  transition: color var(--dur-hover) ease;
}

.nav__link:hover,
.nav__link[aria-current="page"] { color: var(--ink); }

/* --------------------------------------------------------------------------
   Hero + fanning image carousel  (animation 1)

   Rebuilt against the real component source (Image-Animation-8D7J.js,
   fetched and read directly — see animations.js for the numbers pulled out
   of it). Two things that changeup from a first, screenshot-based guess:
   cards are SQUARE (verified against the live DOM, not just the screenshot),
   and there is no 3D/perspective anywhere in the original — every card only
   ever translates and rotates in 2D.
   -------------------------------------------------------------------------- */

.hero {
  padding-top: clamp(56px, 9vw, 120px);
  padding-bottom: clamp(48px, 7vw, 96px);
  text-align: center;
}

.hero__title { margin-inline: auto; max-width: 18ch; }

.hero__lead {
  margin: clamp(28px, 4vw, 48px) auto 0;
  max-width: 44ch;
}

.hero__cue {
  display: block;
  margin-top: clamp(28px, 4vw, 44px);
  font-size: 1.5rem;
  color: var(--grey-mid);
  animation: cue-bob 2.4s ease-in-out infinite;
}

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

/* Full-bleed: the fan runs well past the 1120px text column. --k (card size
   relative to the source's 184px canvas) is computed by animations.js, which
   picks it precisely so the widest hand-placed card offset still fits the
   viewport — so height derives from it too, rather than guessing a clamp()
   independently and risking a mismatch. */
.fan {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-top: clamp(24px, 3vw, 44px);
  height: calc(216px * var(--k, 1));
}

/* The group layer — rotates and scales the whole stack as one piece, in two
   steps: scale(1.2) rotate(-40deg) -> rotate(-14deg) -> rotate(0) scale(1).
   Both steps are driven by animations.js (it needs to swap the transition
   duration partway through — 0.4s then 1s, matching the source's two
   springs), so only the *starting* pose lives here; see .fan.is-* below. */
.fan__group {
  position: absolute;
  inset: 0;
  transform: rotate(-40deg) scale(1.2);
  transform-origin: 50% 50%;
}

.fan.is-in .fan__group {
  transform: rotate(-14deg) scale(1.2);
  transition: transform 0.4s var(--ease-spring);
}

.fan.is-group-final .fan__group {
  transform: rotate(0deg) scale(1);
  transition: transform 1s var(--ease-spring);
}

/* The positioned wrapper. No overflow/radius of its own, so a speech bubble
   can sit outside the photo's clipped box. --dx/--dy/--rot are per-card
   constants set inline in the HTML (see the comment there); --k is the
   current card-size scale factor, written by animations.js. */
.fan__item {
  position: absolute;
  left: 50%;
  top: 50%;
  /* Card size is --k times the source's 184px canvas — the same variable
     that scales --tx/--ty, so the fan's density and its spread always shrink
     together. See the layout() comment in animations.js for how --k is set. */
  width: calc(184px * var(--k, 1));
  aspect-ratio: 1 / 1; /* every card is square in the source component */
  z-index: var(--z, 1);
  /* Stacked start state: every card on top of every other, dead centre. */
  transform: translate3d(-50%, -50%, 0) rotate(0deg);
}

.fan__card {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 18px; /* the source's exact radius, at any card size */
  overflow: hidden;
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

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

/* Fanned-out end state — all six moving cards animate together, no stagger
   between them (confirmed from the source: they share one transition). */
.fan.is-expanded .fan__item {
  transition: transform 1s var(--ease-spring);
  transform:
    translate3d(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px)), 0)
    rotate(var(--rot, 0deg));
}

/* Once the fan completes, drop to a short transition so a window resize
   re-lays-out immediately instead of replaying the whole reveal. */
.fan.is-settled .fan__group,
.fan.is-settled .fan__item {
  transition: transform 0.35s var(--ease-out) !important;
}

/* ---- Speech bubbles ------------------------------------------------------
   Two pill tags, each tethered to one card, matching the source's "Pin 1" /
   "Pin 2" — scale(0) -> scale(1) with a bouncy spring, each with its OWN
   fixed delay (0.4s / 0.5s after the fan starts expanding) baked directly
   into the transition below, exactly like the source's transition4/5.
   --------------------------------------------------------------------------- */

.fan__bubble {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 22px * var(--k, 1));
  z-index: 20;
  padding: 0.5em 0.9em;
  border-radius: calc(15px * var(--k, 1));
  background: #e9e9eb;
  color: #050505; /* the source's Pin 1 text colour, rgb(5,5,5) */
  /* 18px is the source's own text size at its 184px canvas (k=1); scaling by
     --k keeps it in lockstep with the card instead of shrinking on its own
     schedule, which on narrow viewports let the bubble outgrow its card and
     overhang past the fan's fitted footprint. The 11px floor is a pragmatic
     legibility guard the source doesn't need — it isn't drawn on a phone. */
  font-size: max(11px, calc(18px * var(--k, 1)));
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.01em;
  white-space: nowrap;
  transform:
    translateX(calc(-50% + var(--bubble-x, 0%)))
    rotate(calc(-1 * var(--rot, 0deg)))
    scale(0);
  transition: transform 1s var(--ease-bounce) var(--bubble-delay, 0.4s);
}

.fan__bubble[data-tone="blue"] {
  background: rgb(0, 128, 255); /* the source's Pin 2 exact blue */
  color: #ffffff;
}

/* The tail. */
.fan__bubble::after {
  content: "";
  position: absolute;
  top: 100%;
  left: var(--tail-x, 50%);
  width: 0.85em;
  height: 0.6em;
  margin-top: -1px;
  background: inherit;
  clip-path: polygon(0 0, 100% 0, 22% 100%);
  transform: translateX(-50%);
}

.fan.is-expanded .fan__bubble {
  transform:
    translateX(calc(-50% + var(--bubble-x, 0%)))
    rotate(calc(-1 * var(--rot, 0deg)))
    scale(1);
}

/* No-JS fallback: without animations.js, --k/--tx/--ty/--rot are never set,
   so the fan would otherwise sit frozen at its "Out" pose forever — a single
   rotated square hiding the other six photos underneath it. Render the
   expanded layout directly instead, at a fixed, non-responsive k=1 (184px
   cards; the six offsets are the same hand-placed values animations.js uses,
   just written as literals here since there's no script to compute them).
   This can't adapt to viewport width the way the animated version does, so
   on a very narrow screen its outer edges may crop — still far better than
   the six-sevenths of the fan a script-free visitor would otherwise lose. */
.no-js .fan { height: 216px; }
.no-js .fan__group { transform: none; }
.no-js .fan__item { width: 184px; }
.no-js [data-fan-role="window"]   { transform: translate3d(calc(-50% + -334px), calc(-50% + 16px), 0) rotate(-7deg); }
.no-js [data-fan-role="baking"]   { transform: translate3d(calc(-50% + -217px), calc(-50% + -12px), 0) rotate(-8deg); }
.no-js [data-fan-role="workshop"] { transform: translate3d(calc(-50% + -108px), calc(-50% + 0px), 0) rotate(-5deg); }
.no-js [data-fan-role="surfing"]  { transform: translate3d(calc(-50% + 100px), calc(-50% + 0px), 0) rotate(6deg); }
.no-js [data-fan-role="taj"]      { transform: translate3d(calc(-50% + 201px), calc(-50% + 7px), 0) rotate(7deg); }
.no-js [data-fan-role="scuba"]    { transform: translate3d(calc(-50% + 306px), calc(-50% + 8px), 0) rotate(14deg); }
.no-js .fan__bubble {
  transform: translateX(calc(-50% + var(--bubble-x, 0%))) scale(1);
}

/* --rot is inherited from the card's own inline style, which no-JS never
   sets, so the bubbles' counter-rotation needs the same two literals too —
   otherwise they'd tilt along with their card instead of staying level. */
.no-js [data-fan-role="baking"] .fan__bubble { transform: translateX(calc(-50% + var(--bubble-x, 0%))) rotate(8deg) scale(1); }
.no-js [data-fan-role="scuba"] .fan__bubble { transform: translateX(calc(-50% + var(--bubble-x, 0%))) rotate(-14deg) scale(1); }

/* --------------------------------------------------------------------------
   Project cards (animations 3 + 4)
   .reveal lives on the wrapper, the lift lives on the <a> — so the two
   transforms never fight each other.
   -------------------------------------------------------------------------- */

.card-grid {
  display: grid;
  gap: clamp(40px, 6vw, 80px);
}

.card {
  display: grid;
  gap: clamp(14px, 1.6vw, 20px);
  transition:
    transform var(--dur-hover) ease,
    box-shadow var(--dur-hover) ease;
  will-change: transform;
}

.card:hover,
.card:focus-visible { transform: translateY(-4px); }

.card__media {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 1px 0 var(--line);
}

.card__media img {
  width: 100%;
  height: auto;
  transition: transform 0.6s var(--ease-out);
}

.card:hover .card__media img { transform: scale(1.015); }

.card__title {
  font-size: clamp(1.125rem, 1.8vw, 1.5rem);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.025em;
  color: var(--ink);
  text-wrap: balance;
}

.card__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}

.card__cta {
  font-size: 0.875rem;
  color: var(--grey-mid);
  transition: color var(--dur-hover) ease, transform var(--dur-hover) ease;
  white-space: nowrap;
}

.card:hover .card__cta { color: var(--ink); transform: translateX(3px); }

/* --------------------------------------------------------------------------
   Scroll reveal (animation 3)
   -------------------------------------------------------------------------- */

.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity var(--dur-reveal) ease,
    transform var(--dur-reveal) var(--ease-out);
  transition-delay: var(--reveal-delay, 0s);
}

.reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Nothing is hidden when JS never runs. */
.no-js .reveal { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   Expanding reveal on project detail pages (animation 2)
   height: 0 -> measured content height over 0.8s, then released to auto.
   -------------------------------------------------------------------------- */

[data-expand] { overflow: clip; }

[data-expand].is-collapsed {
  height: 0;
  overflow: hidden;
}

[data-expand].is-expanding {
  transition: height var(--dur-expand) var(--ease-expand);
  overflow: hidden;
}

[data-expand].is-open {
  height: auto;
  overflow: visible;
}

.no-js [data-expand] { height: auto; overflow: visible; }

/* --------------------------------------------------------------------------
   Split layout: a narrow intro column + a wider content column. Shared by
   Selected Projects (intro + card list) and About (intro + accordion) — both
   use this one class so the two sections stay visually consistent by
   construction rather than by two separately-tuned copies of the same ratio.
   -------------------------------------------------------------------------- */

.split {
  display: grid;
  gap: clamp(32px, 5vw, 64px);
  grid-template-columns: 1fr;
}

@media (min-width: 860px) {
  /* Right column is deliberately well past 50%: the accordion rows need room
     for role, place, date and icon on one line without wrapping, and this
     keeps the project cards' column the same width for both sections. */
  .split { grid-template-columns: minmax(0, 3fr) minmax(0, 5fr); align-items: start; }
}

.split__intro { align-self: start; }

/* A plain text link, not a button — sits directly under the paragraph. */
.about__cta {
  display: inline-block;
  color: var(--grey-mid);
  font-weight: 500;
  border-bottom: 1px solid transparent;
  transition: color var(--dur-hover) ease, border-color var(--dur-hover) ease;
}

.about__cta:hover {
  color: var(--ink);
  border-bottom-color: var(--line-strong);
}

/* ---- Accordion ------------------------------------------------------------
   One item open at a time. The first ships open in the markup itself
   (.is-open + aria-expanded="true"), so the correct state exists even with
   CSS/JS disabled — animate.js only adds the open/close transition on top.
   --------------------------------------------------------------------------- */

.accordion { border-top: 1px solid var(--line); }

.accordion__item { border-bottom: 1px solid var(--line); }

.accordion__trigger {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: clamp(12px, 2vw, 24px);
  width: 100%;
  padding-block: clamp(20px, 2.6vw, 28px);
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.accordion__head { min-width: 0; }

.accordion__role {
  display: block;
  font-weight: 500;
  letter-spacing: -0.02em;
}

.accordion__where {
  display: block;
  margin-top: 0.25rem;
  color: var(--grey-mid);
  font-size: 0.9375rem;
}

.accordion__right {
  display: flex;
  align-items: center;
  gap: clamp(12px, 2vw, 24px);
  flex-shrink: 0;
  padding-top: 0.15rem;
}

.accordion__date {
  color: var(--grey-mid);
  font-size: 0.875rem;
  white-space: nowrap;
}

/* Plus/minus, drawn with two bars rather than a glyph so it can be recoloured
   and animated with the same transform the rest of the site uses. */
.accordion__icon {
  position: relative;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.accordion__icon::before,
.accordion__icon::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--accent);
  border-radius: 1px;
}

.accordion__icon::before {
  width: 100%;
  height: 2px;
  transform: translate(-50%, -50%);
}

.accordion__icon::after {
  width: 2px;
  height: 100%;
  transform: translate(-50%, -50%);
  transition: transform 0.25s ease, opacity 0.25s ease;
}

/* Open state: the vertical bar retracts, leaving a minus sign. */
.accordion__item.is-open .accordion__icon::after {
  transform: translate(-50%, -50%) scaleY(0);
  opacity: 0;
}

.accordion__panel {
  height: 0;
  overflow: hidden;
}

.accordion__item.is-open .accordion__panel {
  height: auto;
  overflow: visible;
}

.accordion__body {
  color: var(--grey);
  line-height: 1.6;
  font-size: 1rem;
  padding-bottom: clamp(20px, 2.6vw, 28px);
  max-width: 62ch;
}

.no-js .accordion__panel { height: auto; overflow: visible; }

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.875rem 1.5rem;
  border: 1px solid var(--ink);
  border-radius: 999px;
  background: var(--ink);
  color: var(--bg);
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition:
    transform var(--dur-hover) ease,
    background-color var(--dur-hover) ease,
    color var(--dur-hover) ease;
}

.btn:hover { transform: translateY(-2px); }

.btn:disabled { opacity: 0.5; cursor: progress; transform: none; }

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}

.btn--ghost:hover { border-color: var(--ink); }

/* --------------------------------------------------------------------------
   Contact form
   -------------------------------------------------------------------------- */

.form {
  display: grid;
  gap: clamp(20px, 2.5vw, 28px);
  margin-top: clamp(40px, 5vw, 64px);
}

.field { display: grid; gap: 0.625rem; }

.field__label {
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.field input,
.field textarea {
  width: 100%;
  padding: 0.875rem 1rem;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-size: 1rem;
  transition: border-color var(--dur-hover) ease, box-shadow var(--dur-hover) ease;
}

.field input::placeholder,
.field textarea::placeholder { color: var(--grey-light); }

.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.06);
}

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

.form__actions { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }

.form__status {
  font-size: 0.9375rem;
  color: var(--grey);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--dur-reveal) ease, transform var(--dur-reveal) var(--ease-out);
}

.form__status.is-shown { opacity: 1; transform: none; }

.form__status[data-state="error"] { color: #b3261e; }

/* Honeypot — must stay in the DOM, must never be reachable. */
.hp { position: absolute; left: -9999px; opacity: 0; pointer-events: none; }

/* --------------------------------------------------------------------------
   Project detail pages
   -------------------------------------------------------------------------- */

.project-hero { padding-top: clamp(48px, 7vw, 88px); }

.project-hero__title {
  max-width: 24ch;
  margin-top: 1.25rem;
}

.project-hero__lead { max-width: 58ch; margin-top: clamp(20px, 2.5vw, 32px); }

.project-hero__banner {
  margin-top: clamp(36px, 5vw, 64px);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}

.project-meta {
  display: grid;
  gap: clamp(24px, 4vw, 56px);
  margin-top: clamp(48px, 6vw, 80px);
  padding-top: clamp(32px, 4vw, 48px);
  border-top: 1px solid var(--line);
}

@media (min-width: 760px) {
  .project-meta { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.block { padding-block: clamp(40px, 6vw, 72px); }

.block__inner { max-width: 720px; }

.figure { margin-top: clamp(28px, 4vw, 44px); }

.figure img {
  width: 100%;
  border-radius: var(--radius);
  background: var(--surface);
}

.figure figcaption {
  margin-top: 0.75rem;
  font-size: 0.875rem;
  color: var(--grey-mid);
}

.stat {
  font-size: clamp(1.5rem, 3.2vw, 2.25rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1.15;
  text-wrap: balance;
}

.next {
  display: grid;
  gap: clamp(28px, 4vw, 48px);
  padding-top: clamp(32px, 4vw, 48px);
  border-top: 1px solid var(--line);
}

@media (min-width: 760px) {
  .next { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

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

.footer {
  padding-block: clamp(72px, 10vw, 140px);
  background: var(--ink);
  color: var(--on-dark);
}

/* Everything inside the footer flips to the inverted palette. */
.footer .lead,
.footer .nav__link { color: var(--on-dark-muted); }

.footer .nav__link:hover { color: var(--on-dark); }

.footer .link-underline {
  color: var(--on-dark);
  border-bottom-color: var(--line-on-dark);
}

.footer .link-underline:hover { border-bottom-color: var(--on-dark); }

.footer ::selection { background: var(--on-dark); color: var(--ink); }

.footer__grid {
  display: grid;
  gap: clamp(28px, 4vw, 48px);
}

@media (min-width: 760px) {
  .footer__grid { grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
}

.footer__title {
  font-size: clamp(1.75rem, 4vw, 3rem);
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1.08;
}

.footer__mail {
  display: inline-block;
  margin-top: 1.25rem;
  font-size: clamp(1.0625rem, 1.8vw, 1.375rem);
  letter-spacing: -0.02em;
}

.footer__links { display: flex; gap: 1.5rem; flex-wrap: wrap; }

.footer__small {
  margin-top: clamp(48px, 6vw, 80px);
  padding-top: 1.5rem;
  border-top: 1px solid var(--line-on-dark);
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  font-size: 0.8125rem;
  color: var(--on-dark-faint);
}

/* --------------------------------------------------------------------------
   NFC landing page — shares the same footer/.section/.wrap and type as
   every other page (so it doesn't look like a separate site), but
   deliberately has no top nav — see nfc.html for why. The centred card of
   large action links, plus the password block under the last one, is
   page-specific.
   -------------------------------------------------------------------------- */

.nfc__card {
  width: 100%;
  max-width: 380px;
  margin-inline: auto; /* centres the card within the standard .wrap */
  text-align: center;
}

.nfc__links {
  display: grid;
  gap: 1rem;
  margin-top: clamp(40px, 8vw, 56px);
}

.nfc__link {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  padding: 1.125rem 1.5rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font-size: 1.125rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  transition:
    transform var(--dur-hover) ease,
    border-color var(--dur-hover) ease,
    background-color var(--dur-hover) ease;
}

.nfc__link:hover,
.nfc__link:focus-visible {
  border-color: var(--ink);
  background: var(--surface);
  transform: translateY(-2px);
}

.nfc__arrow {
  transition: transform var(--dur-hover) ease;
}

.nfc__link:hover .nfc__arrow { transform: translateX(3px); }

.nfc__copy:hover { border-color: var(--ink); }

.nfc__copy:disabled {
  color: var(--grey-mid);
  cursor: default;
}

/* --------------------------------------------------------------------------
   Reduced motion — jump straight to every end state.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }

  .reveal { opacity: 1; transform: none; }
  .hero__cue { animation: none; }
  [data-expand] { height: auto !important; overflow: visible !important; }
}
