/* =========================================================================
   Emmanuel Tsekpo, Portfolio
   main.css  ·  design system + components + motion
   ========================================================================= */

/* ----------  Fonts  ----------
   The source portfolio is set in one geometric-sans family, Montserrat-style ,
   light + wide-tracked for headings and labels, regular for body. */

:root {
  /* palette, oxblood */
  --bg:        #240909;
  --bg-1:      #2c0f0f;
  --bg-2:      #3a1616;
  --bg-3:      #48201e;
  --paper:     #f2e9e4;
  --paper-2:   #e6d8d0;
  --paper-ink: #2a0c0c;

  --ink:   #f5e2da;
  --ink-2: #d9aea3;
  --ink-3: #ab7d72;

  --accent:   #db6f5f;
  --accent-2: #ef8d7c;
  --accent-ink: #2a0c0c;

  --line:   rgba(239, 141, 124, 0.16);
  --line-2: rgba(239, 141, 124, 0.30);

  /* type, one geometric-sans family, matching the portfolio */
  --font-display: 'Montserrat', system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-sans:    'Montserrat', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* motion */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --dur-fast: 140ms;
  --dur: 240ms;
  --dur-slow: 600ms;

  /* layout */
  --container: 1240px;
  --gutter: clamp(20px, 5vw, 64px);
  --nav-h: 76px;
  --radius: 14px;
  --radius-lg: 22px;
}

/* ----------  reset / base  ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: hidden; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
:where([id]) { scroll-margin-top: calc(var(--nav-h) + 20px); }

body {
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  letter-spacing: -0.002em;
  color: var(--ink-2);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
  min-height: 100vh;

  /* oxblood ground, warm glow top-left, sink + vignette bottom-right,
     matching the shaded backgrounds in the source portfolio */
  background-color: #2a0d0b;
  background-image:
    radial-gradient(115% 85% at 12% -12%, rgba(126, 48, 39, 0.55), rgba(126, 48, 39, 0) 55%),
    radial-gradient(70% 55% at 52% 18%, rgba(96, 34, 28, 0.42), rgba(96, 34, 28, 0) 72%),
    radial-gradient(150% 130% at 102% 118%, rgba(12, 3, 2, 0.72), rgba(12, 3, 2, 0) 55%),
    linear-gradient(158deg, #351310 0%, #2a0d0b 46%, #1f0807 100%);
  background-attachment: fixed;
  background-repeat: no-repeat;
}

img, picture, svg, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
ul { list-style: none; padding: 0; }
h1, h2, h3, h4 { color: var(--ink); font-weight: 400; line-height: 1.08; }
::selection { background: var(--accent); color: #fff; }

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

/* film grain, on the background only: both layers sit at a negative z-index,
   so they paint behind every real element on the page (nothing here has a
   z-index below -1) instead of as a film over text, images and buttons.
   Two turbulence tiles at different scales read as organic 35mm grain. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  opacity: 0.9;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.62' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0.24 0.24 0.24 0 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 200px 200px;
}
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  opacity: 0.5;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='m'><feTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='100%25' height='100%25' filter='url(%23m)'/></svg>");
  background-size: 120px 120px;
}
@media (prefers-reduced-motion: reduce) {
  body::before { opacity: 0.7; }
  body::after { opacity: 0.35; }
}

/* ----------  layout helpers  ---------- */
.container { width: min(100% - var(--gutter) * 2, var(--container)); margin-inline: auto; }
.container--wide { --container: 1440px; }
.section { padding-block: clamp(72px, 12vw, 160px); }
.section--tight { padding-block: clamp(48px, 8vw, 96px); }
.stack > * + * { margin-top: 1rem; }

.eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-2);
  font-weight: 400;
  display: inline-flex;
  align-items: center;
  gap: 0.8em;
}
.eyebrow::before {
  content: "";
  width: 26px;
  height: 1px;
  background: currentColor;
  opacity: 0.7;
}

.h-display {
  font-family: var(--font-display);
  font-weight: 300;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.t-xxl { font-size: clamp(2.15rem, 6.6vw, 6rem); }
.t-xl  { font-size: clamp(1.8rem, 4.6vw, 3.6rem); }
.t-lg  { font-size: clamp(1.5rem, 3.2vw, 2.3rem); }
.lead  { font-size: clamp(1.05rem, 1.8vw, 1.28rem); color: var(--ink-2); max-width: 62ch; }
.muted { color: var(--ink-3); }

/* ----------  buttons / links  ---------- */
.btn {
  --_bg: var(--accent);
  --_fg: var(--accent-ink);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.95em 1.7em;
  border-radius: 999px;
  font-weight: 500;
  font-size: 0.92rem;
  letter-spacing: 0.02em;
  color: var(--_fg);
  background: var(--_bg);
  overflow: hidden;
  isolation: isolate;
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.btn > * { position: relative; z-index: 1; }
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--accent-2);
  transform: translateY(101%);
  transition: transform var(--dur) var(--ease-out);
}
.btn .btn__arrow { transition: transform var(--dur) var(--ease-out); }
.btn--ghost {
  --_bg: transparent;
  --_fg: var(--ink);
  box-shadow: inset 0 0 0 1px var(--line-2);
}
.btn--ghost::before { background: var(--accent); }
.btn--ghost:hover { --_fg: var(--accent-ink); }

@media (hover: hover) and (pointer: fine) {
  .btn:hover { transform: translateY(-2px); box-shadow: 0 14px 30px -12px rgba(0,0,0,0.55); }
  .btn:hover::before { transform: translateY(0); }
  .btn:hover .btn__arrow { transform: translateX(4px); }
}
.btn:active { transform: translateY(0) scale(0.98); }
/* project-page back link: same pill, but the arrow leads (points back)
   and nudges further left on hover instead of right. */
.btn--back { padding: 0.7em 1.3em 0.7em 1.1em; font-size: 0.82rem; }
.btn--back .btn__arrow { order: -1; }
@media (hover: hover) and (pointer: fine) {
  .btn--back:hover .btn__arrow { transform: translateX(-4px); }
}
.proj-hero__back { margin-bottom: clamp(18px, 3vw, 28px); }

/* animated underline link */
.link-u {
  position: relative;
  color: var(--ink);
  padding-bottom: 2px;
}
.link-u::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(var(--u, 0));
  transform-origin: right;
  transition: transform var(--dur) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .link-u:hover::after { --u: 1; transform-origin: left; }
}
.link-u:focus-visible::after { --u: 1; }

/* ----------  custom cursor  ---------- */
.cursor { position: fixed; top: 0; left: 0; z-index: 9999; pointer-events: none; will-change: transform; opacity: 0; }
.cursor-dot  { width: 6px; height: 6px; margin: -3px 0 0 -3px; border-radius: 50%; background: var(--accent-2); }
.cursor-ring {
  width: 38px; height: 38px; margin: -19px 0 0 -19px;
  border-radius: 50%;
  border: 1px solid var(--line-2);
  transition: width var(--dur) var(--ease-out), height var(--dur) var(--ease-out),
              margin var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out),
              border-color var(--dur) var(--ease-out), opacity var(--dur) var(--ease-out);
}
body.cursor-ready .cursor { opacity: 1; }
body.cursor-ready.cursor-hidden .cursor { opacity: 0; }
body.cursor-active .cursor-ring {
  width: 68px; height: 68px; margin: -34px 0 0 -34px;
  background: rgba(219, 111, 95, 0.10);
  border-color: transparent;
}
body.cursor-down .cursor-ring { width: 30px; height: 30px; margin: -15px 0 0 -15px; }
.cursor-label {
  position: fixed; top: 0; left: 0; z-index: 9999; pointer-events: none;
  font-size: 0.62rem; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--accent-ink); background: var(--accent-2);
  padding: 0.5em 0.85em; border-radius: 999px;
  transform: translate(-50%, calc(-50% + 46px)) scale(0.6);
  opacity: 0;
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
  white-space: nowrap;
}
body.cursor-label-on .cursor-label { opacity: 1; transform: translate(-50%, calc(-50% + 46px)) scale(1); }

/* click ripple */
.ripple {
  position: fixed; z-index: 9997; pointer-events: none;
  width: 14px; height: 14px; margin: -7px 0 0 -7px;
  border-radius: 50%;
  border: 1px solid var(--accent-2);
}

@media (hover: none), (pointer: coarse) {
  .cursor, .cursor-label { display: none !important; }
}

/* ----------  loader  ---------- */
.loader {
  position: fixed; inset: 0; z-index: 10000;
  display: grid; place-items: center;
  background: #f2eee9;
  transition: opacity 420ms var(--ease-out);
}
.loader__video {
  width: min(60vw, 360px); max-height: 70vh;
  height: auto; object-fit: contain;
}
.loader.is-done { opacity: 0; pointer-events: none; }
html:not(.js) .loader { display: none; }

/* per-page load-in, the solid page bg sits behind, so a fade reads as a clean hand-off */
.page-main { opacity: 0; transform: translateY(14px); }
body.page-in .page-main { opacity: 1; transform: none; transition: opacity 440ms var(--ease-out), transform 440ms var(--ease-out); }

/* ----------  navigation  ---------- */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  height: var(--nav-h);
  display: flex; align-items: center;
  transition: background-color var(--dur) var(--ease-out), backdrop-filter var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
  border-bottom: 1px solid transparent;
}
.nav.is-stuck {
  background: rgba(36, 9, 9, 0.82);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  -webkit-backdrop-filter: blur(10px) saturate(1.2);
  backdrop-filter: blur(10px) saturate(1.2);
  border-bottom-color: var(--line);
}
.nav__inner { width: min(100% - var(--gutter) * 2, 1440px); margin-inline: auto; display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; }
.nav__brand {
  font-family: var(--font-display);
  font-size: 1.15rem;
  color: var(--ink);
  letter-spacing: 0.01em;
  display: inline-flex; align-items: baseline; gap: 0.5ch;
  will-change: transform;
}
.nav__brand b { font-weight: 500; }
.nav__brand span { color: var(--accent-2); }
.nav__links { display: flex; align-items: center; gap: clamp(1rem, 3vw, 2.4rem); }
.nav__links a {
  font-size: 0.86rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  position: relative;
  padding: 0.4em 0;
  transition: color var(--dur) var(--ease-out);
}
.nav__links a::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 1px; width: 100%;
  background: var(--accent-2); transform: scaleX(0); transform-origin: right;
  transition: transform var(--dur) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) { .nav__links a:hover { color: var(--ink); } .nav__links a:hover::after { transform: scaleX(1); transform-origin: left; } }
.nav__links a[aria-current="page"] { color: var(--ink); }
.nav__links a[aria-current="page"]::after { transform: scaleX(1); }

.nav__toggle { display: none; width: 44px; height: 44px; align-items: center; justify-content: center; }
.nav__toggle span, .nav__toggle span::before, .nav__toggle span::after {
  content: ""; display: block; width: 22px; height: 1.5px; background: var(--ink); transition: transform var(--dur) var(--ease-out), opacity var(--dur) var(--ease-out);
}
.nav__toggle span::before { transform: translateY(-7px); }
.nav__toggle span::after  { transform: translateY(5.5px); }

@media (max-width: 860px) {
  .nav__toggle { display: inline-flex; }
  .nav__links {
    position: fixed; inset: var(--nav-h) 0 auto 0;
    flex-direction: column; align-items: flex-start; gap: 0;
    background: var(--bg-1); border-bottom: 1px solid var(--line);
    padding: 0.5rem var(--gutter) 1.5rem;
    transform: translateY(-12px); opacity: 0; pointer-events: none;
    transition: transform var(--dur) var(--ease-out), opacity var(--dur) var(--ease-out);
  }
  .nav__links a { width: 100%; padding: 0.95rem 0; border-bottom: 1px solid var(--line); font-size: 1rem; }
  body.menu-open .nav__links { transform: none; opacity: 1; pointer-events: all; }
  body.menu-open .nav__toggle span { transform: rotate(45deg); }
  body.menu-open .nav__toggle span::before { transform: translateY(0) rotate(90deg); opacity: 1; }
  body.menu-open .nav__toggle span::after  { opacity: 0; }
}

/* ----------  hero  ---------- */
.hero { position: relative; padding-top: calc(var(--nav-h) + clamp(28px, 6vw, 64px)); padding-bottom: clamp(40px, 7vw, 84px); overflow: clip; }
.hero__grid { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: clamp(24px, 5vw, 72px); align-items: end; }
/* about page: "Creative Director" needs more room than the default 1.1fr
   text column gives it to stay on one line before the "in Accra, Ghana."
   break, at viewport widths between the 900px single-column breakpoint and
   the container's own 1440px cap. */
.hero__grid--wide-text { grid-template-columns: 1.4fr 0.6fr; }
.hero__grid > div:first-child { padding-bottom: clamp(6px, 2vw, 22px); }
/* for a shorter text column next to a tall portrait (no CTA row under the
   lead), align-items: end would strand the text near the bottom of the row
   with empty space above it; pin both to the top instead */
.hero__grid--top { align-items: stretch; }
.hero__grid--top > div:first-child { padding-bottom: 0; padding-top: clamp(6px, 2vw, 22px); }
/* contact page: match the portrait's box to the text column's own height
   (instead of a fixed clamp) and bottom-anchor the photo in it, so the
   photo's bottom edge lines up with the bottom of the contact tiles
   instead of hanging past it. */
.hero__grid--top .ab-portrait {
  height: 100%; display: flex; flex-direction: column; justify-content: flex-end;
}
.hero__services { display: flex; flex-wrap: wrap; gap: 0.6rem 1.4rem; margin-top: 1.6rem; }
.hero__services span { font-size: 0.8rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3); }
.hero__services span + span::before { content: "·"; margin-right: 1.4rem; color: var(--accent); }
.hero__cta { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 2.4rem; }
.hero__media {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  aspect-ratio: 5 / 6;
  max-height: 62vh;
  background: var(--bg-2);
  box-shadow: 0 40px 80px -40px rgba(0,0,0,0.7);
}
.hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: 42% 18%; }
.hero__media::after { content: ""; position: absolute; inset: 0; box-shadow: inset 0 0 0 1px var(--line-2); border-radius: inherit; background: linear-gradient(200deg, transparent 45%, rgba(36,9,9,0.5)); }

@media (max-width: 900px) {
  .hero__grid { grid-template-columns: 1fr; align-items: start; }
  .hero__grid > div:first-child { padding-bottom: 0; }
  .hero__media { aspect-ratio: 4 / 3; max-width: 440px; max-height: none; }
}

/* ==========  home: name animates in bright, then fades as you scroll  ==========
   The full name animates onto the page bright and large on load. As you start
   scrolling it fades back to a faint background ghost and stays there while a
   sticky stage runs three scenes (About, Work, Contact): the first slides in
   from the left, each next one rotates in as the previous rotates out. */
.home { position: relative; }
.reel { position: relative; }
.reel--3 { height: 384svh; }

.reel__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  grid-template-areas: "stage";
  align-items: center;
  overflow: clip;
}

/* the name + CTA group. inner <span> does the bright load-in; the group
   does the scroll fade, so the button fades out with the name instead of
   lingering as a stray control once the reel scrolls into the scenes. */
.home-name-group {
  grid-area: stage;
  place-self: center;
  /* no z-index here on purpose: it's a grid item with no explicit stacking
     order, so it paints in plain DOM order (before the scenes) and stays
     behind the on-stage scene, same as before. .home-cta below opts back
     into its own stacking order so the button escapes that and stays
     clickable even while a scene sits "on stage" underneath the group. */
  text-align: center;
  pointer-events: none;
  opacity: 1;
}
.home-name { margin: 0; pointer-events: none; user-select: none; }
.home-name > span {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(3rem, 14.5vw, 13.5rem); line-height: 0.82; letter-spacing: -0.045em;
  color: var(--accent-2);
  opacity: 0;
  display: block;
}
.home-cta {
  position: relative;
  z-index: 2;
  margin-top: clamp(64px, 7vw, 84px);
  pointer-events: auto;
  opacity: 0;
  display: inline-block;
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 2vw, 1.4rem);
  color: var(--accent-2);
}
.home-name > span { animation: nameIn 1500ms var(--ease-out) both; }
.home-cta { animation: ctaIn 700ms 900ms var(--ease-out) both; }
/* .home-name-group's scroll-linked fade (opacity 1 -> 0.03 over the first
   4% of page scroll) used to run as a native animation-timeline: scroll()
   animation. It's computed and applied from JS now instead (see main.js
   § 3 "Home reel") — see the note above .scene below for why. */
@keyframes nameIn {
  from { opacity: 0; transform: scale(0.4) translateY(6%); filter: blur(12px); }
  55%  { filter: blur(0); }
  to   { opacity: 0.95; transform: scale(1); filter: blur(0); }
}
@keyframes ctaIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .home-name > span { animation: none; opacity: 0.9; }
  .home-cta { animation: none; opacity: 1; }
  .home-name-group { animation: none; opacity: 1; }
}

/* scenes */
.scene {
  grid-area: stage;
  width: min(100% - var(--gutter) * 2, 1280px);
  margin-inline: auto;
  padding-top: var(--nav-h);
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
  z-index: 1;
}
.scene__media { position: relative; }
/* portraits: a real-alpha cut-out PNG (see assets/img/portrait/) — no
   separate mask, so it composites correctly against this page's gradient
   background instead of showing a flat-matte halo at the edges. */
.scene__media img {
  width: 100%; height: auto; display: block;
  max-height: 90svh; object-fit: contain;
}
.scene__text .eyebrow { margin-bottom: 1rem; }
.scene__text h2 { margin-bottom: 1rem; }
.scene__text .lead { margin-bottom: 1.5rem; }

/* desktop only (901px is the exact complement of the 900px collapse
   breakpoint below): pull the text column in closer to the portrait, tuck
   the heading up a bit tighter above the lead, and title-case the heading
   via text-transform (display-only, doesn't touch the actual copy) rather
   than hand-editing every heading's capitalisation in the markup. */
@media (min-width: 901px) {
  .scene { gap: clamp(16px, 3vw, 40px); }
  .scene__text h2 { margin-bottom: 0.6rem; text-transform: capitalize; }
}
.scene__text .scene__lines { margin: 0 0 1.4rem; color: var(--ink-2); font-size: 0.95rem; line-height: 1.8; }

/* This used to be a native animation-timeline: scroll() animation, gated
   behind @supports (animation-timeline: scroll()). Safari's @supports
   check reports support for that syntax while its actual scroll-linked
   execution is unreliable across versions, so the effect silently broke
   there: every scene stayed fully visible, stacked on top of each other
   (they all share grid-area: stage). Computing the exact same fade +
   3D-turn math from a scroll listener instead (see main.js § 3 "Home
   reel") removes the dependency on that native feature entirely, so the
   result is identical in every browser rather than only where the native
   timeline happens to actually run. Defaults to hidden here so there's no
   flash of every scene visible before the first JS update runs —
   html:not(.js) below (true no-JS only, .js is added synchronously in
   <head>) and the reduced-motion / small-screen block further down both
   restore full visibility for their own fallback layouts. */
.scene { opacity: 0; pointer-events: none; will-change: opacity; }
.scene__media { will-change: transform; }
.scene__text { will-change: opacity; }

/* ---- fallback / reduced motion / small screens: normal stacked scroll ---- */
html:not(.js) .reel--3 { height: auto; }
html:not(.js) .reel__stage { position: static; height: auto; display: block; overflow: visible; perspective: none; }
html:not(.js) .home-name-group { position: sticky; top: 30svh; height: 0; }
html:not(.js) .scene { opacity: 1; pointer-events: auto; padding-block: clamp(72px, 12vh, 130px); }
html:not(.js) .scene + .scene { border-top: 1px solid var(--line); }
@media (prefers-reduced-motion: reduce), (max-width: 900px) {
  .reel--3 { height: auto; }
  .reel__stage { position: static; height: auto; display: block; overflow: visible; perspective: none; }
  .home-name-group { position: absolute; top: 6svh; inset-inline: 0; height: auto; }
  .home-name > span { font-size: clamp(2.6rem, 16vw, 5.2rem); }
  /* the giant name sits right under the nav here, so the small nav-bar name
     directly above it reads as clutter; going "home" from the home page is
     also a no-op, so it's dropped whenever the name is in this position.
     justify-content moves to flex-end so whatever's left (links or the
     toggle) stays right-aligned instead of collapsing to the start. */
  .page-home .nav__brand { display: none; }
  .page-home .nav__inner { justify-content: flex-end; }
  .scene {
    animation: none !important; opacity: 1 !important; transform: none !important;
    grid-template-columns: 1fr; gap: clamp(18px, 5vw, 34px);
    padding-block: clamp(56px, 10vh, 100px); pointer-events: auto !important;
    /* the scroll-timeline animation is off here, so there's nothing left to
       optimize for; without this, will-change: transform/opacity forces its
       own stacking context (as if transformed) that outranks the absolutely
       positioned .home-cta sitting on top of it, swallowing its clicks. */
    will-change: auto !important;
  }
  .scene:first-of-type { padding-top: calc(var(--nav-h) + 28svh); }
  .scene + .scene { border-top: 1px solid var(--line); }
  /* full-bleed to the screen edges instead of sitting inset in .scene's own
     gutter - at mobile width the portrait was only ever as wide as that
     gutter-inset column allowed (~335px on a 375px phone), which read as
     small/thin next to the copy; breaking just the image out to 100vw
     (text stays inset) makes it the dominant, "solid" element scenes are
     built around. */
  .scene__media { margin-inline: calc(50% - 50vw); width: 100vw; }
  .scene__media img { width: 100%; max-height: 78svh; margin-inline: auto; }
  .scene__media { animation: none !important; transform: none !important; will-change: auto !important; }
  .scene__text { animation: none !important; opacity: 1 !important; will-change: auto !important; }
}

/* ----------  work grid  ---------- */
/* bento grid: most cards take half the row, a few flagship projects (.card--feat)
   run the full width with a wider, cinematic frame, so the grid has rhythm
   instead of one uniform repeating tile */
.work-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: clamp(18px, 3vw, 40px); }
.work-grid .card { grid-column: span 3; }
.work-grid .card--feat { grid-column: span 6; }
.work-grid .card--feat .card__media { aspect-ratio: 21 / 9; }
@media (max-width: 720px) { .work-grid { grid-template-columns: 1fr; } .work-grid .card, .work-grid .card--feat { grid-column: span 1; } }

.card {
  position: relative;
  display: block;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-1);
  isolation: isolate;
}
.card__num {
  position: absolute; top: 14px; right: 14px; z-index: 2;
  font-family: var(--font-display); font-size: 0.78rem; letter-spacing: 0.06em;
  color: var(--ink-2);
  background: rgba(36, 9, 9, 0.7);
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  padding: 0.45em 0.7em; border-radius: 999px;
  border: 1px solid var(--line);
}
.card__media { position: relative; aspect-ratio: 16 / 10; overflow: hidden; background: var(--bg-2); }
.card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.06);
  transition: transform 700ms var(--ease-out), filter 700ms var(--ease-out);
  filter: saturate(0.95);
}
.card__media--placeholder { display: grid; place-items: center; color: var(--ink-3); }
.card__media--logo { background: #fff; padding: 10%; }
.card__media--logo img { object-fit: contain; }
.card__media--placeholder .ph {
  font-size: 0.7rem; letter-spacing: 0.2em; text-transform: uppercase;
  border: 1px dashed var(--line-2); padding: 0.7em 1.1em; border-radius: 8px;
}
/* film posters are portrait key art in a 16:10 landscape card - object-fit:
   cover (plus the baseline 1.06 zoom every other card gets) blows them up
   into a tight, cropped sliver that loses the title treatment entirely.
   These show the whole poster instead, letterboxed on the card's own
   background rather than cropped to fill it. */
[data-cat="film"] .card__media img { object-fit: contain; transform: none; }
.card__body { padding: 1.3rem 1.4rem 1.5rem; display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
.card__title { font-family: var(--font-display); font-size: 1.3rem; color: var(--ink); }
.card--feat .card__title { font-size: clamp(1.5rem, 2.6vw, 2rem); }
.card--feat .card__body { padding: 1.6rem clamp(1.4rem, 3vw, 2.2rem) 1.8rem; }
.card__meta { font-size: 0.74rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3); margin-top: 0.35rem; }
.card__arrow { flex: none; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; box-shadow: inset 0 0 0 1px var(--line-2); transition: transform var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out); }
.card::after { content: ""; position: absolute; inset: 0; box-shadow: inset 0 0 0 1px var(--line); border-radius: var(--radius); pointer-events: none; transition: box-shadow var(--dur) var(--ease-out); }

@media (hover: hover) and (pointer: fine) {
  .card:hover .card__media img { transform: scale(1.11); filter: saturate(1.08); }
  .card:hover .card__arrow { transform: rotate(-45deg); background: var(--accent); color: var(--accent-ink); }
  .card:hover::after { box-shadow: inset 0 0 0 1px var(--line-2); }
  /* the whole poster is already visible via object-fit: contain above, so
     hover only needs a light zoom rather than the 1.11 every other
     (cover-fit, full-bleed) card uses, which would crop into the poster. */
  [data-cat="film"]:hover .card__media img { transform: scale(1.04); }
}

.card__tag {
  position: absolute; top: 14px; left: 14px; z-index: 2;
  font-size: 0.66rem; letter-spacing: 0.16em; text-transform: uppercase;
  background: rgba(36, 9, 9, 0.7);
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--ink-2); padding: 0.5em 0.85em; border-radius: 999px;
  border: 1px solid var(--line);
}

/* selected artworks */
.artwork-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(10px, 1.6vw, 16px); }
.artwork-tile {
  aspect-ratio: 1 / 1; border-radius: var(--radius); background: var(--bg-2);
  display: grid; place-items: center; box-shadow: inset 0 0 0 1px var(--line-2);
  overflow: hidden;
}
.artwork-tile .ph {
  font-size: 0.68rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-3);
  border: 1px dashed var(--line-2); padding: 0.6em 1em; border-radius: 8px;
}
.artwork-tile img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 700ms var(--ease-out), filter 700ms var(--ease-out);
  filter: saturate(0.95);
}
@media (hover: hover) and (pointer: fine) {
  .artwork-tile:hover img { transform: scale(1.06); filter: saturate(1.05); }
}
@media (max-width: 700px) { .artwork-grid { grid-template-columns: repeat(2, 1fr); } }
.artwork-tile { cursor: pointer; }

/* lightbox */
.lightbox {
  position: fixed; inset: 0; z-index: 300;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(20px, 5vw, 60px);
  background: rgba(16, 5, 4, 0.92);
  opacity: 0; visibility: hidden;
  transition: opacity 280ms var(--ease-out), visibility 0ms linear 280ms;
}
.lightbox.is-open { opacity: 1; visibility: visible; transition: opacity 280ms var(--ease-out); }
.lightbox__img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  border-radius: var(--radius); box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  transform: scale(0.96); transition: transform 280ms var(--ease-out);
}
.lightbox.is-open .lightbox__img { transform: scale(1); }
.lightbox__close {
  position: absolute; top: clamp(14px, 3vw, 28px); right: clamp(14px, 3vw, 28px);
  width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center; color: var(--ink);
  background: rgba(255, 255, 255, 0.06); box-shadow: inset 0 0 0 1px var(--line-2);
  transition: background 200ms var(--ease-out), transform 200ms var(--ease-out);
}
.lightbox__close svg { width: 18px; height: 18px; }
@media (hover: hover) and (pointer: fine) {
  .lightbox__close:hover { background: var(--accent); color: var(--accent-ink); transform: rotate(90deg); }
}
@media (prefers-reduced-motion: reduce) {
  .lightbox, .lightbox__img, .lightbox__close { transition: none !important; }
}

/* filters */
.filters { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: clamp(28px, 4vw, 48px); }
.filters button {
  font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 0.6em 1.1em; border-radius: 999px;
  color: var(--ink-3); box-shadow: inset 0 0 0 1px var(--line);
  transition: color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.filters button[aria-pressed="true"] { color: var(--accent-ink); background: var(--accent-2); box-shadow: none; }
@media (hover: hover) and (pointer: fine) { .filters button:hover { color: var(--ink); box-shadow: inset 0 0 0 1px var(--line-2); } }
.card.is-hidden, .artwork-grid-wrap.is-hidden { display: none; }

/* ----------  reveal / scroll motion  ---------- */
.reveal { opacity: 0; transform: translateY(20px); transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); transition-delay: calc(var(--i, 0) * 70ms); }
.reveal.is-visible { opacity: 1; transform: none; }
/* image reveal, opacity + subtle scale (measurable box, GPU-only) */
.reveal-img { opacity: 0; transform: scale(1.035); transition: opacity 900ms var(--ease-out), transform 900ms var(--ease-out); }
.reveal-img.is-visible { opacity: 1; transform: none; }

/* scroll progress bar — driven from JS (see main.js § 1b), not native
   animation-timeline: scroll(); see the note above .scene further down
   for why. */
.scroll-progress { position: fixed; top: 0; left: 0; height: 2px; width: 100%; transform-origin: 0 50%; background: var(--accent); z-index: 101; transform: scaleX(0); }

/* big section divider word */
.divider-word { position: relative; text-align: center; padding-block: clamp(60px, 12vw, 140px); overflow: clip; }
.divider-word__ghost {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(4rem, 17vw, 15rem); line-height: 0.8; letter-spacing: -0.05em;
  color: transparent; -webkit-text-stroke: 1px var(--line-2);
  user-select: none; pointer-events: none;
}
.divider-word__label { position: absolute; inset: 0; display: grid; place-items: center; }
.divider-word__label span { font-size: clamp(1.2rem, 3vw, 2rem); color: var(--accent-2); letter-spacing: 0.05em; font-family: var(--font-display); }

/* ----------  project detail  ---------- */
.proj-hero { padding-top: calc(var(--nav-h) + clamp(48px, 9vw, 110px)); }
.proj-hero__meta { display: flex; flex-wrap: wrap; gap: 0.6rem 2rem; margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }
.proj-hero__meta div { min-width: 140px; }
.proj-hero__meta dt { font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-3); }
.proj-hero__meta dd { color: var(--ink); margin-top: 0.3rem; }

.figure { border-radius: var(--radius); overflow: hidden; background: var(--bg-2); position: relative; }
.figure img { width: 100%; }
.figure figcaption { font-size: 0.8rem; color: var(--ink-3); padding: 0.9rem 1.1rem; }
.figure--placeholder { aspect-ratio: 16 / 9; display: grid; place-items: center; }
.figure--placeholder .ph {
  font-size: 0.72rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-3);
  border: 1px dashed var(--line-2); padding: 0.9em 1.3em; border-radius: 8px; text-align: center; max-width: 80%;
}

.media-col { display: grid; gap: clamp(18px, 3vw, 36px); align-items: start; }
.media-col--2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 720px) { .media-col--2 { grid-template-columns: 1fr; } }

.video-embed { position: relative; aspect-ratio: 16 / 9; border-radius: var(--radius); overflow: hidden; background: var(--bg-2); }
.video-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video-embed--portrait { aspect-ratio: 9 / 16; max-width: 420px; margin-inline: auto; }
.video-caption { font-size: 0.8rem; color: var(--ink-3); padding: 0.9rem 0 0; text-align: center; }

.prose { max-width: 68ch; }
.prose h3 { font-family: var(--font-display); font-size: 1.5rem; margin-top: 2.4rem; margin-bottom: 0.6rem; }
.prose p + p { margin-top: 1rem; }
.prose ul { display: grid; gap: 0.5rem; margin-top: 1rem; }
.prose li { padding-left: 1.4em; position: relative; }
.prose li::before { content: "→"; position: absolute; left: 0; color: var(--accent); }

/* pull-quote, used to break up long bio copy */
.ab-quote {
  font-family: var(--font-display); font-weight: 300; font-style: italic;
  font-size: clamp(1.25rem, 2.6vw, 1.85rem); line-height: 1.4;
  color: var(--accent-2); max-width: 30ch;
  margin-block: clamp(1.8rem, 4vw, 2.6rem);
  padding-left: 1.1rem; border-left: 2px solid var(--line-2);
}

/* ----------  card-flip (page-flip identity brand books, inline in work.html cards)  ---------- */
.card__media--flip { padding: 0; }
/* these are full-width .card--feat cards for a big, legible book, but
   .card--feat's own 21/9 cinematic ratio is for wide campaign photography
   - a brand-book page is close to 4/3, so it gets its own ratio here
   instead of cropping pages down to a short, wide letterbox. */
.card--feat .card__media--flip { aspect-ratio: 4 / 3; }
.card__media--flip .card-flip { width: 100%; height: 100%; }
.card__media--flip .card-flip .page { background: var(--paper); }
.card__media--flip .card-flip .page img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-flip-nav {
  position: absolute; left: 10px; right: 10px; bottom: 10px; z-index: 3;
  display: flex; align-items: center; justify-content: space-between;
}
.card-flip-nav__btn {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; color: var(--ink);
  background: color-mix(in srgb, var(--bg) 60%, transparent);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  box-shadow: inset 0 0 0 1px var(--line-2);
  transition: background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out), opacity var(--dur) var(--ease-out);
}
.card-flip-nav__btn svg { width: 13px; height: 13px; fill: currentColor; }
.card-flip-nav__btn[disabled] { opacity: 0.3; pointer-events: none; }
.card-flip-nav__count {
  font-size: 0.66rem; letter-spacing: 0.1em; color: var(--ink-2);
  background: color-mix(in srgb, var(--bg) 60%, transparent);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  padding: 0.4em 0.7em; border-radius: 999px;
}
@media (hover: hover) and (pointer: fine) {
  .card-flip-nav__btn:hover { background: var(--accent); color: var(--accent-ink); }
}
/* the drag-a-corner interaction needs real pointer tracking a touch swipe
   would otherwise hijack as page scroll, so small screens get the plain
   swipe-free static image the .card__media img rule already handles */
@media (max-width: 640px) {
  .card__media--flip .card-flip, .card__media--flip .card-flip-nav { display: none; }
  .card__media--flip .card-flip-static { display: block !important; }
}
.card-flip-static { display: none; }

.proj-nav { display: flex; justify-content: space-between; gap: 1rem; border-top: 1px solid var(--line); padding-top: 2rem; margin-top: clamp(48px, 8vw, 96px); }
.proj-nav a { display: inline-flex; flex-direction: column; gap: 0.2rem; }
.proj-nav small { font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-3); }
.proj-nav b { font-family: var(--font-display); font-weight: 400; color: var(--ink); font-size: 1.15rem; }
.proj-nav a:last-child { text-align: right; }

/* ----------  about / cv  ---------- */
/* masthead portrait: a real-alpha cut-out PNG, no frame, sits on the page.
   height is fixed and width follows from it, so the box keeps the source photo's
   own ratio; a mismatched box would stretch the image off the actual figure.
   Used in the about and contact page heroes, inside a .hero__grid. */
.ab-portrait { height: clamp(320px, 78svh, 720px); }
.ab-portrait img {
  /* height:auto + max-height/max-width (not a fixed height:100%) so a photo
     wider than the box has height to spare for still shrinks evenly on both
     axes instead of keeping the full box height and getting its width
     clipped by max-width alone, which stretches it off its own ratio. */
  height: auto; max-height: 100%; width: auto; max-width: 100%; display: block;
}
@media (max-width: 900px) {
  .ab-portrait { height: clamp(260px, 56svh, 520px); }
  .ab-portrait img { margin-inline: auto; }
}
/* contact page only: nudge the portrait toward the right edge of its
   column instead of the block default (flush left) */
.ab-portrait--right img { margin-left: auto; }
/* bio: reading column plus a stat sidebar, so the block uses the full wide
   measure instead of a narrow paragraph stranding whitespace beside it */
.ab-bio { display: grid; grid-template-columns: 1.7fr 1fr; gap: clamp(40px, 6vw, 100px); align-items: start; }
@media (max-width: 860px) { .ab-bio { grid-template-columns: 1fr; gap: clamp(32px, 8vw, 48px); } }

.about-grid { display: grid; grid-template-columns: 0.9fr 1.1fr; gap: clamp(28px, 6vw, 80px); align-items: start; }
@media (max-width: 900px) { .about-grid { grid-template-columns: 1fr; } }
.about-portrait { border-radius: var(--radius-lg); overflow: hidden; position: sticky; top: calc(var(--nav-h) + 24px); background: var(--bg-2); }
.about-portrait img { width: 100%; }
@media (max-width: 900px) { .about-portrait { position: static; max-width: 380px; } }

/* a set of figures, not a data table: each stands on its own accent rule,
   stacked as a sidebar beside the bio rather than boxed into grid cells */
.facts { display: flex; flex-direction: column; gap: clamp(22px, 3vw, 34px); padding-top: 0.3rem; }
.facts > div { padding-left: 1.1rem; border-left: 2px solid var(--line-2); }
.facts b { display: block; font-family: var(--font-display); font-weight: 300; font-size: clamp(2.2rem, 3.4vw, 2.8rem); line-height: 1; color: var(--accent-2); }
.facts span { display: block; margin-top: 0.5rem; font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); }
@media (max-width: 860px) { .facts { flex-direction: row; flex-wrap: wrap; gap: clamp(28px, 6vw, 48px); } }

.brand-list { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1.2rem; }
.brand-list li { font-size: 0.82rem; color: var(--ink-2); padding: 0.5em 0.9em; border-radius: 999px; box-shadow: inset 0 0 0 1px var(--line); transition: color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out); }
@media (hover: hover) and (pointer: fine) { .brand-list li:hover { color: var(--ink); box-shadow: inset 0 0 0 1px var(--line-2); } }

/* CV block */
.cv {
  margin-top: clamp(20px, 3vw, 36px);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(180deg, var(--bg-1), var(--bg));
  overflow: hidden;
}
.cv__bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem; padding: 1.2rem 1.4rem; border-bottom: 1px solid var(--line); }
.cv__bar h3 { font-family: var(--font-display); font-size: 1.3rem; }
.cv__bar .cv__actions { display: flex; gap: 0.6rem; }
.cv__doc { padding: clamp(20px, 4vw, 44px); }
.cv__section + .cv__section { margin-top: 2.2rem; padding-top: 2.2rem; border-top: 1px solid var(--line); }
.cv__section h4 { font-size: 0.74rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--accent-2); margin-bottom: 1rem; }
/* experience timeline: a rail that draws in, with a node per role */
.cv__timeline { position: relative; padding-left: 30px; }
.cv__timeline::before {
  content: ""; position: absolute; left: 5px; top: 6px; bottom: 6px; width: 1px;
  background: var(--line-2); transform: scaleY(0); transform-origin: top;
  transition: transform 1100ms var(--ease-out);
}
.cv__timeline.is-visible::before { transform: scaleY(1); }
.cv__role { position: relative; display: grid; grid-template-columns: 170px 1fr; gap: 0.4rem 1.6rem; }
.cv__role::before {
  content: ""; position: absolute; left: -30px; top: 0.3rem;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--bg); box-shadow: 0 0 0 2px var(--accent-2);
}
.cv__role + .cv__role { margin-top: 1.6rem; }
.cv__role .when { font-size: 0.82rem; color: var(--ink-3); letter-spacing: 0.04em; padding-top: 0.2rem; }
.cv__role .what b { font-family: var(--font-display); font-weight: 400; color: var(--ink); font-size: 1.12rem; display: block; }
.cv__role .what .org { color: var(--accent-2); font-size: 0.9rem; }
.cv__role .what p { margin-top: 0.5rem; font-size: 0.95rem; }
.cv__role .what ul { margin-top: 0.5rem; display: grid; gap: 0.35rem; }
.cv__role .what li { position: relative; padding-left: 1.3em; font-size: 0.93rem; }
.cv__role .what li::before { content: "▸"; position: absolute; left: 0; color: var(--accent); }
@media (max-width: 620px) { .cv__role { grid-template-columns: 1fr; } .cv__role .when { padding-top: 0; } }
.cv__subhead { font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); margin: 1.3rem 0 0.7rem; }
.cv__subhead:first-child { margin-top: 0; }
.cv__skills { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.cv__skills li { font-size: 0.84rem; padding: 0.45em 0.85em; border-radius: 8px; background: var(--bg-2); color: var(--ink-2); }
.cv__note { margin-top: 1.5rem; font-size: 0.8rem; color: var(--ink-3); font-style: italic; }
.cv__embed { margin-top: 1.4rem; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--bg-2); }
.cv__embed object, .cv__embed iframe { width: 100%; height: 78vh; border: 0; display: block; }

/* ----------  contact  ---------- */
/* four big tappable tiles instead of a thin row of link-rows */
.contact-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(14px, 2vw, 20px); }
@media (max-width: 860px) { .contact-tiles { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .contact-tiles { grid-template-columns: 1fr; } }
/* sits under the masthead copy, in its own (narrower) column, so it always
   reads as two rows of two rather than following the page-wide 4-across grid */
.contact-tiles--2col { grid-template-columns: 1fr 1fr; margin-top: clamp(24px, 4vw, 40px); }
.contact-tiles--2col .contact-tile { padding: 1.2rem 1rem 1.3rem; }
.contact-tiles--2col .contact-tile b { font-size: 0.96rem; }
@media (max-width: 380px) { .contact-tiles--2col .contact-tile b { font-size: 0.88rem; } }
.contact-tile {
  position: relative; display: block;
  padding: 1.6rem 1.4rem 1.5rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--bg-1);
  transition: border-color var(--dur) var(--ease-out), transform var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.contact-tile .ico {
  width: 2.6em; height: 2.6em; margin-bottom: 1.1rem;
  border-radius: 50%; display: grid; place-items: center;
  box-shadow: inset 0 0 0 1.6px var(--accent-2); color: var(--accent-2);
}
.contact-tile .ico svg { width: 1.1em; height: 1.1em; fill: currentColor; display: block; }
.contact-tile small { display: block; font-size: 0.68rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-3); }
.contact-tile b { display: block; margin-top: 0.35rem; font-family: var(--font-display); font-weight: 400; font-size: 1.05rem; color: var(--ink); word-break: break-word; line-height: 1.3; }
.contact-tile .arrow {
  position: absolute; top: 1.3rem; right: 1.3rem;
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  box-shadow: inset 0 0 0 1px var(--line-2);
  transition: transform var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .contact-tile:hover { border-color: var(--line-2); transform: translateY(-3px); }
  .contact-tile:hover .arrow { transform: rotate(-45deg); background: var(--accent); color: var(--accent-ink); }
}

/* form card + aside */
.contact-split { display: grid; grid-template-columns: 1.5fr 1fr; gap: clamp(32px, 5vw, 72px); align-items: start; }
@media (max-width: 900px) { .contact-split { grid-template-columns: 1fr; } }
.form-card {
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: linear-gradient(180deg, var(--bg-1), var(--bg));
  padding: clamp(24px, 4vw, 44px);
}
.contact-chips { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 1.1rem 0 1.6rem; }
.contact-chips button {
  font-size: 0.78rem; letter-spacing: 0.04em;
  padding: 0.55em 1.05em; border-radius: 999px;
  color: var(--ink-2); box-shadow: inset 0 0 0 1px var(--line);
  transition: color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) { .contact-chips button:hover { color: var(--ink); box-shadow: inset 0 0 0 1px var(--line-2); } }
.contact-chips button:active { color: var(--accent-ink); background: var(--accent-2); box-shadow: none; }

.contact-aside { padding-top: 0.4rem; }
.contact-clock {
  margin-top: clamp(28px, 4vw, 40px);
  padding-top: clamp(20px, 3vw, 28px);
  border-top: 1px solid var(--line);
}
.contact-clock small { display: block; font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-3); }
.contact-clock b { display: block; margin-top: 0.4rem; font-family: var(--font-display); font-weight: 300; font-size: clamp(2rem, 3.4vw, 2.6rem); color: var(--accent-2); font-variant-numeric: tabular-nums; }
.contact-clock span { font-size: 0.85rem; color: var(--ink-3); }

.field { display: grid; gap: 0.5rem; margin-bottom: 1.2rem; }
.field label { font-size: 0.74rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3); }
.field input, .field textarea {
  font: inherit; color: var(--ink); background: var(--bg-1);
  border: 1px solid var(--line); border-radius: 10px; padding: 0.85em 1em;
  transition: border-color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.field input:focus, .field textarea:focus { outline: none; border-color: var(--accent); background: var(--bg-2); }
.field textarea { min-height: 140px; resize: vertical; }

/* ----------  footer (socials only)  ---------- */
.footer {
  --footer-rose: #b96d60;
  position: relative;
  padding: clamp(32px, 4.6vh, 52px) var(--gutter);
  border-top: 1px solid var(--line);
  background: linear-gradient(180deg, #34120f, #230b09);
}
/* contact row */
.footer__contacts {
  width: min(100% - var(--gutter) * 2, 1640px);
  margin-inline: auto;
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap;
  gap: 1.4rem 2rem;
}
.footer__contacts a {
  display: inline-flex; align-items: center; gap: 0.75em;
  color: var(--footer-rose);
  font-size: clamp(0.85rem, 1.15vw, 1rem);
  letter-spacing: 0.01em;
  transition: color var(--dur) var(--ease-out);
}
.footer__contacts .ico {
  flex: none;
  width: 2.5em; height: 2.5em;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1.6px currentColor;
  display: grid; place-items: center;
}
.footer__contacts .ico svg { width: 1.15em; height: 1.15em; fill: currentColor; display: block; }
@media (hover: hover) and (pointer: fine) {
  .footer__contacts a:hover { color: #e39387; }
}

@media (max-width: 820px) {
  .footer__contacts { gap: 1.1rem 2rem; }
  .footer__contacts a { font-size: 0.82rem; }
}
@media (max-width: 520px) {
  .footer__contacts { display: grid; grid-template-columns: 1fr; justify-items: start; row-gap: 1.1rem; }
  .footer__contacts a { max-width: 100%; }
}

/* client marquee */
.footer__clients {
  width: min(100% - var(--gutter) * 2, 1640px);
  margin: 0 auto clamp(28px, 4vw, 44px);
  padding-bottom: clamp(20px, 3vw, 32px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.footer__marquee { display: flex; width: max-content; gap: clamp(12px, 1.6vw, 18px); animation: footerMarquee 130s linear infinite; }
.footer__marquee__tile {
  flex: none; background: #fff; border-radius: 8px; height: 52px; width: 100px;
  display: grid; place-items: center; padding: 8px 12px;
}
.footer__marquee__tile img { max-width: 100%; max-height: 28px; width: auto; height: auto; object-fit: contain; }
@media (hover: hover) and (pointer: fine) {
  .footer__clients:hover .footer__marquee { animation-play-state: paused; }
}
@keyframes footerMarquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ----------  reduced motion  ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
  .reveal, .reveal-img { opacity: 1 !important; transform: none !important; clip-path: none !important; }
  .cursor, .cursor-label, .ripple { display: none !important; }
  .marquee__track { animation: none; }
  .loader { display: none !important; }
  .page-main { opacity: 1 !important; transform: none !important; }
  html:not(.js) .page-main { opacity: 1; }
}

/* no-JS safety: never leave content hidden */
html:not(.js) .reveal,
html:not(.js) .reveal-img,
html:not(.js) .page-main { opacity: 1 !important; transform: none !important; clip-path: none !important; }

/* ----------  print (CV)  ---------- */
@media print {
  .nav, .footer, .loader, .scroll-progress, .cursor, .cursor-label,
  .cv__bar, .cv__embed, .divider-word { display: none !important; }
  body { background: #fff; color: #111; }
  .page-main { opacity: 1 !important; transform: none !important; }
  .cv, .cv__doc { border: 0; background: #fff; padding: 0; }
  .cv__doc, .prose, .about-grid { color: #111; }
  .cv__section { break-inside: avoid; border-color: #ccc; }
  .cv__subhead { color: #444; }
  .facts b { color: #111; }
  .cv__section h4 { color: #000; }
  .cv__role .what b, .cv h1 { color: #000; }
  .cv__skills li { background: #eee; color: #222; }
  a { color: #111; text-decoration: none; }
  @page { margin: 16mm; }
}
