/* ==========================================================================
   Mara Vellante - painter portfolio
   Design language: exhibition catalogue meets a premium studio site. The
   work stays the centre of attention; the interface around it is soft,
   quiet and responsive to touch.

   Locked decisions (do not mix systems when editing):
   - Corner radius: one soft scale everywhere (--r-sm/md/lg/pill below).
     The one deliberate exception is full-bleed elements (.bleed, .cta),
     which span the viewport edge to edge and are never rounded, since
     there is no edge to round against.
   - Accent: one only, ultramarine, the pigment in the paintings.
   - Type: Archivo for everything structural, JetBrains Mono for wall labels.
   - Theme: light and dark, both first class, follows the system by default.
   - Motion: --ease (smooth, no overshoot) for reveals and content entering
     the page. --ease-spring (slight overshoot) for direct interactive
     feedback: buttons, the theme toggle, tilt, the nav indicator.
   - Anything with a background fill or a full border gets the radius
     scale. Hairline-divided typography (the record, the footer, process
     steps) stays unrounded on purpose, its treatment is the divider.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Neutrals are cool, never cream. */
  --bg: #f1f2f3;
  --bg-raised: #ffffff;
  --bg-sunk: #e6e8ea;
  --ink: #101114;
  --ink-soft: #5d6067;
  --rule: #d5d8dc;
  --rule-strong: #b9bdc3;

  /* Ultramarine. Flat fills only, never a gradient or a glow. */
  --accent: #1b2fd8;
  --accent-fill: #1b2fd8;
  --accent-on-fill: #ffffff;
  --accent-hairline: rgba(27, 47, 216, 0.28);

  /* Semantic, not a second brand colour. Form errors only. Blue errors sat
     too close to the blue focus ring to read as "wrong". */
  --danger: #b3261e;

  --shadow-tint: rgba(16, 17, 20, 0.1);
  --shadow-sm: 0 1px 2px var(--shadow-tint), 0 10px 24px -14px var(--shadow-tint);
  --shadow-md: 0 4px 10px -2px var(--shadow-tint), 0 24px 48px -20px var(--shadow-tint);
  --shadow-lg: 0 8px 16px -4px var(--shadow-tint), 0 40px 80px -28px var(--shadow-tint);

  /* Radius scale. One system, four steps, used consistently everywhere a
     shape has a fill or a border (see header note). */
  --r-sm: 14px;
  --r-md: 22px;
  --r-lg: 32px;
  --r-pill: 999px;

  /* Type */
  --font-sans: "Archivo", ui-sans-serif, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  --step--2: clamp(0.68rem, 0.66rem + 0.08vw, 0.73rem);
  --step--1: clamp(0.78rem, 0.755rem + 0.11vw, 0.855rem);
  --step-0: clamp(0.95rem, 0.92rem + 0.15vw, 1.06rem);
  --step-1: clamp(1.12rem, 1.03rem + 0.42vw, 1.4rem);
  --step-2: clamp(1.45rem, 1.24rem + 0.98vw, 2.1rem);
  --step-3: clamp(1.95rem, 1.52rem + 2.05vw, 3.35rem);
  --step-4: clamp(2.55rem, 1.62rem + 4.3vw, 5.4rem);

  /* Space */
  --gutter: clamp(1.15rem, 0.6rem + 2.4vw, 3.75rem);
  --section: clamp(4.5rem, 2.4rem + 8.4vw, 9.5rem);
  --maxw: 1500px;
  --measure: 62ch;

  --nav-h: 68px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0c0d0f;
    --bg-raised: #141519;
    --bg-sunk: #08090b;
    --ink: #ecedef;
    --ink-soft: #8e9198;
    --rule: #23252a;
    --rule-strong: #363940;

    --accent: #6e80ff;
    --accent-fill: #2036c9;
    --accent-on-fill: #ffffff;
    --accent-hairline: rgba(110, 128, 255, 0.32);
    --danger: #ff8a85;

    --shadow-tint: rgba(0, 0, 0, 0.5);
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --bg: #0c0d0f;
  --bg-raised: #141519;
  --bg-sunk: #08090b;
  --ink: #ecedef;
  --ink-soft: #8e9198;
  --rule: #23252a;
  --rule-strong: #363940;

  --accent: #6e80ff;
  --accent-fill: #2036c9;
  --accent-on-fill: #ffffff;
  --accent-hairline: rgba(110, 128, 255, 0.32);
  --danger: #ff8a85;

  --shadow-tint: rgba(0, 0, 0, 0.5);
  color-scheme: dark;
}

/* Cross-document view transitions between pages, where supported. Purely
   progressive: unsupported browsers just navigate normally. Gated on
   reduced-motion so no transition object is created at all when the
   visitor has asked for less motion, which is the correct way to make
   this feature respect that preference rather than trying to override the
   browser's own crossfade after the fact. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
}

/* --------------------------------------------------------------------------
   2. 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: var(--step-0);
  font-weight: 400;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  margin: 0;
  font-weight: 500;
  letter-spacing: -0.028em;
  line-height: 1.06;
  text-wrap: balance;
}

p { margin: 0; text-wrap: pretty; }
img { display: block; max-width: 100%; height: auto; }
ul, ol { margin: 0; padding: 0; list-style: none; }

a {
  color: inherit;
  text-decoration: none;
  transition: color 0.25s var(--ease);
}

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

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

::selection {
  background: var(--accent-fill);
  color: var(--accent-on-fill);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  padding: 0.75rem 1.25rem;
  background: var(--accent-fill);
  color: var(--accent-on-fill);
  border-radius: var(--r-sm);
}
.skip-link:focus { left: 0; }

/* --------------------------------------------------------------------------
   3. Layout primitives
   -------------------------------------------------------------------------- */

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

.section { padding-block: var(--section); }
.section--tight { padding-block: calc(var(--section) * 0.6); }

.rule {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: 0;
}

/* Section heading: headline stacked over its own body text, never a
   headline on the left with a small paragraph floating top right. */
.s-head { margin-bottom: clamp(2.25rem, 1.4rem + 3vw, 4rem); }

.s-head h2 {
  font-size: var(--step-3);
  max-width: 22ch;
}

.s-head p {
  margin-top: 1.1rem;
  max-width: 52ch;
  color: var(--ink-soft);
  font-size: var(--step-1);
  line-height: 1.5;
  letter-spacing: -0.012em;
}

.s-head--row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem 2rem;
}

.eyebrow {
  display: flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.status-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: var(--r-pill);
  background: var(--accent);
  margin-right: 0.6em;
}

/* --------------------------------------------------------------------------
   4. Wall label - the metadata voice of the whole site
   -------------------------------------------------------------------------- */

.label {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.65;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  font-weight: 400;
}

.label strong {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--step-1);
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--ink);
  line-height: 1.2;
  margin-bottom: 0.45rem;
  transition: color 0.3s var(--ease);
}

.label .avail { color: var(--accent); }

/* --------------------------------------------------------------------------
   5. Buttons and links
   -------------------------------------------------------------------------- */

.btn {
  --btn-bg: var(--accent-fill);
  --btn-fg: var(--accent-on-fill);
  --btn-bd: var(--accent-fill);
  --mx: 50%;
  --my: 50%;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.9rem 1.6rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid var(--btn-bd);
  border-radius: var(--r-pill);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform 0.4s var(--ease-spring), box-shadow 0.35s var(--ease),
    background-color 0.25s var(--ease), color 0.25s var(--ease), border-color 0.25s var(--ease);
}

/* A soft light that tracks the pointer, set via --mx/--my in site.js.
   Sits behind the label (z-index -1 inside the button's own isolated
   stacking context) so it never has to fight the text for legibility. */
.btn::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(160px circle at var(--mx) var(--my),
    color-mix(in srgb, var(--btn-fg) 24%, transparent), transparent 72%);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
}
.btn:hover::after,
.btn:focus-visible::after { opacity: 1; }

.btn svg { width: 1.05em; height: 1.05em; flex: none; }
.btn:hover { box-shadow: var(--shadow-md); }
.btn:focus-visible { box-shadow: var(--shadow-md); transform: translateY(-3px); }
.btn:active { transform: scale(0.96); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-bd: var(--rule-strong);
  box-shadow: none;
}
.btn--ghost:hover {
  --btn-bd: var(--ink);
  box-shadow: var(--shadow-sm);
}

/* Text link with an arrow that steps forward on hover. */
.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.01em;
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 0.35rem;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}
.arrow-link svg {
  width: 1.05em;
  height: 1.05em;
  transition: transform 0.4s var(--ease-spring);
}
.arrow-link:hover { color: var(--accent); border-bottom-color: var(--accent); }
.arrow-link:hover svg { transform: translateX(5px); }

/* --------------------------------------------------------------------------
   6. Navigation - a floating pill, inset from the page edges
   -------------------------------------------------------------------------- */

.nav-sentinel { position: absolute; top: 0; height: 1px; width: 100%; }

/* pointer-events:none on the wrapper so the visible gutter on either side
   of the pill stays click-through to whatever sits underneath it. */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  padding-block: clamp(0.85rem, 0.6rem + 0.9vw, 1.35rem);
  pointer-events: none;
  transition: transform 0.5s var(--ease), opacity 0.4s var(--ease);
}

/* Set by site.js on scroll direction: up shows it, down past a small
   threshold hides it. Sticky positioning is untouched (transform is a
   paint-time effect only), so the page never reflows when this toggles. */
.nav--hidden {
  transform: translateY(-130%);
  opacity: 0;
}
.nav--hidden .nav__inner { pointer-events: none; }

.nav__inner {
  pointer-events: auto;
  width: 100%;
  max-width: 1180px;
  margin-inline: auto;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-inline: clamp(0.9rem, 0.6rem + 1vw, 1.5rem);
  border-radius: var(--r-pill);
  border: 1px solid var(--rule);
  background: color-mix(in srgb, var(--bg-raised) 78%, transparent);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.4s var(--ease), background-color 0.4s var(--ease);
}

.nav[data-stuck="true"] .nav__inner {
  box-shadow: var(--shadow-md);
  background: color-mix(in srgb, var(--bg-raised) 92%, transparent);
}

@supports not (backdrop-filter: blur(1px)) {
  .nav__inner { background: var(--bg-raised); }
}
@media (prefers-reduced-transparency: reduce) {
  .nav__inner {
    background: var(--bg-raised);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

.wordmark {
  font-size: var(--step-0);
  font-weight: 500;
  letter-spacing: -0.02em;
  white-space: nowrap;
}

.nav__links {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(0.1rem, 0.05rem + 0.3vw, 0.35rem);
}

/* Positioned and sized by site.js to sit behind whichever link has focus
   or the pointer, then rests under the current page on the way out. */
.nav__indicator {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 0;
  width: 0;
  border-radius: var(--r-pill);
  background: var(--bg-sunk);
  opacity: 0;
  z-index: 0;
  pointer-events: none;
  transition: left 0.5s var(--ease-spring), width 0.5s var(--ease-spring), opacity 0.3s var(--ease);
}

.nav__links a {
  position: relative;
  z-index: 1;
  padding: 0.5rem 0.95rem;
  border-radius: var(--r-pill);
  font-size: var(--step--1);
  color: var(--ink-soft);
  transition: color 0.25s var(--ease);
}
.nav__links a:hover { color: var(--ink); }
.nav__links a[aria-current="page"] { color: var(--ink); }

.nav__end {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.theme-toggle {
  position: relative;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  background: none;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  color: var(--ink-soft);
  cursor: pointer;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease), box-shadow 0.3s var(--ease);
}
.theme-toggle:hover { color: var(--ink); border-color: var(--rule-strong); box-shadow: var(--shadow-sm); }

/* Both icons always in the DOM, stacked and crossfaded with a slight
   overshoot rather than swapped with display:none, which cannot animate. */
.theme-toggle svg {
  position: absolute;
  width: 16px;
  height: 16px;
  transition: opacity 0.4s var(--ease), transform 0.5s var(--ease-spring);
}
.theme-toggle .i-sun { opacity: 1; transform: rotate(0deg) scale(1); }
.theme-toggle .i-moon { opacity: 0; transform: rotate(-70deg) scale(0.5); }
:root[data-theme="dark"] .theme-toggle .i-sun { opacity: 0; transform: rotate(70deg) scale(0.5); }
:root[data-theme="dark"] .theme-toggle .i-moon { opacity: 1; transform: rotate(0deg) scale(1); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .i-sun { opacity: 0; transform: rotate(70deg) scale(0.5); }
  :root:not([data-theme="light"]) .theme-toggle .i-moon { opacity: 1; transform: rotate(0deg) scale(1); }
}

.nav .btn { padding: 0.6rem 1.15rem; box-shadow: none; }

@media (max-width: 640px) {
  .nav__end .btn { display: none; }
  .nav__links { gap: 0.05rem; }
  .nav__inner { padding-inline: 0.85rem; }
}

/* --------------------------------------------------------------------------
   7. Hero - asymmetric split, the painting takes the taller half
   -------------------------------------------------------------------------- */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  gap: clamp(2rem, 1rem + 4vw, 5rem);
  align-items: center;
  /* Kept tight so the headline and both buttons clear the fold without the
     content drifting to the middle of an empty viewport. */
  padding-top: clamp(1.75rem, 1rem + 2vw, 3rem);
  padding-bottom: clamp(3rem, 2rem + 4vw, 6rem);
  min-height: calc(100dvh - var(--nav-h));
}

.hero__text { min-width: 0; }

.hero h1 {
  /* Capped below --step-4 so a seven word headline still lands on two
     lines inside the narrower half of the split. */
  font-size: clamp(2.4rem, 1.45rem + 3.5vw, 4.4rem);
  letter-spacing: -0.04em;
  line-height: 1.02;
  max-width: 20ch;
  margin-block: 1.35rem 1.5rem;
}

.hero__lede {
  color: var(--ink-soft);
  font-size: var(--step-1);
  line-height: 1.5;
  letter-spacing: -0.012em;
  max-width: 34ch;
}

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: clamp(1.75rem, 1rem + 2vw, 2.75rem);
}

.hero__art {
  position: relative;
  align-self: stretch;
  min-height: min(64vh, 640px);
  background: var(--bg-sunk);
  overflow: hidden;
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
}

.hero__art img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A quiet drift as the hero scrolls past, entirely native: no scroll
   listener anywhere. The 8% scale is a permanent buffer so the vertical
   drift never uncovers an edge. Unsupported browsers just see the still
   image, which is a perfectly fine fallback. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .hero__art img {
      animation: hero-parallax linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 60%;
    }
  }
}
@keyframes hero-parallax {
  from { transform: scale(1.08) translateY(-3%); }
  to   { transform: scale(1.08) translateY(2%); }
}

.hero__art figcaption {
  position: absolute;
  left: 1rem;
  bottom: 1rem;
  padding: 0.55rem 1.05rem;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--bg-raised) 88%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: var(--shadow-sm);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.05em;
  color: var(--ink-soft);
}
@supports not (backdrop-filter: blur(1px)) {
  .hero__art figcaption { background: var(--bg-raised); }
}

@media (max-width: 900px) {
  .hero {
    grid-template-columns: 1fr;
    min-height: 0;
    gap: 2.5rem;
  }
  .hero__text { max-width: none; }
  .hero__art { min-height: 62vh; }
}

/* --------------------------------------------------------------------------
   7b. Marquee - one continuous strip, connective texture between the hero
   and the work below. The only marquee on the page. Pauses on hover and
   stops entirely under reduced motion, which also drops the duplicated
   half of the track since it exists purely to make the loop seamless.
   -------------------------------------------------------------------------- */

.marquee {
  border-block: 1px solid var(--rule);
  overflow: hidden;
}
.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee-scroll 34s linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }

.marquee__track span {
  display: inline-flex;
  align-items: center;
  padding-block: 1.1rem;
  padding-inline: clamp(1.5rem, 1rem + 2vw, 3rem);
  font-family: var(--font-mono);
  font-size: var(--step-1);
  letter-spacing: -0.01em;
  color: var(--ink-soft);
  white-space: nowrap;
}
.marquee__track span:nth-child(odd) { color: var(--ink); }

@keyframes marquee-scroll {
  to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .marquee__track { animation: none; }
  .marquee__track > span:nth-child(n+7) { display: none; }
}

/* --------------------------------------------------------------------------
   8. Work grid - asymmetric, rows offset, aspect ratio varies by orientation
   -------------------------------------------------------------------------- */

.work-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(2rem, 1rem + 3vw, 4.5rem) var(--gutter);
}

.work-card { display: block; grid-column: span 6; }

/* Offsets give the grid its rhythm at desktop only. */
.work-card:nth-child(1) { grid-column: 1 / span 5; }
.work-card:nth-child(2) { grid-column: 7 / span 6; margin-top: clamp(2rem, 6vw, 6rem); }
.work-card:nth-child(3) { grid-column: 2 / span 6; }
.work-card:nth-child(4) { grid-column: 9 / span 4; margin-top: clamp(1rem, 3vw, 3rem); }

.work-card__frame {
  position: relative;
  overflow: hidden;
  background: var(--bg-sunk);
  margin-bottom: 1.1rem;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  /* Pointer tilt is written here directly by site.js (mouse only); the
     transition makes it lag gently behind the cursor instead of snapping,
     and eases it back to flat on the way out. */
  transition: transform 0.35s var(--ease-spring), box-shadow 0.35s var(--ease);
}
.work-card:hover .work-card__frame { box-shadow: var(--shadow-md); }

.work-card__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.7s var(--ease), filter 0.5s var(--ease);
}

.work-card:hover .work-card__frame img { transform: scale(1.035); }

.work-card__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
}

.work-card__meta .year {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink-soft);
  flex: none;
}

.work-card:hover .label strong { color: var(--accent); }

.ar-v { aspect-ratio: 4 / 5; }
.ar-h { aspect-ratio: 4 / 3; }

@media (max-width: 820px) {
  .work-grid { gap: 3rem 1.25rem; }
  .work-card,
  .work-card:nth-child(1),
  .work-card:nth-child(2),
  .work-card:nth-child(3),
  .work-card:nth-child(4) {
    grid-column: 1 / -1;
    margin-top: 0;
  }
}

/* --------------------------------------------------------------------------
   9. Bento - exactly three cells, three different treatments
   -------------------------------------------------------------------------- */

.bento {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gutter);
  /* Natural heights, so the three cells step against each other instead of
     the short ones stretching to match the tall photograph. */
  align-items: start;
}

.bento__cell { min-width: 0; }
.bento__cell--photo { grid-column: span 5; }
.bento__cell--quote { grid-column: span 4; }
.bento__cell--list { grid-column: span 3; }

.bento__cell--photo figure { margin: 0; }
.bento__cell--photo img {
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  background: var(--bg-sunk);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
}
.bento__cell--photo figcaption {
  margin-top: 0.9rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink-soft);
}

.bento__cell--quote {
  background: var(--accent-fill);
  color: var(--accent-on-fill);
  padding: clamp(1.75rem, 1rem + 2vw, 2.75rem);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2rem;
}

.bento__cell--quote blockquote {
  margin: 0;
  font-size: var(--step-2);
  line-height: 1.28;
  letter-spacing: -0.026em;
  font-weight: 500;
}

.bento__cite {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.6;
  color: color-mix(in srgb, var(--accent-on-fill) 78%, transparent);
}
.bento__cite b { display: block; font-weight: 500; color: var(--accent-on-fill); }

.bento__cell--list {
  border-top: 1px solid var(--ink);
  padding-top: 1.1rem;
}
.bento__cell--list h3 {
  font-size: var(--step-0);
  font-weight: 500;
  margin-bottom: 1.4rem;
}
.bento__cell--list li {
  padding-block: 0.85rem;
  border-bottom: 1px solid var(--rule);
  font-size: var(--step--1);
  color: var(--ink-soft);
}
.bento__cell--list li span {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--rule-strong);
  margin-top: 0.2rem;
}

@media (max-width: 980px) {
  .bento__cell--photo { grid-column: span 12; }
  .bento__cell--quote { grid-column: span 7; }
  .bento__cell--list { grid-column: span 5; }
  .bento__cell--photo img { aspect-ratio: 16 / 9; }
}
@media (max-width: 680px) {
  .bento__cell--quote,
  .bento__cell--list { grid-column: span 12; }
}

/* --------------------------------------------------------------------------
   10. Statement - full bleed image, then a single column of text
   -------------------------------------------------------------------------- */

/* Used only as a direct child of a full width section, so it needs no
   viewport-unit escape hatch. Never rounded: see the header note on the
   full-bleed exception. */
.bleed { width: 100%; }

.bleed img {
  width: 100%;
  height: clamp(280px, 46vh, 620px);
  object-fit: cover;
  background: var(--bg-sunk);
}

.statement {
  max-width: var(--measure);
  margin-inline: auto;
  margin-top: clamp(2.5rem, 1.5rem + 3vw, 4.5rem);
  text-align: center;
}

.statement p {
  font-size: var(--step-1);
  line-height: 1.6;
  letter-spacing: -0.012em;
  color: var(--ink-soft);
}
.statement p + p { margin-top: 1.3rem; }
.statement p:first-child { color: var(--ink); }

/* --------------------------------------------------------------------------
   11. Practice - asymmetric three-up, unequal widths and unequal image
   shapes so it never reads as the banned "three identical cards" row, but
   fits inside the page like everything else instead of requiring a scroll.
   -------------------------------------------------------------------------- */

.track {
  display: grid;
  grid-template-columns: 1.15fr 1fr 0.95fr;
  gap: var(--gutter);
  align-items: start;
}

/* The middle panel sits slightly lower, the same offset rhythm used in
   the selected-work grid above, so the row still has some life to it. */
.panel:nth-child(2) { margin-top: clamp(1rem, 3vw, 2.5rem); }

.panel img {
  width: 100%;
  aspect-ratio: 5 / 4;
  object-fit: cover;
  background: var(--bg-sunk);
  margin-bottom: 1.25rem;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  transition: transform 0.35s var(--ease-spring), box-shadow 0.35s var(--ease);
}
.panel:hover img { box-shadow: var(--shadow-md); }
.panel:nth-child(1) img { aspect-ratio: 4 / 5; }
.panel:nth-child(3) img { aspect-ratio: 1 / 1; }

@media (max-width: 860px) {
  .track { grid-template-columns: 1fr; }
  .panel:nth-child(2) { margin-top: 0; }
}

.panel h3 {
  font-size: var(--step-2);
  margin-bottom: 0.7rem;
}

.panel p {
  color: var(--ink-soft);
  max-width: 38ch;
}

.panel dl {
  margin: 1.5rem 0 0;
  border-top: 1px solid var(--rule);
  padding-top: 0.9rem;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.35rem 1.5rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
}
.panel dt { color: var(--ink-soft); }
.panel dd { margin: 0; text-align: right; }

/* --------------------------------------------------------------------------
   12. Process - stepped diagonal stack, no cards
   -------------------------------------------------------------------------- */

.steps { display: grid; gap: clamp(1.5rem, 0.8rem + 2vw, 3rem); }

.step {
  display: grid;
  grid-template-columns: minmax(0, 4.5rem) minmax(0, 1fr);
  gap: clamp(1rem, 0.4rem + 2vw, 2.5rem);
  align-items: start;
  border-top: 1px solid var(--rule);
  padding-top: 1.4rem;
  max-width: 62rem;
}

/* Progressive indent is the whole idea of this section. */
.step:nth-child(2) { margin-left: clamp(0rem, 4vw, 5rem); }
.step:nth-child(3) { margin-left: clamp(0rem, 8vw, 10rem); }
.step:nth-child(4) { margin-left: clamp(0rem, 12vw, 15rem); }

.step__n {
  font-family: var(--font-mono);
  font-size: var(--step-2);
  line-height: 1;
  color: var(--rule-strong);
}

.step h3 {
  font-size: var(--step-2);
  margin-bottom: 0.6rem;
}

.step p {
  color: var(--ink-soft);
  max-width: 46ch;
}

@media (max-width: 720px) {
  .step { grid-template-columns: 1fr; gap: 0.75rem; }
  .step:nth-child(2),
  .step:nth-child(3),
  .step:nth-child(4) { margin-left: 0; }
}

/* --------------------------------------------------------------------------
   13. Accordion
   -------------------------------------------------------------------------- */

/* Each entry is numbered like a catalogue plate rather than a plain list
   row: a big quiet numeral that lights up in accent when its entry opens,
   a question set at display size instead of body size, and a plus that
   turns into a cross by rotating 45deg (drawn with two CSS bars, not an
   icon, so the morph is a single free transform). */

.faq { border-top: 1px solid var(--ink); }

.faq details {
  border-bottom: 1px solid var(--rule);
  transition: background-color 0.4s var(--ease);
}
.faq details:hover,
.faq details[open] { background: var(--bg-sunk); }

.faq summary {
  list-style: none;
  cursor: pointer;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: baseline;
  gap: clamp(1rem, 0.5rem + 1.6vw, 2.25rem);
  padding-block: clamp(1.4rem, 1.1rem + 1vw, 2.1rem);
  padding-inline: clamp(0.75rem, 0.4rem + 1vw, 1.5rem);
}
.faq summary::-webkit-details-marker { display: none; }

.faq__n {
  font-family: var(--font-mono);
  font-size: clamp(1.5rem, 1rem + 1.7vw, 2.4rem);
  font-weight: 500;
  line-height: 1;
  color: var(--rule-strong);
  transition: color 0.4s var(--ease);
}
.faq details:hover .faq__n,
.faq details[open] .faq__n { color: var(--accent); }

.faq__q {
  font-size: clamp(1.2rem, 1.02rem + 0.75vw, 1.7rem);
  font-weight: 500;
  letter-spacing: -0.022em;
  line-height: 1.22;
  transition: color 0.3s var(--ease);
  align-self: center;
}
.faq details:hover .faq__q { color: var(--accent); }

/* A plus drawn from two bars; rotating the whole mark 45deg turns it into
   a cross, so open and closed are the same two strokes, not two icons. */
.faq__toggle {
  position: relative;
  width: 1.35em;
  height: 1.35em;
  flex: none;
  align-self: center;
  transition: transform 0.45s var(--ease-spring);
}
.faq__toggle::before,
.faq__toggle::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--ink-soft);
  transition: background-color 0.3s var(--ease);
}
.faq__toggle::before { width: 100%; height: 1.5px; transform: translate(-50%, -50%); }
.faq__toggle::after { width: 1.5px; height: 100%; transform: translate(-50%, -50%); }
.faq details:hover .faq__toggle::before,
.faq details:hover .faq__toggle::after,
.faq details[open] .faq__toggle::before,
.faq details[open] .faq__toggle::after { background: var(--accent); }
.faq details[open] .faq__toggle { transform: rotate(45deg); }

.faq__body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.45s var(--ease);
}
.faq details[open] .faq__body { grid-template-rows: 1fr; }

.faq__body > div { overflow: hidden; }

.faq__body p {
  color: var(--ink-soft);
  max-width: 58ch;
  /* Lines up under the question, past the numeral column. Not pixel-exact
     against the numeral's own clamp(), which is fine, it only has to read
     as intentional, not measure to the pixel. */
  padding-left: clamp(3.5rem, 2.6rem + 3.6vw, 5.75rem);
  padding-right: clamp(0.75rem, 0.4rem + 1vw, 1.5rem);
  padding-bottom: clamp(1.5rem, 1.2rem + 1vw, 2.1rem);
}

@media (max-width: 560px) {
  .faq__n { font-size: 1.15rem; }
  .faq__body p { padding-left: clamp(0.75rem, 0.4rem + 1vw, 1.5rem); }
}

/* Heading on the left, the accordion itself on the right. The right column
   carries a real interactive element, not a floating explainer paragraph. */
.faq-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr);
  gap: clamp(2rem, 1rem + 4vw, 5rem);
  align-items: start;
}

.faq-layout__head { position: sticky; top: calc(var(--nav-h) + 3.5rem); }
.faq-layout__head h2 { font-size: var(--step-3); max-width: 14ch; }
.faq-layout__head p {
  margin-block: 1.1rem 1.75rem;
  max-width: 34ch;
  color: var(--ink-soft);
}

@media (max-width: 860px) {
  .faq-layout { grid-template-columns: 1fr; }
  .faq-layout__head { position: static; }
  .faq-layout__head h2 { max-width: 20ch; }
}

/* --------------------------------------------------------------------------
   14. Closing call to action
   -------------------------------------------------------------------------- */

.cta {
  background: var(--accent-fill);
  color: var(--accent-on-fill);
}

.cta__inner {
  padding-block: clamp(4rem, 2.5rem + 6vw, 8rem);
  display: grid;
  gap: 2.5rem;
  justify-items: start;
}

.cta h2 {
  font-size: var(--step-3);
  max-width: 17ch;
  letter-spacing: -0.035em;
}

.cta .btn {
  --btn-bg: var(--accent-on-fill);
  --btn-fg: var(--accent-fill);
  --btn-bd: var(--accent-on-fill);
}

.cta__aside {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.7;
  color: color-mix(in srgb, var(--accent-on-fill) 76%, transparent);
}
.cta__aside a { border-bottom: 1px solid currentColor; }

/* --------------------------------------------------------------------------
   15. Footer
   -------------------------------------------------------------------------- */

/* footer__bar already pads its own top and bottom, but that alone left
   only 1.5rem between the copyright line and the end of the page, out of
   step with how generously everything above it breathes. */
.footer {
  border-top: 1px solid var(--rule);
  padding-bottom: clamp(2rem, 1.25rem + 2.5vw, 3.5rem);
}

.footer__grid {
  padding-block: clamp(3rem, 2rem + 4vw, 5.5rem);
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(2, minmax(0, 1fr));
  gap: 2.5rem var(--gutter);
}

.footer__grid h3 {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1.35rem;
}

.footer__grid li { margin-bottom: 0.6rem; }
.footer__grid li a { font-size: var(--step--1); color: var(--ink-soft); }
.footer__grid li a:hover { color: var(--accent); }

.footer__blurb {
  max-width: 30ch;
  color: var(--ink-soft);
  font-size: var(--step--1);
  margin-top: 1rem;
  line-height: 1.7;
}

.footer__bar {
  border-top: 1px solid var(--rule);
  padding-block: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--ink-soft);
}

@media (max-width: 780px) {
  .footer__grid { grid-template-columns: repeat(2, 1fr); }
  .footer__grid > :first-child { grid-column: 1 / -1; }
}

/* --------------------------------------------------------------------------
   16. Page header used by works / about / contact
   -------------------------------------------------------------------------- */

.page-head {
  padding-top: clamp(2.5rem, 1.5rem + 4vw, 5rem);
  padding-bottom: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}

.page-head h1 {
  font-size: clamp(2.3rem, 1.5rem + 3.4vw, 4.25rem);
  letter-spacing: -0.04em;
  max-width: 20ch;
}

.page-head p {
  margin-top: 1.4rem;
  max-width: 50ch;
  color: var(--ink-soft);
  font-size: var(--step-1);
  line-height: 1.5;
  letter-spacing: -0.012em;
}

/* --------------------------------------------------------------------------
   17. Works index - filters, grid view, list view with cursor preview
   -------------------------------------------------------------------------- */

.controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem 2rem;
  border-block: 1px solid var(--rule);
  padding-block: 1rem;
  margin-bottom: clamp(2rem, 1rem + 3vw, 3.5rem);
}

.filters { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.chip {
  padding: 0.5rem 1.05rem;
  border: 1px solid var(--rule);
  background: none;
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink-soft);
  cursor: pointer;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease),
    background-color 0.25s var(--ease), transform 0.3s var(--ease-spring);
}
.chip:hover { border-color: var(--rule-strong); color: var(--ink); transform: translateY(-1px); }
.chip[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
}

.viewswitch {
  display: flex;
  gap: 0;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.viewswitch button {
  padding: 0.5rem 1.05rem;
  background: none;
  border: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink-soft);
  cursor: pointer;
  transition: color 0.25s var(--ease), background-color 0.25s var(--ease);
}
.viewswitch button + button { border-left: 1px solid var(--rule); }
.viewswitch button[aria-pressed="true"] { background: var(--ink); color: var(--bg); }

.index-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(240px, 26vw, 340px), 1fr));
  gap: clamp(2rem, 1rem + 3vw, 4rem) var(--gutter);
  align-items: start;
}

.index-grid .work-card,
.index-grid .work-card:nth-child(n) {
  grid-column: auto;
  margin-top: 0;
}

/* List view. Rows are typographic; the image follows the pointer. */
.index-list { border-top: 1px solid var(--ink); }

.index-list a {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 3fr) minmax(0, 2fr) auto;
  gap: 1.5rem;
  align-items: baseline;
  padding-block: clamp(1rem, 0.8rem + 0.6vw, 1.5rem);
  padding-inline: 0.75rem;
  border-radius: var(--r-sm);
  border-bottom: 1px solid var(--rule);
  transition: color 0.25s var(--ease), background-color 0.3s var(--ease);
}
.index-list a:hover { color: var(--accent); background: var(--bg-sunk); }

.index-list .t { font-size: var(--step-1); font-weight: 500; letter-spacing: -0.02em; }
.index-list .m,
.index-list .d,
.index-list .y {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink-soft);
}
.index-list a:hover .m,
.index-list a:hover .d,
.index-list a:hover .y { color: inherit; }

.peek {
  position: fixed;
  top: 0;
  left: 0;
  width: 230px;
  z-index: 90;
  pointer-events: none;
  opacity: 0;
  transform: translate3d(-50%, -50%, 0) scale(0.94);
  transition: opacity 0.28s var(--ease), transform 0.28s var(--ease);
}
.peek img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  background: var(--bg-sunk);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
}
.peek[data-on="true"] { opacity: 1; transform: translate3d(-50%, -50%, 0) scale(1); }

@media (max-width: 820px) {
  .index-list a { grid-template-columns: 1fr auto; }
  .index-list .m, .index-list .d { display: none; }
  .peek { display: none; }
}

.is-hidden { display: none !important; }

.empty {
  padding-block: clamp(3rem, 8vw, 6rem);
  text-align: center;
  color: var(--ink-soft);
}
.empty p { margin-bottom: 1.5rem; }

/* --------------------------------------------------------------------------
   18. Single work
   -------------------------------------------------------------------------- */

.work-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: clamp(2rem, 1rem + 3.5vw, 4.5rem);
  align-items: start;
  padding-top: clamp(1.5rem, 1rem + 2vw, 3rem);
}

.work-hero__img {
  background: var(--bg-sunk);
  position: sticky;
  top: calc(var(--nav-h) + 3rem);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}
.work-hero__img img { width: 100%; }

.work-facts {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.8rem 1.5rem;
  border-top: 1px solid var(--ink);
  padding-top: 1.2rem;
  margin-top: 1.75rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
}
.work-facts dt { color: var(--ink-soft); }
.work-facts dd { margin: 0; }

.work-note {
  margin-top: 2rem;
  color: var(--ink-soft);
  line-height: 1.7;
}

.work-details {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--gutter);
}
.work-details img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  background: var(--bg-sunk);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
}

.pager {
  display: flex;
  justify-content: space-between;
  gap: 1.5rem 2rem;
  flex-wrap: wrap;
  border-top: 1px solid var(--rule);
  padding-top: 1.75rem;
}
.pager span {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.4rem;
}
.pager a { font-size: var(--step-1); font-weight: 500; letter-spacing: -0.02em; }
.pager a:hover { color: var(--accent); }
.pager > :last-child { text-align: right; }

@media (max-width: 900px) {
  .work-hero { grid-template-columns: 1fr; }
  .work-hero__img { position: static; }
}

/* --------------------------------------------------------------------------
   19. About - bio, stats, grouped record, studio strip
   -------------------------------------------------------------------------- */

.bio {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
  gap: clamp(2rem, 1rem + 4vw, 5rem);
  align-items: start;
}

.bio__portrait img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  background: var(--bg-sunk);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
}
.bio__portrait figcaption {
  margin-top: 0.9rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink-soft);
}

.bio__text p {
  font-size: var(--step-1);
  line-height: 1.62;
  letter-spacing: -0.012em;
  color: var(--ink-soft);
  max-width: 54ch;
}
.bio__text p + p { margin-top: 1.3rem; }
.bio__text p:first-child { color: var(--ink); }

/* Four facts, one hairline grid, no cards. Numbers are traceable to the
   record below (the exhibitions, the collections), never invented. */
.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--rule);
}
.stats > div {
  padding: clamp(1.5rem, 1rem + 2vw, 2.5rem) clamp(1rem, 0.5rem + 1.5vw, 1.75rem);
  border-left: 1px solid var(--rule);
}
.stats > div:first-child { border-left: 0; }
.stats .num {
  font-family: var(--font-mono);
  font-size: clamp(2.2rem, 1.5rem + 2.6vw, 3.6rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--ink);
}
.stats .lbl {
  margin-top: 0.65rem;
  font-size: var(--step--1);
  color: var(--ink-soft);
}

@media (max-width: 720px) {
  .stats { grid-template-columns: repeat(2, 1fr); }
  .stats > div:nth-child(2n+1) { border-left: 0; }
  .stats > div:nth-child(n+3) { border-top: 1px solid var(--rule); }
}

/* The record is long, so it is grouped into clusters instead of one
   endless hairline-per-row list. */
.record {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(2rem, 1rem + 3vw, 3.5rem) var(--gutter);
}

.record section {
  border-top: 1px solid var(--ink);
  padding-top: 1.2rem;
}

.record h3 {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1.35rem;
}

.record li {
  margin-bottom: 1rem;
  font-size: var(--step--1);
  line-height: 1.55;
}
.record li b { font-weight: 500; }
.record li span {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--ink-soft);
  margin-top: 0.15rem;
}

.strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 0.75rem;
}
.strip img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  background: var(--bg-sunk);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
}

@media (max-width: 860px) {
  .bio { grid-template-columns: 1fr; }
  .bio__portrait { max-width: 26rem; }
}

/* --------------------------------------------------------------------------
   20. Contact form
   -------------------------------------------------------------------------- */

.contact {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(2.5rem, 1.5rem + 4vw, 5.5rem);
  align-items: start;
}

.field { display: grid; gap: 0.5rem; margin-bottom: 1.5rem; }

.field label {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink);
}
.field label .req { color: var(--danger); }

.field .hint {
  font-size: var(--step--1);
  color: var(--ink-soft);
  order: 3;
}

.field input,
.field select,
.field textarea {
  order: 2;
  width: 100%;
  padding: 0.95rem 1.15rem;
  background: var(--bg-sunk);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font-size: var(--step-0);
  color: var(--ink);
  transition: border-color 0.3s var(--ease), background-color 0.3s var(--ease),
    box-shadow 0.3s var(--ease);
}
.field textarea { min-height: 9.5rem; resize: vertical; }

.field input::placeholder,
.field textarea::placeholder { color: var(--ink-soft); opacity: 1; }

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  background: var(--bg-raised);
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-hairline);
}

.field .error {
  order: 4;
  font-size: var(--step--1);
  color: var(--danger);
  display: none;
}
.field[data-invalid="true"] input,
.field[data-invalid="true"] select,
.field[data-invalid="true"] textarea {
  border-color: var(--danger);
  background: var(--bg-raised);
}
.field[data-invalid="true"] .error { display: block; }
.field[data-invalid="true"] .hint { display: none; }

.form-note {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink-soft);
  margin-top: 1.25rem;
  max-width: 44ch;
  line-height: 1.65;
}

.form-sent {
  border: 1px solid var(--accent);
  border-radius: var(--r-md);
  padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-raised));
}
.form-sent h3 { font-size: var(--step-2); margin-bottom: 0.75rem; }
.form-sent p { color: var(--ink-soft); max-width: 40ch; }

.contact__aside {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.contact__aside section {
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  padding: clamp(1.25rem, 1rem + 1vw, 1.75rem);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.35s var(--ease), transform 0.35s var(--ease-spring);
}
.contact__aside section:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.contact__aside h3 {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.9rem;
}
.contact__aside p { font-size: var(--step--1); line-height: 1.7; color: var(--ink-soft); }
.contact__aside a { border-bottom: 1px solid var(--rule-strong); }
.contact__aside a:hover { color: var(--accent); border-bottom-color: var(--accent); }

@media (max-width: 860px) {
  .contact { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   21. Scroll reveal. Motion is here to establish reading order, nothing
   else, so it stays on --ease (no overshoot) even though interactive
   elements elsewhere use --ease-spring.
   -------------------------------------------------------------------------- */

/* Uses the independent `translate` and `scale` properties, not `transform`,
   so a reveal can never cancel a layout or hover transform on the same
   element (translate/scale/rotate compose with transform automatically). */
@media (prefers-reduced-motion: no-preference) {
  [data-reveal] {
    opacity: 0;
    translate: 0 26px;
    scale: 0.97;
    transition: opacity 0.85s var(--ease), translate 0.85s var(--ease), scale 0.85s var(--ease);
    transition-delay: var(--d, 0ms);
  }
  [data-reveal].in {
    opacity: 1;
    translate: none;
    scale: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .peek { display: none; }
}
