/* ==========================================================================
   Persona — Freelancer vCard / personal portfolio template
   Design layer (custom). Bootstrap 5 handles reset + grid only.
   Palette: deep forest ink + warm marigold accent on warm paper.
   Type:  Bricolage Grotesque (display) / Hanken Grotesk (body).
   ========================================================================== */

:root {
  /* --- palette --- */
  --ink:        #14231C;   /* deepest — footer, hero name */
  --forest:     #1C3A2E;   /* dark sections */
  --forest-2:   #244A3A;   /* card surface on dark */
  --forest-line:#33604C;   /* hairlines on dark */
  --paper:      #F4F1E8;   /* main light bg */
  --paper-2:    #ECE7D9;   /* alt light bg / cards */
  --paper-line: #DCD6C6;   /* hairlines on light */
  --marigold:   #E6A017;   /* accent — backgrounds, icons, borders, on-dark text only */
  --marigold-d: #C0810A;   /* accent hover / darker — same usage rule as above */
  --marigold-t: #F6E4B4;   /* accent tint */
  --marigold-text: #8A600D; /* accent as TEXT on light backgrounds — meets 4.5:1 on --paper/--paper-2 (marigold/marigold-d don't) */
  --clay:       #B0512C;   /* rare secondary warm accent */
  --sage:       #58685F;   /* muted green-grey text (light bg) — darkened from #6E8377 to meet 4.5:1 on --paper */
  --sage-d:     #A9BBAF;   /* muted text on dark bg */

  --ink-on-light: #22322A;
  --soft-on-dark: #E7E3D4;

  /* --- type --- */
  --f-display: "Bricolage Grotesque", ui-serif, Georgia, serif;
  --f-body:    "Hanken Grotesk", system-ui, -apple-system, sans-serif;

  /* --- shape --- */
  --r:    10px;
  --r-sm: 6px;
  --r-lg: 18px;
  --shadow: 0 18px 50px -22px rgba(20, 35, 28, .45);
  --shadow-sm: 0 8px 24px -14px rgba(20, 35, 28, .40);

  /* --- rhythm --- */
  --sp-section: clamp(4.5rem, 9vw, 8rem);
  --wrap: 1200px;

  /* --- motion ---
     One signature curve (--e) carries ~80% of the site: a decelerating
     ease-out, which is what entrances and hovers want. --e-in is its mirror
     for exits, --e-soft for ambient loops that must not "land". Durations are
     a 3-step palette — anything outside it is a bug, not a decision. */
  --e:      cubic-bezier(.22, .61, .36, 1);
  --e-in:   cubic-bezier(.55, 0, 1, .45);
  --e-soft: cubic-bezier(.4, 0, .2, 1);
  --d-1: 140ms;  /* press, hover, micro-feedback */
  --d-2: 260ms;  /* state change, card hover, nav */
  --d-3: 520ms;  /* entrances, reveals, page-level */
}

/* --------------------------------------------------------------- base --- */
* { box-sizing: border-box; }

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

body {
  font-family: var(--f-body);
  background: var(--paper);
  color: var(--ink-on-light);
  font-size: 1.0625rem;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* Film grain. A single fixed layer over the whole page — breaks the digital
   flatness of the flat paper/forest fills without touching any component.
   Inline SVG turbulence so it costs no request and no image decode. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9;
  pointer-events: none;
  opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

h1, h2, h3, h4 {
  font-family: var(--f-display);
  font-weight: 700;
  line-height: 1.03;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin: 0;
  text-wrap: balance; /* kills orphaned last words in headings */
}

p { margin: 0 0 1rem; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

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

/* Cross-page fade. Native, opt-in, ignored where unsupported, so the jump
   between index / projects / case studies stops flashing white. The default
   root cross-fade is deliberately left alone — it also covers the in-page
   work-filter transition, and a custom asymmetric curve there read as a
   full-page flash. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) {
  /* browsers do not reliably suppress the nav cross-fade on their own */
  @view-transition { navigation: none; }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

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

img {
  max-width: 100%;
  display: block;
  -webkit-touch-callout: none; /* blocks the iOS long-press "Save Image" menu */
  -webkit-user-select: none;
  user-select: none;
}

ul { margin: 0; padding: 0; list-style: none; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 2.75rem);
}

/* accessibility -------------------------------------------------------- */
.skip-link {
  position: absolute;
  left: 1rem;
  top: -100px;
  z-index: 2000;
  background: var(--marigold);
  color: var(--ink);
  font-weight: 700;
  padding: .7rem 1.1rem;
  border-radius: var(--r-sm);
  transition: top var(--d-2) var(--e);
}
.skip-link:focus { top: 1rem; }

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

/* --------------------------------------------------------- typography --- */
.eyebrow {
  font-family: var(--f-body);
  font-weight: 700;
  font-size: .78rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--marigold-text);
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  margin: 0 0 1.1rem;
}
.eyebrow::before {
  content: "";
  width: 26px;
  height: 2px;
  background: var(--marigold);
  display: inline-block;
}
.on-dark .eyebrow { color: var(--marigold); }

.section-head { max-width: 640px; margin-bottom: clamp(2.5rem, 5vw, 3.75rem); }
.section-head h2 { font-size: clamp(2rem, 4.6vw, 3.35rem); }
.section-head p {
  margin-top: 1.1rem;
  color: var(--sage);
  font-size: 1.12rem;
}
.on-dark .section-head p { color: var(--sage-d); }
.on-dark h1, .on-dark h2, .on-dark h3, .on-dark h4 { color: #FBFAF4; }

.lead {
  font-size: clamp(1.12rem, 2vw, 1.4rem);
  line-height: 1.55;
  color: var(--ink-on-light);
}

/* --------------------------------------------------------------- btns --- */
.btn2 {
  --bg: var(--marigold);
  --fg: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  font-family: var(--f-body);
  font-weight: 700;
  font-size: .98rem;
  line-height: 1;
  padding: .95rem 1.55rem;
  border-radius: var(--r-sm);
  border: 2px solid var(--bg);
  background: var(--bg);
  color: var(--fg);
  cursor: pointer;
  transition: transform var(--d-1) var(--e), background var(--d-1) var(--e),
              border-color var(--d-1) var(--e), color var(--d-1) var(--e),
              box-shadow var(--d-2) var(--e);
  white-space: nowrap;
}
.btn2:hover {
  transform: translateY(-2px);
  background: var(--marigold-d);
  border-color: var(--marigold-d);
  box-shadow: 0 10px 22px -12px rgba(192, 129, 10, .75); /* shadow tinted to the button, not black */
}
/* press: overshoot back past rest so the click has a floor to hit */
.btn2:active { transform: translateY(1px) scale(.98); transition-duration: 60ms; box-shadow: none; }
.btn2 svg { width: 18px; height: 18px; transition: transform var(--d-2) var(--e); }
.btn2:hover svg { transform: translateX(3px); }

.btn2--ghost {
  --bg: transparent;
  --fg: var(--ink);
  border-color: var(--ink);
}
.btn2--ghost:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }

.on-dark .btn2--ghost { --fg: #FBFAF4; border-color: rgba(255,255,255,.55); }
.on-dark .btn2--ghost:hover { background: #FBFAF4; color: var(--forest); border-color: #FBFAF4; }

.text-link {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-weight: 700;
  color: var(--ink);
  border-bottom: 2px solid var(--marigold);
  padding-bottom: 2px;
  transition: gap var(--d-2) var(--e), color var(--d-1) var(--e);
}
.text-link:hover { gap: .8rem; color: var(--marigold-d); }
.text-link:active { gap: .55rem; transition-duration: 60ms; }
.on-dark .text-link { color: #FBFAF4; }
.on-dark .text-link:hover { color: var(--marigold); }

/* ------------------------------------------------------------- header --- */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 1000;
  transition: background var(--d-2) var(--e), box-shadow var(--d-2) var(--e);
  padding: .55rem 0;
}
/* ------------------------------------------------------- WebGL hero blobs --
   index.html carries the .hero-3d host; projects.html and 404.html do not, so
   these rules are simply unused there.

   The canvas host starts fully transparent and is only faded in once
   hero-3d.js has rendered a real frame (.is-live). .has-hero3d is set on
   <body> at the same moment to retire the CSS blob backdrop — so if WebGL
   is unavailable, blocked, or reduced-motion is on, the CSS blobs simply stay
   and the hero looks exactly as it did before this was added. */
.hero-3d {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 900ms var(--e);
  contain: paint;
}
.hero-3d.is-live { opacity: 1; }
.hero-3d canvas { display: block; width: 100%; height: 100%; }

/* Cross-fade rather than cut: the blobs ease out as the canvas eases in. */
body.has-hero3d .hero-blobs { opacity: 0; transition: opacity 900ms var(--e); }

@media (prefers-reduced-motion: reduce) {
  /* Belt and braces — hero-3d.js already refuses to start under reduced
     motion, but if it ever did, this keeps the canvas hidden and the static
     blob backdrop in place. */
  .hero-3d { display: none; }
  body.has-hero3d .hero-blobs { opacity: 1; }
}

/* Scroll progress — the element is injected by js/main.js (NOT motion.js, so
   it works on projects.html and 404.html, neither of which loads GSAP) and
   only exists when JS ran and the user has not requested reduced motion. So
   there is no no-JS state to style here. Kept to 2px and the accent tint so
   it reads as a quiet position indicator, not a loading bar. */
.scroll-progress {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 2px;
  background: var(--marigold);
  transform: scaleX(0);
  transform-origin: left center;
  pointer-events: none;
  will-change: transform;
}

.site-header.is-stuck {
  background: rgba(244, 241, 232, .5);
  backdrop-filter: saturate(140%) blur(12px);
  box-shadow: 0 1px 0 var(--paper-line), 0 10px 30px -24px rgba(20,35,28,.5);
}
.header-inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  font-family: var(--f-display);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-size: 1.15rem;
}
.brand small {
  display: block;
  font-family: var(--f-body);
  font-weight: 600;
  font-size: .68rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--sage);
  margin-top: 2px;
}

.nav {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.4vw, 2.1rem);
}
.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.2vw, 2rem);
}
.nav-links a {
  font-weight: 600;
  font-size: .96rem;
  color: var(--ink-on-light);
  position: relative;
  padding: .35rem 0;
}
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; bottom: -1px;
  width: 100%; height: 2px;
  background: var(--marigold);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--d-2) var(--e);
}
.nav-links a:hover::after, .nav-links a.active::after { transform: scaleX(1); }

.nav-toggle {
  display: none;
  width: 46px; height: 46px;
  border: 2px solid var(--ink);
  border-radius: var(--r-sm);
  background: transparent;
  cursor: pointer;
  padding: 0;
  align-items: center;
  justify-content: center;
}
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
  content: "";
  display: block;
  width: 20px; height: 2px;
  background: var(--ink);
  transition: transform var(--d-2) var(--e), opacity var(--d-1) var(--e);
}
.nav-toggle span::before { transform: translateY(-6px); position: absolute; width: 20px; }
.nav-toggle span::after  { transform: translateY(6px);  position: absolute; width: 20px; }
.nav-toggle span { position: relative; }
.nav-toggle[aria-expanded="true"] span { background: transparent; }
.nav-toggle[aria-expanded="true"] span::before { transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] span::after  { transform: rotate(-45deg); }

/* ------------------------------------------------------------- layout --- */
main { display: block; }

.section { padding-block: var(--sp-section); position: relative; }
.section--forest { background: var(--forest); color: var(--soft-on-dark); }
.section--forest p { color: var(--sage-d); }
.section--ink { background: var(--ink); color: var(--soft-on-dark); }
.section--paper2 { background: var(--paper-2); }

/* --------------------------------------------------------------- hero --- */
.hero {
  padding-top: clamp(7rem, 13vw, 10.5rem);
  padding-bottom: clamp(3.5rem, 7vw, 6rem);
  position: relative;
  overflow: hidden;
}

/* ambient blobs, layered behind hero content — 5 independently-timed
   circles so their loops never sync up, reads as organic drifting rather
   than a mechanical loop */
.hero-blobs { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; contain: paint; }
.hero-blobs span {
  position: absolute;
  will-change: transform;
  /* backface-visibility forces its own compositor layer, which stops Safari's
     well-known blur+transform flicker (the blurred edge re-rasterizes each
     frame without it) — everywhere else it's a no-op since these never flip. */
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
/* Five 65vw layers under a 50px blur, transforming forever, are the most
   expensive thing on the page. Once the hero leaves the viewport there is
   nothing to look at — stop paying for it. main.js toggles the class. */
.hero.is-offscreen .hero-blobs span { animation-play-state: paused; }

/* Hero gets its own entrance rather than borrowing the generic scroll-reveal:
   first thing seen, so it earns a wider stagger and more travel on the name.
   NOTE: do not reveal this with a collapsing clip-path. A fully clipped
   element reports isIntersecting = false to IntersectionObserver, so
   .is-visible never lands and the name stays invisible forever. */
.hero .reveal { transition-delay: calc(var(--i, 0) * 90ms); }
#hero-name {
  transform: translateY(34px);
  transition: opacity 760ms var(--e), transform 760ms var(--e);
}
/* ID beats .reveal.is-visible, so the rest state has to be restated here */
#hero-name.is-visible { transform: none; }

/* ---------------------------------------------- hero name outline -> fill --
   "Miranda." holds as a marigold outline while the page settles, then fills
   solid one letter at a time.

   Driven by .is-lit, which js/main.js adds 1s after the name appears — NOT by
   a CSS animation. An animation with `fill-mode: both` would also work, but
   animation declarations outrank normal ones in the cascade, so its held end
   state would then beat every other rule touching .accent colour. A class
   plus a staggered transition keeps everything at normal-declaration weight,
   and therefore predictable.

   Letters are `inline`, never inline-block, so splitting the word adds no
   layout of its own. Only colour changes, which is paint-only. */
.hero h1 .accent .l {
  color: transparent;
  -webkit-text-stroke: 2px var(--marigold);
  transition: color 520ms var(--e), -webkit-text-stroke-color 520ms var(--e);
}

/* The fill. --i is set per letter in the markup; 110ms apart reads as
   deliberate rather than as a wipe. Eight letters means the last one starts
   770ms after the first, finishing about 1.3s after the sequence begins.

   Qualified with #hero-name so it outranks the base rule above regardless of
   where either ends up in the file — that ordering trap has bitten this
   stylesheet repeatedly. The stroke fades as the fill lands so the finished
   word weighs the same as "Kevin"; a 2px stroke left under a solid fill
   reads as a heavier weight. */
#hero-name.is-lit .accent .l {
  color: var(--marigold);
  -webkit-text-stroke-color: transparent;
  transition-delay: calc(var(--i, 0) * 110ms);
}

/* Windows High Contrast strips stroke painting but keeps `color:
   transparent`, which would leave the name invisible. Force a solid system
   colour in every state. */
@media (forced-colors: active) {
  .hero h1 .accent .l,
  #hero-name.is-lit .accent .l {
    color: CanvasText !important;
    -webkit-text-stroke: none !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* No outline phase and no stagger: land filled and stay there. */
  .hero h1 .accent .l,
  #hero-name.is-lit .accent .l {
    color: var(--marigold);
    -webkit-text-stroke-color: transparent;
    transition: none;
    transition-delay: 0s;
  }
}

.hero-blobs .b1 {
  left: -14%; bottom: -22%;
  width: 65vw; height: 65vw;
  max-width: 950px; max-height: 950px;
  background: radial-gradient(circle at center, rgba(230,160,23,.32), transparent 92%);
  border-radius: 42% 58% 65% 35% / 45% 40% 60% 55%;
  filter: blur(50px);
  animation: heroFloat1 34s cubic-bezier(.45,.05,.55,.95) infinite;
}
.hero-blobs .b2 {
  top: -18%; right: -14%;
  width: 60vw; height: 60vw;
  max-width: 800px; max-height: 800px;
  background: radial-gradient(circle at 35% 35%, rgba(246,228,180,.78), transparent 92%);
  border-radius: 60% 40% 38% 62% / 55% 62% 38% 45%;
  filter: blur(46px);
  animation: heroFloat2 40s var(--e-soft) infinite 2s;
}
.hero-blobs .b3 {
  top: 44%; left: 40%;
  width: 39vw; height: 39vw;
  max-width: 525px; max-height: 525px;
  background: radial-gradient(circle at 50% 50%, rgba(206,110,20,.46), transparent 92%);
  border-radius: 38% 62% 55% 45% / 62% 45% 55% 38%;
  filter: blur(38px);
  animation: heroFloat3 27s cubic-bezier(.37,0,.63,1) infinite 1s;
}
.hero-blobs .b4 {
  top: 4%; left: 15%;
  width: 34vw; height: 34vw;
  max-width: 450px; max-height: 450px;
  background: radial-gradient(circle at 50% 50%, rgba(192,129,10,.4), transparent 92%);
  border-radius: 55% 45% 40% 60% / 40% 55% 45% 60%;
  filter: blur(34px);
  animation: heroFloat4 23s var(--e-soft) infinite 4s;
}
.hero-blobs .b5 {
  bottom: 0%; right: 10%;
  width: 36vw; height: 36vw;
  max-width: 500px; max-height: 500px;
  background: radial-gradient(circle at 50% 50%, rgba(216,124,24,.42), transparent 92%);
  border-radius: 48% 52% 62% 38% / 52% 38% 62% 48%;
  filter: blur(36px);
  animation: heroFloat5 37s cubic-bezier(.42,0,.58,1) infinite 6s;
}
.hero-copy, .hero-figure { position: relative; z-index: 1; }

/* each blob wanders through 7-8 irregularly-spaced waypoints (translate +
   scale, several with a slight rotate) using viewport units so the travel
   spans the whole section; the uneven timing, varied magnitudes/signs, and
   distinct durations/delays/easings keep every loop from ever reading as a
   simple back-and-forth or lining up with the others */
@keyframes heroFloat1 {
  0%   { transform: translate(0, 0) scale(1) rotate(0deg); }
  14%  { transform: translate(9vw, -19vh) scale(1.08) rotate(4deg); }
  29%  { transform: translate(27vw, -6vh) scale(0.93) rotate(-3deg); }
  41%  { transform: translate(19vw, 17vh) scale(1.15); }
  58%  { transform: translate(-4vw, 24vh) scale(0.88) rotate(6deg); }
  73%  { transform: translate(-14vw, 4vh) scale(1.05) rotate(-5deg); }
  89%  { transform: translate(-6vw, -13vh) scale(0.95); }
  100% { transform: translate(0, 0) scale(1) rotate(0deg); }
}
@keyframes heroFloat2 {
  0%   { transform: translate(0, 0) scale(1); }
  17%  { transform: translate(-14vw, 11vh) scale(1.1); }
  33%  { transform: translate(-27vw, -4vh) scale(0.9) rotate(-4deg); }
  47%  { transform: translate(-18vw, -21vh) scale(1.05); }
  63%  { transform: translate(3vw, -16vh) scale(0.92) rotate(5deg); }
  78%  { transform: translate(11vw, 2vh) scale(1.12); }
  92%  { transform: translate(-3vw, 13vh) scale(0.96); }
  100% { transform: translate(0, 0) scale(1); }
}
@keyframes heroFloat3 {
  0%   { transform: translate(0, 0) scale(1) rotate(0deg); }
  19%  { transform: translate(-10vw, -17vh) scale(1.12) rotate(-7deg); }
  34%  { transform: translate(6vw, -22vh) scale(0.88); }
  52%  { transform: translate(19vw, -7vh) scale(1.08) rotate(5deg); }
  68%  { transform: translate(14vw, 13vh) scale(0.9); }
  84%  { transform: translate(-5vw, 17vh) scale(1.06) rotate(-4deg); }
  100% { transform: translate(0, 0) scale(1) rotate(0deg); }
}
@keyframes heroFloat4 {
  0%   { transform: translate(0, 0) scale(1); }
  16%  { transform: translate(11vw, 9vh) scale(0.92); }
  31%  { transform: translate(21vw, -6vh) scale(1.14); }
  49%  { transform: translate(9vw, -19vh) scale(0.9) rotate(6deg); }
  66%  { transform: translate(-8vw, -11vh) scale(1.08); }
  83%  { transform: translate(-3vw, 7vh) scale(0.95) rotate(-5deg); }
  100% { transform: translate(0, 0) scale(1); }
}
@keyframes heroFloat5 {
  0%   { transform: translate(0, 0) scale(1); }
  13%  { transform: translate(-8vw, -14vh) scale(1.1); }
  27%  { transform: translate(-19vw, -2vh) scale(0.9) rotate(-6deg); }
  44%  { transform: translate(-13vw, 15vh) scale(1.12); }
  61%  { transform: translate(4vw, 20vh) scale(0.88) rotate(4deg); }
  77%  { transform: translate(13vw, 6vh) scale(1.05); }
  90%  { transform: translate(6vw, -9vh) scale(0.94); }
  100% { transform: translate(0, 0) scale(1); }
}

@media (max-width: 640px) {
  .hero-blobs .b4, .hero-blobs .b5 { display: none; }
  .hero-blobs .b1 { filter: blur(30px); }
  .hero-blobs .b2 { filter: blur(28px); }
  .hero-blobs .b3 { filter: blur(24px); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-blobs span { animation: none; }
}
.hero-grid {
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  background: var(--paper-2);
  border: 1px solid var(--paper-line);
  color: var(--ink-on-light);
  font-weight: 600;
  font-size: .84rem;
  padding: .4rem .85rem;
  border-radius: 100px;
  margin-bottom: 1.6rem;
}
.hero-badge .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: #37a06b;
  box-shadow: 0 0 0 4px rgba(55,160,107,.18);
}
.hero h1 {
  font-size: clamp(3rem, 8.6vw, 6.6rem);
  font-weight: 800;
  letter-spacing: -0.035em;
  margin: 0 0 1.3rem;
}
/* NOTE: the resting marigold for .accent is set by #hero-name.is-visible
   .accent further up this file, as part of the outline -> fill treatment.
   A plain `.hero h1 .accent { color: var(--marigold) }` used to live HERE,
   and because it has the same specificity (0,2,1) but sits later in the
   file, it silently overrode the outline state — the fill never rendered at
   all. Do not reintroduce it. */
.hero .role {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: clamp(1.05rem, 2.3vw, 1.5rem);
  color: var(--sage);
  margin-bottom: 1.5rem;
  letter-spacing: -0.01em;
}
.hero .intro { max-width: 30rem; margin-bottom: 2rem; color: #3c4a41; }

.hero-cta { display: flex; flex-wrap: wrap; gap: .85rem; margin-bottom: 2.2rem; }

.socials { display: flex; align-items: center; gap: .65rem; }
.socials a {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 1.5px solid var(--paper-line);
  border-radius: var(--r-sm);
  color: var(--ink);
  background: var(--paper);
  transition: transform var(--d-2) var(--e), background var(--d-1) var(--e),
              color var(--d-1) var(--e), border-color var(--d-1) var(--e);
}
.socials a:hover { background: var(--ink); color: var(--marigold); border-color: var(--ink); transform: translateY(-3px); }
.socials a:active { transform: translateY(0) scale(.94); transition-duration: 60ms; }
.socials svg { width: 20px; height: 20px; }

/* framed portrait — shared by hero + about */
.hero-figure { position: relative; justify-self: end; width: 100%; max-width: 460px; }
.hero-figure .frame,
.about-figure .frame {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  aspect-ratio: 4 / 5;
  box-shadow: var(--shadow);
  z-index: 2;
  background: var(--marigold);
}
.hero-figure .frame img,
.about-figure .frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-figure::before,
.about-figure::before {
  /* offset marigold block behind portrait */
  content: "";
  position: absolute;
  right: -18px; top: -18px;
  width: 62%; height: 58%;
  background: var(--marigold);
  border-radius: var(--r-lg);
  z-index: 1;
}
.hero-figure::after,
.about-figure::after {
  content: "";
  position: absolute;
  left: -22px; bottom: 26px;
  width: 90px; height: 90px;
  border: 2px solid var(--forest);
  border-radius: 50%;
  z-index: 1;
}
.hero-chip,
.about-chip {
  position: absolute;
  left: -26px; bottom: -22px;
  z-index: 3;
  background: var(--ink);
  color: #fff;
  border-radius: var(--r);
  padding: .85rem 1.1rem;
  box-shadow: var(--shadow-sm);
  display: flex;
  align-items: center;
  gap: .8rem;
}
.hero-chip .num,
.about-chip .num {
  font-family: var(--f-display);
  font-weight: 800;
  font-size: 1.8rem;
  color: var(--marigold);
  line-height: 1;
}
.hero-chip .lbl,
.about-chip .lbl { font-size: .78rem; line-height: 1.25; color: var(--sage-d); }

/* --------------------------------------------------------- marquee/logos --- */
.logos-band {
  padding-block: clamp(2.5rem, 5vw, 3.5rem);
  background: var(--paper-2);
  overflow: hidden;
}
.logos-label {
  text-align: center;
  font-weight: 600;
  font-size: .8rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--sage);
  margin-bottom: 1.5rem !important;
}
.logos-band .marquee-track { animation-duration: 30s; }
.logo-chip {
  display: inline-flex;
  align-items: center;
  height: clamp(2.2rem, 4vw, 2.8rem);
  margin-right: clamp(2.5rem, 5vw, 4rem);
  flex: none;
}
.logo-chip img {
  height: 100%;
  width: auto;
  display: block;
}

.tools-band {
  padding-block: clamp(2.5rem, 5vw, 3.5rem);
  border-top: 1px solid var(--paper-line);
  border-bottom: 1px solid var(--paper-line);
  background: var(--paper-2);
  overflow: hidden;
}
.tools-label {
  text-align: center;
  font-weight: 600;
  font-size: .8rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--sage);
  margin-bottom: 1.5rem !important;
}
.marquee { position: relative; width: 100%; overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.marquee-track {
  display: flex;
  gap: 0;
  width: max-content;
  animation: marquee 34s linear infinite;
}
/* pause on focus too — keyboard users had no way to stop the scroll */
.marquee:hover .marquee-track,
.marquee:focus-within .marquee-track { animation-play-state: paused; }
/* and stop burning frames entirely once the band scrolls out of view (JS) */
.marquee.is-offscreen .marquee-track { animation-play-state: paused; }
/* There are three identical chip sets. Every chip owns its trailing gap, so
   each set has the exact same width and the loop can advance by one third. */
@keyframes marquee { to { transform: translateX(-33.333333%); } }
@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; flex-wrap: wrap; justify-content: center; }
  .marquee { -webkit-mask-image: none; mask-image: none; }
}
.tool-chip {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  padding: .7rem 1.15rem;
  background: var(--paper);
  border: 1px solid var(--paper-line);
  border-radius: 100px;
  font-weight: 600;
  font-size: .95rem;
  color: var(--ink-on-light);
  white-space: nowrap;
  margin-right: 1rem;
}
.tool-chip svg { width: 18px; height: 18px; color: var(--marigold-d); }

/* -------------------------------------------------------------- about --- */
.about-grid {
  display: grid;
  grid-template-columns: .9fr 1.1fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
.about-figure { position: relative; width: 100%; max-width: 460px; }
.about-body h2 { font-size: clamp(1.9rem, 4vw, 3rem); margin-bottom: 1.3rem; }
.about-body .lead { margin-bottom: 1.2rem; }
.about-body p { color: #3c4a41; }

/* ---------------------------------------------------------- experience --- */
.timeline { position: relative; max-width: 54rem; }

.tl-item {
  display: grid;
  grid-template-columns: 22px 1fr;
  column-gap: 1.5rem;
  padding-bottom: clamp(2.25rem, 4.5vw, 3.25rem);
}
.tl-item:last-child { padding-bottom: 0; }

.tl-rail { position: relative; display: flex; justify-content: center; }
.tl-rail::before {
  content: "";
  position: absolute;
  top: 24px;
  bottom: calc(-1 * clamp(2.25rem, 4.5vw, 3.25rem));
  left: 50%;
  width: 1px;
  background: rgba(255, 255, 255, .16);
  /* --draw is animated 0 -> 1 by js/motion.js so the rail draws downward as
     each entry scrolls in. It defaults to 1, so with JS disabled, GSAP
     blocked, or reduced motion on, the rail is simply fully drawn. Do not
     remove the default. */
  transform: translateX(-50%) scaleY(var(--draw, 1));
  transform-origin: top center;
}
.tl-item:last-child .tl-rail::before { display: none; }
.tl-dot {
  width: 20px; height: 20px;
  margin-top: 3px;
  border-radius: 50%;
  background: var(--paper);
  border: 2.5px solid var(--marigold);
  flex-shrink: 0;
}

.tl-body { display: flex; gap: clamp(1rem, 3vw, 2rem); flex-wrap: wrap; }
.tl-date {
  flex: 0 0 130px;
  padding-top: 5px;
  font-weight: 700;
  font-size: .95rem;
  color: var(--soft-on-dark);
  white-space: nowrap;
}
.tl-duration {
  display: block;
  margin-top: .3rem;
  font-weight: 600;
  font-size: .78rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--sage-d);
}
.tl-content { flex: 1 1 320px; min-width: 0; }
.tl-content h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); margin: 0 0 .3rem; color: var(--marigold); }
.tl-meta { color: #ffffff !important; font-weight: 600; font-size: .95rem; margin: 0 0 .75rem; }
.tl-meta a { color: inherit; }
.tl-meta a:hover { text-decoration: underline; }
.tl-content > p:not(.tl-meta):not(.tl-range) {
  color: var(--sage-d);
  margin: 0 0 .6rem;
  max-width: 42rem;
  /* hard cap at 3 lines regardless of viewport width */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.tl-range { font-family: var(--f-display); font-style: italic; font-size: .88rem; color: var(--marigold); margin: 0; }

@media (max-width: 480px) {
  .tl-item { grid-template-columns: 18px 1fr; column-gap: 1rem; }
  .tl-dot { width: 16px; height: 16px; }
  .tl-body { flex-direction: column; gap: .3rem; }
  .tl-date { flex-basis: auto; }
}

/* ------------------------------------------------ leadership & recognition --- */
.lead-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
}
.lead-col h3 { font-size: 1.3rem; margin: 0 0 1rem; }
.lead-col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .9rem; }
.lead-col li {
  position: relative;
  padding-left: 1.35rem;
  font-size: .96rem;
  line-height: 1.6;
  color: var(--ink-on-light);
}
.lead-col li::before {
  content: "";
  position: absolute;
  left: 0; top: .5rem;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--marigold);
}
.lead-col li strong { color: var(--ink); }

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

/* ----------------------------------------------------- quantifiable impact --- */
.impact-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.35rem;
}
.impact-card {
  background: var(--forest-2);
  border: 1px solid var(--forest-line);
  border-radius: var(--r);
  padding: 1.75rem 1.6rem;
  display: flex;
  flex-direction: column;
  gap: .6rem;
  transition: transform var(--d-2) var(--e), border-color var(--d-2) var(--e),
              box-shadow var(--d-2) var(--e);

  /* Cursor spotlight — see .impact-card::before below. --gx/--gy are the
     cursor's position inside the card as percentages, written by main.js.
     They default to the centre, so with JS off, on touch, or under reduced
     motion the hover state is a plain centred glow rather than nothing. */
  position: relative;
  overflow: hidden;
  isolation: isolate;
  --gx: 50%;
  --gy: 50%;
}

/* The glow itself. A lighter tint of the card's own --forest-2 surface, so it
   reads as light falling on the card rather than a coloured wash. Painted as
   a pseudo-element rather than a background-image so the fade in/out can be a
   plain opacity transition. */
.impact-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--d-2) var(--e);
  background: radial-gradient(circle 230px at var(--gx) var(--gy),
              rgba(86, 144, 113, .38) 0%,
              rgba(86, 144, 113, .14) 42%,
              rgba(86, 144, 113, 0) 72%);
}
.impact-card:hover::before,
.impact-card:focus-within::before { opacity: 1; }

/* CONTRAST, and why the copy brightens too.
   --sage-d body text sits at only 4.92:1 on the unlit card — barely over the
   4.5:1 floor — so lightening the surface at all pushes it under. At the .38
   glow above, the brightest point is #37644F, where --sage-d drops to 3.36:1.

   Lowering the glow until the old colour passed would have meant an alpha so
   low the effect stopped reading. Brightening the text instead is both the
   accessible answer AND the physically truthful one: shine a light on a
   surface and everything on it gets brighter, ink included.

   Measured against #37644F (worst case, cursor directly on the text):
     body #CBD6CF  4.54:1  (needs 4.5)
     num  #E6A017  3.04:1  (needs 3.0 — 28px+ at weight 800 is large text)
     link #F6E4B4  5.38:1  (needs 4.5)
   If you raise the glow alpha, re-check these three or the copy goes muddy. */
.impact-card:hover p,
.impact-card:focus-within p { color: #CBD6CF; }
.impact-card p { transition: color var(--d-2) var(--e); }

/* Card content sits above the glow layer. Without this the absolutely
   positioned ::before paints over the in-flow text. */
.impact-card > * { position: relative; z-index: 1; }
/* Qualified with .impact-grid on purpose. Unqualified, `.impact-card:hover`
   is (0,2,0) — exactly the same as `.reveal.is-visible { transform: none }`
   further down this file, which therefore won and silently killed this lift.
   Any hover/active transform on an element that also carries .reveal needs
   the same treatment. */
.impact-grid .impact-card:hover {
  transform: translateY(-4px);
  border-color: var(--marigold);
  box-shadow: 0 20px 40px -28px rgba(0, 0, 0, .85);
}
.impact-num {
  font-family: var(--f-display);
  font-weight: 800;
  font-size: clamp(1.8rem, 3.4vw, 2.4rem);
  color: var(--marigold);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums; /* stats stop shifting width between values */
}
.impact-card p { font-size: .92rem; color: var(--sage-d); margin: 0; flex: 1; }
.impact-link {
  font-weight: 600;
  font-size: .86rem;
  color: var(--marigold-t);
}
.impact-link:hover { color: #fff; }

/* ---------------------------------------------------------- portfolio --- */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin-bottom: 2.25rem;
}
.filter-group {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
}
.filter-btn {
  font-family: var(--f-body);
  font-weight: 600;
  font-size: .92rem;
  padding: .5rem 1.1rem;
  border-radius: 100px;
  border: 1.5px solid var(--paper-line);
  background: transparent;
  color: var(--sage);
  cursor: pointer;
  /* named properties only — `all` was animating layout props too */
  transition: color var(--d-1) var(--e), border-color var(--d-1) var(--e),
              background var(--d-1) var(--e), transform var(--d-1) var(--e);
}
.filter-btn:hover { color: var(--ink); border-color: var(--ink); transform: translateY(-1px); }
.filter-btn:active { transform: scale(.96); transition-duration: 60ms; }
.filter-btn.active { background: var(--ink); color: var(--paper); border-color: var(--ink); }

.filter-bar .btn2 {
  padding: .55rem 1.2rem;
  font-size: .92rem;
  flex-shrink: 0;
}
@media (max-width: 560px) {
  .filter-bar { flex-direction: column; align-items: stretch; }
  .filter-bar .btn2 { justify-content: center; }
}

.work-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.35rem;
}
.work-card {
  position: relative;
  border-radius: var(--r);
  overflow: hidden;
  aspect-ratio: 4/3;
  background: var(--paper-2);
  box-shadow: var(--shadow-sm);
  transition: transform var(--d-2) var(--e), box-shadow var(--d-2) var(--e);
}
/* Same fix as .impact-card:hover above — see the note there. */
.work-grid .work-card:hover { transform: translateY(-5px); box-shadow: var(--shadow); }
.work-grid .work-card:active { transform: translateY(-1px) scale(.995); transition-duration: 80ms; }
.work-card.is-hidden { display: none; }
.work-card img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 600ms var(--e);
}
.work-card:hover img { transform: scale(1.07); }
.work-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(20,35,28,.92) 0%, rgba(20,35,28,.35) 45%, transparent 78%);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 1.4rem;
  opacity: 0;
  transition: opacity var(--d-2) var(--e);
}
.work-card:hover .work-overlay,
.work-card:focus-within .work-overlay { opacity: 1; }
/* Category reads as the headline here — an outsider skimming the grid
   understands "AI Assistant" or "Design System" far faster than a product
   codename like "FourSight" or "Elemental". The actual product name is
   demoted to a small caption underneath. */
.work-overlay .cat {
  font-family: var(--f-display);
  color: #fff;
  font-size: 1.3rem;
  font-weight: 700;
  transform: translateY(10px);
  transition: transform var(--d-2) var(--e) 40ms;
}
.work-overlay .name {
  display: block;
  font-size: .74rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--marigold);
  font-weight: 700;
  margin-top: .35rem;
  transform: translateY(12px);
  transition: transform var(--d-2) var(--e) 70ms;
}
.work-card:hover .work-overlay .cat,
.work-card:hover .work-overlay .name,
.work-card:focus-within .work-overlay .cat,
.work-card:focus-within .work-overlay .name { transform: translateY(0); }
.work-overlay .arrow {
  position: absolute;
  top: 1.2rem; right: 1.2rem;
  width: 42px; height: 42px;
  border-radius: 50%;
  background: var(--marigold);
  color: var(--ink);
  display: grid; place-items: center;
  transform: scale(0) rotate(-30deg);
  /* scale-from-zero plus the rotation is already enough character here; an
     overshoot on top would be the only bouncy curve on an otherwise
     straight-decelerating site. Stays on the signature ease. */
  transition: transform 340ms var(--e) 60ms;
}
.work-card:hover .work-overlay .arrow,
.work-card:focus-within .work-overlay .arrow { transform: scale(1) rotate(0); }
.work-overlay .arrow svg { width: 18px; height: 18px; }
.work-card > a { position: absolute; inset: 0; z-index: 3; }

/* Touch devices have no hover state, so show the overlay info by default.
   Desktop (and any pointer:fine/hover:hover device) keeps the hover-only behavior above. */
@media (hover: none) and (pointer: coarse) {
  .work-overlay { opacity: 1; }
  .work-overlay .cat,
  .work-overlay .name { transform: translateY(0); }
  .work-overlay .arrow { transform: scale(1) rotate(0); }
}

/* --------------------------------------------------------- photography --- */
.photography-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  grid-auto-rows: 60px;
  grid-auto-flow: dense;
  gap: 1.1rem;
}
.photography-card {
  position: relative;
  margin: 0;
  grid-row: span 2;
  overflow: hidden;
  border-radius: var(--r);
  background: var(--paper);
  box-shadow: var(--shadow-sm);
}
.photography-card--tall { grid-row: span 4; }
.photography-card--wide { grid-column: span 2; }
.photography-card .photo-trigger {
  all: unset;
  display: block;
  width: 100%;
  height: 100%;
  cursor: zoom-in;
}
.photography-card .photo-trigger:focus-visible {
  outline: 3px solid var(--marigold);
  outline-offset: -3px;
}
.photography-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 600ms var(--e);
}
.photography-card:hover img { transform: scale(1.04); }
.photography-card figcaption {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 2rem 1rem 1rem;
  margin: 0;
  color: #fff;
  font-family: var(--f-display);
  font-size: .95rem;
  font-weight: 600;
  pointer-events: none;
  background: linear-gradient(to top, rgba(20,35,28,.8), transparent);
}

/* ------------------------------------------------------------- lightbox --- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 3000;
  display: none;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 5vw, 3rem);
  background: rgba(20, 35, 28, .92);
}
.lightbox.is-open { display: flex; flex-direction: column; }
.lightbox-figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 100%;
  max-height: 100%;
}
.lightbox-img {
  max-width: 88vw;
  max-height: 78vh;
  width: auto;
  height: auto;
  border-radius: var(--r);
  box-shadow: 0 30px 70px -20px rgba(0,0,0,.6);
  background: var(--paper-2);
}
.lightbox-caption {
  margin-top: 1rem;
  color: var(--sage-d);
  font-size: .92rem;
  text-align: center;
  max-width: 60ch;
}
.lightbox-close,
.lightbox-nav {
  all: unset;
  position: fixed;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgba(255,255,255,.12);
  color: #fff;
  cursor: pointer;
  transition: background var(--d-1) var(--e);
}
.lightbox-close:hover,
.lightbox-nav:hover { background: rgba(255,255,255,.24); }
.lightbox-close:focus-visible,
.lightbox-nav:focus-visible { outline: 3px solid var(--marigold); outline-offset: 2px; }
.lightbox-close { top: clamp(1rem, 4vw, 2rem); right: clamp(1rem, 4vw, 2rem); }
.lightbox-prev { left: clamp(.5rem, 3vw, 2rem); top: 50%; transform: translateY(-50%); }
.lightbox-next { right: clamp(.5rem, 3vw, 2rem); top: 50%; transform: translateY(-50%); }
.lightbox-nav[hidden] { display: none; }
.lightbox-close svg { width: 18px; height: 18px; }
.lightbox-nav svg { width: 20px; height: 20px; }

/* ------------------------------------------------------------ contact --- */
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}
.contact-intro h2 { font-size: clamp(2rem, 4.5vw, 3.2rem); margin-bottom: 1.2rem; }
.contact-intro p { color: #3c4a41; margin-bottom: 2rem; }
.contact-list { display: flex; flex-direction: column; gap: 1.1rem; margin-bottom: 2rem; }
.contact-list li { display: flex; align-items: center; gap: .95rem; font-weight: 500; }
.contact-list .ico {
  width: 46px; height: 46px; flex: none;
  display: grid; place-items: center;
  background: var(--paper-2);
  border: 1px solid var(--paper-line);
  border-radius: var(--r-sm);
  color: var(--marigold-d);
}
.contact-list .ico svg { width: 20px; height: 20px; }
.contact-list a:hover { color: var(--marigold-d); }
.contact-list small { display: block; color: var(--sage); font-weight: 600; font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; }

.map-card {
  position: relative;
  background: var(--paper-2);
  border: 1px solid var(--paper-line);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.map-card #chennaiMap {
  width: 100%;
  height: clamp(320px, 38vw, 460px);
  background: var(--paper-2);
  pointer-events: none;
  z-index: 0;
}
.map-card .map-pin-tag { z-index: 1; }
.map-card .leaflet-control-attribution {
  font-size: .68rem;
  background: rgba(244,241,232,.85);
  color: var(--sage);
}
.map-card .leaflet-control-attribution a { color: var(--sage); }
.map-card .map-pin-tag {
  position: absolute;
  top: 1.1rem;
  left: 1.1rem;
  display: flex;
  align-items: center;
  gap: .5rem;
  background: var(--paper);
  border: 1px solid var(--paper-line);
  border-radius: var(--r-sm);
  padding: .55rem .9rem;
  font-weight: 700;
  font-size: .88rem;
  color: var(--ink-on-light);
  box-shadow: var(--shadow-sm);
}
.map-card .map-pin-tag svg { width: 18px; height: 18px; color: var(--marigold-d); flex: none; }

/* ------------------------------------------------------------- footer --- */
.site-footer { background: var(--ink); color: var(--sage-d); padding-block: clamp(3.5rem, 6vw, 5rem) 2rem; }
.footer-top {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: clamp(2rem, 4vw, 3.5rem);
  padding-bottom: 3rem;
  border-bottom: 1px solid rgba(255,255,255,.09);
}
.footer-brand .brand { color: #fff; margin-bottom: 1.1rem; }
.footer-brand .brand small { color: var(--sage-d); }
.footer-brand p { max-width: 24rem; color: var(--sage-d); font-size: .98rem; }
.footer-brand .socials { margin-top: 1.5rem; }
.footer-brand .socials a { background: transparent; border-color: rgba(255,255,255,.16); color: #fff; }
.footer-brand .socials a:hover { background: var(--marigold); color: var(--ink); border-color: var(--marigold); }
.footer-col h4 { color: #fff; font-family: var(--f-body); font-weight: 700; font-size: .82rem; letter-spacing: .14em; text-transform: uppercase; margin-bottom: 1.1rem; }
.footer-col a { display: block; padding: .35rem 0; color: var(--sage-d); font-size: .96rem; transition: color var(--d-1) var(--e); }
.footer-col a:hover { color: var(--marigold); }
.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  align-items: center;
  padding-top: 1.75rem;
  font-size: .88rem;
}
.footer-bottom a { color: var(--marigold); font-weight: 600; }

/* ------------------------------------------------------- scroll reveal --- */
/* Entrance: 520ms / 18px. The old 700ms/26px overshot the card-entrance
   budget, so the page always felt like it was catching up to the scroll.
   Stagger comes from --i, which main.js sets per sibling group — the
   hand-written data-delay attributes in the markup are inert now, because
   they produced 1,2,1,2 in the work grid instead of a real cascade.
   No will-change: transform/opacity are auto-promoted for the duration of a
   transition anyway, and pinning it held 51 compositor layers forever. */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--d-3) var(--e), transform var(--d-3) var(--e);
  transition-delay: calc(var(--i, 0) * 55ms);
}
.reveal.is-visible { opacity: 1; transform: none; }

/* Secondary layer: inside a revealed card, the content lands just after the
   card itself rather than with it. Flat entrances are entrances missing this. */
.reveal .work-overlay,
.reveal .impact-num { transition-delay: calc(var(--i, 0) * 55ms + 90ms); }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; transition-delay: 0s; }
}

/* --------------------------------------------------------- responsive --- */
@media (max-width: 991px) {
  .hero-grid { grid-template-columns: 1fr; }
  /* .hero-figure is dead — the hero's framed portrait was replaced by the
     wireframe figure (.wf-figure, styled in index.html's inline block). The
     rule that used to live here carried `grid-row: 1`, which is exactly what
     was forcing the figure above the copy on mobile. Kept as a note so it
     isn't reintroduced; the remaining .hero-figure rules elsewhere in this
     file are inert for the same reason. */
  .about-grid { grid-template-columns: 1fr; }
  .about-figure { max-width: 380px; margin-top: 1rem; }
  .contact-grid { grid-template-columns: 1fr; }
  .work-grid { grid-template-columns: repeat(2, 1fr); }
  .photography-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); grid-auto-rows: 65px; }
  .footer-top { grid-template-columns: 1fr; }
}

@media (max-width: 767px) {
  .impact-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .photography-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-rows: 70px; }
  .nav-toggle { display: inline-flex; }
  .nav-links, .nav .btn2 { display: none; }
  .nav.open .nav-links {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .35rem;
    position: absolute;
    top: calc(100% + .5rem);
    right: clamp(1.25rem, 4vw, 2.75rem);
    left: clamp(1.25rem, 4vw, 2.75rem);
    background: var(--paper);
    border: 1px solid var(--paper-line);
    border-radius: var(--r);
    padding: 1rem 1.25rem;
    box-shadow: var(--shadow);
  }
  .nav.open .nav-links a { display: flex; align-items: center; width: 100%; min-height: 48px; padding: .9rem 0; border-bottom: 1px solid var(--paper-line); }
  .nav.open .nav-links a:last-child { border-bottom: 0; }
  .nav.open .btn2 { display: inline-flex; margin-top: .6rem; }
  .site-header.is-stuck, .site-header { position: fixed; }
}

@media (max-width: 640px) {
  .work-grid { grid-template-columns: 1fr; }
  .impact-grid { grid-template-columns: 1fr; }
  .photography-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 70px; }
  .lightbox-close { width: 40px; height: 40px; }
  .lightbox-nav { width: 38px; height: 38px; }
  .lightbox-img { max-width: 74vw; }
  .lightbox-prev { left: .5rem; }
  .lightbox-next { right: .5rem; }
  .hero-chip, .about-chip { left: 0; }
  .hero-cta { width: 100%; }
  .hero-cta .btn2 { flex: 1 1 auto; justify-content: center; }
}
