/* ============================================================
   KARNIK KHANWILKAR — portfolio
   Vanilla CSS. Warm near-black, acid accent, hairline system.
   ============================================================ */

/* ---------- Fonts (self-hosted) ---------- */
@font-face {
  font-family: 'Clash Display';
  src: url('../fonts/ClashDisplay-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Clash Display';
  src: url('../fonts/ClashDisplay-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'General Sans';
  src: url('../fonts/GeneralSans-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'General Sans';
  src: url('../fonts/GeneralSans-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'JetBrains Mono';
  src: url('../fonts/JetBrainsMono-400.woff2') format('woff2');
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Instrument Serif';
  src: url('../fonts/InstrumentSerif-400i.woff2') format('woff2');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

/* metric-matched fallbacks — the swap must not move the layout */
@font-face {
  font-family: 'Clash Display Fallback';
  src: local('Arial');
  size-adjust: 97%;
  ascent-override: 92%;
  descent-override: 24%;
}
@font-face {
  font-family: 'General Sans Fallback';
  src: local('Arial');
  size-adjust: 99%;
}
@font-face {
  font-family: 'Instrument Serif Fallback';
  src: local('Georgia');
  font-style: italic;
  size-adjust: 88%;
  ascent-override: 96%;
  descent-override: 26%;
}

/* ---------- Tokens ---------- */
:root {
  --bg: #0E0E0C;
  --bg-lift: #131311;
  --surface: #161614;
  --ink: #EDEBE6;
  --muted: #8A887F;
  /* for muted-role text that sits over the hero field, where the
     grey would otherwise lose its contrast against bright pixels */
  --ink-soft: #C9C7C0;
  --hairline: rgba(237, 235, 230, 0.10);
  --accent: #C8F04B;
  --accent-ink: #10120A;

  --font-display: 'Clash Display', 'Clash Display Fallback', 'Arial Narrow', sans-serif;
  --font-body: 'General Sans', 'General Sans Fallback', 'Helvetica Neue', sans-serif;
  --font-mono: 'JetBrains Mono', 'Cascadia Code', 'Consolas', monospace;
  --font-serif: 'Instrument Serif', 'Instrument Serif Fallback', 'Georgia', serif;

  --pad: clamp(1.25rem, 4vw, 4rem);
  --head-h: 4.25rem;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--head-h) + 1rem);
}
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  min-height: 100vh;
}

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

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
ul, ol { list-style: none; }
img, svg, canvas { display: block; max-width: 100%; }

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

/* ---------- Utilities ---------- */
.mono {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  font-weight: 400;
  letter-spacing: 0.01em;
}
.serif {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: 0;
}
.accent { color: var(--accent); }

.skip-link {
  position: fixed;
  top: -100%;
  left: var(--pad);
  z-index: 500;
  padding: 0.6em 1em;
  background: var(--accent);
  color: var(--accent-ink);
  transition: top 0.2s ease;
}
.skip-link:focus-visible { top: 0.75rem; }

/* ---------- Grain ----------
   A pre-baked noise tile. A live feTurbulence filter over the full
   viewport is a repaint hazard, especially in Safari. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 210;
  pointer-events: none;
  opacity: 0.045;
  background-image: url('../assets/noise.png');
  background-repeat: repeat;
  background-size: 128px 128px;
  contain: strict;
}

/* ---------- Preloader ---------- */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 400;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
}
.preloader__line {
  color: var(--ink);
  font-size: clamp(0.9rem, 1.6vw, 1.1rem);
  letter-spacing: 0.06em;
  text-transform: lowercase;
}
.preloader__line::before { content: '> '; color: var(--accent); }
.preloader__count {
  position: absolute;
  right: var(--pad);
  bottom: calc(var(--pad) * 0.75);
  color: var(--muted);
  font-size: 0.8125rem;
}
body.is-loaded .preloader { display: none; }

/* ---------- Cursor: TargetCursor ----------
   Vanilla port of the React Bits <TargetCursor />. Only built once JS has
   confirmed a fine pointer AND a desktop layout, so it can never sit
   stranded in a corner of a phone.

   Two deliberate departures from the component's shipped CSS:

   1. No `mix-blend-mode: difference`. The original is white and inverts
      against whatever sits behind it. Over this site's animated dither
      field that means it re-inverts against every wave pixel and shimmers,
      and a full-viewport blend is the same repaint hazard the grain is
      pre-baked to avoid. Acid accent on the dark palette instead.

   2. No transform on the corners. The original positions them with
      translate(-150%,-150%), which GSAP parses as xPercent — so its x:-18
      on leave stacked on top and the ring settled wider than it started.
      GSAP owns x/y outright here; REST in main.js is the single source. */
.tcursor { display: none; }
body.has-cursor .tcursor { display: block; }

.tcursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  pointer-events: none;
  z-index: 320;
  transition: opacity 0.2s ease;
}
/* pointer gone from the window, or parked over a text field */
.tcursor.is-out,
.tcursor.is-text { opacity: 0; }

.tcursor__dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 4px;
  height: 4px;
  margin: -2px 0 0 -2px;   /* integer offsets — no translate(-50%) to blur it */
  background: var(--accent);
  border-radius: 50%;
  will-change: transform;
  /* the dot sits over a field of mid-tone dither pixels where bare accent
     has nothing to read against; a 1px dark halo guarantees contrast on any
     background without making the dot bigger */
  box-shadow: 0 0 0 1px rgba(14, 14, 12, 0.9);
}

.tcursor__corner {
  position: absolute;
  left: 0;
  top: 0;
  width: 12px;    /* CORNER in main.js — keep in step */
  height: 12px;
  border: 2px solid var(--accent);   /* BORDER in main.js — keep in step */
  will-change: transform;
}
.tcursor__corner--tl { border-right: 0; border-bottom: 0; }
.tcursor__corner--tr { border-left: 0;  border-bottom: 0; }
.tcursor__corner--br { border-left: 0;  border-top: 0; }
.tcursor__corner--bl { border-right: 0; border-top: 0; }

body.has-cursor,
body.has-cursor a,
body.has-cursor button { cursor: none; }

/* `cursor` inherits, so `cursor: none` on body reaches the form fields too.
   Give them the caret back, or you get a text field you cannot see where
   you are typing in. */
body.has-cursor input,
body.has-cursor textarea { cursor: text; }
/* ---------- Header ---------- */
.site-head {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: var(--head-h);
  padding: 0 var(--pad);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  transition: transform 0.45s cubic-bezier(0.33, 1, 0.68, 1), background-color 0.3s ease;
}
.site-head.is-scrolled {
  background: rgba(14, 14, 12, 0.92);
  border-bottom: 1px solid var(--hairline);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-head.is-scrolled {
    background: rgba(14, 14, 12, 0.72);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
  }
}
/* keeps the nav legible over the hero field before any scroll happens */
.site-head::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(14, 14, 12, 0.9) 0%, rgba(14, 14, 12, 0) 100%);
  transition: opacity 0.3s ease;
}
.site-head.is-scrolled::before { opacity: 0; }

.site-head.is-hidden { transform: translateY(-100%); }
.site-head__mark { font-weight: 500; letter-spacing: 0.04em; }
.site-head__nav { display: flex; gap: clamp(1rem, 2.5vw, 2.25rem); }
.site-head__nav a, .site-head__mail { color: var(--ink-soft); }

/* hover-flip: a duplicate label slides up.
   `content: attr(...) / ""` keeps the duplicate out of the accessible name.

   The clipping window MUST be the <a>, not the <span> that moves. overflow
   clips an element's descendants, never the element itself, and the clip
   region travels with the element when it is transformed — so putting both
   roles on the span meant it carried its own window upward: the label was
   never clipped and ::after never came into view. */
.hover-flip {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}
.hover-flip span {
  position: relative;
  display: inline-block;
  vertical-align: top;
}
.hover-flip span > * { display: inline-block; }
/* the duplicate itself is defined with the hover rules — see the note there */

/* ---------- Layout primitives ---------- */
main {
  position: relative;
  z-index: 1;
  background: var(--bg);
  transition: background-color 0.6s ease;
}
main.is-lifted { background-color: var(--bg-lift); }

.section {
  padding: clamp(6rem, 12vw, 10rem) var(--pad) 0;
  position: relative;
}
.section__label {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  font-weight: 400;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: clamp(2.5rem, 6vw, 4.5rem);
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  min-height: 100svh;
  padding: var(--head-h) var(--pad) 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
}
.hero__field {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  opacity: 0.32;
  /* rendered at half resolution — keep the upscale crisp, not blurry */
  image-rendering: pixelated;
}

/* scrim between the field and the type — the wave stays visible at the
   right and top, the headline sits on near-solid background */
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(100deg,
      rgba(14, 14, 12, 0.94) 0%,
      rgba(14, 14, 12, 0.82) 38%,
      rgba(14, 14, 12, 0.45) 72%,
      rgba(14, 14, 12, 0.2) 100%),
    linear-gradient(to top,
      rgba(14, 14, 12, 0.85) 0%,
      rgba(14, 14, 12, 0) 28%);
}
.hero__name { position: relative; z-index: 2; }
.hero__h1 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(3.2rem, 11.5vw, 11.5rem);
  line-height: 0.92;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  display: flex;
  flex-direction: column;
}
.hero__line { display: block; }
.hero__line--indent { margin-left: clamp(2rem, 11vw, 11rem); }

.hero__sub {
  position: relative;
  z-index: 2;
  max-width: 34ch;
  margin-top: clamp(1.5rem, 3vw, 2.5rem);
  margin-left: clamp(0.25rem, 0.8vw, 0.8rem);
  font-size: clamp(1.05rem, 1.7vw, 1.45rem);
  line-height: 1.45;
  color: var(--ink);
  text-wrap: balance;
}
.hero__sub .serif { font-size: 1.12em; }

.hero__meta {
  position: absolute;
  bottom: calc(var(--pad) * 0.85);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  color: var(--ink-soft);
}
.hero__meta--left { left: var(--pad); }
.hero__meta--right { right: var(--pad); }

.pulse-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  position: relative;
}
.pulse-dot::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1px solid var(--accent);
  animation: ping 2.2s cubic-bezier(0, 0, 0.2, 1) infinite;
}
@keyframes ping {
  0% { transform: scale(0.5); opacity: 0.9; }
  80%, 100% { transform: scale(1.8); opacity: 0; }
}

.hero__controls {
  position: absolute;
  top: calc(var(--head-h) + 1.5rem);
  right: var(--pad);
  z-index: 2;
  display: flex;
  gap: 1rem;
}
.hero__controls button {
  color: var(--ink-soft);
  transition: color 0.25s ease;
}

.hero::before {
  content: '';
  position: absolute;
  width: 60vw;
  height: 60vw;
  top: -20vw;
  right: -18vw;
  z-index: 0;
  background: radial-gradient(circle, rgba(200, 240, 75, 0.07) 0%, transparent 62%);
  pointer-events: none;
}

/* ---------- About ---------- */
.about__text {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.6rem, 3.4vw, 3rem);
  line-height: 1.28;
  letter-spacing: -0.01em;
  max-width: 26em;
}
.about__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2.5rem;
  margin-top: clamp(2.5rem, 5vw, 4rem);
  color: var(--muted);
}

/* ---------- Work / projects ---------- */
.project {
  padding: clamp(3rem, 6vw, 5rem) 0;
  border-bottom: 1px solid var(--hairline);
}
.project:first-of-type { padding-top: 0; }
.project__head { margin-bottom: clamp(2rem, 4vw, 3rem); }
.project__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(2.4rem, 6.5vw, 6rem);
  line-height: 1;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  display: flex;
  align-items: baseline;
  gap: 1.25rem;
}
.project__index {
  font-size: 0.8125rem;
  color: var(--accent);
  letter-spacing: 0.08em;
  transform: translateY(-0.5em);
}
.project__oneliner {
  margin-top: 0.9rem;
  font-size: clamp(1.15rem, 2vw, 1.6rem);
  color: var(--muted);
}
.project__oneliner .serif { color: var(--ink); font-size: 1.1em; }

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

.project__brief dt {
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.6875rem;
  margin-top: 1.75rem;
}
.project__brief dt:first-child { margin-top: 0; }
.project__brief dd {
  margin-top: 0.5rem;
  color: var(--ink);
  max-width: 52ch;
}
.project__limits {
  margin-top: 2rem;
  padding: 0.9rem 1.1rem;
  border: 1px dashed var(--hairline);
  color: var(--muted);
  max-width: 52ch;
}
.project__limits .accent { text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.6875rem; }

/* Project stills. aspect-ratio + width/height on the <img> reserve the box
   before the file arrives, so a lazy image can never shift the layout under
   the reader (CLS). object-fit: cover means a slightly-off export still
   fills the frame instead of letterboxing. */
.project__shot[hidden] { display: none; }
.project__shot {
  margin: 0 0 1.75rem;
  border: 1px solid var(--hairline);
  background: var(--surface);
  overflow: hidden;   /* clips the hover scale to the frame */
}
/* Casual-copy guard. `pointer-events: none` is the load-bearing line: with
   the <img> out of the hit-testing path, a right-click lands on the <figure>
   instead, so the browser never offers "Open image in new tab" or "Save
   image as" — those entries only appear when the click target IS an image.
   The drag and callout rules close the drag-to-desktop and iOS long-press
   routes. Hover still works because it is bound to the figure, not the img.

   Be clear-eyed about what this is: a deterrent against casual saving, not
   protection. The file URL is in the page source and the network panel, and
   anything the browser renders can be captured. Real control would mean
   signed expiring URLs plus visible watermarks — overkill for work whose
   whole purpose is to be looked at. */
.project__shot img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  /* the page is a warm near-black; images shot brighter than that sit on
     top of it rather than in it, so they are pulled down to match */
  filter: saturate(0.85) contrast(1.05) brightness(0.92);
  transition: transform 0.7s cubic-bezier(0.33, 1, 0.68, 1),
              filter 0.5s ease;
}
.project__shot figcaption {
  padding: 0.7rem 0.9rem;
  border-top: 1px solid var(--hairline);
  color: var(--muted);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
}

.project__meta {
  margin-top: 1.75rem;
  border-top: 1px solid var(--hairline);
}
.project__meta li {
  display: flex;
  gap: 1.5rem;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--ink);
}
.project__meta li span {
  flex: 0 0 4.5rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.6875rem;
  padding-top: 0.15em;
}

.project__links {
  display: flex;
  gap: 1.5rem;
  margin-top: 1.75rem;
}
.btn-line {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.8em 1.3em;
  border: 1px solid var(--hairline);
  color: var(--ink);
  letter-spacing: 0.04em;
  transition: border-color 0.3s ease, background-color 0.3s ease, color 0.3s ease;
}
.btn-line__arrow { transition: transform 0.3s ease; display: inline-block; }

/* ---------- Archive ---------- */
.archive { padding: clamp(3rem, 6vw, 5rem) 0 clamp(4rem, 8vw, 7rem); }
.archive__title {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  margin-bottom: 1.5rem;
}
.archive__row a {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr) minmax(0, 2fr) auto;
  gap: 1.5rem;
  align-items: baseline;
  padding: 1.1rem 0.5rem;
  border-bottom: 1px solid var(--hairline);
  transition: background-color 0.25s ease, padding-left 0.3s ease;
}
.archive__row:first-child a { border-top: 1px solid var(--hairline); }
.archive__name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.25rem;
  letter-spacing: 0.01em;
}
.archive__desc { color: var(--muted); font-size: 0.9375rem; }
.archive__stack, .archive__year { color: var(--muted); font-size: 0.75rem; }

/* ---------- Capabilities ---------- */
.caps { padding-top: 0; }
.marquee {
  position: relative;
  overflow: hidden;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  padding: 1.4rem 0;
  margin: 0 calc(var(--pad) * -1) clamp(5rem, 10vw, 8rem);
  white-space: nowrap;
}
.marquee__track { display: inline-flex; will-change: transform; }
.marquee__inner {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.6rem, 3.4vw, 3rem);
  letter-spacing: 0.01em;
  color: var(--muted);
  text-transform: uppercase;
  padding-right: 0.5em;
}
.marquee__toggle {
  position: absolute;
  right: var(--pad);
  bottom: 0.35rem;
  z-index: 2;
  color: var(--muted);
  background: var(--bg);
  padding: 0.15rem 0.5rem;
  transition: color 0.25s ease;
}

.caps__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 6rem);
  max-width: 62rem;
}
.caps__head {
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  margin-bottom: 1.25rem;
}
.caps__list li {
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--ink);
  font-size: 1rem;
}

/* ---------- Experience ---------- */
.xp__list { border-top: 1px solid var(--hairline); }
.xp__row {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 7fr) minmax(0, 2fr);
  gap: 1.5rem;
  padding: clamp(1.5rem, 3vw, 2.25rem) 0;
  border-bottom: 1px solid var(--hairline);
}
.xp__when, .xp__where { color: var(--muted); padding-top: 0.35em; }
.xp__where { text-align: right; }
.xp__what h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.25rem, 2.2vw, 1.75rem);
  letter-spacing: 0;
  margin-bottom: 0.4rem;
}
.xp__what p { color: var(--muted); max-width: 52ch; }

.signals {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 3rem;
  margin-top: clamp(3rem, 6vw, 5rem);
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--muted);
}
.signals a { color: var(--muted); }

/* ---------- Contact (fixed footer reveal) ---------- */
.footer-spacer {
  height: 100svh;
  pointer-events: none;
  background: transparent;
}
.contact {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 0;
  height: 100svh;
  background: #0A0A09;
  display: flex;
  align-items: center;
  overflow: hidden;
}
.contact::before {
  content: '';
  position: absolute;
  width: 70vw;
  height: 70vw;
  bottom: -35vw;
  left: -20vw;
  background: radial-gradient(circle, rgba(200, 240, 75, 0.06) 0%, transparent 60%);
  pointer-events: none;
}
/* Two columns: the statement on the left, the form on the right. Laid out
   with grid-template-areas so the credit line can span the full width
   underneath both without wrapping the other blocks in extra divs. */
.contact__inner {
  position: relative;
  width: 100%;
  padding: var(--head-h) var(--pad) 0;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(19rem, 0.95fr);
  grid-template-areas:
    "kicker form"
    "title  form"
    "grid   form"
    "credit credit";
  column-gap: clamp(2rem, 6vw, 6rem);
  align-content: center;
}
.contact__kicker { grid-area: kicker; }
.contact__title  { grid-area: title; }
.contact__form   { grid-area: form; align-self: center; }
.contact__grid   { grid-area: grid; }
.contact__credit { grid-area: credit; }
.contact__kicker {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
}
.contact__title {
  font-family: var(--font-display);
  font-weight: 600;
  /* was 10vw/10rem when it had the full panel width; the form now takes
     the right half, so the headline is sized to the column it actually has */
  font-size: clamp(2.5rem, 6.5vw, 7rem);
  line-height: 0.95;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  display: flex;
  flex-direction: column;
}
.contact__title .serif { text-transform: none; font-size: 1.02em; }
.contact__line--indent { margin-left: clamp(1rem, 4vw, 4rem); }

/* ---------- Contact form ----------
   Replaces the circular mailto: CTA. A mailto: link hands the visitor to a
   mail client that may not be configured — on a shared or work machine it
   often does nothing at all. A form keeps them here, and still degrades to
   a plain native POST if JavaScript never runs. */
.contact__form {
  border: 1px solid var(--hairline);
  background: rgba(22, 22, 20, 0.55);
  padding: clamp(1.25rem, 2.2vw, 1.9rem);
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.contact__form-label {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.6875rem;
}

.field { display: flex; flex-direction: column; gap: 0.35rem; }
.field label {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 0.625rem;
}
.field input,
.field textarea {
  font-family: var(--font-body);
  font-size: 0.9375rem;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  border-radius: 0;          /* iOS rounds inputs unless told otherwise */
  padding: 0.45rem 0;
  width: 100%;
  transition: border-color 0.25s ease;
}
.field textarea { resize: vertical; min-height: 4.5rem; line-height: 1.5; }
/* The accent underline is an affordance, NOT the focus indicator — the
   global :focus-visible outline is deliberately left intact so keyboard
   focus stays obvious. Never trade that away for a tidier field. */
.field input:focus,
.field textarea:focus { border-bottom-color: var(--accent); }

.field--trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.contact__send {
  align-self: flex-start;
  margin-top: 0.35rem;
  padding: 0.8rem 1.7rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  will-change: transform;
  transition: background-color 0.3s ease, opacity 0.3s ease;
}
.contact__send:disabled { opacity: 0.45; }
.contact__send-arrow { display: inline-block; margin-left: 0.7rem; }

.contact__status {
  min-height: 1.3em;
  color: var(--muted);
  font-size: 0.6875rem;
  line-height: 1.5;
}
.contact__status.is-good { color: var(--accent); }
.contact__status.is-bad  { color: #FF8B6B; }

.contact__grid {
  display: grid;
  /* two-up now that this sits in a half-width column, not the full panel */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2rem;
  margin-top: clamp(3rem, 7vw, 6rem);
  padding-top: 2rem;
  border-top: 1px solid var(--hairline);
}
.contact__block { display: flex; flex-direction: column; gap: 0.6rem; }
.contact__block-label {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.6875rem;
}
.contact__email {
  font-size: 1.0625rem;
  user-select: all;
  overflow-wrap: anywhere;
}
.contact__social { display: flex; gap: 1.5rem; }
.contact__social a { color: var(--ink); }
.contact__credit {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-bottom: 1.5rem;
  color: var(--muted);
  font-size: 0.6875rem;
}

/* ---------- Hover states (pointer devices only) ---------- */
@media (hover: hover) and (pointer: fine) {
  .site-head__nav a:hover, .site-head__mail:hover { color: var(--ink); }

  /* The duplicate label exists ONLY where the flip can actually run.
     `overflow` clips at the PADDING box, and the (pointer: coarse) rule
     below adds 0.55rem of vertical padding to these same links for tap
     targets — so on a phone the clip window sat 0.55rem taller than the
     label and the duplicate, parked at top:100%, showed through in that
     strip. It could never animate there either, because this whole block
     is hover-only. Defining it here fixes both: no hover, no duplicate. */
  .hover-flip span::after {
    content: attr(data-text) / "";
    position: absolute;
    left: 0;
    top: 100%;
    color: var(--accent);
  }
  .hover-flip span,
  .hover-flip span::after {
    transition: transform 0.4s cubic-bezier(0.33, 1, 0.68, 1);
  }
  .hover-flip:hover span { transform: translateY(-100%); }
  .hero__controls button:hover { color: var(--accent); }
  .marquee__toggle:hover { color: var(--accent); }
  .btn-line:hover {
    border-color: var(--accent);
    background: var(--accent);
    color: var(--accent-ink);
  }
  .btn-line:hover .btn-line__arrow { transform: translate(2px, -2px); }
  /* the still comes up to full colour as you approach it — one move,
     slow enough to read as the image waking rather than a UI transition */
  .project__shot:hover img {
    transform: scale(1.035);
    filter: saturate(1) contrast(1.05) brightness(1);
  }
  .archive__row a:hover { background: var(--surface); padding-left: 1rem; }
  .archive__row a:hover .archive__name { color: var(--accent); }
  .signals a:hover { color: var(--ink); }
  .contact__send:hover:not(:disabled) { background: #d9fa66; }
}

/* ---------- Responsive ---------- */

/* The fixed-panel reveal needs room. Short viewports (landscape phones,
   200% zoom) get the footer back in normal flow so nothing is cut off. */
@media (max-width: 900px), (max-height: 700px) {
  .contact {
    position: static;
    height: auto;
    min-height: 100svh;
  }
  .contact__inner { padding-bottom: 1rem; }
  .footer-spacer { display: none; }
}

@media (max-width: 900px) {
  .project__body { grid-template-columns: 1fr; }
  .xp__row { grid-template-columns: minmax(0, 3fr) minmax(0, 7fr); }
  .xp__where { display: none; }

  /* one column — the form drops below the statement it answers */
  .contact__inner {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "kicker"
      "title"
      "form"
      "grid"
      "credit";
    row-gap: clamp(2rem, 5vw, 3rem);
  }
  .contact__title { font-size: clamp(2.5rem, 10vw, 6rem); }
}

@media (max-width: 720px) {
  .site-head__mail { display: none; }
  .archive__row a { grid-template-columns: minmax(0, 1fr) auto; }
  .archive__desc, .archive__stack { display: none; }
  .caps__grid { grid-template-columns: 1fr; }
  .hero__meta--right { display: none; }
  .hero__sub { max-width: 100%; }
}

@media (max-width: 540px) {
  .xp__row { grid-template-columns: 1fr; gap: 0.5rem; }
  .contact__grid { grid-template-columns: 1fr; }
  .hero__controls { top: auto; bottom: calc(var(--pad) * 0.85 + 3.5rem); right: var(--pad); }

  /* Display type must keep scaling below phone width. The clamp()
     minimums above are sized for desktop and would otherwise hold the
     headline wider than the screen — "KHANWILKAR" alone overruns a
     320px viewport by ~100px. Drive these from vw instead. */
  .hero__h1 { font-size: 12vw; }
  .hero__line--indent { margin-left: 3vw; }

  .project__title { font-size: 10vw; gap: 0.75rem; }
  .project__index { font-size: 0.6875rem; }

  .contact__title { font-size: 11vw; }
  .contact__line--indent { margin-left: 6vw; }

  /* the wordmark repeats the name that is already in the headline —
     drop it so four nav links get the full width */
  .site-head__mark { display: none; }
  .site-head__nav {
    flex: 1;
    justify-content: space-between;
    gap: 0.5rem;
  }
  .site-head__nav a { font-size: 0.75rem; }
}

/* Touch targets: small mono labels are the site's signature, but a
   13px link is not a 24px tap target (WCAG 2.5.8). Pad them on touch
   devices only, where it costs nothing visually. */
@media (pointer: coarse) {
  .site-head__nav a,
  .site-head__mail,
  .contact__social a,
  .signals a { padding: 0.55rem 0; }
  .hero__controls button { padding: 0.5rem 0.3rem; }
  .marquee__toggle { padding: 0.5rem 0.6rem; }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* the field stays — it renders a single static frame, no motion */
  .preloader { display: none; }
}

/* ---------- Print ---------- */
@media print {
  :root {
    --bg: #fff;
    --bg-lift: #fff;
    --surface: #fff;
    --ink: #111;
    --muted: #444;
    --hairline: rgba(0, 0, 0, 0.2);
    --accent: #4a6b00;
  }
  body { background: #fff; color: #111; }
  .grain, .tcursor, .preloader, .footer-spacer,
  .hero__field, .hero__controls,
  .marquee, .site-head, .skip-link { display: none !important; }
  main { background: #fff; }
  .hero { min-height: auto; padding-top: 0; }
  .contact {
    position: static;
    height: auto;
    background: #fff;
    page-break-before: always;
  }
  /* a form is not printable — the reader gets the email address instead */
  .contact__form { display: none; }
  a[href^="http"]::after { content: ' (' attr(href) ')'; font-size: 0.75em; color: #444; }
}
